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