Skip to content

Commit 85dc84e

Browse files
Merge pull request #520 from goblint/signals
Analysis of `pthread_cond`
2 parents c351df7 + 4353d73 commit 85dc84e

File tree

15 files changed

+445
-23
lines changed

15 files changed

+445
-23
lines changed

src/analyses/accessAnalysis.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,8 @@ struct
203203
ctx.local
204204
| _, "__VERIFIER_atomic_end" when get_bool "ana.sv-comp.functions" ->
205205
ctx.local
206-
| _, "pthread_cond_wait"
207-
| _, "pthread_cond_timedwait" ->
206+
| Wait _ , _
207+
| TimedWait _, _ ->
208208
ctx.local
209209
| _, _ ->
210210
LibraryDesc.Accesses.iter desc.accs (fun {kind; deep = reach} exp ->

src/analyses/extractPthread.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1228,15 +1228,15 @@ module Spec : Analyses.MCPSpec = struct
12281228
add_actions
12291229
@@ List.map (fun id -> Action.CondVarInit id)
12301230
@@ ExprEval.eval_ptr_id ctx cond_var Tbls.CondVarIdTbl.get
1231-
| Unknown, "pthread_cond_broadcast", [ cond_var ] ->
1231+
| Broadcast cond_var, _, _ ->
12321232
add_actions
12331233
@@ List.map (fun id -> Action.CondVarBroadcast id)
12341234
@@ ExprEval.eval_ptr_id ctx cond_var Tbls.CondVarIdTbl.get
1235-
| Unknown, "pthread_cond_signal", [ cond_var ] ->
1235+
| Signal cond_var, _, _ ->
12361236
add_actions
12371237
@@ List.map (fun id -> Action.CondVarSignal id)
12381238
@@ ExprEval.eval_ptr_id ctx cond_var Tbls.CondVarIdTbl.get
1239-
| Unknown, "pthread_cond_wait", [ cond_var; mutex ] ->
1239+
| Wait {cond = cond_var; mutex = mutex}, _, _ ->
12401240
let cond_vars = ExprEval.eval_ptr ctx cond_var in
12411241
let mutex_vars = ExprEval.eval_ptr ctx mutex in
12421242
let cond_var_action (v, m) =

src/analyses/libraryDesc.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ type special =
4141
| ThreadCreate of { thread: Cil.exp; start_routine: Cil.exp; arg: Cil.exp; }
4242
| ThreadJoin of { thread: Cil.exp; ret_var: Cil.exp; }
4343
| ThreadExit of { ret_val: Cil.exp; }
44+
| Signal of Cil.exp
45+
| Broadcast of Cil.exp
46+
| Wait of { cond: Cil.exp; mutex: Cil.exp; }
47+
| TimedWait of { cond: Cil.exp; mutex: Cil.exp; abstime: Cil.exp; }
4448
| Math of { fun_args: math; }
4549
| Memset of { dest: Cil.exp; ch: Cil.exp; count: Cil.exp; }
4650
| Bzero of { dest: Cil.exp; count: Cil.exp; }

src/analyses/libraryFunctions.ml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ let posix_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
3030
let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
3131
("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. *)
3232
("pthread_exit", special [__ "retval" []] @@ fun retval -> ThreadExit { ret_val = retval }); (* Doesn't dereference the void* itself, but just passes to pthread_join. *)
33+
("pthread_cond_signal", special [__ "cond" []] @@ fun cond -> Signal cond);
34+
("pthread_cond_broadcast", special [__ "cond" []] @@ fun cond -> Broadcast cond);
35+
("pthread_cond_wait", special [__ "cond" []; __ "mutex" []] @@ fun cond mutex -> Wait {cond; mutex});
36+
("pthread_cond_timedwait", special [__ "cond" []; __ "mutex" []; __ "abstime" [r]] @@ fun cond mutex abstime -> TimedWait {cond; mutex; abstime});
3337
]
3438

3539
(** GCC builtin functions.

src/analyses/mHPAnalysis.ml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,7 @@ struct
1414
let should_print _ = true
1515
end
1616

17-
let access ctx _: MHP.t =
18-
{
19-
tid = ctx.ask CurrentThreadId;
20-
created = ctx.ask CreatedThreads;
21-
must_joined = ctx.ask MustJoinedThreads
22-
}
17+
let access ctx _: MHP.t = MHP.current (Analyses.ask_of_ctx ctx)
2318
end
2419

2520
let _ =

src/analyses/mutexEventsAnalysis.ml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ struct
116116
| Unlock _, _ ->
117117
(*print_endline @@ "Mutex `Unlock "^f.vname;*)
118118
unlock remove_rw
119+
| Wait {cond = cond; mutex = mutex}, _
120+
| TimedWait {cond = cond; mutex = mutex; _}, _ ->
121+
(* mutex is unlocked while waiting but relocked when returns *)
122+
(* emit unlock-lock events for privatization *)
123+
let ms = eval_exp_addr (Analyses.ask_of_ctx ctx) mutex in
124+
List.iter (fun m ->
125+
(* unlock-lock each possible mutex as a split to be dependent *)
126+
(* otherwise may-point-to {a, b} might unlock a, but relock b *)
127+
ctx.split () [Events.Unlock m; Events.Lock (m, true)];
128+
) ms;
129+
raise Deadcode (* splits cover all cases *)
119130
| _, "spinlock_check" -> ()
120131
| _, "acquire_console_sem" when get_bool "kernel" ->
121132
ctx.emit (Events.Lock (console_sem, true))
@@ -127,18 +138,6 @@ struct
127138
ctx.emit (Events.Lock (verifier_atomic, true))
128139
| _, "__VERIFIER_atomic_end" when get_bool "ana.sv-comp.functions" ->
129140
ctx.emit (Events.Unlock verifier_atomic)
130-
| _, "pthread_cond_wait"
131-
| _, "pthread_cond_timedwait" ->
132-
(* mutex is unlocked while waiting but relocked when returns *)
133-
(* emit unlock-lock events for privatization *)
134-
let m_arg = List.nth arglist 1 in
135-
let ms = eval_exp_addr (Analyses.ask_of_ctx ctx) m_arg in
136-
List.iter (fun m ->
137-
(* unlock-lock each possible mutex as a split to be dependent *)
138-
(* otherwise may-point-to {a, b} might unlock a, but relock b *)
139-
ctx.split () [Events.Unlock m; Events.Lock (m, true)];
140-
) ms;
141-
raise Deadcode (* splits cover all cases *)
142141
| _, x ->
143142
()
144143

src/analyses/pthreadSignals.ml

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
(** Analysis of must-received pthread_signals. *)
2+
3+
open Prelude.Ana
4+
open Analyses
5+
module LF = LibraryFunctions
6+
7+
module Spec : Analyses.MCPSpec =
8+
struct
9+
module Signals = SetDomain.ToppedSet (ValueDomain.Addr) (struct let topname = "All signals" end)
10+
module MustSignals = Lattice.Reverse (Signals)
11+
12+
include Analyses.DefaultSpec
13+
module V = VarinfoV
14+
15+
let name () = "pthreadSignals"
16+
module D = MustSignals
17+
module C = MustSignals
18+
module G = SetDomain.ToppedSet (MHP) (struct let topname = "All Threads" end)
19+
20+
let rec conv_offset x =
21+
match x with
22+
| `NoOffset -> `NoOffset
23+
| `Index (Const (CInt (i,_,s)),o) -> `Index (IntDomain.of_const (i,Cilfacade.ptrdiff_ikind (),s), conv_offset o)
24+
| `Index (_,o) -> `Index (ValueDomain.IndexDomain.top (), conv_offset o)
25+
| `Field (f,o) -> `Field (f, conv_offset o)
26+
27+
let eval_exp_addr (a: Queries.ask) exp =
28+
let gather_addr (v,o) b = ValueDomain.Addr.from_var_offset (v,conv_offset o) :: b in
29+
match a.f (Queries.MayPointTo exp) with
30+
| a when not (Queries.LS.is_top a) && not (Queries.LS.mem (dummyFunDec.svar,`NoOffset) a) ->
31+
Queries.LS.fold gather_addr (Queries.LS.remove (dummyFunDec.svar, `NoOffset) a) []
32+
| _ -> []
33+
34+
let possible_vinfos a cv_arg =
35+
List.filter_map ValueDomain.Addr.to_var_may (eval_exp_addr a cv_arg)
36+
37+
(* transfer functions *)
38+
let assign ctx (lval:lval) (rval:exp) : D.t =
39+
ctx.local
40+
41+
let branch ctx (exp:exp) (tv:bool) : D.t =
42+
ctx.local
43+
44+
let body ctx (f:fundec) : D.t =
45+
ctx.local
46+
47+
let return ctx (exp:exp option) (f:fundec) : D.t =
48+
ctx.local
49+
50+
let enter ctx (lval: lval option) (f:fundec) (args:exp list) : (D.t * D.t) list =
51+
[ctx.local, ctx.local]
52+
53+
let combine ctx (lval:lval option) fexp (f:fundec) (args:exp list) fc (au:D.t) : D.t =
54+
au
55+
56+
let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t =
57+
let desc = LF.find f in
58+
match desc.special arglist with
59+
| Signal cond
60+
| Broadcast cond ->
61+
let mhp = G.singleton @@ MHP.current (Analyses.ask_of_ctx ctx) in
62+
let publish_one a = ctx.sideg a mhp in
63+
let possible_vars = possible_vinfos (Analyses.ask_of_ctx ctx) cond in
64+
List.iter publish_one possible_vars;
65+
ctx.local
66+
| Wait {cond = cond; _} ->
67+
let current_mhp = MHP.current (Analyses.ask_of_ctx ctx) in
68+
let module Signalled = struct
69+
type signalled = Never | NotConcurrently | PossiblySignalled
70+
71+
let (|||) a b = match a,b with
72+
| PossiblySignalled, _
73+
| _, PossiblySignalled -> PossiblySignalled
74+
| NotConcurrently , _
75+
| _, NotConcurrently -> NotConcurrently
76+
| Never, Never -> Never
77+
78+
let can_be_signalled a =
79+
let signalling_tids = ctx.global a in
80+
if G.is_top signalling_tids then
81+
PossiblySignalled
82+
else if G.is_empty signalling_tids then
83+
Never
84+
else if not @@ G.exists (MHP.may_happen_in_parallel current_mhp) signalling_tids then
85+
NotConcurrently
86+
else
87+
PossiblySignalled
88+
end
89+
in
90+
let open Signalled in
91+
let add_if_singleton conds = match conds with | [a] -> Signals.add (ValueDomain.Addr.from_var a) ctx.local | _ -> ctx.local in
92+
let conds = possible_vinfos (Analyses.ask_of_ctx ctx) cond in
93+
(match List.fold_left (fun acc cond -> can_be_signalled cond ||| acc) Never conds with
94+
| PossiblySignalled -> add_if_singleton conds
95+
| NotConcurrently ->
96+
(M.warn ~category:Deadcode "The condition variable(s) pointed to by %a are never signalled concurrently, succeeding code is live due to spurious wakeups only!" Basetype.CilExp.pretty cond; ctx.local)
97+
| Never ->
98+
(M.warn ~category:Deadcode "The condition variable(s) pointed to by %a are never signalled, succeeding code is live due to spurious wakeups only!" Basetype.CilExp.pretty cond; ctx.local)
99+
)
100+
101+
| TimedWait _ ->
102+
(* Time could simply have elapsed *)
103+
ctx.local
104+
| _ -> ctx.local
105+
106+
let startstate v = Signals.empty ()
107+
let threadenter ctx lval f args = [ctx.local]
108+
let threadspawn ctx lval f args fctx = ctx.local
109+
let exitstate v = Signals.empty ()
110+
end
111+
112+
let _ =
113+
MCP.register_analysis ~dep:["mutex"] (module Spec : MCPSpec)

src/cdomains/mHP.ml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ type t = {
99
must_joined: ConcDomain.ThreadSet.t;
1010
} [@@deriving eq, ord, hash]
1111

12+
let current (ask:Queries.ask) =
13+
{
14+
tid = ask.f Queries.CurrentThreadId;
15+
created = ask.f Queries.CreatedThreads;
16+
must_joined = ask.f Queries.MustJoinedThreads
17+
}
18+
1219
let pretty () {tid; created; must_joined} =
1320
let tid_doc = Some (Pretty.dprintf "tid=%a" ThreadIdDomain.ThreadLifted.pretty tid) in
1421
(* avoid useless empty sets in race output *)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// PARAM: --set ana.activated[+] 'pthreadSignals'
2+
#include<pthread.h>
3+
#include<stdio.h>
4+
#include<unistd.h>
5+
#include <assert.h>
6+
7+
int g;
8+
9+
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
10+
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
11+
12+
void* f1(void* ptr) {
13+
int top;
14+
pthread_mutex_lock(&mut);
15+
g = 1;
16+
if(top) {
17+
pthread_cond_wait(&cond,&mut); //NOWARN
18+
}
19+
pthread_mutex_unlock(&mut);
20+
return NULL;
21+
}
22+
23+
void* f2(void* ptr) {
24+
pthread_mutex_lock(&mut);
25+
pthread_cond_signal(&cond);
26+
pthread_mutex_unlock(&mut);
27+
return NULL;
28+
}
29+
30+
int main(int argc, char const *argv[])
31+
{
32+
pthread_t t1;
33+
pthread_t t2;
34+
35+
pthread_create(&t1,NULL,f1,NULL);
36+
sleep(1);
37+
pthread_create(&t2,NULL,f2,NULL);
38+
39+
pthread_join(t1, NULL);
40+
pthread_join(t2, NULL);
41+
42+
return 0;
43+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// PARAM: --set ana.activated[+] 'pthreadSignals'
2+
#include<pthread.h>
3+
#include<stdio.h>
4+
#include<unistd.h>
5+
#include <assert.h>
6+
7+
int g;
8+
9+
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
10+
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
11+
12+
void* f1(void* ptr) {
13+
int top;
14+
pthread_mutex_lock(&mut);
15+
int res = 0;
16+
pthread_cond_wait(&cond,&mut); //WARN
17+
assert(res == 0);
18+
pthread_mutex_unlock(&mut);
19+
return NULL;
20+
}
21+
22+
int main(int argc, char const *argv[])
23+
{
24+
pthread_t t1;
25+
pthread_t t2;
26+
27+
pthread_create(&t1,NULL,f1,NULL);
28+
29+
return 0;
30+
}

0 commit comments

Comments
 (0)