Skip to content

Commit e9df254

Browse files
authored
Merge pull request #855 from goblint/fix_evalAssert
Fix EvalAssert transformation to work on coreutil programs
2 parents 790b3a9 + a929c64 commit e9df254

File tree

7 files changed

+174
-78
lines changed

7 files changed

+174
-78
lines changed

src/cdomains/valueDomain.ml

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,14 +1239,32 @@ struct
12391239
in
12401240
let offset = offs_to_offset offs in
12411241

1242+
let cast_to_void_ptr e =
1243+
Cilfacade.mkCast ~e ~newt:(TPtr (TVoid [], []))
1244+
in
12421245
let i =
12431246
if InvariantCil.(not (exp_contains_tmp c_exp) && exp_is_in_scope scope c_exp && not (var_is_tmp vi) && var_is_in_scope scope vi && not (var_is_heap vi)) then
12441247
let addr_exp = AddrOf (Var vi, offset) in (* AddrOf or Lval? *)
1248+
let addr_exp, c_exp = if typeSig (typeOf addr_exp) <> typeSig (typeOf c_exp) then
1249+
cast_to_void_ptr addr_exp, cast_to_void_ptr c_exp
1250+
else
1251+
addr_exp, c_exp
1252+
in
12451253
Invariant.of_exp Cil.(BinOp (Eq, c_exp, addr_exp, intType))
12461254
else
12471255
Invariant.none
12481256
in
1249-
let i_deref = deref_invariant ~vs vi ~offset ~lval:(Mem c_exp, NoOffset) in
1257+
let i_deref =
1258+
match Cilfacade.typeOfLval (Var vi, offset) with
1259+
| typ ->
1260+
(* Address set for a void* variable contains pointers to values of non-void type,
1261+
so insert pointer cast to make invariant expression valid (no field/index on void). *)
1262+
let newt = TPtr (typ, []) in
1263+
let c_exp = Cilfacade.mkCast ~e:c_exp ~newt in
1264+
deref_invariant ~vs vi ~offset ~lval:(Mem c_exp, NoOffset)
1265+
| exception Cilfacade.TypeOfError _ -> (* typeOffset: Index on a non-array on calloc-ed alloc variables *)
1266+
Invariant.none
1267+
in
12501268

12511269
Some (Invariant.(acc || (i && i_deref)))
12521270
| Addr.NullPtr ->
@@ -1285,9 +1303,9 @@ struct
12851303
else
12861304
Invariant.none
12871305
| `Address n -> ad_invariant ~vs ~offset ~lval n
1288-
| `Blob n -> blob_invariant ~vs ~offset ~lval n
12891306
| `Struct n -> Structs.invariant ~value_invariant:(vd_invariant ~vs) ~offset ~lval n
12901307
| `Union n -> Unions.invariant ~value_invariant:(vd_invariant ~vs) ~offset ~lval n
1308+
| `Blob n when GobConfig.get_bool "ana.base.invariant.blobs" -> blob_invariant ~vs ~offset ~lval n
12911309
| _ -> Invariant.none (* TODO *)
12921310

12931311
and deref_invariant ~vs vi ~offset ~lval =

src/framework/control.ml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,13 @@ struct
580580
; context = (fun () -> ctx_failwith "No context in query context.")
581581
; edge = MyCFG.Skip
582582
; local = local
583-
; global = (fun g -> EQSys.G.spec (GHT.find gh (EQSys.GVar.spec g)))
583+
; global =
584+
(fun g ->
585+
try
586+
EQSys.G.spec (GHT.find gh (EQSys.GVar.spec g))
587+
with Not_found ->
588+
M.warn ~category:MessageCategory.Unsound "Global %a not found during transformation, proceeding with bot." Spec.V.pretty g;
589+
Spec.G.bot ())
584590
; spawn = (fun v d -> failwith "Cannot \"spawn\" in query context.")
585591
; split = (fun d es -> failwith "Cannot \"split\" in query context.")
586592
; sideg = (fun v g -> failwith "Cannot \"split\" in query context.")

src/transform/evalAssert.ml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ module EvalAssert = struct
2626
let surroundByAtomic = true
2727

2828
(* Cannot use Cilfacade.name_fundecs as assert() is external and has no fundec *)
29-
let ass = makeVarinfo true "assert" (TVoid [])
29+
let ass = makeVarinfo true "__VERIFIER_assert" (TVoid [])
3030
let atomicBegin = makeVarinfo true "__VERIFIER_atomic_begin" (TVoid [])
3131
let atomicEnd = makeVarinfo true "__VERIFIER_atomic_end" (TVoid [])
3232

@@ -138,8 +138,14 @@ module EvalAssert = struct
138138
in
139139
ChangeDoChildrenPost (s, instrument_statement)
140140
end
141+
141142
let transform (ask: ?node:Node.t -> Cil.location -> Queries.ask) file = begin
142143
visitCilFile (new visitor ask) file;
144+
145+
(* Add function declarations before function definitions.
146+
This way, asserts may reference functions defined later. *)
147+
Cilfacade.add_function_declarations file;
148+
143149
let assert_filename = GobConfig.get_string "trans.output" in
144150
let oc = Stdlib.open_out assert_filename in
145151
dumpFile defaultCilPrinter oc assert_filename file; end

src/util/cilfacade.ml

Lines changed: 92 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -296,32 +296,32 @@ let locs = Hashtbl.create 200
296296

297297
(** Visitor to count locs appearing inside a fundec. *)
298298
class countFnVisitor = object
299-
inherit nopCilVisitor
300-
method! vstmt s =
301-
match s.skind with
302-
| Return (_, loc)
303-
| Goto (_, loc)
304-
| ComputedGoto (_, loc)
305-
| Break loc
306-
| Continue loc
307-
| If (_,_,_,loc,_)
308-
| Switch (_,_,_,loc,_)
309-
| Loop (_,loc,_,_,_)
310-
-> Hashtbl.replace locs loc.line (); DoChildren
311-
| _ ->
312-
DoChildren
313-
314-
method! vinst = function
315-
| Set (_,_,loc,_)
316-
| Call (_,_,_,loc,_)
317-
| Asm (_,_,_,_,_,loc)
318-
-> Hashtbl.replace locs loc.line (); SkipChildren
319-
| _ -> SkipChildren
320-
321-
method! vvdec _ = SkipChildren
322-
method! vexpr _ = SkipChildren
323-
method! vlval _ = SkipChildren
324-
method! vtype _ = SkipChildren
299+
inherit nopCilVisitor
300+
method! vstmt s =
301+
match s.skind with
302+
| Return (_, loc)
303+
| Goto (_, loc)
304+
| ComputedGoto (_, loc)
305+
| Break loc
306+
| Continue loc
307+
| If (_,_,_,loc,_)
308+
| Switch (_,_,_,loc,_)
309+
| Loop (_,loc,_,_,_)
310+
-> Hashtbl.replace locs loc.line (); DoChildren
311+
| _ ->
312+
DoChildren
313+
314+
method! vinst = function
315+
| Set (_,_,loc,_)
316+
| Call (_,_,_,loc,_)
317+
| Asm (_,_,_,_,_,loc)
318+
-> Hashtbl.replace locs loc.line (); SkipChildren
319+
| _ -> SkipChildren
320+
321+
method! vvdec _ = SkipChildren
322+
method! vexpr _ = SkipChildren
323+
method! vlval _ = SkipChildren
324+
method! vtype _ = SkipChildren
325325
end
326326

327327
let fnvis = new countFnVisitor
@@ -346,16 +346,16 @@ module StmtH = Hashtbl.Make (CilType.Stmt)
346346

347347
let stmt_fundecs: fundec StmtH.t ResettableLazy.t =
348348
ResettableLazy.from_fun (fun () ->
349-
let h = StmtH.create 113 in
350-
iterGlobals !current_file (function
351-
| GFun (fd, _) ->
352-
List.iter (fun stmt ->
353-
StmtH.replace h stmt fd
354-
) fd.sallstmts
355-
| _ -> ()
356-
);
357-
h
358-
)
349+
let h = StmtH.create 113 in
350+
iterGlobals !current_file (function
351+
| GFun (fd, _) ->
352+
List.iter (fun stmt ->
353+
StmtH.replace h stmt fd
354+
) fd.sallstmts
355+
| _ -> ()
356+
);
357+
h
358+
)
359359

360360
let pseudo_return_to_fun = StmtH.create 113
361361

@@ -369,14 +369,14 @@ module VarinfoH = Hashtbl.Make (CilType.Varinfo)
369369

370370
let varinfo_fundecs: fundec VarinfoH.t ResettableLazy.t =
371371
ResettableLazy.from_fun (fun () ->
372-
let h = VarinfoH.create 111 in
373-
iterGlobals !current_file (function
374-
| GFun (fd, _) ->
375-
VarinfoH.replace h fd.svar fd
376-
| _ -> ()
377-
);
378-
h
379-
)
372+
let h = VarinfoH.create 111 in
373+
iterGlobals !current_file (function
374+
| GFun (fd, _) ->
375+
VarinfoH.replace h fd.svar fd
376+
| _ -> ()
377+
);
378+
h
379+
)
380380

381381
(** Find [fundec] by the function's [varinfo] (has the function name and type). *)
382382
let find_varinfo_fundec vi = VarinfoH.find (ResettableLazy.force varinfo_fundecs) vi (* vi argument must be explicit, otherwise force happens immediately *)
@@ -386,14 +386,14 @@ module StringH = Hashtbl.Make (Printable.Strings)
386386

387387
let name_fundecs: fundec StringH.t ResettableLazy.t =
388388
ResettableLazy.from_fun (fun () ->
389-
let h = StringH.create 111 in
390-
iterGlobals !current_file (function
391-
| GFun (fd, _) ->
392-
StringH.replace h fd.svar.vname fd
393-
| _ -> ()
394-
);
395-
h
396-
)
389+
let h = StringH.create 111 in
390+
iterGlobals !current_file (function
391+
| GFun (fd, _) ->
392+
StringH.replace h fd.svar.vname fd
393+
| _ -> ()
394+
);
395+
h
396+
)
397397

398398
(** Find [fundec] by the function's name. *)
399399
(* TODO: why unused? *)
@@ -408,19 +408,19 @@ type varinfo_role =
408408

409409
let varinfo_roles: varinfo_role VarinfoH.t ResettableLazy.t =
410410
ResettableLazy.from_fun (fun () ->
411-
let h = VarinfoH.create 113 in
412-
iterGlobals !current_file (function
413-
| GFun (fd, _) ->
414-
VarinfoH.replace h fd.svar Function; (* function itself can be used as a variable (function pointer) *)
415-
List.iter (fun vi -> VarinfoH.replace h vi (Formal fd)) fd.sformals;
416-
List.iter (fun vi -> VarinfoH.replace h vi (Local fd)) fd.slocals
417-
| GVar (vi, _, _)
418-
| GVarDecl (vi, _) ->
419-
VarinfoH.replace h vi Global
420-
| _ -> ()
421-
);
422-
h
423-
)
411+
let h = VarinfoH.create 113 in
412+
iterGlobals !current_file (function
413+
| GFun (fd, _) ->
414+
VarinfoH.replace h fd.svar Function; (* function itself can be used as a variable (function pointer) *)
415+
List.iter (fun vi -> VarinfoH.replace h vi (Formal fd)) fd.sformals;
416+
List.iter (fun vi -> VarinfoH.replace h vi (Local fd)) fd.slocals
417+
| GVar (vi, _, _)
418+
| GVarDecl (vi, _) ->
419+
VarinfoH.replace h vi Global
420+
| _ -> ()
421+
);
422+
h
423+
)
424424

425425
(** Find the role of the [varinfo]. *)
426426
let find_varinfo_role vi = VarinfoH.find (ResettableLazy.force varinfo_roles) vi (* vi argument must be explicit, otherwise force happens immediately *)
@@ -449,15 +449,15 @@ let find_scope_fundec vi =
449449
let original_names: string VarinfoH.t ResettableLazy.t =
450450
(* only invert environment map when necessary (e.g. witnesses) *)
451451
ResettableLazy.from_fun (fun () ->
452-
let h = VarinfoH.create 113 in
453-
Hashtbl.iter (fun original_name (envdata, _) ->
454-
match envdata with
455-
| Cabs2cil.EnvVar vi when vi.vname <> "" -> (* TODO: fix temporary variables with empty names being in here *)
456-
VarinfoH.replace h vi original_name
457-
| _ -> ()
458-
) Cabs2cil.environment;
459-
h
460-
)
452+
let h = VarinfoH.create 113 in
453+
Hashtbl.iter (fun original_name (envdata, _) ->
454+
match envdata with
455+
| Cabs2cil.EnvVar vi when vi.vname <> "" -> (* TODO: fix temporary variables with empty names being in here *)
456+
VarinfoH.replace h vi original_name
457+
| _ -> ()
458+
) Cabs2cil.environment;
459+
h
460+
)
461461

462462
(** Find the original name (in input source code) of the [varinfo].
463463
If it was renamed by CIL, then returns the original name before renaming.
@@ -480,3 +480,21 @@ let stmt_pretty_short () x =
480480
| Instr (y::ys) -> dn_instr () y
481481
| If (exp,_,_,_,_) -> dn_exp () exp
482482
| _ -> dn_stmt () x
483+
484+
(** Given a [Cil.file], reorders its [globals], inserts function declarations before function definitions.
485+
This function may be used after a code transformation to ensure that the order of globals yields a compilable program. *)
486+
let add_function_declarations (file: Cil.file): unit =
487+
let globals = file.globals in
488+
let functions, non_functions = List.partition (fun g -> match g with GFun _ -> true | _ -> false) globals in
489+
let upto_last_type, non_types = GobList.until_last_with (fun g -> match g with GType _ -> true | _ -> false) non_functions in
490+
let declaration_from_GFun f = match f with
491+
| GFun (f, _) when String.starts_with ~prefix:"__builtin" f.svar.vname ->
492+
(* Builtin functions should not occur in asserts generated, so there is no need to add declarations for them.*)
493+
None
494+
| GFun (f, _) ->
495+
Some (GVarDecl (f.svar, locUnknown))
496+
| _ -> failwith "Expected GFun, but was something else."
497+
in
498+
let fun_decls = List.filter_map declaration_from_GFun functions in
499+
let globals = upto_last_type @ fun_decls @ non_types @ functions in
500+
file.globals <- globals

src/util/gobList.ml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,12 @@ let rec fold_while_some (f : 'a -> 'b -> 'a option) (acc: 'a) (xs: 'b list): 'a
2727
end
2828

2929
let equal = List.eq
30+
31+
(** Given a predicate and a list, returns two lists [(l1, l2)].
32+
[l1] contains the prefix of the list until the last element that satisfies the predicate, [l2] contains all subsequent elements. The order of elements is preserved. *)
33+
let until_last_with (pred: 'a -> bool) (xs: 'a list) =
34+
let rec until_last_helper last seen = function
35+
| [] -> List.rev last, List.rev seen
36+
| h::t -> if pred h then until_last_helper (h::seen@last) [] t else until_last_helper last (h::seen) t
37+
in
38+
until_last_helper [] [] xs

src/util/options.schema.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,12 @@
717717
"description": "Generate base analysis invariants",
718718
"type": "boolean",
719719
"default": true
720+
},
721+
"blobs": {
722+
"title": "ana.base.invariant.blobs",
723+
"description": "Whether to dump assertions about all blobs. Enabling this option may lead to unsound asserts.",
724+
"type": "boolean",
725+
"default": false
720726
}
721727
},
722728
"additionalProperties": false
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// PARAM: --set trans.activated[+] "assert"
2+
3+
// Running the assert transformation on this test used to yield code that cannot be compiled with gcc, due to superfluous offsets on a pointer
4+
struct s {
5+
int a;
6+
int b;
7+
};
8+
9+
union u {
10+
struct s str;
11+
int i;
12+
};
13+
14+
15+
int main(){
16+
union u un;
17+
18+
struct s* ptr;
19+
20+
un.str.a = 1;
21+
un.str.b = 2;
22+
23+
ptr = &un.str;
24+
int r;
25+
int x;
26+
if(r){
27+
x = 2;
28+
} else {
29+
x = 3;
30+
}
31+
32+
return 0;
33+
}

0 commit comments

Comments
 (0)