1 /* Fuzz-testing of libgccjit API.
2 Currently this triggers internal compiler errors, typically due to type
3 mismatches. */
4 #include <limits.h>
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <assert.h>
8
9 #include "libgccjit.h"
10
11 #define TEST_PROVIDES_MAIN
12 #include "harness.h"
13
14 typedef struct fuzzer
15 {
16 gcc_jit_context *ctxt;
17
18 unsigned int seed;
19
20 int num_types;
21 gcc_jit_type **types;
22
23 int num_globals;
24 gcc_jit_lvalue **globals;
25
26 int num_funcs;
27 gcc_jit_function **funcs;
28
29 } fuzzer;
30
31 static void
32 fuzzer_init (fuzzer *f, gcc_jit_context *ctxt, unsigned int seed);
33
34 static int
35 fuzzer_randrange (fuzzer *f, int min, int max);
36
37 static gcc_jit_location *
38 get_random_location (fuzzer *f);
39
40 static gcc_jit_type *
41 get_random_type (fuzzer *f);
42
43 static gcc_jit_type *
44 make_random_type (fuzzer *f);
45
46 static gcc_jit_lvalue *
47 make_random_global (fuzzer *f);
48
49 static gcc_jit_function *
50 make_random_function (fuzzer *f);
51
52 typedef struct function_fuzzer
53 {
54 fuzzer *f;
55
56 int num_params;
57 gcc_jit_param **params;
58
59 gcc_jit_function *fn;
60
61 int num_locals;
62 gcc_jit_lvalue **locals;
63
64 gcc_jit_block *block;
65
66 } function_fuzzer;
67
68 static void
69 function_fuzzer_add_stmt (function_fuzzer *ff);
70
71 static gcc_jit_lvalue *
72 get_random_lvalue (function_fuzzer *ff, int max_depth);
73
74 static gcc_jit_rvalue *
75 get_random_rvalue (function_fuzzer *ff, int max_depth);
76
77 /* fuzzer defns. */
78
79 static void
fuzzer_init(fuzzer * f,gcc_jit_context * ctxt,unsigned int seed)80 fuzzer_init (fuzzer *f, gcc_jit_context *ctxt, unsigned int seed)
81 {
82 int i;
83 memset (f, 0, sizeof (*f));
84 f->ctxt = ctxt;
85 f->seed = seed;
86
87 int num_types = fuzzer_randrange (f, 5, 10);
88 f->types = malloc (num_types * sizeof (gcc_jit_type *));
89
90 int num_funcs = fuzzer_randrange (f, 3, 5);
91 f->funcs = malloc (num_funcs * sizeof (gcc_jit_function *));
92
93 int num_globals = fuzzer_randrange (f, 5, 10);
94 f->globals = malloc (num_globals * sizeof (gcc_jit_lvalue *));
95
96 for (i = 0; i < num_types; i++)
97 {
98 gcc_jit_type *type = make_random_type (f);
99 assert (type);
100 f->types[f->num_types++] = type;
101 }
102
103 for (i = 0; i < num_globals; i++)
104 f->globals[f->num_globals++] = make_random_global (f);
105
106 for (i = 0; i < num_funcs; i++)
107 f->funcs[f->num_funcs++] = make_random_function (f);
108
109 /* Now clean out f. */
110 free (f->types);
111 free (f->funcs);
112 free (f->globals);
113 }
114
115 /* Get random int in inclusive range [min, max]. */
116
fuzzer_randrange(fuzzer * f,int min,int max)117 static int fuzzer_randrange (fuzzer *f, int min, int max)
118 {
119 assert (min <= max);
120 int i = rand_r (&f->seed);
121 int result = (i % (max + 1 - min)) + min;
122 assert (result >= min);
123 assert (result <= max);
124 return result;
125 }
126
127 static gcc_jit_location *
get_random_location(fuzzer * f)128 get_random_location (fuzzer *f)
129 {
130 const char *filename = NULL;
131
132 if (fuzzer_randrange (f, 0, 1))
133 return NULL;
134
135 switch (fuzzer_randrange (f, 1, 2))
136 {
137 case 1:
138 filename = "foo.c";
139 break;
140 case 2:
141 filename = "bar.c";
142 break;
143 }
144
145 return gcc_jit_context_new_location (f->ctxt,
146 filename,
147 fuzzer_randrange (f, 1, 1000),
148 fuzzer_randrange (f, 1, 1000));
149 }
150
151 const enum gcc_jit_types types[] = {
152 GCC_JIT_TYPE_VOID,
153
154 GCC_JIT_TYPE_VOID_PTR,
155
156 GCC_JIT_TYPE_CHAR,
157 GCC_JIT_TYPE_SIGNED_CHAR,
158 GCC_JIT_TYPE_UNSIGNED_CHAR,
159
160 GCC_JIT_TYPE_SHORT,
161 GCC_JIT_TYPE_UNSIGNED_SHORT,
162
163 GCC_JIT_TYPE_INT,
164 GCC_JIT_TYPE_UNSIGNED_INT,
165
166 GCC_JIT_TYPE_LONG,
167 GCC_JIT_TYPE_UNSIGNED_LONG,
168
169 GCC_JIT_TYPE_LONG_LONG,
170 GCC_JIT_TYPE_UNSIGNED_LONG_LONG,
171
172 GCC_JIT_TYPE_FLOAT,
173 GCC_JIT_TYPE_DOUBLE,
174 GCC_JIT_TYPE_LONG_DOUBLE,
175
176 GCC_JIT_TYPE_CONST_CHAR_PTR,
177
178 GCC_JIT_TYPE_SIZE_T,
179
180 GCC_JIT_TYPE_FILE_PTR
181 };
182 #define NUM_TYPES (sizeof(types)/sizeof(types[0]))
183
184 static gcc_jit_type *
get_random_type(fuzzer * f)185 get_random_type (fuzzer *f)
186 {
187 int i = fuzzer_randrange (f, 0, (NUM_TYPES - 1) + f->num_types);
188 if (i < NUM_TYPES)
189 return gcc_jit_context_get_type (f->ctxt, types[i]);
190 assert ((i - NUM_TYPES) < f->num_types);
191 assert (f->types[i - NUM_TYPES]);
192 return f->types[i - NUM_TYPES];
193 }
194
195 static gcc_jit_type *
make_random_type(fuzzer * f)196 make_random_type (fuzzer *f)
197 {
198 switch (fuzzer_randrange (f, 0, 5))
199 {
200 case 0:
201 return gcc_jit_type_get_pointer (get_random_type (f));
202 case 1:
203 return gcc_jit_type_get_const (get_random_type (f));
204 default:
205 {
206 /* Create a struct. */
207 int num_fields = fuzzer_randrange (f, 0, 10);
208 gcc_jit_field **fields = \
209 malloc (num_fields * sizeof (gcc_jit_field *));
210 int i;
211 for (i = 0; i < num_fields ; i++)
212 {
213 char field_name[256];
214 sprintf (field_name, "field%i", i);
215 fields[i] = gcc_jit_context_new_field (f->ctxt,
216 get_random_location (f),
217 get_random_type (f),
218 field_name);
219 }
220 char struct_name[256];
221 sprintf (struct_name, "s%i", f->num_types);
222 gcc_jit_struct *struct_ = \
223 gcc_jit_context_new_struct_type (f->ctxt,
224 get_random_location (f),
225 struct_name,
226 num_fields,
227 fields);
228 free (fields);
229 return gcc_jit_struct_as_type (struct_);
230 }
231 }
232 }
233
234 static gcc_jit_lvalue *
make_random_global(fuzzer * f)235 make_random_global (fuzzer *f)
236 {
237 char global_name[256];
238 sprintf (global_name, "g%i", f->num_globals);
239 return gcc_jit_context_new_global (f->ctxt,
240 get_random_location (f),
241 GCC_JIT_GLOBAL_EXPORTED,
242 get_random_type (f),
243 global_name);
244 }
245
246 static gcc_jit_function *
make_random_function(fuzzer * f)247 make_random_function (fuzzer *f)
248 {
249 char func_name[256];
250 sprintf (func_name, "fn%i", f->num_funcs);
251
252 function_fuzzer *ff = malloc (sizeof (function_fuzzer));
253 memset (ff, 0, sizeof (*ff));
254
255 ff->f = f;
256
257 ff->num_params = fuzzer_randrange (f, 0, 10);
258 ff->params = malloc (ff->num_params * sizeof (gcc_jit_param *));
259 int i;
260 for (i = 0; i < ff->num_params; i++)
261 {
262 char param_name[256];
263 sprintf (param_name, "param%i", i);
264 ff->params[i] = \
265 gcc_jit_context_new_param (f->ctxt,
266 get_random_location (f),
267 get_random_type (f),
268 param_name);
269 }
270
271 enum gcc_jit_function_kind kind =
272 ((enum gcc_jit_function_kind)
273 fuzzer_randrange (f, 0, GCC_JIT_FUNCTION_IMPORTED));
274
275 ff->fn = \
276 gcc_jit_context_new_function (
277 f->ctxt,
278 get_random_location (f),
279 kind,
280 get_random_type (f),
281 func_name,
282 ff->num_params,
283 ff->params,
284 fuzzer_randrange (f, 0, 1));
285 ff->block = gcc_jit_function_new_block (ff->fn, NULL);
286
287 /* Create locals. */
288 if (kind != GCC_JIT_FUNCTION_IMPORTED)
289 {
290 ff->num_locals = fuzzer_randrange (f, 0, 10);
291 ff->locals = malloc (ff->num_locals * sizeof (gcc_jit_lvalue *));
292 for (i = 0; i < ff->num_locals; i++)
293 {
294 char local_name[256];
295 sprintf (local_name, "local%i", i);
296 ff->locals[i] =
297 gcc_jit_function_new_local (ff->fn,
298 get_random_location (f),
299 get_random_type (f),
300 local_name);
301 }
302 }
303 /* TODO: use locals. */
304
305 if (kind != GCC_JIT_FUNCTION_IMPORTED)
306 {
307 /* TODO: create body */
308 int num_stmts = fuzzer_randrange (f, 0, 10);
309 for (i = 0; i < num_stmts; i++)
310 function_fuzzer_add_stmt (ff);
311 }
312
313 gcc_jit_block_end_with_return (ff->block, NULL, get_random_rvalue (ff, 3));
314
315
316 gcc_jit_function *result = ff->fn;
317
318 free (ff->locals);
319 free (ff->params);
320 free (ff);
321
322 return result;
323 }
324
325 /* function_fuzzer defns. */
326
function_fuzzer_add_stmt(function_fuzzer * ff)327 static void function_fuzzer_add_stmt (function_fuzzer *ff)
328 {
329 gcc_jit_block_add_eval (ff->block,
330 get_random_location (ff->f),
331 get_random_rvalue (ff, 4));
332 gcc_jit_block_add_assignment (ff->block,
333 get_random_location (ff->f),
334 get_random_lvalue (ff, 4),
335 get_random_rvalue (ff, 4));
336 /* TODO: place more kinds of statement */
337 /* TODO: labels */
338 }
339
get_random_lvalue(function_fuzzer * ff,int max_depth)340 static gcc_jit_lvalue *get_random_lvalue (function_fuzzer *ff, int max_depth)
341 {
342 int choice = fuzzer_randrange (ff->f, 0,
343 ff->num_params
344 + ff->num_locals
345 + ff->f->num_globals - 1);
346 if (choice < ff->num_params)
347 return gcc_jit_param_as_lvalue (ff->params[choice]);
348 choice -= ff->num_params;
349
350 if (choice < ff->num_locals)
351 return ff->locals[choice];
352 choice -= ff->num_locals;
353
354 assert (choice < ff->f->num_globals);
355 return ff->f->globals[choice];
356 }
357
get_random_rvalue(function_fuzzer * ff,int max_depth)358 static gcc_jit_rvalue *get_random_rvalue (function_fuzzer *ff, int max_depth)
359 {
360 int use_lvalue = fuzzer_randrange (ff->f, 0, 1);
361 if (use_lvalue)
362 return gcc_jit_lvalue_as_rvalue (get_random_lvalue (ff, max_depth));
363
364 int choice = fuzzer_randrange (ff->f, 0, 1);
365
366 /* Compound op: */
367 switch (choice)
368 {
369 case 0:
370 return gcc_jit_context_new_string_literal (ff->f->ctxt, "hello");
371 case 1:
372 return gcc_jit_context_new_rvalue_from_int (
373 ff->f->ctxt,
374 get_random_type (ff->f),
375 fuzzer_randrange (ff->f, 0, INT_MAX));
376 case 2:
377 return gcc_jit_context_new_rvalue_from_double (
378 ff->f->ctxt,
379 get_random_type (ff->f),
380 ((double)fuzzer_randrange (ff->f, 0, INT_MAX))
381 / (double)fuzzer_randrange (ff->f, 0, INT_MAX));
382 case 3:
383 return gcc_jit_context_new_unary_op (
384 ff->f->ctxt,
385 get_random_location (ff->f),
386 ((enum gcc_jit_unary_op)
387 fuzzer_randrange (ff->f, 0, GCC_JIT_UNARY_OP_LOGICAL_NEGATE)),
388 get_random_type (ff->f),
389 get_random_rvalue (ff, max_depth - 1));
390 case 4:
391 return gcc_jit_context_new_binary_op (
392 ff->f->ctxt,
393 get_random_location (ff->f),
394 ((enum gcc_jit_binary_op)
395 fuzzer_randrange (ff->f, 0, GCC_JIT_BINARY_OP_LOGICAL_OR)),
396 get_random_type (ff->f),
397 get_random_rvalue (ff, max_depth - 1),
398 get_random_rvalue (ff, max_depth - 1));
399 case 5:
400 return gcc_jit_lvalue_get_address (
401 get_random_lvalue (ff, max_depth - 1),
402 get_random_location (ff->f));
403
404 /* TODO:
405 - comparisons
406 - calls
407 - array lookup
408 - fields
409 - dereferencing */
410 }
411 return NULL;
412 }
413
414
415 /* Top-level defns for use by harness. */
416 void
create_code(gcc_jit_context * ctxt,void * user_data)417 create_code (gcc_jit_context *ctxt, void *user_data)
418 {
419 fuzzer f;
420 int seed = *(int*)user_data;
421
422 fuzzer_init (&f, ctxt, seed);
423 }
424
425 static int num_completed_compilations = 0;
426
427 void
verify_code(gcc_jit_context * ctxt,gcc_jit_result * result)428 verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
429 {
430 /* We can make no guarantees about whether we built something
431 valid or not, and the result might have an infinite loop,
432 so we can't execute it.
433
434 If we survive to reach here, note the fact for DejaGnu. */
435 pass ("%s: survived compilation", test);
436 if (result)
437 num_completed_compilations++;
438 }
439
440 static void
test_fuzzer(const char * argv0,int seed)441 test_fuzzer (const char *argv0, int seed)
442 {
443 test_jit (argv0, &seed);
444 }
445
446 int
main(int argc,char ** argv)447 main (int argc, char **argv)
448 {
449 int i, seed;
450 const int NUM_ITERATIONS = 2;
451 const int NUM_SEEDS = 100;
452 for (i = 1; i <= NUM_ITERATIONS; i++)
453 {
454 for (seed = 0; seed < NUM_SEEDS ; seed++)
455 {
456 snprintf (test, sizeof (test),
457 "%s iteration %d of %d; seed %d of %d",
458 extract_progname (argv[0]),
459 i, NUM_ITERATIONS, seed, NUM_SEEDS);
460 test_fuzzer (argv[0], seed);
461 }
462 }
463 pass ("%s: survived running all tests", extract_progname (argv[0]));
464 note ("%s: num completed compilations: %d", extract_progname (argv[0]),
465 num_completed_compilations);
466 totals ();
467
468 return 0;
469 }
470