Coverage Report

Created: 2026-07-16 07:12

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