Skip to content

Commit 81969f5

Browse files
committed
switch to latest symex and add equalities optimizations
1 parent bddf3ae commit 81969f5

File tree

8 files changed

+107
-68
lines changed

8 files changed

+107
-68
lines changed

doc/src/symex/quickstart.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -638,8 +638,8 @@ owi: [ERROR] Reached problem!
638638
$ owi rust ./mean.rs --entry-point=check --invoke-with-symbols -w1 --fail-on-assertion-only --no-assert-failure-expression-printing --deterministic-result-order
639639
owi: [ERROR] Assert failure
640640
model {
641-
symbol symbol_0 i32 915013628
642-
symbol symbol_1 i32 -1225654275
641+
symbol symbol_0 i32 685822388
642+
symbol symbol_1 i32 -685822389
643643
}
644644

645645
owi: [ERROR] Reached problem!

dune-project

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@
7676
(>= 3.3))
7777
(smtml
7878
(>= 0.20.0))
79-
symex
79+
(symex
80+
(>= 0.2))
8081
(synchronizer
8182
(>= 0.3))
8283
(uutf

owi.opam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ depends: [
4040
"processor" {>= "0.2"}
4141
"sedlex" {>= "3.3"}
4242
"smtml" {>= "0.20.0"}
43-
"symex"
43+
"symex" {>= "0.2"}
4444
"synchronizer" {>= "0.3"}
4545
"uutf" {>= "1.0.3"}
4646
"xmlm" {>= "1.4.0"}

shell.nix

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,41 @@
44
}:
55

66
let
7-
ocamlPackages = pkgs.ocaml-ng.ocamlPackages_5_4;
8-
landmarks = ocamlPackages.landmarks.overrideAttrs (old: {
9-
src = pkgs.fetchFromGitHub {
10-
owner = "hra687261";
11-
repo = "landmarks";
12-
rev = "17be3567a63650090f9cf94654fcc8d99f946e27";
13-
hash = "sha256-3ui4uvSAvUgzk2UMVtH9A4BhAX6nWbwx7q0YwkANNv8=";
14-
};
15-
});
16-
landmarks-ppx = ocamlPackages.landmarks-ppx.overrideAttrs (old: {
17-
src = pkgs.fetchFromGitHub {
18-
owner = "hra687261";
19-
repo = "landmarks";
20-
rev = "17be3567a63650090f9cf94654fcc8d99f946e27";
21-
hash = "sha256-3ui4uvSAvUgzk2UMVtH9A4BhAX6nWbwx7q0YwkANNv8=";
22-
};
23-
meta.broken = false;
7+
ocamlPackages = pkgs.ocaml-ng.ocamlPackages_5_4.overrideScope (self: super: {
8+
smtml = super.smtml.overrideAttrs (old: {
9+
src = pkgs.fetchFromGitHub {
10+
owner = "formalsec";
11+
repo = "smtml";
12+
rev = "a9dff52e7ef2215c786ee8ce2c24d716db0b5ace";
13+
hash = "sha256-TIOOE/bsis6oYV3Dt6TcI/r/aN3S1MQNtxDAnvBbVO0=";
14+
};
15+
doCheck = false;
16+
});
17+
symex = super.symex.overrideAttrs (old: {
18+
src = pkgs.fetchFromGitHub {
19+
owner = "ocamlpro";
20+
repo = "symex";
21+
rev = "c4200e160f69d3cd9443301c1c4dc4224c3cab2e";
22+
hash = "sha256-KX+OHiCsAHEw0kBWLUDVakIcshUNXLYjm2f2le75Mj8=";
23+
};
24+
});
25+
landmarks = super.landmarks.overrideAttrs (old: {
26+
src = pkgs.fetchFromGitHub {
27+
owner = "hra687261";
28+
repo = "landmarks";
29+
rev = "17be3567a63650090f9cf94654fcc8d99f946e27";
30+
hash = "sha256-3ui4uvSAvUgzk2UMVtH9A4BhAX6nWbwx7q0YwkANNv8=";
31+
};
32+
});
33+
landmarks-ppx = super.landmarks-ppx.overrideAttrs (old: {
34+
src = pkgs.fetchFromGitHub {
35+
owner = "hra687261";
36+
repo = "landmarks";
37+
rev = "17be3567a63650090f9cf94654fcc8d99f946e27";
38+
hash = "sha256-3ui4uvSAvUgzk2UMVtH9A4BhAX6nWbwx7q0YwkANNv8=";
39+
};
40+
meta.broken = false;
41+
});
2442
});
2543
in
2644

src/symbolic/solver.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ let model_of_path_condition (S (solver_module, s)) ~path_condition :
4343
let exception Unknown in
4444
let module Solver = (val solver_module) in
4545
try
46-
let sub_conditions = Symex.Path_condition.slice path_condition in
46+
let sub_conditions = Symex.Path_condition.to_list path_condition in
4747
let models =
4848
List.map
4949
(fun pc ->

src/symbolic/symbolic_choice.ml

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -23,21 +23,15 @@ let solver_dls_key =
2323

2424
let[@inline] solver () = Domain.DLS.get solver_dls_key
2525

26-
let add_pc (c : Symbolic_boolean.t) =
27-
let c = Smtml.Typed.simplify c in
28-
match Smtml.Typed.view c with
29-
| Val True -> return ()
30-
| Val False -> prune ()
31-
| _ ->
32-
let* state in
33-
let new_thread = Thread.add_pc state c in
34-
set_state new_thread
26+
let add_already_checked_condition_to_pc (condition : Symbolic_boolean.t) =
27+
let* state in
28+
let state = Thread.add_already_checked_condition_to_pc state condition in
29+
set_state state
3530
[@@inline]
3631

3732
let get_pc () =
3833
let+ state in
39-
let pc = state.pc in
40-
let pc = Symex.Path_condition.slice pc in
34+
let pc = Symex.Path_condition.to_list state.pc in
4135
List.fold_left Smtml.Expr.Set.union Smtml.Expr.Set.empty pc
4236

4337
let add_breadcrumb crumb = modify_state (fun t -> Thread.add_breadcrumb t crumb)
@@ -60,16 +54,24 @@ let with_new_symbol ty f =
6054
let n = state.num_symbols in
6155
let sym = Fmt.kstr (Smtml.Symbol.make ty) "symbol_%d" n in
6256
let+ () =
63-
modify_state (fun thread ->
64-
let thread = Thread.add_symbol thread sym in
65-
Thread.incr_num_symbols thread )
57+
modify_state (fun state ->
58+
let state = Thread.add_symbol state sym in
59+
Thread.incr_num_symbols state )
6660
in
6761
f sym
6862

69-
let check_reachability v =
63+
let check_reachability condition =
7064
let* state in
7165
let solver = solver () in
72-
let pc = state.pc |> Symex.Path_condition.slice_on_condition v in
66+
let pc = Symex.Path_condition.slice_on_new_condition condition state.pc in
67+
(* the condition must be simplified only *after* slicing when we don't know if it is SAT, otherwise, we may exclude some slices and go to an unsat path! *)
68+
let condition =
69+
let equalities = Symex.Path_condition.get_known_equalities state.pc in
70+
Smtml.Expr.inline_symbol_values equalities
71+
(Smtml.Typed.Unsafe.unwrap condition)
72+
|> Smtml.Typed.Unsafe.wrap |> Smtml.Typed.simplify
73+
in
74+
let pc = Smtml.Expr.Set.add (Smtml.Typed.Unsafe.unwrap condition) pc in
7375
let stats = state.bench_stats in
7476
let reachability =
7577
Benchmark.handle_time_span stats.solver_sat_time @@ fun () ->
@@ -111,24 +113,25 @@ let select_inner ~with_breadcrumbs (cond : Symbolic_boolean.t)
111113
| _ ->
112114
let is_other_branch_unsat = Atomic.make false in
113115
let branch condition final_value priority =
114-
let* () = add_pc condition in
115116
let* () =
116117
if with_breadcrumbs then add_breadcrumb (if final_value then 1 else 0)
117118
else return ()
118119
in
120+
let* () = yield priority in
119121
(* this is an optimisation under the assumption that the PC is always SAT (i.e. we are performing eager pruning), in such a case, when a branch is unsat, we don't have to check the reachability of the other's branch negation, because it is always going to be SAT. *)
120122
if Atomic.get is_other_branch_unsat then begin
123+
let* () = add_already_checked_condition_to_pc condition in
121124
Log.debug (fun m ->
122125
m "The SMT call for the %b branch was optimized away" final_value );
123126
(* the other branch is unsat, we must be SAT and don't need to check reachability! *)
124127
return final_value
125128
end
126129
else begin
127130
(* the other branch is SAT (or we haven't computed it yet), so we have to check reachability *)
128-
let* () = yield priority in
129131
let* satisfiability = check_reachability condition in
130132
begin match satisfiability with
131133
| `Sat ->
134+
let* () = add_already_checked_condition_to_pc condition in
132135
let* () = modify_state (Thread.set_priority priority) in
133136
return final_value
134137
| `Unsat ->
@@ -137,7 +140,7 @@ let select_inner ~with_breadcrumbs (cond : Symbolic_boolean.t)
137140
| `Unknown ->
138141
(* It can happen when the solver is interrupted *)
139142
(* TODO: once https://github.com/formalsec/smtml/pull/479 is merged
140-
if solver was interrupted then prune () else assert false *)
143+
if solver was interrupted then prune () else assert false *)
141144
prune ()
142145
end
143146
end
@@ -175,28 +178,34 @@ let select (cond : Symbolic_boolean.t) ~instr_counter_true ~instr_counter_false
175178
~with_breadcrumbs:true
176179
[@@inline]
177180

178-
let summary_symbol (e : Smtml.Typed.Bitv32.t) :
179-
(Smtml.Typed.Bool.t option * Smtml.Symbol.t) t =
181+
let select_i32 (e : Symbolic_i32.t) : int32 t =
180182
let* state in
183+
let equalities = Symex.Path_condition.get_known_equalities state.pc in
184+
let e =
185+
Smtml.Expr.inline_symbol_values equalities (Smtml.Typed.Unsafe.unwrap e)
186+
|> Smtml.Typed.Unsafe.wrap |> Smtml.Typed.simplify
187+
in
181188
match Smtml.Typed.view e with
182-
| Symbol sym -> return (None, sym)
183-
| _ ->
184-
let num_symbols = state.num_symbols in
185-
let+ () = modify_state Thread.incr_num_symbols in
186-
let name = Fmt.str "choice_i32_%i" num_symbols in
187-
(* TODO: having to build two times the symbol this way is not really elegant... *)
188-
let sym = Smtml.Symbol.make Smtml.Typed.Types.(to_ty bitv32) name in
189-
let assign = Smtml.Typed.Bitv32.(eq (symbol sym) e) in
190-
(Some assign, sym)
191-
192-
let select_i32 (i : Symbolic_i32.t) : int32 t =
193-
match Smtml.Typed.view i with
194189
| Val (Bitv bv) when Smtml.Bitvector.numbits bv <= 32 ->
195190
return (Smtml.Bitvector.to_int32 bv)
196191
| _ ->
197-
let* assign, symbol = summary_symbol i in
192+
let* assign, symbol =
193+
match Smtml.Typed.view e with
194+
| Symbol symbol -> return (None, symbol)
195+
| _ ->
196+
let num_symbols = state.num_symbols in
197+
let+ () = modify_state Thread.incr_num_symbols in
198+
let name = Fmt.str "choice_i32_%i" num_symbols in
199+
let sym = Smtml.Symbol.make Smtml.Typed.Types.(to_ty bitv32) name in
200+
let assign = Smtml.Typed.Bitv32.(eq (symbol sym) e) in
201+
(Some assign, sym)
202+
in
198203
let* () =
199-
match assign with Some assign -> add_pc assign | None -> return ()
204+
match assign with
205+
| Some assign ->
206+
(* it must be SAT because we only introduced a constraint: new_symbol = e *)
207+
add_already_checked_condition_to_pc assign
208+
| None -> return ()
200209
in
201210
let rec generator () =
202211
let* possible_value = get_model_or_prune symbol in
@@ -215,16 +224,26 @@ let select_i32 (i : Symbolic_i32.t) : int32 t =
215224
in
216225
let this_val_branch =
217226
let* () = add_breadcrumb (Int32.to_int i) in
218-
let* () = add_pc this_value_cond in
227+
let* () = add_already_checked_condition_to_pc this_value_cond in
219228
return i
220229
in
221230

222-
let not_this_value_cond = Symbolic_boolean.not this_value_cond in
223231
let not_this_val_branch =
224-
let* () = add_pc not_this_value_cond in
225-
generator ()
232+
let not_this_value_cond = Symbolic_boolean.not this_value_cond in
233+
(* TODO: this is annoying as the next call to get_model_or_prune is also going to check for satisfiability which is useless! it can probably be simplified.
234+
I'm leaving it for now to be sure this is correct. It may actually not be required but better safe than sorry! *)
235+
let* satisfiability = check_reachability not_this_value_cond in
236+
match satisfiability with
237+
| `Sat ->
238+
let* () = add_already_checked_condition_to_pc not_this_value_cond in
239+
generator ()
240+
| `Unsat -> prune ()
241+
| `Unknown ->
242+
(* It can happen when the solver is interrupted *)
243+
(* TODO: once https://github.com/formalsec/smtml/pull/479 is merged
244+
if solver was interrupted then prune () else assert false *)
245+
prune ()
226246
in
227-
let* state in
228247
Thread.incr_path_count state;
229248

230249
(* TODO: better prio here? *)
@@ -289,13 +308,14 @@ let assume condition =
289308
| Val True -> return ()
290309
| Val False -> prune ()
291310
| _ -> (
292-
let* () = add_pc condition in
293311
let* satisfiability = check_reachability condition in
294312
match satisfiability with
295-
| `Sat -> return ()
313+
| `Sat ->
314+
let* () = add_already_checked_condition_to_pc condition in
315+
return ()
296316
| `Unsat -> prune ()
297317
| `Unknown ->
298318
(* It can happen when the solver is interrupted *)
299319
(* TODO: once https://github.com/formalsec/smtml/pull/479 is merged
300-
if solver was interrupted then prune () else assert false *)
320+
if solver was interrupted then prune () else assert false *)
301321
prune () )

src/symbolic/thread.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ let add_symbol t s =
7171
let open Symbol_scope in
7272
{ t with symbol_scopes = symbol s t.symbol_scopes }
7373

74-
let add_pc t c =
75-
let pc = Symex.Path_condition.add c t.pc in
74+
let add_already_checked_condition_to_pc t c =
75+
let pc = Symex.Path_condition.add_checked_sat_condition c t.pc in
7676
{ t with pc }
7777

7878
let add_breadcrumb t crumb =

src/symbolic/thread.mli

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type t = private
3030

3131
val init : unit -> t
3232

33-
val add_pc : t -> Symbolic_boolean.t -> t
33+
val add_already_checked_condition_to_pc : t -> Symbolic_boolean.t -> t
3434

3535
val add_breadcrumb : t -> int -> t
3636

0 commit comments

Comments
 (0)