|
| 1 | +#!/bin/bash |
| 2 | +# |
| 3 | +# This script runs CSmith to generate N examples, and for each of them: |
| 4 | +# 1. Compiles the code with the system compiler. |
| 5 | +# 2. Runs the compiled code to obtain the checksum that the CSmith test |
| 6 | +# computes. |
| 7 | +# 3. Takes that checksum to generate an assertion. |
| 8 | +# 4. Runs CBMC (for up to 90 seconds and with a loop unwinding of 257) to verify |
| 9 | +# the generated assertion. If CBMC were to come up with a counterexample, then |
| 10 | +# CBMC's symbolic execution would differ from the concrete execution of what |
| 11 | +# ought to be a fully deterministic test. Such a failing test would therefore |
| 12 | +# indicate a bug in CBMC. For such a failing case, the source code of the test |
| 13 | +# is emitted. |
| 14 | +# 5. If CBMC's execution completed successfully or timed out, the test is next |
| 15 | +# compiled using goto-cc and dumped back to C code using goto-instrument. |
| 16 | +# 6. The dumped C code is compiled using GCC and executed again, computing a |
| 17 | +# fresh checksum. |
| 18 | +# 7. This checksum is compared to the previous one to ensure that the program is |
| 19 | +# equivalent to the original test generated by CSmith. |
| 20 | +# |
| 21 | +# The number "N" of such tests run can be specified as (the only) command-line |
| 22 | +# argument to this script. If no command-line arguments are provided, only a |
| 23 | +# single test is run. |
| 24 | +# |
| 25 | +# The script assumes that csmith (including its development headers) is |
| 26 | +# installed (or otherwise found on the PATH). cbmc, goto-cc, and goto-instrument |
| 27 | +# must also be found via the system PATH. |
| 28 | + |
| 29 | +set -e |
| 30 | + |
| 31 | +workdir=$(mktemp -d csmith.XXX) |
| 32 | +print_dir() { |
| 33 | + echo "CSmith working directory: $workdir" |
| 34 | +} |
| 35 | + |
| 36 | +# Run a single (previously generated) test. |
| 37 | +csmith_test() { |
| 38 | + trap print_dir ERR |
| 39 | + |
| 40 | + local f=$1 |
| 41 | + local r=$(cbmc --version) |
| 42 | + local bn=`basename $f` |
| 43 | + bn=`basename $bn .c` |
| 44 | + |
| 45 | + echo "Trying $f with build $r" |
| 46 | + |
| 47 | + # Compile and run (for up to 30 seconds) the CSmith-generated test. |
| 48 | + gcc -I/usr/include/csmith -w $f -o $bn |
| 49 | + check="`timeout 30 ./$bn | sed 's/checksum = /0x/'`" |
| 50 | + if [ -z "$check" ] ; then |
| 51 | + echo "TIMEOUT while executing CSmith test" |
| 52 | + return 0 |
| 53 | + fi |
| 54 | + |
| 55 | + # Prepare the test CBMC by injecting the checksum as an assertion. |
| 56 | + gcc -E -I/usr/include/csmith -D__FRAMAC \ |
| 57 | + -D'Frama_C_dump_assert_each()'="assert($check==(crc))" $f -o $bn.i |
| 58 | + # Run the test using CBMC for up to 90 seconds, unwinding loops up to 257 |
| 59 | + # times. |
| 60 | + ec=0 |
| 61 | + /usr/bin/time -v timeout -k15 90 cbmc \ |
| 62 | + --unwind 257 --no-unwinding-assertions --object-bits 13 \ |
| 63 | + $bn.i || ec=$? |
| 64 | + if [ $ec -eq 124 ] || [ $ec -eq 137 ] ; then |
| 65 | + echo "TIMEOUT in CBMC" |
| 66 | + elif [ $ec -eq 6 ] ; then |
| 67 | + echo "Limitation in CBMC" |
| 68 | + elif [ $ec -ne 0 ] ; then |
| 69 | + cat $f |
| 70 | + return 1 |
| 71 | + fi |
| 72 | +
|
| 73 | + # Compile the test using goto-cc and dump it back to C code using |
| 74 | + # goto-instrument. |
| 75 | + goto-cc -I /usr/include/csmith $f -o $bn |
| 76 | + goto-instrument --dump-c $bn ${bn}_dumped.c |
| 77 | + # Re-compile the dumped test and execute again to confirm the checksum hasn't |
| 78 | + # changed. |
| 79 | + gcc ${bn}_dumped.c -D__CPROVER_bool=_Bool -w -o $bn |
| 80 | + check_d="`timeout 30 ./$bn | sed 's/checksum = /0x/'`" |
| 81 | + if [ -z "$check_d" ] ; then |
| 82 | + echo "TIMEOUT while executing dumped CSmith test" |
| 83 | + return 0 |
| 84 | + fi |
| 85 | + if [ "$check" != "$check_d" ] ; then |
| 86 | + echo "Checksum mismatch: GCC: $check dump-c: $check_d" |
| 87 | + cat $f |
| 88 | + return 1 |
| 89 | + fi |
| 90 | + |
| 91 | + echo "OK: $f" |
| 92 | + echo |
| 93 | +} |
| 94 | + |
| 95 | +# Generate and run a number of tests. |
| 96 | +csmith_random_test() { |
| 97 | + trap print_dir ERR |
| 98 | + |
| 99 | + for i in `seq 1 $1`; do |
| 100 | + seed=`date +%s` |
| 101 | + echo "Random seed being used: $seed" |
| 102 | + csmith --seed $seed > t_$i.c |
| 103 | + if ! csmith_test t_$i.c ; then |
| 104 | + echo "Failed test obtained with random seed $seed" |
| 105 | + return 1 |
| 106 | + fi |
| 107 | + done |
| 108 | +} |
| 109 | + |
| 110 | +# Determine the number of tests to run from the command line; the default is to |
| 111 | +# run a single test. |
| 112 | +N=$1 |
| 113 | +if [ -z $N ] ; then |
| 114 | + N=1 |
| 115 | +fi |
| 116 | + |
| 117 | +basedir=$PWD |
| 118 | +cd $workdir |
| 119 | +trap print_dir ERR |
| 120 | + |
| 121 | +csmith_random_test $N |
| 122 | + |
| 123 | +cd $basedir |
| 124 | +rm -rf $workdir |
0 commit comments