Skip to content

Commit 003ff81

Browse files
committed
LZW_GenerateStream() allocated the LZW code buffer using:
sizeof(uint16_t) * (numPixel + 2 + maxResets) The size expression was computed in 32-bit arithmetic, allowing unsigned overflow when numPixel is large. This could result in an undersized allocation and subsequent out-of-bounds writes to pLZWData during LZW code emission. Fix by: - Performing size calculation in size_t - Adding explicit overflow checks for addition and multiplication - Recording allocated capacity - Adding bounds checks before all pLZWData writes This change does not alter encoder behavior for valid inputs and adds negligible overhead.
1 parent dcd45c7 commit 003ff81

1 file changed

Lines changed: 47 additions & 5 deletions

File tree

src/cgif_raw.c

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <stdlib.h>
22
#include <string.h>
3+
#include <limits.h>
34

45
#include "cgif_raw.h"
56

@@ -45,6 +46,7 @@ typedef struct {
4546
uint16_t* pTreeList; // LZW dictionary tree as list (max. number of children per node = 1)
4647
uint16_t* pTreeMap; // LZW dictionary tree as map (backup to pTreeList in case more than 1 child is present)
4748
uint16_t* pLZWData; // pointer to LZW data
49+
size_t lzwCapacity; // number of entries allocated in pLZWData
4850
const uint8_t* pImageData; // pointer to image data
4951
uint32_t numPixel; // number of pixels per frame
5052
uint32_t LZWPos; // position of the current LZW code
@@ -85,14 +87,18 @@ static uint8_t calcInitCodeLen(uint16_t numEntries) {
8587
}
8688

8789
/* reset the dictionary of known LZW codes -- will reset the current code length as well */
88-
static void resetDict(LZWGenState* pContext, const uint16_t initDictLen) {
90+
static int resetDict(LZWGenState* pContext, const uint16_t initDictLen) {
8991
pContext->dictPos = initDictLen + 2; // reset current position in dictionary (number of colors + 2 for start and end code)
9092
pContext->mapPos = 1;
93+
if(pContext->LZWPos >= pContext->lzwCapacity) {
94+
return CGIF_EALLOC;
95+
}
9196
pContext->pLZWData[pContext->LZWPos] = initDictLen; // issue clear-code
9297
++(pContext->LZWPos); // increment position in LZW data
9398
// reset LZW list
9499
memset(pContext->pTreeInit, 0, initDictLen * sizeof(uint16_t) * initDictLen);
95100
memset(pContext->pTreeList, 0, ((sizeof(uint16_t) * 2) + sizeof(uint16_t)) * MAX_DICT_LEN);
101+
return CGIF_OK;
96102
}
97103

98104
/* add new child node */
@@ -146,13 +152,19 @@ static int lzw_crawl_tree(LZWGenState* pContext, uint32_t* pStrPos, uint16_t par
146152
parentIndex = nextParent;
147153
++strPos;
148154
} else {
155+
if(pContext->LZWPos >= pContext->lzwCapacity) {
156+
return CGIF_EALLOC;
157+
}
149158
pContext->pLZWData[pContext->LZWPos] = parentIndex; // write last LZW code in LZW data
150159
++(pContext->LZWPos);
151160
if(pContext->dictPos < MAX_DICT_LEN) {
152161
pTreeInit[parentIndex * initDictLen + pContext->pImageData[strPos + 1]] = pContext->dictPos;
153162
++(pContext->dictPos);
154163
} else {
155-
resetDict(pContext, initDictLen);
164+
const int rReset = resetDict(pContext, initDictLen);
165+
if(rReset != CGIF_OK) {
166+
return rReset;
167+
}
156168
}
157169
++strPos;
158170
*pStrPos = strPos;
@@ -181,18 +193,27 @@ static int lzw_crawl_tree(LZWGenState* pContext, uint32_t* pStrPos, uint16_t par
181193
}
182194
}
183195
// still not found child? add current parentIndex to LZW data and add new child
196+
if(pContext->LZWPos >= pContext->lzwCapacity) {
197+
return CGIF_EALLOC;
198+
}
184199
pContext->pLZWData[pContext->LZWPos] = parentIndex; // write last LZW code in LZW data
185200
++(pContext->LZWPos);
186201
if(pContext->dictPos < MAX_DICT_LEN) { // if LZW-dictionary is not full yet
187202
add_child(pContext, parentIndex, pContext->dictPos, initDictLen, pContext->pImageData[strPos + 1]); // add new LZW code to dictionary
188203
} else {
189204
// the dictionary reached its maximum code => reset it (not required by GIF-standard but mostly done like this)
190-
resetDict(pContext, initDictLen);
205+
const int rReset = resetDict(pContext, initDictLen);
206+
if(rReset != CGIF_OK) {
207+
return rReset;
208+
}
191209
}
192210
++strPos;
193211
*pStrPos = strPos;
194212
return CGIF_OK;
195213
}
214+
if(pContext->LZWPos >= pContext->lzwCapacity) {
215+
return CGIF_EALLOC;
216+
}
196217
pContext->pLZWData[pContext->LZWPos] = parentIndex; // if the end of the image is reached, write last LZW code
197218
++(pContext->LZWPos);
198219
++strPos;
@@ -207,7 +228,10 @@ static int lzw_generate(LZWGenState* pContext, uint16_t initDictLen) {
207228
uint8_t parentIndex;
208229

209230
strPos = 0; // start at beginning of the image data
210-
resetDict(pContext, initDictLen); // reset dictionary and issue clear-code at first
231+
r = resetDict(pContext, initDictLen); // reset dictionary and issue clear-code at first
232+
if(r != CGIF_OK) {
233+
return r;
234+
}
211235
while(strPos < pContext->numPixel) { // while there are still image data to be encoded
212236
parentIndex = pContext->pImageData[strPos]; // start at root node
213237
// get longest sequence that is still in dictionary, return new position in image data
@@ -216,6 +240,9 @@ static int lzw_generate(LZWGenState* pContext, uint16_t initDictLen) {
216240
return r; // error: return error code to callee
217241
}
218242
}
243+
if(pContext->LZWPos >= pContext->lzwCapacity) {
244+
return CGIF_EALLOC;
245+
}
219246
pContext->pLZWData[pContext->LZWPos] = initDictLen + 1; // termination code
220247
++(pContext->LZWPos);
221248
return CGIF_OK;
@@ -330,11 +357,26 @@ static int LZW_GenerateStream(LZWResult* pResult, const uint32_t numPixel, const
330357
// where N = max dictionary resets = numPixel / (MAX_DICT_LEN - initDictLen - 2)
331358
entriesPerCycle = MAX_DICT_LEN - initDictLen - 2; // maximum added number of dictionary entries per cycle: -2 to account for start and end code
332359
maxResets = numPixel / entriesPerCycle;
333-
pContext->pLZWData = malloc(sizeof(uint16_t) * (numPixel + 2 + maxResets));
360+
size_t neededEntries = (size_t)numPixel + 2u;
361+
if(neededEntries < (size_t)numPixel) {
362+
r = CGIF_EALLOC;
363+
goto LZWGENERATE_Cleanup;
364+
}
365+
neededEntries += (size_t)maxResets;
366+
if(neededEntries < (size_t)maxResets) {
367+
r = CGIF_EALLOC;
368+
goto LZWGENERATE_Cleanup;
369+
}
370+
if(neededEntries > (SIZE_MAX / sizeof(uint16_t))) {
371+
r = CGIF_EALLOC;
372+
goto LZWGENERATE_Cleanup;
373+
}
374+
pContext->pLZWData = malloc(neededEntries * sizeof(uint16_t));
334375
if(pContext->pLZWData == NULL) {
335376
r = CGIF_EALLOC;
336377
goto LZWGENERATE_Cleanup;
337378
}
379+
pContext->lzwCapacity = neededEntries;
338380
pContext->LZWPos = 0;
339381

340382
// actually generate the LZW sequence.

0 commit comments

Comments
 (0)