1 #include "../../common/help.h"
2 #include "ValueType.h"
3 #include "environ.h"
4 #include "generate.h"
5 #include "has-start-state.h"
6 #include "log.h"
7 #include "optimise-field-ordering.h"
8 #include "options.h"
9 #include "resources.h"
10 #include "smt/except.h"
11 #include "smt/simplify.h"
12 #include "utils.h"
13 #include <algorithm>
14 #include <cassert>
15 #include <cstddef>
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cstring>
19 #include <fstream>
20 #include <getopt.h>
21 #include <iostream>
22 #include <memory>
23 #include <rumur/rumur.h>
24 #include <spawn.h>
25 #include <sstream>
26 #include <string>
27 #include <sys/stat.h>
28 #include <sys/wait.h>
29 #include <unistd.h>
30 #include <utility>
31 
32 using namespace rumur;
33 
34 static std::shared_ptr<std::istream> in;
35 static std::shared_ptr<std::string> out;
36 
string_to_percentage(const std::string & s)37 static unsigned string_to_percentage(const std::string &s) {
38   int p;
39   try {
40     p = std::stoi(s);
41     if (p < 1 || p > 100)
42       throw std::invalid_argument("");
43   } catch (std::out_of_range &) {
44     throw std::invalid_argument("");
45   }
46   return (unsigned)p;
47 }
48 
parse_args(int argc,char ** argv)49 static void parse_args(int argc, char **argv) {
50 
51   for (;;) {
52     enum {
53       OPT_BOUND = 128,
54       OPT_COLOUR,
55       OPT_COUNTEREXAMPLE_TRACE,
56       OPT_DEADLOCK_DETECTION,
57       OPT_MAX_ERRORS,
58       OPT_MONOPOLISE,
59       OPT_OUTPUT_FORMAT,
60       OPT_PACK_STATE,
61       OPT_POINTER_BITS,
62       OPT_REORDER_FIELDS,
63       OPT_SANDBOX,
64       OPT_SCALARSET_SCHEDULES,
65       OPT_SMT_ARG,
66       OPT_SMT_BITVECTORS,
67       OPT_SMT_BUDGET,
68       OPT_SMT_PATH,
69       OPT_SMT_PRELUDE,
70       OPT_SMT_SIMPLIFICATION,
71       OPT_SYMMETRY_REDUCTION,
72       OPT_TRACE,
73       OPT_VALUE_TYPE,
74       OPT_VERSION,
75     };
76 
77     static struct option opts[] = {
78         {"bound", required_argument, 0, OPT_BOUND},
79         {"color", required_argument, 0, OPT_COLOUR},
80         {"colour", required_argument, 0, OPT_COLOUR},
81         {"counterexample-trace", required_argument, 0,
82          OPT_COUNTEREXAMPLE_TRACE},
83         {"deadlock-detection", required_argument, 0, OPT_DEADLOCK_DETECTION},
84         {"debug", no_argument, 0, 'd'},
85         {"help", no_argument, 0, 'h'},
86         {"max-errors", required_argument, 0, OPT_MAX_ERRORS},
87         {"monopolise", no_argument, 0, OPT_MONOPOLISE},
88         {"monopolize", no_argument, 0, OPT_MONOPOLISE},
89         {"output", required_argument, 0, 'o'},
90         {"output-format", required_argument, 0, OPT_OUTPUT_FORMAT},
91         {"pack-state", required_argument, 0, OPT_PACK_STATE},
92         {"pointer-bits", required_argument, 0, OPT_POINTER_BITS},
93         {"quiet", no_argument, 0, 'q'},
94         {"reorder-fields", required_argument, 0, OPT_REORDER_FIELDS},
95         {"sandbox", required_argument, 0, OPT_SANDBOX},
96         {"scalarset-schedules", required_argument, 0, OPT_SCALARSET_SCHEDULES},
97         {"set-capacity", required_argument, 0, 's'},
98         {"set-expand-threshold", required_argument, 0, 'e'},
99         {"smt-arg", required_argument, 0, OPT_SMT_ARG},
100         {"smt-bitvectors", required_argument, 0, OPT_SMT_BITVECTORS},
101         {"smt-budget", required_argument, 0, OPT_SMT_BUDGET},
102         {"smt-path", required_argument, 0, OPT_SMT_PATH},
103         {"smt-prelude", required_argument, 0, OPT_SMT_PRELUDE},
104         {"smt-simplification", required_argument, 0, OPT_SMT_SIMPLIFICATION},
105         {"symmetry-reduction", required_argument, 0, OPT_SYMMETRY_REDUCTION},
106         {"threads", required_argument, 0, 't'},
107         {"trace", required_argument, 0, OPT_TRACE},
108         {"value-type", required_argument, 0, OPT_VALUE_TYPE},
109         {"verbose", no_argument, 0, 'v'},
110         {"version", no_argument, 0, OPT_VERSION},
111         {0, 0, 0, 0},
112     };
113 
114     int option_index = 0;
115     int c = getopt_long(argc, argv, "de:ho:qs:t:v", opts, &option_index);
116 
117     if (c == -1)
118       break;
119 
120     switch (c) {
121 
122     case 'd': // --debug
123       options.log_level = LogLevel::DEBUG;
124       set_log_level(options.log_level);
125       break;
126 
127     case 'e': { // --set-expand-threshold ...
128       bool valid = true;
129       try {
130         options.set_expand_threshold = string_to_percentage(optarg);
131       } catch (std::invalid_argument &) {
132         valid = false;
133       }
134       if (!valid) {
135         std::cerr << "invalid --set-expand-threshold argument \"" << optarg
136                   << "\"\n";
137         exit(EXIT_FAILURE);
138       }
139       break;
140     }
141 
142     case 'h': // --help
143       help(doc_rumur_1, doc_rumur_1_len);
144       exit(EXIT_SUCCESS);
145 
146     case 'o': // --output ...
147       out = std::make_shared<std::string>(optarg);
148       break;
149 
150     case 'q': // --quiet
151       options.log_level = LogLevel::SILENT;
152       set_log_level(options.log_level);
153       break;
154 
155     case 's': { // --set-capacity ...
156       bool valid = true;
157       try {
158         options.set_capacity = optarg;
159         if (options.set_capacity <= 0)
160           valid = false;
161       } catch (std::invalid_argument &) {
162         valid = false;
163       }
164       if (!valid) {
165         std::cerr << "invalid --set-capacity argument \"" << optarg << "\"\n";
166         exit(EXIT_FAILURE);
167       }
168       break;
169     }
170 
171     case 't': { // --threads ...
172       bool valid = true;
173       try {
174         options.threads = optarg;
175         if (options.threads < 0)
176           valid = false;
177       } catch (std::invalid_argument &) {
178         valid = false;
179       }
180       if (!valid) {
181         std::cerr << "invalid --threads argument \"" << optarg << "\"\n";
182         exit(EXIT_FAILURE);
183       }
184       break;
185     }
186 
187     case 'v': // --verbose
188       options.log_level = LogLevel::INFO;
189       set_log_level(options.log_level);
190       break;
191 
192     case '?':
193       std::cerr << "run `" << argv[0] << " --help` to see available options\n";
194       exit(EXIT_FAILURE);
195 
196     case OPT_COLOUR: // --colour ...
197       if (strcmp(optarg, "auto") == 0) {
198         options.color = Color::AUTO;
199       } else if (strcmp(optarg, "on") == 0) {
200         if (options.machine_readable_output) {
201           std::cerr << "colour is not supported in combination with "
202                     << "--output-format \"machine readable\"\n";
203           exit(EXIT_FAILURE);
204         }
205         options.color = Color::ON;
206       } else if (strcmp(optarg, "off") == 0) {
207         options.color = Color::OFF;
208       } else {
209         std::cerr << "invalid --colour argument \"" << optarg << "\"\n"
210                   << "valid arguments are \"auto\", \"off\", and \"on\"\n";
211         exit(EXIT_FAILURE);
212       }
213       break;
214 
215     case OPT_TRACE: // --trace ...
216       if (strcmp(optarg, "handle_reads") == 0) {
217         options.traces |= TC_HANDLE_READS;
218       } else if (strcmp(optarg, "handle_writes") == 0) {
219         options.traces |= TC_HANDLE_WRITES;
220       } else if (strcmp(optarg, "memory_usage") == 0) {
221         options.traces |= TC_MEMORY_USAGE;
222       } else if (strcmp(optarg, "queue") == 0) {
223         options.traces |= TC_QUEUE;
224       } else if (strcmp(optarg, "set") == 0) {
225         options.traces |= TC_SET;
226       } else if (strcmp(optarg, "symmetry_reduction") == 0) {
227         options.traces |= TC_SYMMETRY_REDUCTION;
228       } else if (strcmp(optarg, "all") == 0) {
229         options.traces = uint64_t(-1);
230       } else {
231         std::cerr << "invalid --trace argument \"" << optarg << "\"\n"
232                   << "valid arguments are \"handle_reads\", \"handle_writes\", "
233                      "\"memory_usage\", \"queue\", \"set\", and "
234                      "\"symmetry_reduction\"\n";
235         exit(EXIT_FAILURE);
236       }
237       break;
238 
239     case OPT_DEADLOCK_DETECTION: // --deadlock-detection ...
240       if (strcmp(optarg, "off") == 0) {
241         options.deadlock_detection = DeadlockDetection::OFF;
242       } else if (strcmp(optarg, "stuck") == 0) {
243         options.deadlock_detection = DeadlockDetection::STUCK;
244       } else if (strcmp(optarg, "stuttering") == 0) {
245         options.deadlock_detection = DeadlockDetection::STUTTERING;
246       } else {
247         std::cerr << "invalid argument to --deadlock-detection, \"" << optarg
248                   << "\"\n";
249         exit(EXIT_FAILURE);
250       }
251       break;
252 
253     case OPT_MONOPOLISE: { // --monopolise
254 
255       long pagesize = sysconf(_SC_PAGESIZE);
256       if (pagesize < 0) {
257         perror("failed to retrieve page size");
258         exit(EXIT_FAILURE);
259       }
260 
261       long physpages = sysconf(_SC_PHYS_PAGES);
262       if (physpages < 0) {
263         perror("failed to retrieve physical pages");
264         exit(EXIT_FAILURE);
265       }
266 
267       /* Allocate a set that will eventually cover all of memory upfront. Note
268        * that this will never actually reach 100% occupancy because memory
269        * also needs to contain our code and data as well as the OS.
270        */
271       options.set_capacity = size_t(pagesize) * size_t(physpages);
272 
273       // Never expand the set.
274       options.set_expand_threshold = 100;
275 
276       break;
277     }
278 
279     case OPT_PACK_STATE: // --pack-state ...
280       if (strcmp(optarg, "on") == 0) {
281         options.pack_state = true;
282       } else if (strcmp(optarg, "off") == 0) {
283         options.pack_state = false;
284       } else {
285         std::cerr << "invalid argument to --pack_state, \"" << optarg << "\"\n";
286         exit(EXIT_FAILURE);
287       }
288       break;
289 
290     case OPT_POINTER_BITS: // --pointer-bits ...
291       if (strcmp(optarg, "auto") == 0) {
292         options.pointer_bits = 0;
293       } else {
294         bool valid = true;
295         try {
296           options.pointer_bits = optarg;
297           if (options.pointer_bits <= 0)
298             valid = false;
299         } catch (std::invalid_argument &) {
300           valid = false;
301         }
302         if (!valid) {
303           std::cerr << "invalid --pointer-bits argument \"" << optarg << "\"\n";
304           exit(EXIT_FAILURE);
305         }
306       }
307       break;
308 
309     case OPT_SYMMETRY_REDUCTION: // --symmetry-reduction ...
310       if (strcmp(optarg, "off") == 0) {
311         options.symmetry_reduction = SymmetryReduction::OFF;
312       } else if (strcmp(optarg, "heuristic") == 0) {
313         options.symmetry_reduction = SymmetryReduction::HEURISTIC;
314       } else if (strcmp(optarg, "exhaustive") == 0) {
315         options.symmetry_reduction = SymmetryReduction::EXHAUSTIVE;
316       } else {
317         std::cerr << "invalid argument to --symmetry-reduction, \"" << optarg
318                   << "\"\n";
319         exit(EXIT_FAILURE);
320       }
321       break;
322 
323     case OPT_SANDBOX: // --sandbox ...
324       if (strcmp(optarg, "on") == 0) {
325         options.sandbox_enabled = true;
326       } else if (strcmp(optarg, "off") == 0) {
327         options.sandbox_enabled = false;
328       } else {
329         std::cerr << "invalid argument to --sandbox, \"" << optarg << "\"\n";
330         exit(EXIT_FAILURE);
331       }
332       break;
333 
334     case OPT_SCALARSET_SCHEDULES: // --scalarset-schedules ...
335       if (strcmp(optarg, "on") == 0) {
336         options.scalarset_schedules = true;
337       } else if (strcmp(optarg, "off") == 0) {
338         options.scalarset_schedules = false;
339       } else {
340         std::cerr << "invalid argument to --scalarset-schedules, \"" << optarg
341                   << "\"\n";
342         exit(EXIT_FAILURE);
343       }
344       break;
345 
346     case OPT_MAX_ERRORS: { // --max-errors ...
347       bool valid = true;
348       try {
349         options.max_errors = optarg;
350         if (options.max_errors <= 0)
351           valid = false;
352       } catch (std::invalid_argument &) {
353         valid = false;
354       }
355       if (!valid) {
356         std::cerr << "invalid --max-errors argument \"" << optarg << "\"\n";
357         exit(EXIT_FAILURE);
358       }
359       break;
360     }
361 
362     case OPT_COUNTEREXAMPLE_TRACE: // --counterexample-trace ...
363       if (strcmp(optarg, "full") == 0) {
364         options.counterexample_trace = CounterexampleTrace::FULL;
365       } else if (strcmp(optarg, "diff") == 0) {
366         options.counterexample_trace = CounterexampleTrace::DIFF;
367       } else if (strcmp(optarg, "off") == 0) {
368         options.counterexample_trace = CounterexampleTrace::OFF;
369       } else {
370         std::cerr << "invalid argument to --counterexample-trace, \"" << optarg
371                   << "\"\n";
372         exit(EXIT_FAILURE);
373       }
374       break;
375 
376     case OPT_OUTPUT_FORMAT: // --output-format ...
377       if (strcmp(optarg, "machine-readable") == 0) {
378         options.machine_readable_output = true;
379         // Disable colour that would interfere with XML
380         options.color = Color::OFF;
381       } else if (strcmp(optarg, "human-readable") == 0) {
382         options.machine_readable_output = false;
383       } else {
384         std::cerr << "invalid argument to --output-format, \"" << optarg
385                   << "\"\n";
386         exit(EXIT_FAILURE);
387       }
388       break;
389 
390     case OPT_VERSION: // --version
391       std::cout << "Rumur version " << get_version() << "\n";
392       exit(EXIT_SUCCESS);
393 
394     case OPT_BOUND: { // --bound ...
395       bool valid = true;
396       try {
397         options.bound = optarg;
398         if (options.bound < 0)
399           valid = false;
400       } catch (std::invalid_argument &) {
401         valid = false;
402       }
403       if (!valid) {
404         std::cerr << "invalid --bound argument \"" << optarg << "\"\n";
405         exit(EXIT_FAILURE);
406       }
407       break;
408     }
409 
410     case OPT_VALUE_TYPE: // --value-type ...
411       options.value_type = optarg;
412       break;
413 
414     case OPT_REORDER_FIELDS: // --reorder-fields ...
415       if (strcmp(optarg, "on") == 0) {
416         options.reorder_fields = true;
417       } else if (strcmp(optarg, "off") == 0) {
418         options.reorder_fields = false;
419       } else {
420         std::cerr << "invalid argument to --reorder-fields, \"" << optarg
421                   << "\"\n";
422         exit(EXIT_FAILURE);
423       }
424       break;
425 
426     case OPT_SMT_ARG: // --smt-arg ...
427       options.smt.args.emplace_back(optarg);
428       if (options.smt.simplification == SmtSimplification::AUTO) {
429         options.smt.simplification = SmtSimplification::ON;
430       }
431       break;
432 
433     case OPT_SMT_BITVECTORS: // --smt-bitvectors ...
434       if (strcmp(optarg, "on") == 0) {
435         options.smt.use_bitvectors = true;
436       } else if (strcmp(optarg, "off") == 0) {
437         options.smt.use_bitvectors = false;
438       } else {
439         std::cerr << "invalid argument to --smt-bitvectors, \"" << optarg
440                   << "\"\n";
441         exit(EXIT_FAILURE);
442       }
443       if (options.smt.simplification == SmtSimplification::AUTO) {
444         options.smt.simplification = SmtSimplification::ON;
445       }
446       break;
447 
448     case OPT_SMT_BUDGET: { // --smt-budget ...
449       bool valid = true;
450       try {
451         options.smt.budget = optarg;
452         if (options.smt.budget < 0)
453           valid = false;
454       } catch (std::invalid_argument &) {
455         valid = false;
456       }
457       if (!valid) {
458         std::cerr << "invalid --smt-budget, \"" << optarg << "\"\n";
459         exit(EXIT_FAILURE);
460       }
461       if (options.smt.simplification == SmtSimplification::AUTO) {
462         options.smt.simplification = SmtSimplification::ON;
463       }
464       break;
465     }
466 
467     case OPT_SMT_PATH: // --smt-path ...
468       options.smt.path = optarg;
469       if (options.smt.simplification == SmtSimplification::AUTO) {
470         options.smt.simplification = SmtSimplification::ON;
471       }
472       break;
473 
474     case OPT_SMT_PRELUDE: // --smt-prelude ...
475       options.smt.prelude.emplace_back(optarg);
476       if (options.smt.simplification == SmtSimplification::AUTO) {
477         options.smt.simplification = SmtSimplification::ON;
478       }
479       break;
480 
481     case OPT_SMT_SIMPLIFICATION: // --smt-simplification ...
482       if (strcmp(optarg, "on") == 0) {
483         options.smt.simplification = SmtSimplification::ON;
484       } else if (strcmp(optarg, "off") == 0) {
485         options.smt.simplification = SmtSimplification::OFF;
486       } else {
487         std::cerr << "invalid argument to --smt-simplification, \"" << optarg
488                   << "\"\n";
489         exit(EXIT_FAILURE);
490       }
491       break;
492 
493     default:
494       std::cerr << "unexpected error\n";
495       exit(EXIT_FAILURE);
496     }
497   }
498 
499   if (optind == argc - 1) {
500     struct stat buf;
501     if (stat(argv[optind], &buf) < 0) {
502       std::cerr << "failed to open " << argv[optind] << ": " << strerror(errno)
503                 << "\n";
504       exit(EXIT_FAILURE);
505     }
506 
507     if (S_ISDIR(buf.st_mode)) {
508       std::cerr << "failed to open " << argv[optind]
509                 << ": this is a directory\n";
510       exit(EXIT_FAILURE);
511     }
512 
513     auto inf = std::make_shared<std::ifstream>(argv[optind]);
514     if (!inf->is_open()) {
515       std::cerr << "failed to open " << argv[optind] << "\n";
516       exit(EXIT_FAILURE);
517     }
518     input_filename = argv[optind];
519     in = inf;
520   }
521 
522   if (out == nullptr) {
523     std::cerr << "output file is required\n";
524     exit(EXIT_FAILURE);
525   }
526 
527   if (options.threads == 0) {
528     // automatic
529     long r = sysconf(_SC_NPROCESSORS_ONLN);
530     if (r < 1) {
531       options.threads = 1;
532     } else {
533       options.threads = r;
534     }
535   }
536 
537   if (options.smt.simplification == SmtSimplification::ON &&
538       options.smt.path == "") {
539     *warn << "SMT simplification was enabled but no path was provided to the "
540           << "solver (--smt-path ...), so it will be disabled\n";
541     options.smt.simplification = SmtSimplification::OFF;
542   }
543 }
544 
use_colors()545 static bool use_colors() {
546   return options.color == Color::ON ||
547          (options.color == Color::AUTO && isatty(STDERR_FILENO));
548 }
549 
bold()550 static std::string bold() {
551   if (use_colors())
552     return "\033[1m";
553   return "";
554 }
555 
green()556 static std::string green() {
557   if (use_colors())
558     return "\033[32m";
559   return "";
560 }
561 
red()562 static std::string red() {
563   if (use_colors())
564     return "\033[31m";
565   return "";
566 }
567 
reset()568 static std::string reset() {
569   if (use_colors())
570     return "\033[0m";
571   return "";
572 }
573 
white()574 static std::string white() {
575   if (use_colors())
576     return "\033[37m";
577   return "";
578 }
579 
print_location(const std::string & file,const location & location)580 static void print_location(const std::string &file, const location &location) {
581 
582   // don't try to open stdin (see default in options.cc)
583   if (file == "<stdin>")
584     return;
585 
586   std::ifstream f(file);
587   if (!f.is_open()) {
588     // ignore failure here as it's non-critical
589     return;
590   }
591 
592   // the type of position.line and position.column changes across Bison
593   // releases, so avoid some -Wsign-compare warnings by casting them in advance
594   auto loc_line = static_cast<unsigned long>(location.begin.line);
595   auto loc_col = static_cast<unsigned long>(location.begin.column);
596 
597   std::string line;
598   unsigned long lineno = 0;
599   while (lineno < loc_line) {
600     if (!std::getline(f, line))
601       return;
602     lineno++;
603   }
604 
605   // print the line, and construct an underline indicating the column location
606   std::ostringstream buf;
607   unsigned long col = 1;
608   for (const char &c : line) {
609     if (col == loc_col) {
610       buf << green() << bold() << "^" << reset();
611     } else if (col < loc_col) {
612       if (c == '\t') {
613         buf << '\t';
614       } else {
615         buf << ' ';
616       }
617     }
618     std::cerr << c;
619     col++;
620   }
621   std::cerr << "\n";
622 
623   std::cerr << buf.str() << "\n";
624 }
625 
main(int argc,char ** argv)626 int main(int argc, char **argv) {
627 
628   // Parse command line options
629   parse_args(argc, argv);
630 
631   // Parse input model
632   *debug << "parsing input model...\n";
633   Ptr<Model> m;
634   try {
635     m = parse(in == nullptr ? std::cin : *in);
636   } catch (Error &e) {
637     std::cerr << white() << bold() << input_filename << ":" << e.loc << ":"
638               << reset() << " " << red() << bold() << "error:" << reset() << " "
639               << white() << bold() << e.what() << reset() << "\n";
640     print_location(input_filename, e.loc);
641     return EXIT_FAILURE;
642   }
643 
644   assert(m != nullptr);
645 
646   /* Re-index the model (assign unique identifiers to each node that are used in
647    * generation of the verifier).
648    */
649   *debug << "re-indexing...\n";
650   m->reindex();
651 
652   // resolve symbolic references and validate the model
653   try {
654     *debug << "resolving symbols...\n";
655     resolve_symbols(*m);
656     *debug << "validating AST...\n";
657     validate(*m);
658   } catch (Error &e) {
659     std::cerr << white() << bold() << input_filename << ":" << e.loc << ":"
660               << reset() << " " << red() << bold() << "error:" << reset() << " "
661               << white() << bold() << e.what() << reset() << "\n";
662     print_location(input_filename, e.loc);
663     return EXIT_FAILURE;
664   }
665 
666   // Check whether we have a start state.
667   if (!has_start_state(*m))
668     *warn << "warning: model has no start state\n";
669 
670   // run SMT simplification if the user enabled it
671   if (options.smt.simplification == SmtSimplification::ON) {
672     *debug << "SMT simplification...\n";
673     try {
674       smt::simplify(*m);
675     } catch (smt::BudgetExhausted &) {
676       *info << "SMT solver budget (" << options.smt.budget << "ms) exhausted\n";
677     } catch (smt::Unsupported &e) {
678       if (e.expr != nullptr)
679         *info << e.expr->loc << ": ";
680       *info << e.what() << "\n";
681     }
682   }
683 
684   // re-order fields to optimise access to them
685   if (options.reorder_fields) {
686     *debug << "optimising field ordering...\n";
687     optimise_field_ordering(*m);
688   }
689 
690   // get value_t to use in the checker
691   *debug << "determining value_t type...\n";
692   std::pair<ValueType, ValueType> value_types;
693   try {
694     value_types = get_value_type(options.value_type, *m);
695   } catch (std::runtime_error &e) {
696     std::cerr << "invalid --value-type " << options.value_type << ": "
697               << e.what() << "\n";
698     return EXIT_FAILURE;
699   }
700 
701   *debug << "generating verifier...\n";
702   assert(out != nullptr);
703   if (output_checker(*out, *m, value_types) != 0)
704     return EXIT_FAILURE;
705 
706 #ifndef __AFL_COMPILER
707 #define __AFL_COMPILER 0
708 #endif
709 
710   if (__AFL_COMPILER) { // extra steps for when we're being fuzzed
711 
712     // find the C compiler
713     const char *cc = getenv("CC");
714     if (cc == nullptr)
715       cc = "cc";
716 
717     // setup an argument vector for calling the C compiler
718     const char *args[] = {cc,
719                           "-std=c11",
720                           "-x",
721                           "c",
722                           "-o",
723                           "/dev/null",
724                           "-Werror=format",
725                           "-Werror=sign-compare",
726                           "-Werror=type-limits",
727                           out->c_str(),
728 #ifdef __x86_64__
729                           "-mcx16",
730 #endif
731                           "-lpthread"};
732 
733     // start the C compiler
734     int r = posix_spawnp(nullptr, args[0], nullptr, nullptr,
735                          const_cast<char *const *>(args), get_environ());
736     if (r != 0) {
737       std::cerr << "posix_spawnp failed: " << strerror(r) << "\n";
738       abort();
739     }
740 
741     // wait for it to finish
742     int stat_loc;
743     pid_t pid = wait(&stat_loc);
744     if (pid == -1) {
745       std::cerr << "wait failed\n";
746       abort();
747     }
748 
749     // if it terminated abnormally, pass an error up to the fuzzer
750     if (WIFSIGNALED(stat_loc) || WIFSTOPPED(stat_loc)) {
751       std::cerr << "subprocess either signalled or stopped\n";
752       abort();
753     }
754 
755     assert(WIFEXITED(stat_loc));
756   }
757 
758   return EXIT_SUCCESS;
759 }
760