/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 |