djpeg/jpeg_crop_scanline(): Disallow crop vals < 0
Because the crop spec was parsed using unsigned 32-bit integers,
negative numbers were interpreted as values ~= UINT_MAX (4,294,967,295).
This had the following ramifications:
- If the cropping region width was negative and the adjusted width + the
adjusted left boundary was greater than 0, then the 32-bit unsigned
integer bounds checks in djpeg and jpeg_crop_scanline() overflowed and
failed to detect the out-of-bounds width, jpeg_crop_scanline() set
cinfo->output_width to a value ~= UINT_MAX, and a buffer overrun and
subsequent segfault occurred in the upsampling or color conversion
routine. The segfault occurred in the body of
jpeg_skip_scanlines() --> read_and_discard_scanlines() if the cropping
region upper boundary was greater than 0 and the JPEG image used
chrominance subsampling and in the body of jpeg_read_scanlines()
otherwise.
- If the cropping region width was negative and the adjusted width + the
adjusted left boundary was 0, then a zero-width output image was
generated.
- If the cropping region left boundary was negative, then an output
image with bogus data was generated.
This commit modifies djpeg and jpeg_crop_scanline() so that the
aforementioned bounds checks use 64-bit unsigned integers, thus guarding
against overflow. It similarly modifies jpeg_skip_scanlines(). In the
case of jpeg_skip_scanlines(), the issue was not reproducible with
djpeg, but passing a negative number of lines to jpeg_skip_scanlines()
caused a similar overflow if the number of lines +
cinfo->output_scanline was greater than 0. That caused
jpeg_skip_scanlines() to read past the end of the JPEG image, throw a
warning ("Corrupt JPEG data: premature end of data segment"), and fail
to return unless warnings were treated as fatal. Also, djpeg now parses
the crop spec using signed integers and checks for negative values.
This commit is contained in:
23
djpeg.c
23
djpeg.c
@@ -401,22 +401,31 @@ parse_switches(j_decompress_ptr cinfo, int argc, char **argv,
|
||||
usage();
|
||||
|
||||
} else if (keymatch(arg, "skip", 2)) {
|
||||
int temp_start = -1, temp_end = -1;
|
||||
if (++argn >= argc)
|
||||
usage();
|
||||
if (sscanf(argv[argn], "%u,%u", &skip_start, &skip_end) != 2 ||
|
||||
skip_start > skip_end)
|
||||
if (sscanf(argv[argn], "%d,%d", &temp_start, &temp_end) != 2 ||
|
||||
temp_start < 0 || temp_end < 0 || temp_start > temp_end)
|
||||
usage();
|
||||
skip = TRUE;
|
||||
skip_start = temp_start;
|
||||
skip_end = temp_end;
|
||||
|
||||
} else if (keymatch(arg, "crop", 2)) {
|
||||
int temp_width = -1, temp_height = -1, temp_x = -1, temp_y = -1;
|
||||
char c;
|
||||
if (++argn >= argc)
|
||||
usage();
|
||||
if (sscanf(argv[argn], "%u%c%u+%u+%u", &crop_width, &c, &crop_height,
|
||||
&crop_x, &crop_y) != 5 ||
|
||||
(c != 'X' && c != 'x') || crop_width < 1 || crop_height < 1)
|
||||
if (sscanf(argv[argn], "%d%c%d+%d+%d", &temp_width, &c, &temp_height,
|
||||
&temp_x, &temp_y) != 5 ||
|
||||
(c != 'X' && c != 'x') || temp_width < 1 || temp_height < 1 ||
|
||||
temp_x < 0 || temp_y < 0)
|
||||
usage();
|
||||
crop = TRUE;
|
||||
crop_width = temp_width;
|
||||
crop_height = temp_height;
|
||||
crop_x = temp_x;
|
||||
crop_y = temp_y;
|
||||
|
||||
} else if (keymatch(arg, "strict", 2)) {
|
||||
strict = TRUE;
|
||||
@@ -776,8 +785,8 @@ main(int argc, char **argv)
|
||||
/* Check for valid crop dimensions. We cannot check these values until
|
||||
* after jpeg_start_decompress() is called.
|
||||
*/
|
||||
if (crop_x + crop_width > cinfo.output_width ||
|
||||
crop_y + crop_height > cinfo.output_height) {
|
||||
if ((unsigned long long)crop_x + crop_width > cinfo.output_width ||
|
||||
(unsigned long long)crop_y + crop_height > cinfo.output_height) {
|
||||
fprintf(stderr, "%s: crop dimensions exceed image dimensions %u x %u\n",
|
||||
progname, cinfo.output_width, cinfo.output_height);
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
Reference in New Issue
Block a user