Skip to content

Commit

Permalink
Prevent JPEG errors from crashing process
Browse files Browse the repository at this point in the history
  • Loading branch information
LinusU committed Mar 22, 2018
1 parent a815257 commit c3e4ccb
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 5 deletions.
58 changes: 55 additions & 3 deletions src/Image.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ typedef struct {
} gif_data_t;
#endif

#ifdef HAVE_JPEG
#include <setjmp.h>

struct canvas_jpeg_error_mgr: jpeg_error_mgr {
jmp_buf setjmp_buffer;
};
#endif

/*
* Read closure used by loadFromBuffer.
*/
Expand Down Expand Up @@ -752,6 +760,17 @@ Image::decodeJPEGIntoSurface(jpeg_decompress_struct *args) {
return CAIRO_STATUS_SUCCESS;
}

/*
* Callback to recover from jpeg errors
*/

METHODDEF(void) canvas_jpeg_error_exit (j_common_ptr cinfo) {
canvas_jpeg_error_mgr *cjerr = static_cast<canvas_jpeg_error_mgr*>(cinfo->err);

// Return control to the setjmp point
longjmp(cjerr->setjmp_buffer, 1);
}

#if CAIRO_VERSION_MINOR >= 10

/*
Expand All @@ -764,8 +783,19 @@ Image::decodeJPEGBufferIntoMimeSurface(uint8_t *buf, unsigned len) {
// TODO: remove this duplicate logic
// JPEG setup
struct jpeg_decompress_struct args;
struct jpeg_error_mgr err;
struct canvas_jpeg_error_mgr err;

args.err = jpeg_std_error(&err);
args.err->error_exit = canvas_jpeg_error_exit;

// Establish the setjmp return context for canvas_jpeg_error_exit to use
if (setjmp(err.setjmp_buffer)) {
// If we get here, the JPEG code has signaled an error.
// We need to clean up the JPEG object, close the input file, and return.
jpeg_destroy_decompress(&args);
return CAIRO_STATUS_READ_ERROR;
}

jpeg_create_decompress(&args);

jpeg_mem_src(&args, buf, len);
Expand Down Expand Up @@ -858,8 +888,19 @@ Image::loadJPEGFromBuffer(uint8_t *buf, unsigned len) {
// TODO: remove this duplicate logic
// JPEG setup
struct jpeg_decompress_struct args;
struct jpeg_error_mgr err;
struct canvas_jpeg_error_mgr err;

args.err = jpeg_std_error(&err);
args.err->error_exit = canvas_jpeg_error_exit;

// Establish the setjmp return context for canvas_jpeg_error_exit to use
if (setjmp(err.setjmp_buffer)) {
// If we get here, the JPEG code has signaled an error.
// We need to clean up the JPEG object, close the input file, and return.
jpeg_destroy_decompress(&args);
return CAIRO_STATUS_READ_ERROR;
}

jpeg_create_decompress(&args);

jpeg_mem_src(&args, buf, len);
Expand All @@ -883,8 +924,19 @@ Image::loadJPEG(FILE *stream) {
if (data_mode == DATA_IMAGE) { // Can lazily read in the JPEG.
// JPEG setup
struct jpeg_decompress_struct args;
struct jpeg_error_mgr err;
struct canvas_jpeg_error_mgr err;

args.err = jpeg_std_error(&err);
args.err->error_exit = canvas_jpeg_error_exit;

// Establish the setjmp return context for canvas_jpeg_error_exit to use
if (setjmp(err.setjmp_buffer)) {
// If we get here, the JPEG code has signaled an error.
// We need to clean up the JPEG object, close the input file, and return.
jpeg_destroy_decompress(&args);
return CAIRO_STATUS_READ_ERROR;
}

jpeg_create_decompress(&args);

jpeg_stdio_src(&args, stream);
Expand Down
Binary file added test/fixtures/chrome.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
37 changes: 35 additions & 2 deletions test/image.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

var Canvas = require('../')
, Image = Canvas.Image
, assert = require('assert');
, assert = require('assert')
, fs = require('fs');

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

describe('Image', function () {
it('should require new', function () {
Expand Down Expand Up @@ -206,4 +208,35 @@ describe('Image', function () {
assert.equal(img.src, png_clock + 's3');
assert.equal(onerrorCalled, 0);
});
});

it('does not crash on invalid images', function () {
function tryImage (src) {
var img = new Image()
img.src = src
// if we came this far we didn't crash!
}

function withIncreasedByte (source, index) {
var copy = source.slice(0)

copy[index] += 1

return copy
}

var source = fs.readFileSync(jpg_chrome)

tryImage(withIncreasedByte(source, 0))
tryImage(withIncreasedByte(source, 1))
tryImage(withIncreasedByte(source, 1060))
tryImage(withIncreasedByte(source, 1061))
tryImage(withIncreasedByte(source, 1062))
tryImage(withIncreasedByte(source, 1063))
tryImage(withIncreasedByte(source, 1064))
tryImage(withIncreasedByte(source, 1065))
tryImage(withIncreasedByte(source, 1066))
tryImage(withIncreasedByte(source, 1067))
tryImage(withIncreasedByte(source, 1068))
tryImage(withIncreasedByte(source, 1069))
})
})

0 comments on commit c3e4ccb

Please sign in to comment.