Skip to content

Commit 635d6d5

Browse files
author
Pramod Subramanyan
committed
uclid5 version 0.9.1
1 parent 49f2541 commit 635d6d5

File tree

165 files changed

+5027
-3216
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

165 files changed

+5027
-3216
lines changed

build.sbt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name := "uclid"
2-
version := "0.9.0"
2+
version := "0.9.1"
33
scalaVersion := "2.12.0"
44

55
scalacOptions += "-feature"
@@ -8,8 +8,11 @@ scalacOptions += "-deprecation"
88

99
resolvers += "Artima Maven Repository" at "http://repo.artima.com/releases"
1010

11+
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.8.0"
12+
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3"
1113
libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.6" withSources()
1214
libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.1"
1315
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1" % "test"
16+
libraryDependencies += "com.github.scopt" %% "scopt" % "3.7.0"
1417

1518
enablePlugins(JavaAppPackaging)

examples/README

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
11
The examples from the tutorial are in the tutorial/ subfolder.
22

3-
The names correspond to the example numbers in the tutorial with the
4-
exception that all of the CPU model is in ex4.1-cpu but the tutorial presents
5-
it in pieces as Examples 4.1, 4.2, 4.3 and 4.4
3+
The names correspond to the example numbers in the tutorial.
64

75
tutorials/
86
- ex1.1-fib-model.ucl
97
- ex2.1-alu.ucl
108
- ex3.1-fib-induction.ucl
119
- ex3.2-fib-induction-cex.ucl
1210
- ex3.3-fib-induction-proof.ucl
13-
- ex4.1-cpu.ucl
11+
- CPU model:
12+
+ ex4.1-cpu.ucl
13+
+ ex4.2-cpu.ucl
14+
+ ex4.3-cpu.ucl
1415

15-
16-
cpu_unint_addr.ucl
17-
- This is the CPU model from Chapter 4 of the tutorial but uses uninterpreted
18-
types for the addresses.
16+
cpu_induction.ucl
17+
- This is an extension of the the CPU model from Chapter 4 of the tutorial. It
18+
adds privilege separation to the CPU, and proves non-interference between
19+
supervisor mode and user mode using an inductive argument.
1920

2021
setuid.ucl
21-
- This is a model based on the automaton in the paper:
22+
- This is a model based on the automaton in the paper:
2223
Setuid Demystified. Chen, Wagen and Dean [Usenix Security 2002].
2324

2425
setuid_fixed.ucl
2526
- This is a "fixed" version of the setuid model.
2627

27-
28-

examples/cpu_induction.ucl

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
module common {
2+
// address type.
3+
type addr_t;
4+
// word type.
5+
type word_t = bv8;
6+
// list of operations supported by the CPU.
7+
type op_t = enum {
8+
op_alu, op_load, op_store,
9+
op_syscall, op_sysret
10+
};
11+
// CPU mode.
12+
type mode_t = enum { usr_mode, sup_mode };
13+
// CPU memory type.
14+
type mem_t = [addr_t]word_t;
15+
16+
// define a constant that evaluates to zero.
17+
function k0_word_t() : word_t;
18+
axiom k0_word_t() == 0bv8;
19+
20+
// the enter address for syscall.
21+
function syscall_enter_addr() : addr_t;
22+
// the exit address for syscalls.
23+
function sysret_exit_addr() : addr_t;
24+
// The above two must be different.
25+
axiom syscall_enter_addr() != sysret_exit_addr();
26+
}
27+
28+
module cpu {
29+
type addr_t = common.addr_t;
30+
type mem_t = common.mem_t;
31+
type word_t = common.word_t;
32+
type op_t = common.op_t;
33+
type mode_t = common.mode_t;
34+
35+
type regindex_t;
36+
type regs_t = [regindex_t]word_t;
37+
38+
input imem : mem_t; // program memory.
39+
var dmem : mem_t; // data memory.
40+
var regs : regs_t; // registers.
41+
var pc : addr_t; // program counter.
42+
var inst, result : word_t; // inst reg, result value
43+
var mode : mode_t; // usr/sup mode?
44+
45+
// range of privileged memory.
46+
const sup_range_lo : addr_t;
47+
const sup_range_hi : addr_t;
48+
// predicate for the above.
49+
function in_range (a : addr_t, b1 : addr_t, b2 : addr_t) : boolean;
50+
51+
// uninterpreted functions for decoding instructions.
52+
function inst2op (i : word_t) : op_t;
53+
function inst2reg0 (i : word_t) : regindex_t;
54+
function inst2reg1 (i : word_t) : regindex_t;
55+
function inst2addr (i : word_t, r0 : word_t, r1 : word_t) : addr_t;
56+
function aluOp (i : word_t, r0 : word_t, r1 : word_t) : word_t;
57+
function nextPC (i : word_t, pc : addr_t, r0 : word_t) : addr_t;
58+
59+
define in_privileged_memory (a : addr_t) : boolean =
60+
in_range(a, sup_range_lo, sup_range_hi);
61+
62+
axiom (forall (a : addr_t) ::
63+
(a == common.syscall_enter_addr()) ==> in_privileged_memory(a));
64+
axiom (forall (a : addr_t) ::
65+
(a == common.sysret_exit_addr()) ==> !in_privileged_memory(a));
66+
67+
procedure exec_inst(instr : word_t, pc : addr_t)
68+
returns (pc_next : addr_t)
69+
modifies regs, result, dmem, mode;
70+
{
71+
var op : op_t;
72+
var r0ind : regindex_t;
73+
var r1ind : regindex_t;
74+
var r0 : word_t;
75+
var r1 : word_t;
76+
var addr : addr_t;
77+
78+
// get opcode.
79+
op = inst2op(instr);
80+
// get operands.
81+
r0ind, r1ind = inst2reg0(instr), inst2reg1(instr);
82+
r0, r1 = regs[r0ind], regs[r1ind];
83+
// get next pc (might be overwritten by syscall/sysret).
84+
pc_next = nextPC(instr, pc, r0);
85+
// get memory address
86+
addr = inst2addr(instr, r0, r1);
87+
88+
// If we are in supervisor mode, we only set pc_next to supervisor address.
89+
assume (mode == sup_mode) ==> (in_privileged_memory(pc_next));
90+
// If we are in supervisor mode, we only read from supervisor memory.
91+
assume (mode == sup_mode && op == op_load) ==> in_privileged_memory(addr);
92+
// If we are already in supervisor mode, we don't execute syscalls.
93+
assume (mode == sup_mode) ==> (op != op_syscall);
94+
95+
case
96+
(op == op_alu) : {
97+
result = aluOp(instr, r0, r1);
98+
regs[r0ind] = result;
99+
}
100+
(op == op_load) : {
101+
if (mode == sup_mode || !in_privileged_memory(addr)) {
102+
// read memory.
103+
result = dmem[addr];
104+
} else {
105+
// operation failed. return 0.
106+
result = common.k0_word_t();
107+
}
108+
regs[r0ind] = result;
109+
}
110+
(op == op_store) : {
111+
result = common.k0_word_t();
112+
// is writing legal?
113+
if (mode == sup_mode || !in_privileged_memory(addr)) {
114+
// write to memory.
115+
dmem[addr] = r0;
116+
}
117+
}
118+
(op == op_syscall) : {
119+
assert (mode == usr_mode);
120+
121+
// zero out result.
122+
result = common.k0_word_t();
123+
// zero out registers.
124+
havoc regs;
125+
assume (forall (r : regindex_t) :: regs[r] == common.k0_word_t());
126+
// set PC.
127+
pc_next = common.syscall_enter_addr();
128+
// set mode.
129+
mode = sup_mode;
130+
}
131+
(op == op_sysret) : {
132+
// zero out result.
133+
result = common.k0_word_t();
134+
// zero out registers.
135+
havoc regs;
136+
assume (forall (r : regindex_t) :: regs[r] == common.k0_word_t());
137+
// set PC.
138+
pc_next = common.sysret_exit_addr();
139+
// set mode.
140+
mode = usr_mode;
141+
}
142+
esac
143+
}
144+
145+
init {
146+
// reset registers.
147+
assume (forall (r : regindex_t) :: regs[r] == common.k0_word_t());
148+
// set instruction to some deterministic value.
149+
inst = common.k0_word_t();
150+
// start off-execution at a deterministic address.
151+
pc = common.syscall_enter_addr();
152+
// in supervisor mode.
153+
mode = sup_mode;
154+
}
155+
156+
next {
157+
inst' = imem[pc];
158+
call (pc') = exec_inst(inst', pc);
159+
}
160+
}
161+
162+
module main {
163+
// Import types.
164+
type addr_t = common.addr_t;
165+
type mem_t = common.mem_t;
166+
type word_t = common.word_t;
167+
type op_t = common.op_t;
168+
type mode_t = common.mode_t;
169+
type regindex_t = cpu.regindex_t;
170+
171+
// Instruction memory is the same for both CPUs.
172+
var imem1, imem2 : mem_t;
173+
174+
// Create two instances of the CPU module.
175+
instance cpu1 : cpu(imem : (imem1));
176+
instance cpu2 : cpu(imem : (imem2));
177+
178+
init {
179+
// Assume same supervisor ranges.
180+
assume (cpu1.sup_range_lo == cpu2.sup_range_lo);
181+
assume (cpu1.sup_range_hi == cpu2.sup_range_hi);
182+
// Assume supervisor memory starts off identical.
183+
assume (forall (a : addr_t) ::
184+
(cpu.in_range(a, cpu1.sup_range_lo, cpu2.sup_range_hi))
185+
==> (cpu1.dmem[a] == cpu2.dmem[a]));
186+
assume (forall (a : addr_t) ::
187+
(cpu.in_range(a, cpu1.sup_range_lo, cpu1.sup_range_hi))
188+
==> (imem1[a] == imem2[a]));
189+
}
190+
191+
next {
192+
next (cpu1);
193+
next (cpu2);
194+
// If one CPU takes a syscall, the other does too!
195+
assume (cpu.inst2op(cpu1.inst) == op_syscall || cpu.inst2op(cpu2.inst) == op_syscall)
196+
==> (cpu1.inst == cpu2.inst);
197+
}
198+
199+
// The two CPUs change mode in sync.
200+
invariant eq_mode : (cpu1.mode == cpu2.mode);
201+
// The two CPUs have the same PC when in supervisor mode.
202+
invariant eq_pc : (cpu1.mode == sup_mode) ==> (cpu1.pc == cpu2.pc);
203+
// In supervisor mode, the two CPUs execute the same code.
204+
invariant eq_inst : (cpu1.mode == sup_mode) ==> (cpu1.inst == cpu2.inst);
205+
// In supervisor mode, the two CPUs have the same register values.
206+
invariant eq_regs : (forall (ri : regindex_t) ::
207+
(cpu1.mode == sup_mode) ==> (cpu1.regs[ri] == cpu2.regs[ri]));
208+
// The supervisor range of memory is the same in both CPUs.
209+
invariant eq_sup_range : (cpu1.sup_range_lo == cpu2.sup_range_lo) &&
210+
(cpu1.sup_range_hi == cpu2.sup_range_hi);
211+
// When in supervisor mode, the PC is also in the supervisor mode range.
212+
invariant in_pc_range1 : (cpu1.mode == sup_mode)
213+
==> cpu.in_range(cpu1.pc, cpu1.sup_range_lo, cpu1.sup_range_hi);
214+
invariant in_pc_range2 : (cpu2.mode == sup_mode)
215+
==> cpu.in_range(cpu2.pc, cpu2.sup_range_lo, cpu2.sup_range_hi);
216+
// The CPU memory that is private to supervisor mode is identical.
217+
invariant eq_dmem : (forall (a : addr_t) ::
218+
(cpu.in_range(a, cpu1.sup_range_lo, cpu2.sup_range_hi))
219+
==> (cpu1.dmem[a] == cpu2.dmem[a]));
220+
// Same for instruction memory.
221+
invariant eq_imem : (forall (a : addr_t) ::
222+
(cpu.in_range(a, cpu1.sup_range_lo, cpu1.sup_range_hi))
223+
==> (imem1[a] == imem2[a]));
224+
225+
control {
226+
v = induction;
227+
check;
228+
print_results;
229+
v.print_cex(
230+
cpu.inst2op(cpu1.inst), cpu.inst2op(cpu2.inst),
231+
cpu1.result, cpu2.result, cpu1.mode, cpu2.mode,
232+
cpu1.pc, cpu2.pc,
233+
cpu1.sup_range_lo, cpu2.sup_range_lo,
234+
cpu1.sup_range_hi, cpu2.sup_range_hi,
235+
cpu.in_range(cpu1.pc, cpu1.sup_range_lo, cpu1.sup_range_hi),
236+
cpu.in_range(cpu2.pc, cpu2.sup_range_lo, cpu2.sup_range_hi));
237+
}
238+
}

0 commit comments

Comments
 (0)