-
Notifications
You must be signed in to change notification settings - Fork 26
Expand file tree
/
Copy pathlzw_alloc_overflow.c
More file actions
83 lines (72 loc) · 2.76 KB
/
Copy pathlzw_alloc_overflow.c
File metadata and controls
83 lines (72 loc) · 2.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include "cgif_raw.h"
/*
* Regression test for integer overflow in the LZW code-buffer allocation.
*
* LZW_GenerateStream() sizes pLZWData from:
*
* (numPixel + 2 + maxResets) * sizeof(uint16_t)
*
* On a 32-bit target size_t is 32 bits wide, so for a numPixel close to
* UINT32_MAX (e.g. a 65535x65535 GIF is ~4.3 billion pixels) the additive
* expression wraps and malloc receives a drastically undersized request.
* LZW encoding then writes past the end of that buffer.
*
* The fix promotes the arithmetic to size_t and returns CGIF_EALLOC via the
* overflow guard before malloc is ever called.
*
* We reach the static function by including the translation unit directly and
* redirecting its malloc (same approach as ealloc_raw.c).
*
* Detecting the buffer without depending on malloc call order:
* pLZWData is the only allocation that scales with numPixel. Every other
* allocation is bounded by MAX_DICT_LEN and stays well under 64 KiB for the
* inputs used here, so we treat any request larger than that threshold as the
* LZW buffer. We record its size and return NULL so encoding never runs on a
* dummy image buffer.
*
* main (unfixed), 32-bit: request wraps to ~161 KiB (< numPixel bytes) -> FAIL
* fixed, 32-bit: guard returns CGIF_EALLOC, buffer never sized -> PASS
* 64-bit (either): no wrap, request is > 8 GiB, we force NULL -> PASS
*/
#define LZW_ALLOC_THRESHOLD 65536 /* every non-LZW allocation stays below this */
static size_t lzwAllocSize; /* size of the LZW-buffer request, 0 if none seen */
static void* test_malloc(size_t size) {
if(size > LZW_ALLOC_THRESHOLD) {
lzwAllocSize = size;
return NULL; /* abort before encoding a dummy image */
}
return malloc(size);
}
/* redirect malloc calls inside cgif_raw.c to our wrapper */
#define malloc(s) test_malloc(s)
#include "../src/cgif_raw.c"
#undef malloc
int main(void) {
LZWResult result;
/*
* numPixel large enough to overflow (numPixel + 2 + maxResets) in 32-bit
* size_t arithmetic. A 65535x65535 frame produces a value in this range.
*/
const uint32_t numPixel = 4294000000U;
const uint16_t initDictLen = 4; /* 1-color palette */
const uint8_t initCodeLen = 3;
uint8_t imageData[1] = {0};
lzwAllocSize = 0;
memset(&result, 0, sizeof(result));
int r = LZW_GenerateStream(&result, numPixel, imageData, initDictLen, initCodeLen);
if(lzwAllocSize != 0 && lzwAllocSize < (size_t)numPixel) {
fprintf(stderr,
"FAIL: LZW buffer request wrapped (%zu bytes for %u pixels)\n",
lzwAllocSize, numPixel);
return 1;
}
if(r != CGIF_EALLOC) {
fprintf(stderr, "FAIL: expected CGIF_EALLOC, got %d\n", r);
return 1;
}
return 0;
}