From 2c4c0390564da4acf4186663b6bcd1c4ccbe96d8 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 2 Aug 2022 13:50:09 +0100 Subject: [PATCH] add "unlimited" to jpegload To disable DoS limits for JPEG loading. Adding API on a stable branch is bad, but this fixes a regression, so I think it's necessary, unfortunately. See https://github.com/libvips/libvips/issues/2973 --- .gitignore | 1 + ChangeLog | 1 + libvips/deprecated/im_jpeg2vips.c | 2 +- libvips/foreign/jpeg2vips.c | 37 ++++++++++++++++++++++--------- libvips/foreign/jpegload.c | 15 +++++++++++-- libvips/foreign/pforeign.h | 2 +- libvips/foreign/vips2jpeg.c | 6 ----- test/test_formats.sh | 2 +- 8 files changed, 44 insertions(+), 22 deletions(-) diff --git a/.gitignore b/.gitignore index c25e08b5..0924987b 100644 --- a/.gitignore +++ b/.gitignore @@ -154,6 +154,7 @@ tmp-* # Auto-generated tag files tags +TAGS ### End of VIM ### Distribution diff --git a/ChangeLog b/ChangeLog index 54af4ecc..7ac5dcb7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ - fix im7 feature detection in meson - add a summary table at the end of configure in meson - fix libpng fallback when spng is disabled in meson +- add "unlimited" to jpegload 21/11/21 started 8.13 - configure fails for requested but unmet dependencies [remicollet] diff --git a/libvips/deprecated/im_jpeg2vips.c b/libvips/deprecated/im_jpeg2vips.c index a2cac637..bd26401c 100644 --- a/libvips/deprecated/im_jpeg2vips.c +++ b/libvips/deprecated/im_jpeg2vips.c @@ -117,7 +117,7 @@ jpeg2vips( const char *name, IMAGE *out, gboolean header_only ) if( !(source = vips_source_new_from_file( filename )) ) return( -1 ); if( vips__jpeg_read_source( source, out, - header_only, shrink, fail_on_warn, FALSE ) ) { + header_only, shrink, fail_on_warn, FALSE, FALSE ) ) { VIPS_UNREF( source ); return( -1 ); } diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 0ee9af69..4f996ec2 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -112,6 +112,8 @@ * - set resolution unit from JFIF * 24/7/21 * - add fail_on support + * 2/8/22 + * - add "unlimited" */ /* @@ -169,6 +171,8 @@ /* Stuff we track during a read. */ typedef struct _ReadJpeg { + VipsImage *out; + /* Shrink by this much during load. 1, 2, 4, 8. */ int shrink; @@ -190,6 +194,10 @@ typedef struct _ReadJpeg { */ gboolean autorotate; + /* Remove DoS limits. + */ + gboolean unlimited; + /* cinfo->output_width and height can be larger than we want since * libjpeg rounds up on shrink-on-load. This is the real size we will * output, as opposed to the size we decompress to. @@ -251,8 +259,12 @@ source_fill_input_buffer( j_decompress_ptr cinfo ) src->pub.bytes_in_buffer = bytes_read; } else { - if( src->jpeg->fail_on >= VIPS_FAIL_ON_TRUNCATED ) + if( src->jpeg->fail_on >= VIPS_FAIL_ON_TRUNCATED ) { + /* Knock the output out of cache. + */ + vips_foreign_load_invalidate( src->jpeg->out ); ERREXIT( cinfo, JERR_INPUT_EOF ); + } else WARNMS( cinfo, JWRN_JPEG_EOF ); @@ -317,6 +329,8 @@ readjpeg_open_input( ReadJpeg *jpeg ) static void readjpeg_emit_message( j_common_ptr cinfo, int msg_level ) { + ReadJpeg *jpeg = (ReadJpeg *) cinfo->client_data; + long num_warnings; if( msg_level < 0 ) { @@ -325,11 +339,13 @@ readjpeg_emit_message( j_common_ptr cinfo, int msg_level ) num_warnings = ++cinfo->err->num_warnings; /* Corrupt files may give many warnings, the policy here is to - * show only the first warning and treat many warnings as fatal. + * show only the first warning and treat many warnings as fatal, + * unless unlimited is set. */ if( num_warnings == 1 ) (*cinfo->err->output_message)( cinfo ); - else if( num_warnings >= 100 ) + else if( !jpeg || + (!jpeg->unlimited && num_warnings >= 100) ) cinfo->err->error_exit( cinfo ); } else if( cinfo->err->trace_level >= msg_level ) @@ -381,13 +397,15 @@ readjpeg_minimise_cb( VipsImage *image, ReadJpeg *jpeg ) static ReadJpeg * readjpeg_new( VipsSource *source, VipsImage *out, - int shrink, VipsFailOn fail_on, gboolean autorotate ) + int shrink, VipsFailOn fail_on, gboolean autorotate, + gboolean unlimited ) { ReadJpeg *jpeg; if( !(jpeg = VIPS_NEW( out, ReadJpeg )) ) return( NULL ); + jpeg->out = out; jpeg->source = source; g_object_ref( source ); jpeg->shrink = shrink; @@ -399,11 +417,8 @@ readjpeg_new( VipsSource *source, VipsImage *out, jpeg->eman.fp = NULL; jpeg->y_pos = 0; jpeg->autorotate = autorotate; - - /* This is used by the error handlers to signal invalidate on the - * output image. - */ - jpeg->cinfo.client_data = out; + jpeg->unlimited = unlimited; + jpeg->cinfo.client_data = jpeg; /* jpeg_create_decompress() can fail on some sanity checks. Don't * readjpeg_free() since we don't want to jpeg_destroy_decompress(). @@ -982,12 +997,12 @@ vips__jpeg_read( ReadJpeg *jpeg, VipsImage *out, gboolean header_only ) int vips__jpeg_read_source( VipsSource *source, VipsImage *out, gboolean header_only, int shrink, VipsFailOn fail_on, - gboolean autorotate ) + gboolean autorotate, gboolean unlimited ) { ReadJpeg *jpeg; if( !(jpeg = readjpeg_new( source, out, shrink, fail_on, - autorotate )) ) + autorotate, unlimited )) ) return( -1 ); /* Here for longjmp() from vips__new_error_exit() during diff --git a/libvips/foreign/jpegload.c b/libvips/foreign/jpegload.c index 386f6942..04190587 100644 --- a/libvips/foreign/jpegload.c +++ b/libvips/foreign/jpegload.c @@ -74,6 +74,10 @@ typedef struct _VipsForeignLoadJpeg { */ VipsSource *source; + /* Remove DoS limits. + */ + gboolean unlimited; + /* Shrink by this much during load. */ int shrink; @@ -140,7 +144,7 @@ vips_foreign_load_jpeg_header( VipsForeignLoad *load ) if( vips__jpeg_read_source( jpeg->source, load->out, TRUE, jpeg->shrink, load->fail_on, - jpeg->autorotate ) ) + jpeg->autorotate, jpeg->unlimited ) ) return( -1 ); return( 0 ); @@ -153,7 +157,7 @@ vips_foreign_load_jpeg_load( VipsForeignLoad *load ) if( vips__jpeg_read_source( jpeg->source, load->real, FALSE, jpeg->shrink, load->fail_on, - jpeg->autorotate ) ) + jpeg->autorotate, jpeg->unlimited ) ) return( -1 ); return( 0 ); @@ -199,6 +203,13 @@ vips_foreign_load_jpeg_class_init( VipsForeignLoadJpegClass *class ) G_STRUCT_OFFSET( VipsForeignLoadJpeg, autorotate ), FALSE ); + VIPS_ARG_BOOL( class, "unlimited", 22, + _( "Unlimited" ), + _( "Remove all denial of service limits" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadJpeg, unlimited ), + FALSE ); + } static void diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index e2c18ddb..e8cec087 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -153,7 +153,7 @@ int vips__jpeg_write_target( VipsImage *in, VipsTarget *target, int vips__jpeg_read_source( VipsSource *source, VipsImage *out, gboolean header_only, int shrink, VipsFailOn fail_on, - gboolean autorotate ); + gboolean autorotate, gboolean unlimited ); int vips__isjpeg_source( VipsSource *source ); int vips__png_ispng_source( VipsSource *source ); diff --git a/libvips/foreign/vips2jpeg.c b/libvips/foreign/vips2jpeg.c index 6fe1366f..01078f44 100644 --- a/libvips/foreign/vips2jpeg.c +++ b/libvips/foreign/vips2jpeg.c @@ -173,12 +173,6 @@ vips__new_output_message( j_common_ptr cinfo ) #ifdef DEBUG printf( "vips__new_output_message: \"%s\"\n", buffer ); #endif /*DEBUG*/ - - /* This is run for things like file truncated. Signal invalidate to - * force this op out of cache. - */ - if( cinfo->client_data ) - vips_foreign_load_invalidate( VIPS_IMAGE( cinfo->client_data ) ); } /* New error_exit handler. diff --git a/test/test_formats.sh b/test/test_formats.sh index 0f4c60d6..d3983f7f 100755 --- a/test/test_formats.sh +++ b/test/test_formats.sh @@ -204,7 +204,7 @@ if test_supported matload; then # test blocked and untrusted printf "testing VIPS_BLOCK_UNTRUSTED with matio ... " export VIPS_BLOCK_UNTRUSTED=1 - if vips matload $matlab $tmp/block.png; then + if $vips matload $matlab $tmp/block.png; then echo "failed to block matload" exit 1 fi