Upstream-Status: Backport Reference: http://squashfs.git.sourceforge.net/git/gitweb.cgi? p=squashfs/squashfs;a=patch;h=8515b3d420f502c5c0236b86e2d6d7e3b23c190e Integer overflow in the queue_init function in unsquashfs.c in unsquashfs in Squashfs 4.2 and earlier allows remote attackers to execute arbitrary code via a crafted block_log field in the superblock of a .sqsh file, leading to a heap-based buffer overflow. http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2012-4025 Signed-off-by: yanjun.zhu --- a/unsquashfs.c 2012-11-30 17:57:57.000000000 +0800 +++ b/unsquashfs.c 2012-11-30 17:58:09.000000000 +0800 @@ -33,6 +33,7 @@ #include #include #include +#include struct cache *fragment_cache, *data_cache; struct queue *to_reader, *to_deflate, *to_writer, *from_writer; @@ -138,6 +139,24 @@ void sigalrm_handler() } +int add_overflow(int a, int b) +{ + return (INT_MAX - a) < b; +} + + +int shift_overflow(int a, int shift) +{ + return (INT_MAX >> shift) < a; +} + + +int multiply_overflow(int a, int multiplier) +{ + return (INT_MAX / multiplier) < a; +} + + struct queue *queue_init(int size) { struct queue *queue = malloc(sizeof(struct queue)); @@ -145,6 +164,10 @@ struct queue *queue_init(int size) if(queue == NULL) EXIT_UNSQUASH("Out of memory in queue_init\n"); + if(add_overflow(size, 1) || + multiply_overflow(size + 1, sizeof(void *))) + EXIT_UNSQUASH("Size too large in queue_init\n"); + queue->data = malloc(sizeof(void *) * (size + 1)); if(queue->data == NULL) EXIT_UNSQUASH("Out of memory in queue_init\n"); @@ -1948,13 +1971,30 @@ void initialise_threads(int fragment_buf * allocate to_reader, to_deflate and to_writer queues. Set based on * open file limit and cache size, unless open file limit is unlimited, * in which case set purely based on cache limits + * + * In doing so, check that the user supplied values do not overflow + * a signed int */ if (max_files != -1) { + if(add_overflow(data_buffer_size, max_files) || + add_overflow(data_buffer_size, max_files * 2)) + EXIT_UNSQUASH("Data queue size is too large\n"); + to_reader = queue_init(max_files + data_buffer_size); to_deflate = queue_init(max_files + data_buffer_size); to_writer = queue_init(max_files * 2 + data_buffer_size); } else { - int all_buffers_size = fragment_buffer_size + data_buffer_size; + int all_buffers_size; + + if(add_overflow(fragment_buffer_size, data_buffer_size)) + EXIT_UNSQUASH("Data and fragment queues combined are" + " too large\n"); + + all_buffers_size = fragment_buffer_size + data_buffer_size; + + if(add_overflow(all_buffers_size, all_buffers_size)) + EXIT_UNSQUASH("Data and fragment queues combined are" + " too large\n"); to_reader = queue_init(all_buffers_size); to_deflate = queue_init(all_buffers_size); @@ -2059,6 +2099,32 @@ void progress_bar(long long current, lon } +int parse_number(char *arg, int *res) +{ + char *b; + long number = strtol(arg, &b, 10); + + /* check for trailing junk after number */ + if(*b != '\0') + return 0; + + /* check for strtol underflow or overflow in conversion */ + if(number == LONG_MIN || number == LONG_MAX) + return 0; + + /* reject negative numbers as invalid */ + if(number < 0) + return 0; + + /* check if long result will overflow signed int */ + if(number > INT_MAX) + return 0; + + *res = number; + return 1; +} + + #define VERSION() \ printf("unsquashfs version 4.2 (2011/02/28)\n");\ printf("copyright (C) 2011 Phillip Lougher "\ @@ -2140,8 +2206,8 @@ int main(int argc, char *argv[]) } else if(strcmp(argv[i], "-data-queue") == 0 || strcmp(argv[i], "-da") == 0) { if((++i == argc) || - (data_buffer_size = strtol(argv[i], &b, - 10), *b != '\0')) { + !parse_number(argv[i], + &data_buffer_size)) { ERROR("%s: -data-queue missing or invalid " "queue size\n", argv[0]); exit(1); @@ -2154,8 +2220,8 @@ int main(int argc, char *argv[]) } else if(strcmp(argv[i], "-frag-queue") == 0 || strcmp(argv[i], "-fr") == 0) { if((++i == argc) || - (fragment_buffer_size = strtol(argv[i], - &b, 10), *b != '\0')) { + !parse_number(argv[i], + &fragment_buffer_size)) { ERROR("%s: -frag-queue missing or invalid " "queue size\n", argv[0]); exit(1); @@ -2280,11 +2346,39 @@ options: block_log = sBlk.s.block_log; /* + * Sanity check block size and block log. + * + * Check they're within correct limits + */ + if(block_size > SQUASHFS_FILE_MAX_SIZE || + block_log > SQUASHFS_FILE_MAX_LOG) + EXIT_UNSQUASH("Block size or block_log too large." + " File system is corrupt.\n"); + + /* + * Check block_size and block_log match + */ + if(block_size != (1 << block_log)) + EXIT_UNSQUASH("Block size and block_log do not match." + " File system is corrupt.\n"); + + /* * convert from queue size in Mbytes to queue size in - * blocks + * blocks. + * + * In doing so, check that the user supplied values do not + * overflow a signed int */ - fragment_buffer_size <<= 20 - block_log; - data_buffer_size <<= 20 - block_log; + if(shift_overflow(fragment_buffer_size, 20 - block_log)) + EXIT_UNSQUASH("Fragment queue size is too large\n"); + else + fragment_buffer_size <<= 20 - block_log; + + if(shift_overflow(data_buffer_size, 20 - block_log)) + EXIT_UNSQUASH("Data queue size is too large\n"); + else + data_buffer_size <<= 20 - block_log; + initialise_threads(fragment_buffer_size, data_buffer_size); fragment_data = malloc(block_size);