Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/cgif_raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ static int LZW_GenerateStream(LZWResult* pResult, const uint32_t numPixel, const
if(pContext == NULL) {
return CGIF_EALLOC;
}
memset(pContext, 0, sizeof(LZWGenState));

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found by the new ealloc_raw test with ASAN:
When an early malloc fails, the goto cleanup path frees all pContext fields, including ones never assigned. Zero-initialize pContext so unassigned pointers are NULL and safe to free.

pContext->pTreeInit = malloc((initDictLen * sizeof(uint16_t)) * initDictLen);
if(pContext->pTreeInit == NULL) {
r = CGIF_EALLOC;
Expand Down
97 changes: 97 additions & 0 deletions tests/ealloc_raw.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>

#include "cgif_raw.h"

#define WIDTH 2
#define HEIGHT 2

/* malloc failure injection */
static int fail_after = 0;
static int malloc_count = 0;

static void* cgif_test_malloc(size_t size) {
if(fail_after > 0) {
++malloc_count;
if(malloc_count == fail_after) {
return NULL;
}
}
return malloc(size);
}

/* redirect malloc calls inside cgif_raw.c to our wrapper */
#define malloc(s) cgif_test_malloc(s)
#include "../src/cgif_raw.c"
#undef malloc

/* no-op write callback */
static int writeFn(void* pContext, const uint8_t* pData, const size_t numBytes) {
(void)pContext;
(void)pData;
(void)numBytes;
return 0;
}

int main(void) {
CGIFRaw* pGIF;
CGIFRaw_Config gConfig;
CGIFRaw_FrameConfig fConfig;
cgif_result r;
uint8_t aPalette[] = {
0x00, 0x00, 0x00, // black
0xFF, 0xFF, 0xFF, // white
};
uint8_t aImageData[WIDTH * HEIGHT];
memset(aImageData, 0, sizeof(aImageData));

for(int n = 1; ; ++n) {
memset(&gConfig, 0, sizeof(gConfig));
memset(&fConfig, 0, sizeof(fConfig));
gConfig.pWriteFn = writeFn;
gConfig.width = WIDTH;
gConfig.height = HEIGHT;
gConfig.pGCT = aPalette;
gConfig.sizeGCT = 2;

fConfig.pImageData = aImageData;
fConfig.width = WIDTH;
fConfig.height = HEIGHT;

fail_after = n;
malloc_count = 0;

pGIF = cgif_raw_newgif(&gConfig);
if(pGIF == NULL) {
if(malloc_count >= n) {
continue; // expected: our injected malloc failure
}
fputs("unexpected NULL from cgif_raw_newgif\n", stderr);
return 1;
}

r = cgif_raw_addframe(pGIF, &fConfig);
if(r != CGIF_OK) {
if(r == CGIF_EALLOC && malloc_count >= n) {
cgif_raw_close(pGIF);
continue; // expected: our injected malloc failure
}
fprintf(stderr, "unexpected error from cgif_raw_addframe: %d\n", r);
return 1;
}

r = cgif_raw_close(pGIF);
if(malloc_count < n) {
// all mallocs succeeded without hitting our failure point -- done
if(r != CGIF_OK) {
fprintf(stderr, "unexpected error from cgif_raw_close: %d\n", r);
return 1;
}
break;
}
}

return 0;
}
8 changes: 8 additions & 0 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,14 @@ foreach t : tests_index + tests_rgb
test(name, test_exe, priority : 0)
endforeach

# malloc failure tests (compile source directly to intercept malloc)
test_ealloc_raw_exe = executable(
'test_ealloc_raw',
'ealloc_raw.c',
include_directories : ['../inc/'],
)
test('ealloc_raw', test_ealloc_raw_exe, priority : 0)

sha256sumc = find_program('scripts/sha256sum.py')
# get the ordering right:
# md5sum check on output GIFs should be run once all of the above tests are done.
Expand Down
Loading