Skip to content

Commit cdfb4a2

Browse files
authored
Merge pull request #1603 from goblint/unknown-function-spawn
Fix `sem.unknown_function.spawn` handling in base
2 parents 913220d + 4d4de22 commit cdfb4a2

File tree

6 files changed

+72
-39
lines changed

6 files changed

+72
-39
lines changed

src/analyses/base.ml

+1-6
Original file line numberDiff line numberDiff line change
@@ -2172,11 +2172,7 @@ struct
21722172
in
21732173
List.filter_map (create_thread ~multiple (Some (Mem id, NoOffset)) (Some ptc_arg)) start_funvars_with_unknown
21742174
end
2175-
| _, _ when get_bool "sem.unknown_function.spawn" ->
2176-
(* TODO: Remove sem.unknown_function.spawn check because it is (and should be) really done in LibraryFunctions.
2177-
But here we consider all non-ThreadCreate functions also unknown, so old-style LibraryFunctions access
2178-
definitions using `Write would still spawn because they are not truly unknown functions (missing from LibraryFunctions).
2179-
Need this to not have memmove spawn in SV-COMP. *)
2175+
| _, _ ->
21802176
let shallow_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = false } args in
21812177
let deep_args = LibraryDesc.Accesses.find desc.accs { kind = Spawn; deep = true } args in
21822178
let shallow_flist = collect_invalidate ~deep:false ~ctx ctx.local shallow_args in
@@ -2185,7 +2181,6 @@ struct
21852181
let addrs = List.concat_map AD.to_var_may flist in
21862182
if addrs <> [] then M.debug ~category:Analyzer "Spawning non-unique functions from unknown function: %a" (d_list ", " CilType.Varinfo.pretty) addrs;
21872183
List.filter_map (create_thread ~multiple:true None None) addrs
2188-
| _, _ -> []
21892184

21902185
let assert_fn ctx e refine =
21912186
(* make the state meet the assertion in the rest of the code *)

src/config/options.schema.json

+13
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,19 @@
15441544
}
15451545
},
15461546
"additionalProperties": false
1547+
},
1548+
"atexit": {
1549+
"title": "sem.atexit",
1550+
"type": "object",
1551+
"properties": {
1552+
"ignore": {
1553+
"title": "sem.atexit.ignore",
1554+
"description": "Ignore atexit callbacks (unsound).",
1555+
"type": "boolean",
1556+
"default": false
1557+
}
1558+
},
1559+
"additionalProperties": false
15471560
}
15481561
},
15491562
"additionalProperties": false

src/util/library/libraryDsl.ml

+37-17
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,20 @@ struct
3030
| [] -> fail "^::"
3131
end
3232

33+
type access =
34+
| Access of LibraryDesc.Access.t
35+
| If of (unit -> bool) * access
36+
37+
let rec eval_access = function
38+
| Access acc -> Some acc
39+
| If (p, access) ->
40+
if p () then
41+
eval_access access
42+
else
43+
None
44+
3345
type ('k, 'l, 'r) arg_desc = {
34-
accesses: Access.t list;
46+
accesses: access list;
3547
match_arg: (Cil.exp, 'k, 'r) Pattern.t;
3648
match_var_args: (Cil.exp list, 'l, 'r) Pattern.t;
3749
}
@@ -51,15 +63,21 @@ let rec accs: type k r. (k, r) args_desc -> Accesses.t = fun args_desc args ->
5163
match args_desc, args with
5264
| [], [] -> []
5365
| VarArgs arg_desc, args ->
54-
List.map (fun acc ->
55-
(acc, args)
66+
List.filter_map (fun access ->
67+
match eval_access access with
68+
| Some acc -> Some (acc, args)
69+
| None -> None
5670
) arg_desc.accesses
5771
| arg_desc :: args_desc, arg :: args ->
5872
let accs'' = accs args_desc args in
59-
List.fold_left (fun (accs'': (Access.t * Cil.exp list) list) (acc: Access.t) ->
60-
match List.assoc_opt acc accs'' with
61-
| Some args -> (acc, arg :: args) :: List.remove_assoc acc accs''
62-
| None -> (acc, [arg]) :: accs''
73+
List.fold_left (fun (accs'': (Access.t * Cil.exp list) list) (access: access) ->
74+
match eval_access access with
75+
| Some acc ->
76+
begin match List.assoc_opt acc accs'' with
77+
| Some args -> (acc, arg :: args) :: List.remove_assoc acc accs''
78+
| None -> (acc, [arg]) :: accs''
79+
end
80+
| None -> accs''
6381
) accs'' arg_desc.accesses
6482
| _, _ -> invalid_arg "accs"
6583

@@ -94,13 +112,15 @@ let drop (_name: string) accesses = { empty_drop_desc with accesses; }
94112
let drop' accesses = { empty_drop_desc with accesses; }
95113

96114

97-
let r = Access.{ kind = Read; deep = false; }
98-
let r_deep = Access.{ kind = Read; deep = true; }
99-
let w = Access.{ kind = Write; deep = false; }
100-
let w_deep = Access.{ kind = Write; deep = true; }
101-
let f = Access.{ kind = Free; deep = false; }
102-
let f_deep = Access.{ kind = Free; deep = true; }
103-
let s = Access.{ kind = Spawn; deep = false; }
104-
let s_deep = Access.{ kind = Spawn; deep = true; }
105-
let c = Access.{ kind = Spawn; deep = false; } (* TODO: Sound, but very imprecise hack for calls to function pointers given as arguments. *)
106-
let c_deep = Access.{ kind = Spawn; deep = true; }
115+
let r = Access { kind = Read; deep = false; }
116+
let r_deep = Access { kind = Read; deep = true; }
117+
let w = Access { kind = Write; deep = false; }
118+
let w_deep = Access { kind = Write; deep = true; }
119+
let f = Access { kind = Free; deep = false; }
120+
let f_deep = Access { kind = Free; deep = true; }
121+
let s = Access { kind = Spawn; deep = false; }
122+
let s_deep = Access { kind = Spawn; deep = true; }
123+
let c = Access { kind = Spawn; deep = false; } (* TODO: Sound, but very imprecise hack for calls to function pointers given as arguments. *)
124+
let c_deep = Access { kind = Spawn; deep = true; }
125+
126+
let if_ p access = If (p, access)

src/util/library/libraryDsl.mli

+19-14
Original file line numberDiff line numberDiff line change
@@ -28,52 +28,57 @@ val special': ?attrs:LibraryDesc.attr list -> (LibraryDesc.special, LibraryDesc.
2828
(** Create unknown library function descriptor from arguments descriptor, which must {!drop} all arguments. *)
2929
val unknown: ?attrs:LibraryDesc.attr list -> (LibraryDesc.special, LibraryDesc.special) args_desc -> LibraryDesc.t
3030

31+
(** Argument access descriptor. *)
32+
type access
3133

3234
(** Argument descriptor, which captures the named argument with accesses for continuation function of {!special}. *)
33-
val __: string -> LibraryDesc.Access.t list -> (Cil.exp -> 'r, Cil.exp list -> 'r, 'r) arg_desc
35+
val __: string -> access list -> (Cil.exp -> 'r, Cil.exp list -> 'r, 'r) arg_desc
3436

3537
(** Argument descriptor, which captures an unnamed argument with accesses for continuation function of {!special}. *)
36-
val __': LibraryDesc.Access.t list -> (Cil.exp -> 'r, Cil.exp list -> 'r, 'r) arg_desc
38+
val __': access list -> (Cil.exp -> 'r, Cil.exp list -> 'r, 'r) arg_desc
3739

3840
(** Argument descriptor, which drops (does not capture) the named argument with accesses. *)
39-
val drop: string -> LibraryDesc.Access.t list -> ('r, 'r, 'r) arg_desc
41+
val drop: string -> access list -> ('r, 'r, 'r) arg_desc
4042

4143
(** Argument descriptor, which drops (does not capture) an unnamed argument with accesses. *)
42-
val drop': LibraryDesc.Access.t list -> ('r, 'r, 'r) arg_desc
44+
val drop': access list -> ('r, 'r, 'r) arg_desc
4345

4446

4547
(** Shallow {!AccessKind.Read} access.
4648
All immediate arguments of function calls are always read, this specifies the reading of pointed-to values. *)
47-
val r: LibraryDesc.Access.t
49+
val r: access
4850

4951
(** Deep {!AccessKind.Read} access.
5052
All immediate arguments of function calls are always read, this specifies the reading of pointed-to values.
5153
Rarely needed. *)
52-
val r_deep: LibraryDesc.Access.t
54+
val r_deep: access
5355

5456
(** Shallow {!AccessKind.Write} access. *)
55-
val w: LibraryDesc.Access.t
57+
val w: access
5658

5759
(** Deep {!AccessKind.Write} access.
5860
Rarely needed. *)
59-
val w_deep: LibraryDesc.Access.t
61+
val w_deep: access
6062

6163
(** Shallow {!AccessKind.Free} access. *)
62-
val f: LibraryDesc.Access.t
64+
val f: access
6365

6466
(** Deep {!AccessKind.Free} access.
6567
Rarely needed. *)
66-
val f_deep: LibraryDesc.Access.t
68+
val f_deep: access
6769

6870
(** Shallow {!AccessKind.Spawn} access. *)
69-
val s: LibraryDesc.Access.t
71+
val s: access
7072

7173
(** Deep {!AccessKind.Spawn} access.
7274
Rarely needed. *)
73-
val s_deep: LibraryDesc.Access.t
75+
val s_deep: access
7476

7577
(** Shallow {!AccessKind.Spawn} access, substituting function pointer calls for now (TODO). *)
76-
val c: LibraryDesc.Access.t
78+
val c: access
7779

7880
(** Deep {!AccessKind.Spawn} access, substituting deep function pointer calls for now (TODO) *)
79-
val c_deep: LibraryDesc.Access.t
81+
val c_deep: access
82+
83+
(** Conditional access, e.g. on an option. *)
84+
val if_: (unit -> bool) -> access -> access

src/util/library/libraryFunctions.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ let c_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
145145
("_setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env }); (* only has one underscore *)
146146
("setjmp", special [__ "env" [w]] @@ fun env -> Setjmp { env });
147147
("longjmp", special [__ "env" [r]; __ "value" []] @@ fun env value -> Longjmp { env; value });
148-
("atexit", unknown [drop "function" [s]]);
148+
("atexit", unknown [drop "function" [if_ (fun () -> not (get_bool "sem.atexit.ignore")) s]]);
149149
("atoi", unknown [drop "nptr" [r]]);
150150
("atol", unknown [drop "nptr" [r]]);
151151
("atoll", unknown [drop "nptr" [r]]);

tests/regression/41-stdlib/08-atexit-no-spawn.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// PARAM: --disable sem.unknown_function.spawn
1+
// PARAM: --enable sem.atexit.ignore
22
#include <stdlib.h>
33
#include <goblint.h>
44

0 commit comments

Comments
 (0)