Coverage Report

Created: 2026-02-26 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
57.5k
static int readdata(ByteStream* pStream, void* pDest, size_t size) {
17
57.5k
  if((pStream->curPos + size) <= pStream->numBytes) {
18
46.5k
    memcpy(pDest, &pStream->pData[pStream->curPos], size);
19
46.5k
    pStream->curPos += size;
20
46.5k
    return 0;
21
46.5k
  }
22
  // error: out of bounds
23
10.9k
  pStream->curPos = pStream->numBytes; // mark stream as out of sync
24
10.9k
  return -1;
25
57.5k
}
26
27
2.08k
static int read_gifconfig(ByteStream* pStream, CGIF_Config* pDest) {
28
2.08k
  int r;
29
30
2.08k
  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
2.08k
  r  = readdata(pStream, &pDest->width,                   2);
39
2.08k
  r |= readdata(pStream, &pDest->height,                  2);
40
2.08k
  r |= readdata(pStream, &pDest->attrFlags,               4);
41
2.08k
  r |= readdata(pStream, &pDest->genFlags,                4);
42
2.08k
  r |= readdata(pStream, &pDest->numGlobalPaletteEntries, 2);
43
2.08k
  if(!(pDest->attrFlags & CGIF_ATTR_NO_GLOBAL_TABLE)) {
44
1.93k
    pDest->pGlobalPalette = (uint8_t*)malloc(pDest->numGlobalPaletteEntries * 3);
45
1.93k
    if(pDest->pGlobalPalette == NULL) return 0; // malloc failed
46
1.93k
    r |= readdata(pStream, pDest->pGlobalPalette, pDest->numGlobalPaletteEntries * 3);
47
1.93k
  }
48
2.08k
  if(r) {
49
44
    free(pDest->pGlobalPalette);
50
44
  }
51
2.08k
  return (r ? 0 : 1);
52
2.08k
}
53
54
7.32k
static int read_frameconfig(ByteStream* pStream, CGIF_FrameConfig* pDest, size_t sizeImageData) {
55
7.32k
  int r;
56
57
7.32k
  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
7.32k
  r  = readdata(pStream, &pDest->attrFlags,              2);
66
7.32k
  r |= readdata(pStream, &pDest->genFlags,               2);
67
7.32k
  r |= readdata(pStream, &pDest->delay,                  2);
68
7.32k
  r |= readdata(pStream, &pDest->transIndex,             1);
69
7.32k
  r |= readdata(pStream, &pDest->numLocalPaletteEntries, 2);
70
7.32k
  if(pDest->attrFlags & CGIF_FRAME_ATTR_USE_LOCAL_TABLE) {
71
1.24k
    pDest->pLocalPalette = (uint8_t*)malloc(pDest->numLocalPaletteEntries * 3);
72
1.24k
    if(pDest->pLocalPalette == NULL) return 0; // malloc failed
73
1.24k
    r |= readdata(pStream, pDest->pLocalPalette, pDest->numLocalPaletteEntries * 3);
74
1.24k
  }
75
7.32k
  pDest->pImageData = (uint8_t*)malloc(sizeImageData);
76
7.32k
  if(pDest->pImageData == NULL) return 0; // malloc failed
77
7.32k
  r |= readdata(pStream, pDest->pImageData, sizeImageData);
78
7.32k
  if(r) {
79
1.97k
    free(pDest->pImageData);
80
1.97k
    free(pDest->pLocalPalette);
81
1.97k
  }
82
7.32k
  return (r ? 0 : 1);
83
7.32k
}
84
85
2.08k
static int processInput(ByteStream* pStream) {
86
2.08k
  CGIF_Config      gconfig;
87
2.08k
  CGIF_FrameConfig fconfig;
88
2.08k
  CGIF*            pGIF;
89
2.08k
  size_t           sizeImageData;
90
2.08k
  int              r;
91
2.08k
  int              numFrames;
92
93
2.08k
  r = read_gifconfig(pStream, &gconfig);
94
2.08k
  if(!r) {
95
44
    return -1;
96
44
  }
97
2.03k
  sizeImageData    = (uint32_t)gconfig.width * (uint32_t)gconfig.height;
98
  // limit dimensions of GIF to be created
99
2.03k
  if(sizeImageData > (10000 * 10000)) {
100
9
    free(gconfig.pGlobalPalette);
101
9
    return -1;
102
9
  }
103
2.03k
  gconfig.path = "/tmp/out.gif"; // write to temporary file
104
2.03k
  pGIF         = cgif_newgif(&gconfig);
105
2.03k
  free(gconfig.pGlobalPalette);
106
2.03k
  if(pGIF == NULL) {
107
15
    unlink("/tmp/out.gif");
108
15
    return -1;
109
15
  }
110
2.01k
  r = read_frameconfig(pStream, &fconfig, sizeImageData);
111
2.01k
  numFrames = 1;
112
7.32k
  while(r) {
113
5.35k
    cgif_addframe(pGIF, &fconfig);
114
5.35k
    free(fconfig.pImageData);
115
5.35k
    free(fconfig.pLocalPalette);
116
5.35k
    if(numFrames >= 16) {
117
      // limit number of frames to avoid timeouts: 16 should be more than enough
118
37
      break;
119
37
    }
120
5.31k
    numFrames++;
121
5.31k
    r = read_frameconfig(pStream, &fconfig, sizeImageData);
122
5.31k
  }
123
2.01k
  r = cgif_close(pGIF);
124
2.01k
  unlink("/tmp/out.gif");
125
2.01k
  return r;
126
2.03k
}
127
128
#ifdef __cplusplus
129
extern "C"
130
#endif
131
5.29k
int LLVMFuzzerTestOneInput(const uint8_t* pData, size_t numBytes) {
132
5.29k
  ByteStream input = { pData, numBytes, 0 };
133
5.29k
  processInput(&input);
134
5.29k
  return 0;
135
5.29k
}