Skip to content

Commit c3e4ccb

Browse files
committed
Prevent JPEG errors from crashing process
1 parent a815257 commit c3e4ccb

File tree

3 files changed

+90
-5
lines changed

3 files changed

+90
-5
lines changed

src/Image.cc

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ typedef struct {
1919
} gif_data_t;
2020
#endif
2121

22+
#ifdef HAVE_JPEG
23+
#include <setjmp.h>
24+
25+
struct canvas_jpeg_error_mgr: jpeg_error_mgr {
26+
jmp_buf setjmp_buffer;
27+
};
28+
#endif
29+
2230
/*
2331
* Read closure used by loadFromBuffer.
2432
*/
@@ -752,6 +760,17 @@ Image::decodeJPEGIntoSurface(jpeg_decompress_struct *args) {
752760
return CAIRO_STATUS_SUCCESS;
753761
}
754762

763+
/*
764+
* Callback to recover from jpeg errors
765+
*/
766+
767+
METHODDEF(void) canvas_jpeg_error_exit (j_common_ptr cinfo) {
768+
canvas_jpeg_error_mgr *cjerr = static_cast<canvas_jpeg_error_mgr*>(cinfo->err);
769+
770+
// Return control to the setjmp point
771+
longjmp(cjerr->setjmp_buffer, 1);
772+
}
773+
755774
#if CAIRO_VERSION_MINOR >= 10
756775

757776
/*
@@ -764,8 +783,19 @@ Image::decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len) {
764783
// TODO: remove this duplicate logic
765784
// JPEG setup
766785
struct jpeg_decompress_struct args;
767-
struct jpeg_error_mgr err;
786+
struct canvas_jpeg_error_mgr err;
787+
768788
args.err = jpeg_std_error(&err);
789+
args.err->error_exit = canvas_jpeg_error_exit;
790+
791+
// Establish the setjmp return context for canvas_jpeg_error_exit to use
792+
if (setjmp(err.setjmp_buffer)) {
793+
// If we get here, the JPEG code has signaled an error.
794+
// We need to clean up the JPEG object, close the input file, and return.
795+
jpeg_destroy_decompress(&args);
796+
return CAIRO_STATUS_READ_ERROR;
797+
}
798+
769799
jpeg_create_decompress(&args);
770800

771801
jpeg_mem_src(&args, buf, len);
@@ -858,8 +888,19 @@ Image::loadJPEGFromBuffer(uint8_t *buf, unsigned len) {
858888
// TODO: remove this duplicate logic
859889
// JPEG setup
860890
struct jpeg_decompress_struct args;
861-
struct jpeg_error_mgr err;
891+
struct canvas_jpeg_error_mgr err;
892+
862893
args.err = jpeg_std_error(&err);
894+
args.err->error_exit = canvas_jpeg_error_exit;
895+
896+
// Establish the setjmp return context for canvas_jpeg_error_exit to use
897+
if (setjmp(err.setjmp_buffer)) {
898+
// If we get here, the JPEG code has signaled an error.
899+
// We need to clean up the JPEG object, close the input file, and return.
900+
jpeg_destroy_decompress(&args);
901+
return CAIRO_STATUS_READ_ERROR;
902+
}
903+
863904
jpeg_create_decompress(&args);
864905

865906
jpeg_mem_src(&args, buf, len);
@@ -883,8 +924,19 @@ Image::loadJPEG(FILE *stream) {
883924
if (data_mode == DATA_IMAGE) { // Can lazily read in the JPEG.
884925
// JPEG setup
885926
struct jpeg_decompress_struct args;
886-
struct jpeg_error_mgr err;
927+
struct canvas_jpeg_error_mgr err;
928+
887929
args.err = jpeg_std_error(&err);
930+
args.err->error_exit = canvas_jpeg_error_exit;
931+
932+
// Establish the setjmp return context for canvas_jpeg_error_exit to use
933+
if (setjmp(err.setjmp_buffer)) {
934+
// If we get here, the JPEG code has signaled an error.
935+
// We need to clean up the JPEG object, close the input file, and return.
936+
jpeg_destroy_decompress(&args);
937+
return CAIRO_STATUS_READ_ERROR;
938+
}
939+
888940
jpeg_create_decompress(&args);
889941

890942
jpeg_stdio_src(&args, stream);

test/fixtures/chrome.jpg

5.62 KB
Loading

test/image.test.js

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55

66
var Canvas = require('../')
77
, Image = Canvas.Image
8-
, assert = require('assert');
8+
, assert = require('assert')
9+
, fs = require('fs');
910

1011
var png_checkers = __dirname + '/fixtures/checkers.png';
1112
var png_clock = __dirname + '/fixtures/clock.png';
13+
var jpg_chrome = __dirname + '/fixtures/chrome.jpg'
1214

1315
describe('Image', function () {
1416
it('should require new', function () {
@@ -206,4 +208,35 @@ describe('Image', function () {
206208
assert.equal(img.src, png_clock + 's3');
207209
assert.equal(onerrorCalled, 0);
208210
});
209-
});
211+
212+
it('does not crash on invalid images', function () {
213+
function tryImage (src) {
214+
var img = new Image()
215+
img.src = src
216+
// if we came this far we didn't crash!
217+
}
218+
219+
function withIncreasedByte (source, index) {
220+
var copy = source.slice(0)
221+
222+
copy[index] += 1
223+
224+
return copy
225+
}
226+
227+
var source = fs.readFileSync(jpg_chrome)
228+
229+
tryImage(withIncreasedByte(source, 0))
230+
tryImage(withIncreasedByte(source, 1))
231+
tryImage(withIncreasedByte(source, 1060))
232+
tryImage(withIncreasedByte(source, 1061))
233+
tryImage(withIncreasedByte(source, 1062))
234+
tryImage(withIncreasedByte(source, 1063))
235+
tryImage(withIncreasedByte(source, 1064))
236+
tryImage(withIncreasedByte(source, 1065))
237+
tryImage(withIncreasedByte(source, 1066))
238+
tryImage(withIncreasedByte(source, 1067))
239+
tryImage(withIncreasedByte(source, 1068))
240+
tryImage(withIncreasedByte(source, 1069))
241+
})
242+
})

0 commit comments

Comments
 (0)