diff --git a/libvips/foreign/libnsgif/libnsgif.c b/libvips/foreign/libnsgif/libnsgif.c index 6cce6b6b..55c25999 100644 --- a/libvips/foreign/libnsgif/libnsgif.c +++ b/libvips/foreign/libnsgif/libnsgif.c @@ -418,7 +418,7 @@ static gif_result gif_initialise_frame(gif_animation *gif) if (gif_bytes < 1) { return GIF_INSUFFICIENT_FRAME_DATA; } - if (gif_data[0] > LZW_CODE_MAX) { + if (gif_data[0] >= LZW_CODE_MAX) { return GIF_DATA_ERROR; } @@ -864,10 +864,10 @@ gif_internal_decode_frame(gif_animation *gif, } } - if (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE) { - /* Store the previous frame for later restoration */ - gif__record_previous_frame(gif); - } + if (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE) { + /* Store the previous frame for later restoration */ + gif__record_previous_frame(gif); + } gif->decoded_frame = frame; gif->buffer_position = (gif_data - gif->gif_data) + 1; diff --git a/libvips/foreign/libnsgif/lzw.c b/libvips/foreign/libnsgif/lzw.c index 1d4babfd..1f854964 100644 --- a/libvips/foreign/libnsgif/lzw.c +++ b/libvips/foreign/libnsgif/lzw.c @@ -160,7 +160,6 @@ static inline lzw_result lzw__next_code( uint32_t code = 0; uint8_t current_bit = ctx->sb_bit & 0x7; uint8_t byte_advance = (current_bit + code_size) >> 3; - uint32_t new_code; assert(byte_advance <= 2); @@ -210,11 +209,7 @@ static inline lzw_result lzw__next_code( } } - new_code = (code >> current_bit) & ((1 << code_size) - 1); - if (new_code > ((1 << LZW_CODE_MAX) - 1)) - return LZW_BAD_CODE; - *code_out = new_code; - + *code_out = (code >> current_bit) & ((1 << code_size) - 1); return LZW_OK; } @@ -235,7 +230,7 @@ static lzw_result lzw__clear_codes( /* Reset dictionary building context */ ctx->current_code_size = ctx->initial_code_size + 1; - ctx->current_code_size_max = (1 << ctx->current_code_size) - 1;; + ctx->current_code_size_max = (1 << ctx->current_code_size) - 1; ctx->current_entry = (1 << ctx->initial_code_size) + 2; /* There might be a sequence of clear codes, so process them all */ @@ -277,6 +272,10 @@ lzw_result lzw_decode_init( { struct lzw_dictionary_entry *table = ctx->table; + if (code_size >= LZW_CODE_MAX) { + return LZW_BAD_ICODE; + } + /* Initialise the input reading context */ ctx->input.data = compressed_data; ctx->input.data_len = compressed_data_len; diff --git a/libvips/foreign/libnsgif/patches/lzw-decode.patch b/libvips/foreign/libnsgif/patches/lzw-decode.patch deleted file mode 100644 index 329a8357..00000000 --- a/libvips/foreign/libnsgif/patches/lzw-decode.patch +++ /dev/null @@ -1,23 +0,0 @@ ---- lzw-orig.c 2021-03-29 09:16:40.730562757 +0100 -+++ lzw.c 2021-03-29 09:16:30.390679354 +0100 -@@ -160,6 +160,7 @@ - uint32_t code = 0; - uint8_t current_bit = ctx->sb_bit & 0x7; - uint8_t byte_advance = (current_bit + code_size) >> 3; -+ uint32_t new_code; - - assert(byte_advance <= 2); - -@@ -209,7 +210,11 @@ - } - } - -- *code_out = (code >> current_bit) & ((1 << code_size) - 1); -+ new_code = (code >> current_bit) & ((1 << code_size) - 1); -+ if (new_code > ((1 << LZW_CODE_MAX) - 1)) -+ return LZW_BAD_CODE; -+ *code_out = new_code; -+ - return LZW_OK; - } - diff --git a/libvips/foreign/libnsgif/test/decode_gif.c b/libvips/foreign/libnsgif/test/decode_gif.c new file mode 100644 index 00000000..dd692d8b --- /dev/null +++ b/libvips/foreign/libnsgif/test/decode_gif.c @@ -0,0 +1,258 @@ +/* under libvips, compile with: + * + * gcc -g -Wall decode_gif.c `pkg-config vips --cflags --libs` + */ + +/* + * Copyright 2008 Sean Fox + * Copyright 2008 James Bursa + * + * This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/ + * Licenced under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + */ + +#include +#include +#include +#include +#include +#include +#include + + +#include +#include "../libnsgif.h" + +#define BYTES_PER_PIXEL 4 +#define MAX_IMAGE_BYTES (48 * 1024 * 1024) + + +static void *bitmap_create(int width, int height) +{ + /* ensure a stupidly large bitmap is not created */ + if (((long long)width * (long long)height) > (MAX_IMAGE_BYTES/BYTES_PER_PIXEL)) { + return NULL; + } + return calloc(width * height, BYTES_PER_PIXEL); +} + + +static void bitmap_set_opaque(void *bitmap, bool opaque) +{ + (void) opaque; /* unused */ + (void) bitmap; /* unused */ + assert(bitmap); +} + + +static bool bitmap_test_opaque(void *bitmap) +{ + (void) bitmap; /* unused */ + assert(bitmap); + return false; +} + + +static unsigned char *bitmap_get_buffer(void *bitmap) +{ + assert(bitmap); + return bitmap; +} + + +static void bitmap_destroy(void *bitmap) +{ + assert(bitmap); + free(bitmap); +} + + +static void bitmap_modified(void *bitmap) +{ + (void) bitmap; /* unused */ + assert(bitmap); + return; +} + +static unsigned char *load_file(const char *path, size_t *data_size) +{ + FILE *fd; + struct stat sb; + unsigned char *buffer; + size_t size; + size_t n; + + fd = fopen(path, "rb"); + if (!fd) { + perror(path); + exit(EXIT_FAILURE); + } + + if (stat(path, &sb)) { + perror(path); + exit(EXIT_FAILURE); + } + size = sb.st_size; + + buffer = malloc(size); + if (!buffer) { + fprintf(stderr, "Unable to allocate %lld bytes\n", + (long long) size); + exit(EXIT_FAILURE); + } + + n = fread(buffer, 1, size, fd); + if (n != size) { + perror(path); + exit(EXIT_FAILURE); + } + + fclose(fd); + + *data_size = size; + return buffer; +} + + +static void warning(const char *context, gif_result code) +{ + fprintf(stderr, "%s failed: ", context); + switch (code) + { + case GIF_INSUFFICIENT_FRAME_DATA: + fprintf(stderr, "GIF_INSUFFICIENT_FRAME_DATA"); + break; + case GIF_FRAME_DATA_ERROR: + fprintf(stderr, "GIF_FRAME_DATA_ERROR"); + break; + case GIF_INSUFFICIENT_DATA: + fprintf(stderr, "GIF_INSUFFICIENT_DATA"); + break; + case GIF_DATA_ERROR: + fprintf(stderr, "GIF_DATA_ERROR"); + break; + case GIF_INSUFFICIENT_MEMORY: + fprintf(stderr, "GIF_INSUFFICIENT_MEMORY"); + break; + default: + fprintf(stderr, "unknown code %i", code); + break; + } + fprintf(stderr, "\n"); +} + +static void write_ppm(FILE* fh, const char *name, gif_animation *gif, + bool no_write) +{ + unsigned int i; + gif_result code; + + if (!no_write) { + fprintf(fh, "P3\n"); + fprintf(fh, "# %s\n", name); + fprintf(fh, "# width %u \n", gif->width); + fprintf(fh, "# height %u \n", gif->height); + fprintf(fh, "# frame_count %u \n", gif->frame_count); + fprintf(fh, "# frame_count_partial %u \n", gif->frame_count_partial); + fprintf(fh, "# loop_count %u \n", gif->loop_count); + fprintf(fh, "%u %u 256\n", gif->width, gif->height * gif->frame_count); + } + + /* decode the frames */ + for (i = 0; i != gif->frame_count; i++) { + unsigned int row, col; + unsigned char *image; + + code = gif_decode_frame(gif, i); + if (code != GIF_OK) + warning("gif_decode_frame", code); + + if (!no_write) { + fprintf(fh, "# frame %u:\n", i); + image = (unsigned char *) gif->frame_image; + for (row = 0; row != gif->height; row++) { + for (col = 0; col != gif->width; col++) { + size_t z = (row * gif->width + col) * 4; + fprintf(fh, "%u %u %u ", + (unsigned char) image[z], + (unsigned char) image[z + 1], + (unsigned char) image[z + 2]); + } + fprintf(fh, "\n"); + } + } + } + +} + +int main(int argc, char *argv[]) +{ + gif_bitmap_callback_vt bitmap_callbacks = { + bitmap_create, + bitmap_destroy, + bitmap_get_buffer, + bitmap_set_opaque, + bitmap_test_opaque, + bitmap_modified + }; + gif_animation gif; + size_t size; + gif_result code; + unsigned char *data; + FILE *outf = stdout; + bool no_write = false; + + if (argc < 2) { + fprintf(stderr, "Usage: %s image.gif [out]\n", argv[0]); + fprintf(stderr, "\n"); + fprintf(stderr, "If [out] is NOWRITE, the gif will be docoded " + "but not output.\n"); + fprintf(stderr, "Otherwise [out] is an output filename.\n"); + fprintf(stderr, "When [out] is unset, output is to stdout.\n"); + + return 1; + } + + if (argc > 2) { + if (strcmp(argv[2], "NOWRITE") == 0) { + no_write = true; + } else { + outf = fopen(argv[2], "w+"); + if (outf == NULL) { + fprintf(stderr, "Unable to open %s for writing\n", argv[2]); + return 2; + } + } + } + + /* create our gif animation */ + gif_create(&gif, &bitmap_callbacks); + + /* load file into memory */ + data = load_file(argv[1], &size); + + /* begin decoding */ + do { + code = gif_initialise(&gif, size, data); + if (code != GIF_OK && code != GIF_WORKING) { + warning("gif_initialise", code); + gif_finalise(&gif); + free(data); + return 1; + } + } while (code != GIF_OK); + + write_ppm(outf, argv[1], &gif, no_write); + + if (argc > 2 && !no_write) { + fclose(outf); + } + + /* clean up */ + gif_finalise(&gif); + free(data); + + return 0; +} +