Coverage Report

Created: 2026-02-26 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libredwg/examples/llvmfuzz.c
Line
Count
Source
1
/*****************************************************************************/
2
/*  LibreDWG - free implementation of the DWG file format                    */
3
/*                                                                           */
4
/*  Copyright (C) 2021, 2023 Free Software Foundation, Inc.                  */
5
/*                                                                           */
6
/*  This library is free software, licensed under the terms of the GNU       */
7
/*  General Public License as published by the Free Software Foundation,     */
8
/*  either version 3 of the License, or (at your option) any later version.  */
9
/*  You should have received a copy of the GNU General Public License        */
10
/*  along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
11
/*****************************************************************************/
12
13
/*
14
 * llvmfuzz.c: libfuzzer testing, esp. for oss-fuzz. with libfuzzer or
15
 * standalone written by Reini Urban
16
 */
17
18
#include <stdio.h>
19
#include <stdlib.h>
20
#include <assert.h>
21
// #include <unistd.h>
22
#include <sys/stat.h>
23
24
#include "common.h"
25
#include <dwg.h>
26
#ifdef HAVE_SYS_TIME_H
27
#  include <sys/time.h>
28
#endif
29
#include "decode.h"
30
#include "encode.h"
31
#include "bits.h"
32
#ifndef DISABLE_DXF
33
#  include "out_dxf.h"
34
#  ifndef DISABLE_JSON
35
#    include "in_json.h"
36
#    include "out_json.h"
37
#  endif
38
#  include "in_dxf.h"
39
#endif
40
41
int out;
42
int ver;
43
44
extern int LLVMFuzzerTestOneInput (const unsigned char *data, size_t size);
45
46
// libfuzzer limitation:
47
// Enforce NULL-termination of the input buffer, to avoid bogus reports. copy
48
// it. Problematic is mostly strtol(3) which also works with \n termination.
49
static int
50
enforce_null_termination (Bit_Chain *dat, bool enforce)
51
39
{
52
39
  unsigned char *copy;
53
39
  unsigned char c;
54
39
  if (!dat->size)
55
0
    return 0;
56
39
  c = dat->chain[dat->size - 1];
57
  // Allow \n termination without \0 in DXF? No, still crashes
58
39
  if (!enforce && ((c == '\n' && c + 1 == '\0') || c == '\0'))
59
6
    return 0;
60
#ifdef STANDALONE
61
  fprintf (stderr,
62
           "llvmfuzz_standalone: enforce libfuzzer buffer NULL termination\n");
63
#endif
64
33
  copy = malloc (dat->size + 1);
65
33
  memcpy (copy, dat->chain, dat->size);
66
33
  copy[dat->size] = '\0';
67
33
  dat->chain = copy;
68
33
  return 1;
69
39
}
70
71
int
72
LLVMFuzzerTestOneInput (const unsigned char *data, size_t size)
73
370
{
74
370
  Dwg_Data dwg;
75
370
  Bit_Chain dat = { NULL, 0, 0, 0, 0, 0, 0, NULL, 0 };
76
370
  Bit_Chain out_dat = { NULL, 0, 0, 0, 0, 0, 0, NULL, 0 };
77
370
  int copied = 0;
78
370
  struct ly_ctx *ctx = NULL;
79
80
370
  static char tmp_file[256];
81
370
  dat.chain = (unsigned char *)data;
82
370
  dat.size = size;
83
370
  memset (&dwg, 0, sizeof (dwg));
84
85
  // Detect the input format: DWG, DXF or JSON
86
370
  if (dat.size > 2 && dat.chain[0] == 'A' && dat.chain[1] == 'C')
87
331
    {
88
331
      if (dwg_decode (&dat, &dwg) >= DWG_ERR_CRITICAL)
89
275
        {
90
275
          dwg_free (&dwg);
91
275
          return 0;
92
275
        }
93
331
    }
94
39
#ifndef DISABLE_JSON
95
39
  else if (dat.size > 1 && dat.chain[0] == '{')
96
12
    {
97
12
      copied = enforce_null_termination (&dat, true);
98
12
      if (dwg_read_json (&dat, &dwg) >= DWG_ERR_CRITICAL)
99
9
        {
100
9
          if (copied)
101
9
            bit_chain_free (&dat);
102
9
          dwg_free (&dwg);
103
9
          return 0;
104
9
        }
105
3
      dat.opts |= DWG_OPTS_INJSON;
106
3
      dwg.opts |= DWG_OPTS_INJSON;
107
3
    }
108
27
#endif
109
27
#ifndef DISABLE_DXF
110
27
  else
111
27
    {
112
27
      copied = enforce_null_termination (&dat, false);
113
27
      if (dwg_read_dxf (&dat, &dwg) >= DWG_ERR_CRITICAL)
114
27
        {
115
27
          if (copied)
116
21
            bit_chain_free (&dat);
117
27
          dwg_free (&dwg);
118
27
          return 0;
119
27
        }
120
27
    }
121
#else
122
  else
123
    return 0;
124
#endif
125
126
59
  memset (&out_dat, 0, sizeof (out_dat));
127
59
  bit_chain_set_version (&out_dat, &dat);
128
59
  if (copied)
129
3
    bit_chain_free (&dat);
130
131
#if 0
132
    snprintf (tmp_file, 255, "/tmp/llvmfuzzer%d.out", getpid());
133
    tmp_file[255] = '\0';
134
#elif defined _WIN32
135
  strcpy (tmp_file, "NUL");
136
#else
137
59
  strcpy (tmp_file, "/dev/null");
138
59
#endif
139
59
  out_dat.fh = fopen (tmp_file, "w");
140
141
59
  switch (out)
142
59
    {
143
59
    case 0:
144
59
      {
145
59
        switch (ver)
146
59
          {
147
          // TODO support preR13, many downconverters still missing
148
59
          case 0:
149
59
            out_dat.version = dwg.header.version = R_1_4;
150
59
            break;
151
0
          case 1:
152
0
            out_dat.version = dwg.header.version = R_2_0;
153
0
            break;
154
0
          case 2:
155
0
            out_dat.version = dwg.header.version = R_2_10;
156
0
            break;
157
0
          case 3:
158
0
            out_dat.version = dwg.header.version = R_2_21;
159
0
            break;
160
0
          case 4:
161
0
            out_dat.version = dwg.header.version = R_2_4;
162
0
            break;
163
0
          case 5:
164
0
            out_dat.version = dwg.header.version = R_2_6;
165
0
            break;
166
0
          case 6:
167
0
            out_dat.version = dwg.header.version = R_9;
168
0
            break;
169
0
          case 7:
170
0
            out_dat.version = dwg.header.version = R_10;
171
0
            break;
172
0
          case 8:
173
0
            out_dat.version = dwg.header.version = R_11;
174
0
            break;
175
0
          case 9:
176
0
            out_dat.version = dwg.header.version = R_12;
177
0
            break;
178
0
          case 10:
179
0
            out_dat.version = dwg.header.version = R_13;
180
0
            break;
181
0
          case 11:
182
0
            out_dat.version = dwg.header.version = R_13c3;
183
0
            break;
184
0
          case 12:
185
0
            out_dat.version = dwg.header.version = R_14;
186
0
            break;
187
0
          case 13:
188
0
            out_dat.version = dwg.header.version = R_2004;
189
0
            break;
190
0
          default: // favor this one
191
0
            out_dat.version = dwg.header.version = R_2000;
192
0
            break;
193
59
          }
194
59
        dwg_encode (&dwg, &out_dat);
195
59
        break;
196
59
      }
197
0
#ifndef DISABLE_DXF
198
0
    case 1:
199
0
      dwg_write_dxf (&out_dat, &dwg);
200
0
      break;
201
0
    case 2: // experimental
202
0
      dwg_write_dxfb (&out_dat, &dwg);
203
0
      break;
204
0
#  ifndef DISABLE_JSON
205
0
    case 3:
206
0
      dwg_write_json (&out_dat, &dwg);
207
0
      break;
208
0
    case 4:
209
0
      dwg_write_geojson (&out_dat, &dwg);
210
0
      break;
211
0
#  endif
212
0
#endif
213
0
    default:
214
0
      break;
215
59
    }
216
59
  dwg_free (&dwg);
217
59
  free (out_dat.chain);
218
59
  fclose (out_dat.fh);
219
  // unlink (tmp_file);
220
59
  return 0;
221
59
}
222
223
#ifdef STANDALONE
224
/*
225
# ifdef __GNUC__
226
__attribute__((weak))
227
# endif
228
extern int LLVMFuzzerInitialize(int *argc, char ***argv);
229
*/
230
231
static int
232
usage (void)
233
{
234
  printf ("\nUsage: OUT=0 VER=3 llvmfuzz_standalone INPUT...");
235
  return 1;
236
}
237
// llvmfuzz_standalone reproducer, see OUT and VER env vars
238
int
239
main (int argc, char *argv[])
240
{
241
  unsigned seed;
242
  const unsigned int possible_outputformats =
243
#  ifdef DISABLE_DXF
244
#    ifdef DISABLE_JSON
245
      1;
246
#    else
247
      3;
248
#    endif
249
#  else
250
      5;
251
#  endif
252
253
  if (argc <= 1 || !*argv[1])
254
    return usage ();
255
  if (getenv ("SEED"))
256
    seed = (unsigned)strtol (getenv ("SEED"), NULL, 10) % 9999;
257
  else
258
    {
259
#  ifdef HAVE_GETTIMEOFDAY
260
      struct timeval tval;
261
      gettimeofday (&tval, NULL);
262
      seed = (unsigned)(tval.tv_sec * 1000 + tval.tv_usec) % 9999;
263
#  else
264
      seed = (unsigned)time (NULL) % 9999;
265
#  endif
266
    }
267
  srand (seed);
268
  /* works only on linux
269
  if (LLVMFuzzerInitialize)
270
    LLVMFuzzerInitialize (&argc, &argv);
271
  */
272
  for (int i = 1; i < argc; i++)
273
    {
274
      unsigned char *buf;
275
      FILE *f = fopen (argv[i], "rb");
276
      struct stat attrib;
277
      long len;
278
      size_t n_read;
279
      int fd;
280
      if (!f)
281
        {
282
          fprintf (stderr, "Illegal file argument %s\n", argv[i]);
283
          continue;
284
        }
285
      fd = fileno (f);
286
      if (fd < 0 || fstat (fd, &attrib)
287
          || !(S_ISREG (attrib.st_mode)
288
#  ifndef _WIN32
289
               || S_ISLNK (attrib.st_mode)
290
#  endif
291
                   ))
292
        {
293
          fprintf (stderr, "Illegal input file \"%s\"\n", argv[i]);
294
          continue;
295
        }
296
      // libFuzzer design bug, not zero-terminating its text buffer
297
      fseek (f, 0, SEEK_END);
298
      len = ftell (f);
299
      fseek (f, 0, SEEK_SET);
300
      if (len <= 0)
301
        continue;
302
      buf = (unsigned char *)malloc (len);
303
      n_read = fread (buf, 1, len, f);
304
      fclose (f);
305
      assert ((long)n_read == len);
306
307
      out = rand () % possible_outputformats;
308
#  ifdef STANDALONE
309
      if (getenv ("OUT"))
310
        out = strtol (getenv ("OUT"), NULL, 10);
311
      // print SEED onlyu when needed (no env vars given)
312
      if (!(out || getenv ("VER")))
313
        fprintf (stderr, "SEED=%04u ", seed);
314
      fprintf (stderr, "OUT=%d ", out);
315
#  endif
316
      if (out == 0)
317
        {
318
          ver = rand () % 20;
319
#  ifdef STANDALONE
320
          if (getenv ("VER"))
321
            ver = strtol (getenv ("VER"), NULL, 10);
322
          fprintf (stderr, "VER=%d ", ver);
323
#  endif
324
        }
325
      fprintf (stderr, "examples/llvmfuzz_standalone %s [%" PRIuSIZE "]\n",
326
               argv[i], len);
327
      LLVMFuzzerTestOneInput (buf, len);
328
      free (buf);
329
      // Bit_Chain dat = { 0 };
330
      // dat_read_file (&dat, fp, argv[i]);
331
      // LLVMFuzzerTestOneInput (dat.chain, dat.size);
332
      // bit_free_chain (&dat);
333
    }
334
}
335
#endif