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