Skip to content

Commit 4f09c28

Browse files
authored
Merge pull request #688 from goblint/klever
Klever concurrency safety support
2 parents 9d1dc02 + 27684fe commit 4f09c28

File tree

7 files changed

+71
-15
lines changed

7 files changed

+71
-15
lines changed

conf/ldv-races.json

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
"escape",
3030
"expRelation",
3131
"mhp",
32-
"assert"
32+
"assert",
33+
"var_eq",
34+
"symb_locks"
3335
],
3436
"malloc": {
3537
"wrappers": [
@@ -52,6 +54,19 @@
5254
]
5355
}
5456
},
57+
"lib": {
58+
"activated": [
59+
"c",
60+
"posix",
61+
"pthread",
62+
"gcc",
63+
"glibc",
64+
"linux-userspace",
65+
"goblint",
66+
"ncurses",
67+
"klever"
68+
]
69+
},
5570
"witness": {
5671
"graphml": {
5772
"enabled": true,

src/analyses/base.ml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1953,8 +1953,8 @@ struct
19531953

19541954

19551955

1956-
let forkfun (ctx:(D.t, G.t, C.t, V.t) Analyses.ctx) (lv: lval option) (f: varinfo) (args: exp list) : (lval option * varinfo * exp list) list * bool =
1957-
let create_thread lval arg v =
1956+
let forkfun (ctx:(D.t, G.t, C.t, V.t) Analyses.ctx) (lv: lval option) (f: varinfo) (args: exp list) : (lval option * varinfo * exp list * bool) list =
1957+
let create_thread ~multiple lval arg v =
19581958
try
19591959
(* try to get function declaration *)
19601960
let fd = Cilfacade.find_varinfo_fundec v in
@@ -1963,7 +1963,7 @@ struct
19631963
| Some x -> [x]
19641964
| None -> List.map (fun x -> MyCFG.unknown_exp) fd.sformals
19651965
in
1966-
Some (lval, v, args)
1966+
Some (lval, v, args, multiple)
19671967
with Not_found ->
19681968
if LF.use_special f.vname then None (* we handle this function *)
19691969
else if isFunctionType v.vtype then
@@ -1973,7 +1973,7 @@ struct
19731973
| Some x -> [x]
19741974
| None -> List.map (fun x -> MyCFG.unknown_exp) (Cil.argsToList v_args)
19751975
in
1976-
Some (lval, v, args)
1976+
Some (lval, v, args, multiple)
19771977
else (
19781978
M.debug ~category:Analyzer "Not creating a thread from %s because its type is %a" v.vname d_type v.vtype;
19791979
None
@@ -1982,7 +1982,7 @@ struct
19821982
let desc = LF.find f in
19831983
match desc.special args, f.vname with
19841984
(* handling thread creations *)
1985-
| ThreadCreate { thread = id; start_routine = start; arg = ptc_arg }, _ -> begin
1985+
| ThreadCreate { thread = id; start_routine = start; arg = ptc_arg; multiple }, _ -> begin
19861986
(* extra sync so that we do not analyze new threads with bottom global invariant *)
19871987
publish_all ctx `Thread;
19881988
(* Collect the threads. *)
@@ -1994,7 +1994,7 @@ struct
19941994
else
19951995
start_funvars
19961996
in
1997-
List.filter_map (create_thread (Some (Mem id, NoOffset)) (Some ptc_arg)) start_funvars_with_unknown, false
1997+
List.filter_map (create_thread ~multiple (Some (Mem id, NoOffset)) (Some ptc_arg)) start_funvars_with_unknown
19981998
end
19991999
| _, _ when get_bool "sem.unknown_function.spawn" ->
20002000
(* TODO: Remove sem.unknown_function.spawn check because it is (and should be) really done in LibraryFunctions.
@@ -2008,8 +2008,8 @@ struct
20082008
let flist = shallow_flist @ deep_flist in
20092009
let addrs = List.concat_map AD.to_var_may flist in
20102010
if addrs <> [] then M.debug ~category:Analyzer "Spawning non-unique functions from unknown function: %a" (d_list ", " CilType.Varinfo.pretty) addrs;
2011-
List.filter_map (create_thread None None) addrs, true
2012-
| _, _ -> [], false
2011+
List.filter_map (create_thread ~multiple:true None None) addrs
2012+
| _, _ -> []
20132013

20142014
let assert_fn ctx e refine =
20152015
(* make the state meet the assertion in the rest of the code *)
@@ -2140,9 +2140,9 @@ struct
21402140
let addr = eval_lv (Analyses.ask_of_ctx ctx) ctx.global ctx.local lval in
21412141
(addr, AD.type_of addr)
21422142
in
2143-
let forks, multiple = forkfun ctx lv f args in
2144-
if M.tracing then if not (List.is_empty forks) then M.tracel "spawn" "Base.special %s: spawning functions %a\n" f.vname (d_list "," CilType.Varinfo.pretty) (List.map BatTuple.Tuple3.second forks);
2145-
List.iter (BatTuple.Tuple3.uncurry (ctx.spawn ~multiple)) forks;
2143+
let forks = forkfun ctx lv f args in
2144+
if M.tracing then if not (List.is_empty forks) then M.tracel "spawn" "Base.special %s: spawning functions %a\n" f.vname (d_list "," CilType.Varinfo.pretty) (List.map BatTuple.Tuple4.second forks);
2145+
List.iter (fun (lval, f, args, multiple) -> ctx.spawn ~multiple lval f args) forks;
21462146
let st: store = ctx.local in
21472147
let gs = ctx.global in
21482148
let desc = LF.find f in

src/config/options.schema.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1313,6 +1313,7 @@
13131313
"linux-kernel",
13141314
"goblint",
13151315
"sv-comp",
1316+
"klever",
13161317
"ncurses",
13171318
"zstd",
13181319
"pcre",

src/util/library/libraryDesc.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ type special =
5353
| Assert of { exp: Cil.exp; check: bool; refine: bool; }
5454
| Lock of { lock: Cil.exp; try_: bool; write: bool; return_on_success: bool; }
5555
| Unlock of Cil.exp
56-
| ThreadCreate of { thread: Cil.exp; start_routine: Cil.exp; arg: Cil.exp; }
56+
| ThreadCreate of { thread: Cil.exp; start_routine: Cil.exp; arg: Cil.exp; multiple: bool }
5757
| ThreadJoin of { thread: Cil.exp; ret_var: Cil.exp; }
5858
| ThreadExit of { ret_val: Cil.exp; }
5959
| Signal of Cil.exp

src/util/library/libraryFunctions.ml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
436436

437437
(** Pthread functions. *)
438438
let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
439-
("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *)
439+
("pthread_create", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg; multiple = false }); (* For precision purposes arg is not considered accessed here. Instead all accesses (if any) come from actually analyzing start_routine. *)
440440
("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *)
441441
("pthread_join", special [__ "thread" []; __ "retval" [w]] @@ fun thread retval -> ThreadJoin {thread; ret_var = retval});
442442
("pthread_kill", unknown [drop "thread" []; drop "sig" []]);
@@ -1019,6 +1019,21 @@ let svcomp_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
10191019
("__VERIFIER_nondet_size_t", unknown []); (* cannot give it in sv-comp.c without including stdlib or similar *)
10201020
]
10211021

1022+
let rtnl_lock = AddrOf (Cil.var (Cilfacade.create_var (makeGlobalVar "[rtnl_lock]" intType)))
1023+
1024+
(** LDV Klever functions. *)
1025+
let klever_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
1026+
("pthread_create_N", special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [s]; __ "arg" []] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg; multiple = true });
1027+
("pthread_join_N", special [__ "thread" []; __ "retval" [w]] @@ fun thread retval -> ThreadJoin {thread; ret_var = retval});
1028+
("ldv_mutex_model_lock", special [__ "lock" []; drop "sign" []] @@ fun lock -> Lock { lock; try_ = get_bool "sem.lock.fail"; write = true; return_on_success = true });
1029+
("ldv_mutex_model_unlock", special [__ "lock" []; drop "sign" []] @@ fun lock -> Unlock lock);
1030+
("ldv_spin_model_lock", unknown [drop "sign" []]);
1031+
("ldv_spin_model_unlock", unknown [drop "sign" []]);
1032+
("rtnl_lock", special [] @@ Lock { lock = rtnl_lock; try_ = false; write = true; return_on_success = true });
1033+
("rtnl_unlock", special [] @@ Unlock rtnl_lock);
1034+
("__rtnl_unlock", special [] @@ Unlock rtnl_lock);
1035+
]
1036+
10221037
let ncurses_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
10231038
("echo", unknown []);
10241039
("noecho", unknown []);
@@ -1104,6 +1119,7 @@ let libraries = Hashtbl.of_list [
11041119
("linux-kernel", linux_kernel_descs_list);
11051120
("goblint", goblint_descs_list);
11061121
("sv-comp", svcomp_descs_list);
1122+
("klever", klever_descs_list);
11071123
("ncurses", ncurses_descs_list);
11081124
("zstd", zstd_descs_list);
11091125
("pcre", pcre_descs_list);
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//PARAM: --set ana.activated[+] threadJoins --set lib.activated[+] klever
2+
#include <pthread.h>
3+
#include <goblint.h>
4+
5+
int g = 0;
6+
7+
void *t_fun(void *arg) {
8+
g++; // RACE!
9+
return NULL;
10+
}
11+
12+
int main() {
13+
pthread_t id;
14+
pthread_create_N(&id, NULL, t_fun, NULL); // spawns multiple threads
15+
pthread_join(id, NULL);
16+
17+
g++; // RACE!
18+
19+
pthread_join_N(id, NULL); // TODO: should this join one (do nothing) or all (like assume join)?
20+
21+
g++; // RACE!
22+
23+
return 0;
24+
}

unittest/analyses/libraryDslTest.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ let pthread_mutex_lock_desc: LibraryDesc.t = LibraryDsl.(
1111
)
1212

1313
let pthread_create_desc: LibraryDesc.t = LibraryDsl.(
14-
special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [r]; __ "arg" [r]] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg }
14+
special [__ "thread" [w]; drop "attr" [r]; __ "start_routine" [r]; __ "arg" [r]] @@ fun thread start_routine arg -> ThreadCreate { thread; start_routine; arg; multiple = false }
1515
)
1616

1717
let realloc_desc: LibraryDesc.t = LibraryDsl.(

0 commit comments

Comments
 (0)