/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 | 55.0k | static int readdata(ByteStream* pStream, void* pDest, size_t size) { |
15 | 55.0k | if((pStream->curPos + size) <= pStream->numBytes) { |
16 | 44.6k | memcpy(pDest, &pStream->pData[pStream->curPos], size); |
17 | 44.6k | pStream->curPos += size; |
18 | 44.6k | return 0; |
19 | 44.6k | } |
20 | | // error: out of bounds |
21 | 10.3k | pStream->curPos = pStream->numBytes; // mark stream as out of sync |
22 | 10.3k | return -1; |
23 | 55.0k | } |
24 | | |
25 | 1.95k | static int read_gifconfig(ByteStream* pStream, CGIF_Config* pDest) { |
26 | 1.95k | int r; |
27 | | |
28 | 1.95k | 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.95k | r = readdata(pStream, &pDest->width, 2); |
37 | 1.95k | r |= readdata(pStream, &pDest->height, 2); |
38 | 1.95k | r |= readdata(pStream, &pDest->attrFlags, 4); |
39 | 1.95k | r |= readdata(pStream, &pDest->genFlags, 4); |
40 | 1.95k | r |= readdata(pStream, &pDest->numGlobalPaletteEntries, 2); |
41 | 1.95k | if(!(pDest->attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE)) { |
42 | 1.82k | pDest->pGlobalPalette = (uint8_t*)malloc(pDest->numGlobalPaletteEntries * 3); |
43 | 1.82k | if(pDest->pGlobalPalette == NULL) return 0; // malloc failed |
44 | 1.82k | r |= readdata(pStream, pDest->pGlobalPalette, pDest->numGlobalPaletteEntries * 3); |
45 | 1.82k | } |
46 | 1.95k | if(r) { |
47 | 47 | free(pDest->pGlobalPalette); |
48 | 47 | } |
49 | 1.95k | return (r ? 0 : 1); |
50 | 1.95k | } |
51 | | |
52 | 7.03k | static int read_frameconfig(ByteStream* pStream, CGIF_FrameConfig* pDest, size_t sizeImageData) { |
53 | 7.03k | int r; |
54 | | |
55 | 7.03k | 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 | 7.03k | r = readdata(pStream, &pDest->attrFlags, 2); |
64 | 7.03k | r |= readdata(pStream, &pDest->genFlags, 2); |
65 | 7.03k | r |= readdata(pStream, &pDest->delay, 2); |
66 | 7.03k | r |= readdata(pStream, &pDest->transIndex, 1); |
67 | 7.03k | r |= readdata(pStream, &pDest->numLocalPaletteEntries, 2); |
68 | 7.03k | if(pDest->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) { |
69 | 1.21k | pDest->pLocalPalette = (uint8_t*)malloc(pDest->numLocalPaletteEntries * 3); |
70 | 1.21k | if(pDest->pLocalPalette == NULL) return 0; // malloc failed |
71 | 1.21k | r |= readdata(pStream, pDest->pLocalPalette, pDest->numLocalPaletteEntries * 3); |
72 | 1.21k | } |
73 | 7.03k | pDest->pImageData = (uint8_t*)malloc(sizeImageData); |
74 | 7.03k | if(pDest->pImageData == NULL) return 0; // malloc failed |
75 | 7.03k | r |= readdata(pStream, pDest->pImageData, sizeImageData); |
76 | 7.03k | if(r) { |
77 | 1.83k | free(pDest->pImageData); |
78 | 1.83k | free(pDest->pLocalPalette); |
79 | 1.83k | } |
80 | 7.03k | return (r ? 0 : 1); |
81 | 7.03k | } |
82 | | |
83 | 33.4k | static int writecb(void* pContext, const uint8_t* pData, size_t size) { |
84 | 33.4k | (void)pContext; |
85 | 33.4k | (void)pData; |
86 | 33.4k | (void)size; |
87 | | // eat input |
88 | 33.4k | return 0; |
89 | 33.4k | } |
90 | | |
91 | 1.95k | static int processInput(ByteStream* pStream) { |
92 | 1.95k | CGIF_Config gconfig; |
93 | 1.95k | CGIF_FrameConfig fconfig; |
94 | 1.95k | CGIF* pGIF; |
95 | 1.95k | size_t sizeImageData; |
96 | 1.95k | int r; |
97 | 1.95k | int numFrames; |
98 | | |
99 | 1.95k | r = read_gifconfig(pStream, &gconfig); |
100 | 1.95k | if(!r) { |
101 | 47 | return -1; |
102 | 47 | } |
103 | 1.90k | sizeImageData = (uint32_t)gconfig.width * (uint32_t)gconfig.height; |
104 | | // limit dimensions of GIF to be created |
105 | 1.90k | if(sizeImageData > (10000 * 10000)) { |
106 | 11 | free(gconfig.pGlobalPalette); |
107 | 11 | return -1; |
108 | 11 | } |
109 | 1.89k | gconfig.pWriteFn = writecb; // discard output |
110 | 1.89k | pGIF = cgif_newgif(&gconfig); |
111 | 1.89k | free(gconfig.pGlobalPalette); |
112 | 1.89k | if(pGIF == NULL) { |
113 | 21 | return -1; |
114 | 21 | } |
115 | 1.87k | r = read_frameconfig(pStream, &fconfig, sizeImageData); |
116 | 1.87k | numFrames = 1; |
117 | 7.03k | while(r) { |
118 | 5.19k | cgif_addframe(pGIF, &fconfig); |
119 | 5.19k | free(fconfig.pImageData); |
120 | 5.19k | free(fconfig.pLocalPalette); |
121 | 5.19k | if(numFrames >= 16) { |
122 | | // limit number of frames to avoid timeouts: 16 should be more than enough |
123 | 40 | break; |
124 | 40 | } |
125 | 5.15k | numFrames++; |
126 | 5.15k | r = read_frameconfig(pStream, &fconfig, sizeImageData); |
127 | 5.15k | } |
128 | 1.87k | r = cgif_close(pGIF); |
129 | 1.87k | return r; |
130 | 1.89k | } |
131 | | |
132 | | #ifdef __cplusplus |
133 | | extern "C" |
134 | | #endif |
135 | 5.04k | int LLVMFuzzerTestOneInput(const uint8_t* pData, size_t numBytes) { |
136 | 5.04k | ByteStream input = { pData, numBytes, 0 }; |
137 | 5.04k | processInput(&input); |
138 | 5.04k | return 0; |
139 | 5.04k | } |