Skip to content

Commit 0031f7e

Browse files
committed
Add CSmith GitHub action
Run 10 randomly generated CSmith tests as a GitHub action to perform an integration test involving CBMC and goto-instrument --dump-c. Each test is first compiled and run using GCC to obtain a checksum, which is then included as an assertion for CBMC to verify.
1 parent deba4ae commit 0031f7e

File tree

2 files changed

+169
-0
lines changed

2 files changed

+169
-0
lines changed

.github/workflows/csmith.yaml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Run CSmith
2+
3+
on:
4+
pull_request:
5+
branches: [ develop ]
6+
7+
jobs:
8+
CompileXen:
9+
runs-on: ubuntu-20.04
10+
steps:
11+
- uses: actions/checkout@v2
12+
with:
13+
submodules: recursive
14+
- name: Fetch dependencies
15+
env:
16+
# This is needed in addition to -yq to prevent apt-get from asking for
17+
# user input
18+
DEBIAN_FRONTEND: noninteractive
19+
run: |
20+
sudo apt-get install --no-install-recommends -y build-essential flex bison maven ccache
21+
sudo apt-get install --no-install-recommends -y csmith libcsmith-dev
22+
make -C src minisat2-download
23+
- name: Prepare ccache
24+
uses: actions/cache@v2
25+
with:
26+
path: .ccache
27+
key: ${{ runner.os }}-20.04-make-${{ github.ref }}-${{ github.sha }}-CSMITH
28+
restore-keys: |
29+
${{ runner.os }}-20.04-make-${{ github.ref }}
30+
${{ runner.os }}-20.04-make
31+
- name: ccache environment
32+
run: |
33+
echo "CCACHE_BASEDIR=$PWD" >> $GITHUB_ENV
34+
echo "CCACHE_DIR=$PWD/.ccache" >> $GITHUB_ENV
35+
- name: Zero ccache stats and limit in size
36+
run: ccache -z --max-size=500M
37+
- name: Build with make
38+
run: |
39+
make -C src CXX='ccache /usr/bin/g++' cbmc.dir goto-cc.dir goto-instrument.dir -j2
40+
- name: Print ccache stats
41+
run: ccache -s
42+
- name: Run 10 randomly-generated CSmith tests
43+
run: |
44+
export PATH=$PWD/src/cbmc:$PWD/src/goto-cc:$PWD/src/goto-instrument:$PATH
45+
scripts/csmith.sh 10

scripts/csmith.sh

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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

Comments
 (0)