forked from mbrown1413/SymEx
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexecutor.dfy
More file actions
145 lines (121 loc) · 3.54 KB
/
executor.dfy
File metadata and controls
145 lines (121 loc) · 3.54 KB
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
143
144
145
// An executor executes an instruction set or language as a state
// machine. For a given state "s", "Execute(s)" returns two possible next
// states. "BranchCondition(s)" returns the condition under which the
// first of those states should be taken. When "s" is executing a
// non-branching instruction, it is common for "state(s)" to return (s2,
// null), and for "BranchCondition(s)" to return true.
type Reg = int // Index into map of registers, State.regs
// Fields cannot be duplicated, so fields are appended with the instruction.
datatype Instr =
Assign(assign_dest: Reg, assign_value: int)
| Add(add_dest: Reg, add_op1: Reg, add_op2: Reg)
| Icmp(icmp_dest: Reg, icmp_op1: Reg, icmp_op2: Reg)
| Br(br_cond: Reg, br_label1: int, br_label2: int)
datatype State = State(ip: int, regs: map<Reg, IntExpr>) | HaltedState
// Implements a basic subset of the LLVM intermediate representation.
// http://llvm.org/docs/LangRef.html
class Executor {
var program: array<Instr>;
constructor (prog: array<Instr>)
requires prog != null
ensures fresh(this.program)
ensures Valid()
modifies this
{
this.program := new Instr[prog.Length];
// Copy prog to program
forall i | 0 <= i < prog.Length
{
program[i] := prog[i];
}
}
predicate Valid()
reads this
{
program != null
}
method {:opaque} GetReg(s: State, r: Reg) returns (regExpr: IntExpr)
requires Valid()
ensures Valid()
requires s.State?
{
if r in s.regs {
return s.regs[r];
} else {
return intSymbolic(r);
}
}
method {:opaque} GetInitialState() returns (s: State)
requires Valid()
ensures Valid()
{
return State(0, map[]);
}
method {:opaque} BranchCondition(s: State) returns (cond: SatLib.BoolExpr)
requires Valid()
ensures Valid()
{
// If we're halted, or we run out of instructions, it
// doesn't matter, the children will be halted.
if !(s.State? && 0 <= s.ip < program.Length) {
return not(getTrueBool());
}
match program[s.ip] {
case Assign(_, _) =>
return getTrueBool();
case Add(_, _, _) =>
return getTrueBool();
case Icmp(_, _, _) =>
return getTrueBool();
case Br(cond, _, _) =>
var condReg := GetReg(s, cond);
return cmp(condReg, intConst(1));
}
}
method {:opaque} Execute(s: State) returns (s1: State, s2: State)
requires Valid()
ensures Valid()
{
// If we're halted, both children are also halted
if !(s.State? && 0 <= s.ip < program.Length) {
return HaltedState, HaltedState;
}
// Return two children states.
// If not branching, return (state, HaltedState).
match program[s.ip] {
case Assign(dest, value) =>
return State(
s.ip + 1,
s.regs[dest := intConst(value)]
), HaltedState;
case Add(dest, op1, op2) =>
var x1 := GetReg(s, op1);
var x2 := GetReg(s, op2);
return State(
s.ip + 1,
s.regs[dest := add(x1, x2)]
), HaltedState;
case Icmp(dest, op1, op2) =>
var x1 := GetReg(s, op1);
var x2 := GetReg(s, op2);
return State(
s.ip + 1,
s.regs[dest := boolToInt(cmp(x1, x2))]
), HaltedState;
case Br(cond, label1, label2) =>
return State(
label1,
s.regs
), State(
label2,
s.regs
);
}
}
predicate method {:opaque} IsHalted(s: State)
requires Valid()
reads this
{
!(s.State? && 0 <= s.ip < program.Length)
}
}