Bandera Tool Set
Presented by: Dor Nir
Outline
Specification Language (LTL)
Software verification problems
Introduction to Bandera tool Set
Bandera Specification Language (BSL)
Data Type Abstraction
Slicing
Results and conclusions
The Vision
Program
Approved
Formal System
Requirements
Specification
Automatic Verifier
Source
Code
Counter example
Use case
Specification Languages
Linear Temporal Logic (LTL)
Computation Tree Logic (CTL, CTL*)
Excplicit Clock Temporal Logic (XCTL)
Temporal Logic of Actions (TLA, TLA+)
Interval Temporal Logic (ITL)
Propositional Linear Temporal Logic
O(p) – next
p – always
◊p – eventually
pUq – Until
pWq – pUq V ٱp
P
P
P
P
P
P
P
P
Notation: p => q ≡ (ٱpq)
P
P
q
Some typical property patterns
Recurrence
◊p infinitely often
◊p eventually always
Precendence
pU(qUr)
p* q* r
(pUq)Ur
(p*q)* r
¬pWq
p cannot occur before q
r
P
q
Mutual Exclusion Properties
• Resource is never owned by P1 and P2, simultaneously
(P1_ownP2_own)
• Whenever P1 has requested the resource it will eventually get it
(P1_reqP1_own)
• If P1 requests the resource infinitely often (when the resource is
free then it will infinitely often get it (strong fairness)
(P1_req (P1_ownP2_own)) P1_own
Finite-state Verification
OK
Finite-state system
Verification
tool
or
Error trace
(F
W)
Specification
Line
Line
Line
Line
Line
Line
…
Line
Line
5: …
12: …
15:…
21:…
25:…
27:…
41:…
47:…
Finite-state Verification
Effective for analyzing properties of
hardware systems
Widespread success and
adoption in industry
Controller
Software verification
Recent years have seen many efforts to
apply those techniques to software
Limited success due to the
enormous state spaces
associated with most software systems
Abstraction: the key to scaling
up
represents a
set of states
symbolic state
abstraction
Original
system
Abstract
system
Safety: The set of behaviors of the abstract system over-approximates
the set of behaviors of the original system
Goals…
•Verification tool for software, using
abstraction.
•Easy for writing specifications.
•Use of existing verification tools.
•Verification time is reasonable.
•Easy to understand the counter example.
•Reusable. (Specifications)
Bandera:
An open tool set for model-checking Java source code
Graphical User Interface
Optimization Control
?
Temporal
Specification
void add(Object o) {
buffer[head] = o;
head = (head+1)%size;
}
Object take() {
…
tail=(tail+1)%size;
return buffer[tail];
}
Checker
Inputs
Slicing
Abstract
Interpretation
Static
Analysis
Model
Checkers
Transformation &
Abstraction Tools
Java Source
Error Trace Mapping
Bandera
Checker
Outputs
Issue: Rendering Requirement
Often difficult to formalize a
requirement in temporal logic
“Between the window open and the window
close, button X can be pushed at most twice.”
…is rendered in LTL as...
[]((open && <>close) ->
((!pushX && !close) U
(close || ((pushX && !close) U
(close || ((!pushX && !close) U
(close || ((pushX && !close) U
(close || (!pushX U close))))))))))
Issue: Checker Dependence
Graphical User Interface
mismatch!
LTL
CTL
CSP
Checker
Inputs
Spin
SMV
FDR
Model
Checkers
LTL
Temporal
Specification
void add(Object o) {
buffer[head] = o;
head = (head+1)%size;
}
Object take() {
…
tail=(tail+1)%size;
return buffer[tail];
}
Transformation &
Abstraction Tools
Java Source
Bandera
Issue: Representation
Dependence
Source’s representation
Heap.b.head == Heap.b.tail
Model’s representation
(((_collect(heap_b) == 1)\
&& (BoundedBuffer_col.instance[_index(heap _b)].head ==
BoundedBuffer_col.instance[_index(heap _b)].tail) )\
|| ((_collect(heap _b) == 3)\
&& (BoundedBuffer_col_0.instance[_index(heap _b)].head ==
BoundedBuffer_col_0.instance[_index(heap _b)].tail) )\
|| ((_collect(heap _b) == 0) && TRAP))
Problem summary
Rendering
Checker dependence
Representation dependence
Quantification
BSL: Bandera Specification
Language
Propositions stated in terms of source code
features
Based on an extensible system of temporal
specification patterns
Heap objects are named via object quantification
Quantification
Temporal Property
Specification
(via pattern language)
Assertion Property
Specification
(selective enabling)
Predicate Definition
Assertion Definition
Predicate Forms
Static/Instance Data
Constraints
@observable [static] EXP <name> <exp>;
Invocation
@observable INVOKE <name> <exp>;
Return
@observable RETURN <name> <exp>;
Arbitrary Locations
@observable LOCATION[<label>] <name> <exp>;
Semantic Issues
(o1 != null) &&
(o1.next != null) &&
( o1.next.value == 0 )
Quantification (Cont’d)
forall[b:BoundedBuffer].
{Full(b)} leads to {!Full(b)} globally;
(heap.b == null U
(heap.b != null &&
([](heap.b.head == heap.b.tail) ->
<>(heap.b.head != heap.b.tail))))
|| [](heap.b == null)
Methodology:
Property Specification
Given a software requirement...
Identify observables (propositions) in requirement
Define propositions in source Java-doc comments
Use GUI to select appropriate temporal pattern
parameterized by declared observables
Add quantification if property contains instance
propositions.
Bounded Buffer
class BoundedBuffer {
Object [] buffer;
int head;
/* next available slot */
int tail;
/* last available slot */
int bound; /* max # of elements
*/
Initialization
head
tail
Add,Add
public BoundedBuffer(int b)
head
tail
{…}
public synchronized boolean isEmpty()
{…}
public synchronized void add(Object o)
{…}
public synchronized Object take ()
{…}
}
Add,Take,Take
tail head
Bounded Buffer
Initialization
public BoundedBuffer(int b) {
bound = b;
buffer = new Object[bound];
head
= 0;
tail
= bound-1;
}
public synchronized boolean isEmpty() {
return head == ((tail+1) % bound);
}
head
tail
Add,Add
head
tail
Add,Take,Take
tail head
Bounded Buffer
public synchronized void add(Object o) {
while ( tail == head )
try { wait(); } catch
(InterruptedException ex) {}
buffer_[head] = o;
head = (head+1) % bound;
notifyAll();
}
public synchronized Object take() {
while (head == ((tail+1) % bound))
try { wait(); } catch
(InterruptedException ex) {}
tail = (tail+1) % bound;
notifyAll();
return buffer_[tail];
}
Initialization
head
tail
Add,Add
head
tail
Add,Take,Take
tail head
Bounded Buffer Properties
Full buffers eventually become
non-full
Indices always stay in range
Empty buffers must be added to
before being taken from
Buffers are constructed with
positive bounds
Property Specification
/**
* @observable
*
EXP Full: (head == tail);
*/
class BoundedBuffer {
Object [] buffer;
int head, tail, bound;
public synchronized
void add(Object o)
{…}
public synchronized
Object take ()
{…}
}
Requirement 1:
If a buffer becomes full,
it will eventually become
non-full.
Bandera Specification:
FullToNonFull:
forall[b:BoundedBuffer].
{!Full(b)} responds to
{Full(b)} globally;
Property Specification
/**
* @observable
* EXP HeadRange:
* head >= 0 && head < bound;
* Exp TailRange:
* tail >= 0 && tail < bound;
*/
Requirement 2:
Indices always stay in range.
class BoundedBuffer {
Object [] buffer;
int head, tail, bound;
public synchronized
void add(Object o)
{…}
public synchronized
Object take ()
{…}
}
Bandera Specification:
IndexRangeInvariant:
forall[b:BoundedBuffer].
{HeadRange(b) &&
TailRange(b)}
is universal globally;
Property Specification
/**
* @observable
* EXP Empty:
* head == ((tail+1) % bound);
*/
class BoundedBuffer {
int head, tail, bound;
/**
* @observable INVOKE Call;
*/
public synchronized
void add(Object o)
{…}
/**
* @observable RETURN Return;
*/
public synchronized
Object take ()
{…}
}
Requirement 3:
Empty buffers must
added to before being
taken from
Bandera Specification:
NoTakeWhileEmpty:
forall[b:BoundedBuffer].
{take.Return(b)} is absent
after {Empty(b)}
until {add.Call(b)};
Property Specification
/**
* @assert
*
PRE PositiveBound:
*
(b > 0);
*/
public BoundedBuffer(int b) {
bound = b;
buffer = new Object[bound];
head
= 0;
tail
= bound-1;
}
Requirement 4:
Buffers are constructed
with positive bounds
Bandera Specification:
PositiveBound:
enable assertions
{PositiveBound};
Quantification
forall[b:BoundedBuffer].P(b)
Quantified set is not fixed
varies within executions
varies across executions
Solution
by adding a state variable (for b) that
will eventually be bound nondeterministically to each instance
by enabling checking of the formula only
when variable is bound to an instance
Data Type Abstraction
Collapses data domains via abstract interpretation:
Code
int x = 0;
if (x == 0)
x = x + 1;
Data domains
int
(n<0) : NEG
(n==0): ZERO
(n>0) : POS
Signs x = ZERO;
if (Signs.eq(x,ZERO))
x = Signs.add(x,POS);
Signs
NEG ZERO POS
Bandera hypothesis
Abstraction of data domains is necessary
Automated support for
Defining abstract domains (and operators)
Selecting abstractions for program components
Generating abstract program models
Interpreting abstract counter-examples
will make it possible to
Scale property verification to realistic systems
Ensure the safety of the verification process
Abstraction in
Bandera
Variable
x
y
done
count
….
o
b
Bandera
Abstraction
Specification
Language
Concrete Abstract Inferred
Type
Type
Type
int
int
bool
int
Object
Buffer
Program
Signs
Signs
Signs
bool
int
….
Point
Buffer
Abstract Code
Generator
PVS
Abstraction
Definition
Abstraction
Library
Abstracted
Program
BASL
Compiler
Definition of Abstractions in BASL
abstraction Signs abstracts int
begin
TOKENS = { NEG, ZERO, POS };
abstract(n)
begin
n < 0
n == 0
n > 0
end
-> {NEG};
-> {ZERO};
-> {POS};
Automatic
Generation
operator + add
begin
(NEG , NEG) -> {NEG} ;
(NEG , ZERO) -> {NEG} ;
(ZERO, NEG) -> {NEG} ;
(ZERO, ZERO) -> {ZERO} ;
(ZERO, POS) -> {POS} ;
(POS , ZERO) -> {POS} ;
(POS , POS) -> {POS} ;
(_,_) -> {NEG,ZERO,POS};
/* case (POS,NEG),(NEG,POS) */
end
Example: Start safe, then refine: +(NEG,NEG)={NEG,ZERO,POS}
Proof obligations submitted to PVS...
Forall n1,n2: neg?(n1) and neg?(n2) implies not pos?(n1+n2)
Forall n1,n2: neg?(n1) and neg?(n2) implies not zero?(n1+n2)
Forall n1,n2: neg?(n1) and neg?(n2) implies not neg?(n1+n2)
Compiling BASL Definitions
abstraction Signs abstracts int
begin
TOKENS = { NEG, ZERO, POS };
abstract(n)
begin
n < 0
n == 0
n > 0
end
public class Signs {
public static final int NEG = 0; // mask 1
public static final int ZERO = 1; // mask 2
public static final int POS = 2; // mask 4
-> {NEG};
-> {ZERO};
-> {POS};
Compiled
operator + add
begin
(NEG , NEG) -> {NEG} ;
(NEG , ZERO) -> {NEG} ;
(ZERO, NEG) -> {NEG} ;
(ZERO, ZERO) -> {ZERO} ;
(ZERO, POS) -> {POS} ;
(POS , ZERO) -> {POS} ;
(POS , POS) -> {POS} ;
(_,_)-> {NEG, ZERO, POS};
/* case (POS,NEG), (NEG,POS) */
end
public static int abs(int n) {
if (n < 0) return NEG;
if (n == 0) return ZERO;
if (n > 0) return POS;
}
public static int add(int arg1, int arg2) {
if (arg1==NEG && arg2==NEG) return NEG;
if (arg1==NEG && arg2==ZERO) return NEG;
if (arg1==ZERO && arg2==NEG) return NEG;
if (arg1==ZERO && arg2==ZERO) return ZERO;
if (arg1==ZERO && arg2==POS) return POS;
if (arg1==POS && arg2==ZERO) return POS;
if (arg1==POS && arg2==POS) return POS;
return Bandera.choose(7);
/* case (POS,NEG), (NEG,POS) */
}
Data Type Abstractions
Library of abstractions for base types contains:
Range(i,j), i..j modeled precisely, e.g., Range(0,0) is the
signs abstraction
Modulo(k), Set(v,…)
Point maps all concrete values to unknown
User extendable for base types
Array abstractions
Specified by an index abstraction and an element abstraction
Class abstractions
Specified by abstractions for each field
Interpreting Results
For an abstracted program, a counter-example
may be infeasible because:
– Over-approximation introduced by abstraction
Example:
x = -2; if(x + 2 == 0) then ...
x = NEG; if(Signs.eq(Signs.add(x,POS),ZERO)) then ...
{NEG,ZERO,POS}
Slicing
Generate reduced model
Program P with specification Φ
Bandera collect statements of interest
From Φ (slicing criterion for Φ)
Compute P’: reduce version of P.
P |= Φ P’|= Φ
Slicing
In Property
Relevant
Does program dependence-based slicing to
get a reduced version of P
dependences: data, control, interference, ready,
wait
backwards slicing
Effectiveness based on structure of program
Counter-Example: Overview
Counter-example with a thousand
states?!?!
Bandera provides debugger-like features:
map states to source code
program tracing
create checkpoints
keep track of variables and objects
UML-like object displays
lock graphs
Using Bandera
• Launch the Bandera User Interface
(BUI) from the command line
• Future runs: save which
components you want to use in
session files
Counter-Example:
Program Tracing
Counter-Example:
Lock Graph
Counter-Example:
Object Viewer
Property Specification
Property Specification
Mandatory Performance Slide
Problem
Extract Check
Time
Time
(s)
(s)
Check
Result
States
b, r1, n
24
2674
true
7338120
b, r1, s
13
4
true
3478
b, r1, a
15
4
true
895
b, r2, s
13
56
true
528059
b, r2, a
16
11
true
27519
b, p1, s
13
4
true
2507
b, p1, a
15
4
true
331
d, r1, s
13
3
false
88
d, r1, a
15
2
false
17
Threaded Pipeline
b: basic
d: defective variant
r: response property
p: precedence property
n: no reductions
s: slicing
a: slicing + data abstraction
BoundedBuffer Propery Check
Data
Property
Sliced Never-Claim States
States
Stored
Buffer Assertions
Yes
-
17797
IndexRangeInv
Yes
14
45215
IndexRangeInv,
Buffer Assertions
Yes
14
115387
Full To Non Full
Yes
25
64687
Full to Non Full,
Buffer Assertions
Yes
25
154842
When to Use Model Checking
Control-related properties
Container objects
assertions
pre- and post-conditions
simple data invariants
Stacks
Queues
Verifying concurrent behavior
Necessity for counter-examples
Automatic property verification of source code
Analysis Not Appropriate for
Model Checking
Data-related properties
Verification of sorting algorithms
Use other formal methods instead (theorem
proving)
Where static dataflow analysis is better
array-bounds errors
buffer overruns
null-pointer de-referencing
Conclusions
Software verification is applicable and
can help. (but…)
Slicing is critical.
BSL making the specification writing
much more simple. (but…)
The counter example is intuitive.
Future work
More quantifications (array)
Support predicate with arbitrary
parameters
Method invocation in predicate.
More model checkers.
Increase the template library
© Copyright 2026 Paperzz