Usage
Video Demo
LLBMC: The Low-Level Bounded Model Checker
Example 1: Detecting Memory Access Violations
Consider, as a first example, the following C program:
1
2
3 int a[3];
4
5 void foo(int m)
6 {
7 int i;
8
9 for (i = 0; i < m; i++)
10 a[i] = i;
11 }
In line 10 of this program (ex1.c), a memory access error occurs if m > 3. To run LLBMC on this example, we first have to compile it to LLVM IR (intermediate representation, bitcode)
using LLVM's clang compiler:
clang -c -g -emit-llvm ex1.c -o ex1.bc
clang produces the bitcode file 'ex1.bc', which we can then pass to LLBMC:
LLBMC reports—among others—that the default loop iteration bound (zero in this case)
has not been sufficient for the check
(often LLBMC can determine a suitable loop bound automatically, though):
Running LLBMC with automatically determined function call depth and
at most 0 iterations for loops with non-derivable trip count.
Loop iteration bound is not sufficient. The program analysis is thus not complete.
Increase the limit using "--max-loop-iterations=<uint>" or switch off
loop unroll checks with the option "--no-max-loop-iterations-checks".
This is the Low-Level Bounded Model Checker LLBMC (version 2012.2).
Running LLBMC with automatically determined function call depth and
at most 0 iterations for loops with non-derivable trip count.
3 assertions before simplification (1 no_back_edge, 1 valid_arithmetic,
1 valid_store).
Performing formula transformations and simplifications... done. [0.000 sec]
1 assertion remains after simplification (1 no_back_edge).
Running SMT solver (STP with MiniSat)... done. [0.004 sec]
Result:
=======
Error detected.
Error synopsis:
===============
Loop iteration bound is not sufficient. The program analysis is thus not complete.
Increase the limit using "--max-loop-iterations=<uint>" or switch off
loop unroll checks with the option "--no-max-loop-iterations-checks".
Error location:
===============
Error occurs in basic block "for.inc" of function "foo".
Source file: ex1.c
Line / column number: 9:8
Error context:
4:
5: void foo(int m)
6: {
7: int i;
8:
9: for (i = 0; i < m; i++)
^^^^^^^^^^^^^^^^^^^^^^^
10: a[i] = i; // memory access violation, if m > 3
11: }
Stack trace:
============
#0 void @foo(i32 %m=2)
We thus tell LLBMC, as suggested, to use a larger loop iteration bound using the option
--max-loop-iterations=<uint>.
A bound of 4 is sufficient:
llbmc ex1.bc --max-loop-iterations=4
LLBMC then correctly detects a write access error in line 10 of the program:
Error detected.
Assertion failed: Memory write access error in the store instruction
store i32 %i.0.3, i32* %arrayidx.3
Source file: ex1.c
Line / column number: 10:5
This is the Low-Level Bounded Model Checker LLBMC (version 2012.2).
Running LLBMC with automatically determined function call depth and
at most 4 iterations for loops with non-derivable trip count.
9 assertions before simplification (1 no_back_edge, 4 valid_arithmetic,
4 valid_store).
Performing formula transformations and simplifications... done. [0.000 sec]
1 assertion remains after simplification (1 valid_store).
Running SMT solver (STP with MiniSat)... done. [0.004 sec]
Result:
=======
Error detected.
Error synopsis:
===============
Assertion failed: Memory write access error in the store instruction
store i32 %i.0.3, i32* %arrayidx.3, align 4
when trying to write to the invalid address
%arrayidx.3 = getelementptr inbounds [3 x i32]* @a, i32 0, i32 %i.0.3
where
%i.0.3 = 3
Error location:
===============
Error occurs in basic block "for.body.3" of function "foo".
Source file: ex1.c
Line / column number: 10:5
Error context:
5: void foo(int m)
6: {
7: int i;
8:
9: for (i = 0; i < m; i++)
10: a[i] = i; // memory access violation, if m > 3
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
11: }
Stack trace:
============
#0 void @foo(i32 %m=6)
For detailed information about when the error occurs, we run LLBMC with the additional option
--counterexample. It then produces an error trace (on the level of LLVM's intermediate representation),
which shows exactly how the program gets to the error location. In this case, we can see that
the error occurs, e.g., when i=3 and m=6.
Counter-example:
================
@a = common global [3 x i32] zeroinitializer ; @ 0x1ffffff4
define void @foo(i32 %m) {
; i32 %m = 6
...
%i.0.3 = phi i32 [ %inc.3, %for.inc.3 ], [ %inc.2, %for.inc.2 ] ; 3
%arrayidx.3 = getelementptr inbounds [3 x i32]* @a, i32 0, i32 %i.0.3 ; 0x20000000
store i32 %i.0.3, i32* %arrayidx.3 ; INVALID MEMORY ACCESS
...
This is the Low-Level Bounded Model Checker LLBMC (version 2012.2).
Running LLBMC with automatically determined function call depth and
at most 4 iterations for loops with non-derivable trip count.
9 assertions before simplification (1 no_back_edge, 4 valid_arithmetic,
4 valid_store).
Performing formula transformations and simplifications... done. [0.000 sec]
9 assertions remain after simplification (1 no_back_edge, 4 valid_arithmetic,
4 valid_store).
Running SMT solver (STP with MiniSat)... done. [0.005 sec]
Result:
=======
Error detected.
Counter-example:
================
globals:
@a = common global [3 x i32] zeroinitializer, align 4 ; @ 0x1ffffff4
define void @foo(i32 %m) nounwind ssp {
; i32 %m = 6
entry: ; executed
%0 = call i8* @llvm.stacksave()
%cmp1 = icmp slt i32 0, %m ; 1
br i1 %cmp1, label %for.body.lr.ph, label %for.end ; branched to for.body.lr.ph
for.body.lr.ph: ; executed
br label %for.body
for.body: ; executed
%i.0 = phi i32 [ 0, %for.body.lr.ph ] ; 0
%arrayidx = getelementptr inbounds [3 x i32]* @a, i32 0, i32 %i.0 ; 0x1ffffff4
store i32 %i.0, i32* %arrayidx, align 4
; 1ffffff0 : 00 00 00 00 | | 00 00 00 00 | 00 00 00 00 : ................
; default = 00
br label %for.inc
for.inc: ; executed
%inc = add nsw i32 %i.0, 1 ; 1
%exitcond = icmp ne i32 %inc, %m ; 1
br i1 %exitcond, label %for.body.1, label %for.cond.for.end_crit_edge
; branched to for.body.1
for.body.1: ; executed
%i.0.1 = phi i32 [ %inc, %for.inc ] ; 1
%arrayidx.1 = getelementptr inbounds [3 x i32]* @a, i32 0, i32 %i.0.1 ; 0x1ffffff8
store i32 %i.0.1, i32* %arrayidx.1, align 4
; 1ffffff0 : 00 00 00 00 | 00 00 00 00 | | 00 00 00 00 : ................
; default = 00
br label %for.inc.1
for.inc.1: ; executed
%inc.1 = add nsw i32 %i.0.1, 1 ; 2
%exitcond.1 = icmp ne i32 %inc.1, %m ; 1
br i1 %exitcond.1, label %for.body.2, label %for.cond.for.end_crit_edge
; branched to for.body.2
for.body.2: ; executed
%i.0.2 = phi i32 [ %inc.1, %for.inc.1 ] ; 2
%arrayidx.2 = getelementptr inbounds [3 x i32]* @a, i32 0, i32 %i.0.2 ; 0x1ffffffc
store i32 %i.0.2, i32* %arrayidx.2, align 4
; 1ffffff0 : 00 00 00 00 | 00 00 00 00 | 01 00 00 00 | : ................
; default = 00
br label %for.inc.2
for.inc.2: ; executed
%inc.2 = add nsw i32 %i.0.2, 1 ; 3
%exitcond.2 = icmp ne i32 %inc.2, %m ; 1
br i1 %exitcond.2, label %for.body.3, label %for.cond.for.end_crit_edge
; branched to for.body.3
for.body.3: ; executed
%i.0.3 = phi i32 [ %inc.3, %for.inc.3 ], [ %inc.2, %for.inc.2 ] ; 3
%arrayidx.3 = getelementptr inbounds [3 x i32]* @a, i32 0, i32 %i.0.3 ; 0x20000000
store i32 %i.0.3, i32* %arrayidx.3, align 4 ; INVALID MEMORY ACCESS
; 1ffffff0 : 00 00 00 00 | 00 00 00 00 | 01 00 00 00 | 02 00 00 00 : ................
; 20000000 : | 00 00 00 00 | 00 00 00 00 | 00 00 00 00 : ................
; default = 00
This concludes our first example.
Example 2: Functional Equivalence
In our second example, we want to check whether two functions exhibit equivalent behavior. Both functions
are intended to compute the population count of a 32-bit integer (i.e. the number of 1-bits in
its binary representation). We therefore set up a test function (called __llbmc_main
in our example), which calls both functions with an uninitalized input variable x, and
then compares their results using __llbmc_assert. This has the effect, that
LLBMC searches through all possible values of x for a value on which the assertion
fails. (Instead of using an undetermined parameter for function __llbmc_main, we could
also have set variable x to an undefined—and thus non-deterministically chosen—value
explicitly using LLBMC's function __llbmc_nondef_uint32_t.)
1
2
3 #include <stdint.h>
4 #include "llbmc.h"
5
6 uint32_t optimized_popcount(uint32_t x)
7 {
8 x = x - ((x >> 1 ) & 0x55555555);
9 x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
10 x = (x + (x >> 4)) & 0x0F0F0F0F;
11 x += (x >> 8);
12 x += (x >> 16);
13 return x & 0x0000003F;
14 }
15
16 uint32_t reference_popcount(uint32_t x)
17 {
18 int i, s = 0;
19 for(i = 0; i < 32; ++i)
20 if(x & (1 << i))
21 s++;
22 return s;
23 }
24
25 void __llbmc_main(uint32_t x)
26 {
27 uint32_t opt = optimized_popcount(x);
28 uint32_t ref = reference_popcount(x);
29
30 __llbmc_assert(opt == ref);
31 }
When we compile the program with clang and run LLBMC on its bitcode file, using the commands
clang -c -g -I. -emit-llvm ex2.c -o ex2.bc
llbmc ex2.bc
(
-I. is needed for the include file
llbmc.h) we obtain this output from LLBMC, which shows that the assertion holds and the functions are indeed
equivalent:
Running LLBMC with automatically determined function call depth and
at most 0 iterations for loops with non-derivable trip count.
No error detected.
This is the Low-Level Bounded Model Checker LLBMC (version 2012.2).
Running LLBMC with automatically determined function call depth and
at most 0 iterations for loops with non-derivable trip count.
108 assertions before simplification (1 no_back_edge, 69 valid_arithmetic,
37 valid_shift, 1 custom).
Performing formula transformations and simplifications... done. [0.005 sec]
31 assertions remain after simplification (30 valid_arithmetic, 1 custom).
Running SMT solver (STP with MiniSat)... done. [0.938 sec]
Result:
=======
No error detected.