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