/src/cgif/fuzz/cgif_fuzzer.c
Line | Count | Source (jump to first uncovered line) |
1 | | #include <cgif.h> |
2 | | |
3 | | #include <stddef.h> |
4 | | #include <stdint.h> |
5 | | #include <stdlib.h> |
6 | | #include <string.h> |
7 | | |
8 | | typedef struct { |
9 | | const uint8_t* pData; |
10 | | size_t numBytes; |
11 | | size_t curPos; |
12 | | } ByteStream; |
13 | | |
14 | 511k | static int readdata(ByteStream* pStream, void* pDest, size_t size) { |
15 | 511k | if((pStream->curPos + size) <= pStream->numBytes) { |
16 | 502k | memcpy(pDest, &pStream->pData[pStream->curPos], size); |
17 | 502k | pStream->curPos += size; |
18 | 502k | return 0; |
19 | 502k | } |
20 | | // error: out of bounds |
21 | 9.38k | pStream->curPos = pStream->numBytes; // mark stream as out of sync |
22 | 9.38k | return -1; |
23 | 511k | } |
24 | | |
25 | 1.74k | static int read_gifconfig(ByteStream* pStream, CGIF_Config* pDest) { |
26 | 1.74k | int r; |
27 | | |
28 | 1.74k | memset(pDest, 0, sizeof(CGIF_Config)); |
29 | | // width : U16 |
30 | | // height : U16 |
31 | | // attrFlags : U32 |
32 | | // genFlags : U32 |
33 | | // sizeGCT : U16 |
34 | | // numLoops : U16 |
35 | | // pGCT : U8[sizeGCT * 3] |
36 | 1.74k | r = readdata(pStream, &pDest->width, 2); |
37 | 1.74k | r |= readdata(pStream, &pDest->height, 2); |
38 | 1.74k | r |= readdata(pStream, &pDest->attrFlags, 4); |
39 | 1.74k | r |= readdata(pStream, &pDest->genFlags, 4); |
40 | 1.74k | r |= readdata(pStream, &pDest->numGlobalPaletteEntries, 2); |
41 | 1.74k | if(!(pDest->attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE)) { |
42 | 1.58k | pDest->pGlobalPalette = (uint8_t*)malloc(pDest->numGlobalPaletteEntries * 3); |
43 | 1.58k | if(pDest->pGlobalPalette == NULL) return 0; // malloc failed |
44 | 1.58k | r |= readdata(pStream, pDest->pGlobalPalette, pDest->numGlobalPaletteEntries * 3); |
45 | 1.58k | } |
46 | 1.74k | if(r) { |
47 | 43 | free(pDest->pGlobalPalette); |
48 | 43 | } |
49 | 1.74k | return (r ? 0 : 1); |
50 | 1.74k | } |
51 | | |
52 | 82.8k | static int read_frameconfig(ByteStream* pStream, CGIF_FrameConfig* pDest, size_t sizeImageData) { |
53 | 82.8k | int r; |
54 | | |
55 | 82.8k | memset(pDest, 0, sizeof(CGIF_FrameConfig)); |
56 | | // attrFlags : U16 |
57 | | // genFlags : U16 |
58 | | // delay : U16 |
59 | | // transIndex : U8 |
60 | | // sizeLCT : U16 |
61 | | // pLCT : U8[sizeLCT * 3] |
62 | | // pImageData : U8[sizeImageData] |
63 | 82.8k | r = readdata(pStream, &pDest->attrFlags, 2); |
64 | 82.8k | r |= readdata(pStream, &pDest->genFlags, 2); |
65 | 82.8k | r |= readdata(pStream, &pDest->delay, 2); |
66 | 82.8k | r |= readdata(pStream, &pDest->transIndex, 1); |
67 | 82.8k | r |= readdata(pStream, &pDest->numLocalPaletteEntries, 2); |
68 | 82.8k | if(pDest->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) { |
69 | 4.12k | pDest->pLocalPalette = (uint8_t*)malloc(pDest->numLocalPaletteEntries * 3); |
70 | 4.12k | if(pDest->pLocalPalette == NULL) return 0; // malloc failed |
71 | 4.12k | r |= readdata(pStream, pDest->pLocalPalette, pDest->numLocalPaletteEntries * 3); |
72 | 4.12k | } |
73 | 82.8k | pDest->pImageData = (uint8_t*)malloc(sizeImageData); |
74 | 82.8k | if(pDest->pImageData == NULL) return 0; // malloc failed |
75 | 82.8k | r |= readdata(pStream, pDest->pImageData, sizeImageData); |
76 | 82.8k | if(r) { |
77 | 1.66k | free(pDest->pImageData); |
78 | 1.66k | free(pDest->pLocalPalette); |
79 | 1.66k | } |
80 | 82.8k | return (r ? 0 : 1); |
81 | 82.8k | } |
82 | | |
83 | 61.7k | static int writecb(void* pContext, const uint8_t* pData, size_t size) { |
84 | 61.7k | (void)pContext; |
85 | 61.7k | (void)pData; |
86 | 61.7k | (void)size; |
87 | | // eat input |
88 | 61.7k | return 0; |
89 | 61.7k | } |
90 | | |
91 | 1.74k | static int processInput(ByteStream* pStream) { |
92 | 1.74k | CGIF_Config gconfig; |
93 | 1.74k | CGIF_FrameConfig fconfig; |
94 | 1.74k | CGIF* pGIF; |
95 | 1.74k | size_t sizeImageData; |
96 | 1.74k | int r; |
97 | | |
98 | 1.74k | r = read_gifconfig(pStream, &gconfig); |
99 | 1.74k | if(!r) { |
100 | 43 | return -1; |
101 | 43 | } |
102 | 1.70k | sizeImageData = (uint32_t)gconfig.width * (uint32_t)gconfig.height; |
103 | | // limit dimensions of GIF to be created |
104 | 1.70k | if(sizeImageData > (10000 * 10000)) { |
105 | 8 | free(gconfig.pGlobalPalette); |
106 | 8 | return -1; |
107 | 8 | } |
108 | 1.69k | gconfig.pWriteFn = writecb; // discard output |
109 | 1.69k | pGIF = cgif_newgif(&gconfig); |
110 | 1.69k | free(gconfig.pGlobalPalette); |
111 | 1.69k | if(pGIF == NULL) { |
112 | 23 | return -1; |
113 | 23 | } |
114 | 1.66k | r = read_frameconfig(pStream, &fconfig, sizeImageData); |
115 | 82.8k | while(r) { |
116 | 81.2k | cgif_addframe(pGIF, &fconfig); |
117 | 81.2k | free(fconfig.pImageData); |
118 | 81.2k | free(fconfig.pLocalPalette); |
119 | 81.2k | r = read_frameconfig(pStream, &fconfig, sizeImageData); |
120 | 81.2k | } |
121 | 1.66k | r = cgif_close(pGIF); |
122 | 1.66k | return r; |
123 | 1.69k | } |
124 | | |
125 | | #ifdef __cplusplus |
126 | | extern "C" |
127 | | #endif |
128 | 1.74k | int LLVMFuzzerTestOneInput(const uint8_t* pData, size_t numBytes) { |
129 | 1.74k | ByteStream input = { pData, numBytes, 0 }; |
130 | 1.74k | processInput(&input); |
131 | 1.74k | return 0; |
132 | 1.74k | } |