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