Skip to content

Commit bdb69f0

Browse files
Merge pull request #839 from goblint/issue_718
MayLock Analysis & Sanity Checks of Mutex Usage & Analysis of Mutex Types
2 parents e56e045 + 01b9841 commit bdb69f0

27 files changed

+926
-41
lines changed

src/analyses/base.ml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,7 @@ struct
536536
| `Struct s -> ValueDomain.Structs.fold (fun k v acc -> AD.join (reachable_from_value ask gs st v t description) acc) s empty
537537
| `Int _ -> empty
538538
| `Float _ -> empty
539+
| `MutexAttr _ -> empty
539540
| `Thread _ -> empty (* thread IDs are abstract and nothing known can be reached from them *)
540541
| `JmpBuf _ -> empty (* Jump buffers are abstract and nothing known can be reached from them *)
541542
| `Mutex -> empty (* mutexes are abstract and nothing known can be reached from them *)
@@ -677,6 +678,7 @@ struct
677678
ValueDomain.Structs.fold f s (empty, TS.bot (), false)
678679
| `Int _ -> (empty, TS.bot (), false)
679680
| `Float _ -> (empty, TS.bot (), false)
681+
| `MutexAttr _ -> (empty, TS.bot (), false)
680682
| `Thread _ -> (empty, TS.bot (), false) (* TODO: is this right? *)
681683
| `JmpBuf _ -> (empty, TS.bot (), false) (* TODO: is this right? *)
682684
| `Mutex -> (empty, TS.bot (), false) (* TODO: is this right? *)
@@ -1254,6 +1256,12 @@ struct
12541256
end
12551257
| Q.EvalInt e ->
12561258
query_evalint (Analyses.ask_of_ctx ctx) ctx.global ctx.local e
1259+
| Q.EvalMutexAttr e -> begin
1260+
let e:exp = Lval (Cil.mkMem ~addr:e ~off:NoOffset) in
1261+
match eval_rv (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with
1262+
| `MutexAttr a -> a
1263+
| v -> MutexAttrDomain.top ()
1264+
end
12571265
| Q.EvalLength e -> begin
12581266
match eval_rv_address (Analyses.ask_of_ctx ctx) ctx.global ctx.local e with
12591267
| `Address a ->
@@ -2095,6 +2103,26 @@ struct
20952103
| _ -> ()
20962104
end;
20972105
raise Deadcode
2106+
| MutexAttrSetType {attr = attr; typ = mtyp}, _ ->
2107+
begin
2108+
let get_type lval =
2109+
let address = eval_lv (Analyses.ask_of_ctx ctx) gs st lval in
2110+
AD.get_type address
2111+
in
2112+
let dst_lval = mkMem ~addr:(Cil.stripCasts attr) ~off:NoOffset in
2113+
let dest_typ = get_type dst_lval in
2114+
let dest_a = eval_lv (Analyses.ask_of_ctx ctx) gs st dst_lval in
2115+
match eval_rv (Analyses.ask_of_ctx ctx) gs st mtyp with
2116+
| `Int x ->
2117+
begin
2118+
match ID.to_int x with
2119+
| Some z ->
2120+
if M.tracing then M.tracel "attr" "setting\n";
2121+
set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (`MutexAttr (ValueDomain.MutexAttr.of_int z))
2122+
| None -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (`MutexAttr (ValueDomain.MutexAttr.top ()))
2123+
end
2124+
| _ -> set ~ctx (Analyses.ask_of_ctx ctx) gs st dest_a dest_typ (`MutexAttr (ValueDomain.MutexAttr.top ()))
2125+
end
20982126
| Identity e, _ ->
20992127
begin match lv with
21002128
| Some x -> assign ctx x e

src/analyses/libraryDesc.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ type special =
5454
| ThreadExit of { ret_val: Cil.exp; }
5555
| Signal of Cil.exp
5656
| Broadcast of Cil.exp
57+
| MutexAttrSetType of { attr:Cil.exp; typ: Cil.exp; }
58+
| MutexInit of { mutex:Cil.exp; attr: Cil.exp; }
5759
| Wait of { cond: Cil.exp; mutex: Cil.exp; }
5860
| TimedWait of { cond: Cil.exp; mutex: Cil.exp; abstime: Cil.exp; (** Unused *) }
5961
| Math of { fun_args: math; }

src/analyses/libraryFunctions.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ let pthread_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
147147
("pthread_cond_broadcast", special [__ "cond" []] @@ fun cond -> Broadcast cond);
148148
("pthread_cond_wait", special [__ "cond" []; __ "mutex" []] @@ fun cond mutex -> Wait {cond; mutex});
149149
("pthread_cond_timedwait", special [__ "cond" []; __ "mutex" []; __ "abstime" [r]] @@ fun cond mutex abstime -> TimedWait {cond; mutex; abstime});
150+
("pthread_mutexattr_settype", special [__ "attr" []; __ "type" []] @@ fun attr typ -> MutexAttrSetType {attr; typ});
151+
("pthread_mutex_init", special [__ "mutex" []; __ "attr" []] @@ fun mutex attr -> MutexInit {mutex; attr});
150152
("pthread_attr_destroy", unknown [drop "attr" [f]]);
151153
("pthread_setspecific", unknown ~attrs:[InvalidateGlobals] [drop "key" []; drop "value" [w_deep]]);
152154
("pthread_getspecific", unknown ~attrs:[InvalidateGlobals] [drop "key" []]);

src/analyses/mayLocks.ml

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,43 @@
1-
(** May lockset analysis ([maylocks]). *)
2-
3-
(* TODO: unused *)
1+
(** May lockset analysis and analysis of double locking ([maylocks]). *)
42

53
open Analyses
4+
open GoblintCil
5+
module LF = LibraryFunctions
66

7-
module Arg =
7+
module Arg:LocksetAnalysis.MayArg =
88
struct
9-
module D = LockDomain.MayLockset
9+
module D = LockDomain.MayLocksetNoRW
1010
module G = DefaultSpec.G
1111
module V = DefaultSpec.V
1212

13-
let add ctx l =
14-
D.add l ctx.local
13+
let add ctx (l,r) =
14+
if D.mem l ctx.local then
15+
let default () =
16+
M.warn ~category:M.Category.Behavior.Undefined.double_locking "Acquiring a (possibly non-recursive) mutex that may be already held";
17+
ctx.local
18+
in
19+
match D.Addr.to_var_offset l with
20+
| Some (v,o) ->
21+
(let mtype = ctx.ask (Queries.MutexType (v, Lval.OffsetNoIdx.of_offs o)) in
22+
match mtype with
23+
| `Lifted MutexAttrDomain.MutexKind.Recursive -> ctx.local
24+
| `Lifted MutexAttrDomain.MutexKind.NonRec ->
25+
M.warn ~category:M.Category.Behavior.Undefined.double_locking "Acquiring a non-recursive mutex that may be already held";
26+
ctx.local
27+
| _ -> default ())
28+
| _ -> default ()
29+
else
30+
D.add l ctx.local
1531

1632
let remove ctx l =
17-
D.remove (l, true) (D.remove (l, false) ctx.local)
33+
if not (D.mem l ctx.local) then M.warn "Releasing a mutex that is definitely not held";
34+
match D.Addr.to_var_offset l with
35+
| Some (v,o) ->
36+
(let mtype = ctx.ask (Queries.MutexType (v, Lval.OffsetNoIdx.of_offs o)) in
37+
match mtype with
38+
| `Lifted MutexAttrDomain.MutexKind.NonRec -> D.remove l ctx.local
39+
| _ -> ctx.local (* we cannot remove them here *))
40+
| None -> ctx.local (* we cannot remove them here *)
1841
end
1942

2043
module Spec =
@@ -23,6 +46,17 @@ struct
2346
let name () = "maylocks"
2447

2548
let exitstate v = D.top () (* TODO: why? *)
49+
50+
let return ctx exp fundec =
51+
if not (D.is_bot ctx.local) && ThreadReturn.is_current (Analyses.ask_of_ctx ctx) then M.warn "Exiting thread while still holding a mutex!";
52+
ctx.local
53+
54+
let special ctx (lv:lval option) (f: varinfo) (args: exp list) =
55+
(match(LF.find f).special args with
56+
| ThreadExit _ -> if not @@ D.is_bot ctx.local then M.warn "Exiting thread while still holding a mutex!"
57+
| _ -> ())
58+
;
59+
ctx.local
2660
end
2761

2862
let _ =

src/analyses/mutexAnalysis.ml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,15 @@ struct
7171
D.add l ctx.local
7272

7373
let remove ctx l =
74+
if not (D.mem (l,true) ctx.local || D.mem (l,false) ctx.local) then M.warn "unlocking mutex which may not be held";
7475
D.remove (l, true) (D.remove (l, false) ctx.local)
7576

7677
let remove_all ctx =
7778
(* Mutexes.iter (fun m ->
7879
ctx.emit (MustUnlock m)
7980
) (D.export_locks ctx.local); *)
8081
(* TODO: used to have remove_nonspecial, which kept v.vname.[0] = '{' variables *)
82+
M.warn "unlocking unknown mutex which may not be held";
8183
D.empty ()
8284
end
8385
include LocksetAnalysis.MakeMust (Arg)

src/analyses/mutexTypeAnalysis.ml

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
(** An analysis tracking the type of a mutex. *)
2+
3+
open GoblintCil
4+
open Analyses
5+
6+
module MAttr = ValueDomain.MutexAttr
7+
module LF = LibraryFunctions
8+
9+
module Spec : Analyses.MCPSpec with module D = Lattice.Unit and module C = Lattice.Unit =
10+
struct
11+
include Analyses.IdentitySpec
12+
13+
let name () = "pthreadMutexType"
14+
module D = Lattice.Unit
15+
module C = Lattice.Unit
16+
17+
(* Removing indexes here avoids complicated lookups and allows to have the LVals as vars here, at the price that different types of mutexes in arrays are not dinstinguished *)
18+
module O = Lval.OffsetNoIdx
19+
20+
module V = struct
21+
include Printable.Prod(CilType.Varinfo)(O)
22+
let is_write_only _ = false
23+
end
24+
25+
module G = MAttr
26+
27+
(* transfer functions *)
28+
let assign ctx (lval:lval) (rval:exp) : D.t =
29+
match lval with
30+
| (Var v, o) ->
31+
(* There's no way to use the PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP etc for accesses via pointers *)
32+
let rec helper o t = function
33+
| Field ({fname = "__data"; _}, Field ({fname = "__kind"; _}, NoOffset)) when ValueDomain.Compound.is_mutex_type t ->
34+
let kind =
35+
(match Cil.constFold true rval with
36+
| Const (CInt (c, _, _)) -> MAttr.of_int c
37+
| _ -> `Top)
38+
in
39+
ctx.sideg (v,o) kind;
40+
ctx.local
41+
| Index (i,o') ->
42+
let o'' = O.of_offs (`Index (i, `NoOffset)) in
43+
helper (O.add_offset o o'') (Cilfacade.typeOffset t (Index (i,NoOffset))) o'
44+
| Field (f,o') ->
45+
let o'' = O.of_offs (`Field (f, `NoOffset)) in
46+
helper (O.add_offset o o'') (Cilfacade.typeOffset t (Field (f,NoOffset))) o'
47+
| NoOffset -> ctx.local
48+
in
49+
helper `NoOffset v.vtype o
50+
| _ -> ctx.local
51+
52+
let special ctx (lval: lval option) (f:varinfo) (arglist:exp list) : D.t =
53+
let desc = LF.find f in
54+
match desc.special arglist with
55+
| MutexInit {mutex = mutex; attr = attr} ->
56+
let attr = ctx.ask (Queries.EvalMutexAttr attr) in
57+
let mutexes = ctx.ask (Queries.MayPointTo mutex) in
58+
(* It is correct to iter over these sets here, as mutexes need to be intialized before being used, and an analysis that detects usage before initialization is a different analysis. *)
59+
Queries.LS.iter (function (v, o) -> ctx.sideg (v,O.of_offs o) attr) mutexes;
60+
ctx.local
61+
| _ -> ctx.local
62+
63+
let startstate v = D.bot ()
64+
let threadenter ctx lval f args = [D.top ()]
65+
let threadspawn ctx lval f args fctx = ctx.local
66+
let exitstate v = D.top ()
67+
68+
let query ctx (type a) (q: a Queries.t): a Queries.result =
69+
match q with
70+
| Queries.MutexType (v,o) -> (ctx.global (v,o):MutexAttrDomain.t)
71+
| _ -> Queries.Result.top q
72+
end
73+
74+
let _ =
75+
MCP.register_analysis (module Spec : MCPSpec)

src/analyses/threadAnalysis.ml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ struct
2222
module P = IdentityP (D)
2323

2424
(* transfer functions *)
25-
2625
let return ctx (exp:exp option) (f:fundec) : D.t =
2726
let tid = ThreadId.get_current (Analyses.ask_of_ctx ctx) in
2827
begin match tid with

src/autoTune.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ let activateLongjmpAnalysesWhenRequired () =
204204
let isLongjmp = function
205205
| LibraryDesc.Longjmp _ -> true
206206
| _ -> false
207-
in
207+
in
208208
if hasFunction isLongjmp then (
209209
print_endline @@ "longjmp -> enabling longjmp analyses \"" ^ (String.concat ", " longjmpAnalyses) ^ "\"";
210210
enableAnalyses longjmpAnalyses;

src/cdomains/lockDomain.ml

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,7 @@ struct
4343
)
4444
end
4545

46-
(* TODO: use SetDomain.Reverse *)
47-
module ReverseAddrSet = SetDomain.ToppedSet (Lock)
48-
(struct let topname = "All mutexes" end)
49-
50-
module AddrSet = Lattice.Reverse (ReverseAddrSet)
51-
52-
include AddrSet
46+
include SetDomain.Reverse(SetDomain.ToppedSet (Lock) (struct let topname = "All mutexes" end))
5347

5448
let rec may_be_same_offset of1 of2 =
5549
match of1, of2 with
@@ -62,7 +56,7 @@ struct
6256

6357
let add (addr,rw) set =
6458
match (Addr.to_var_offset addr) with
65-
| Some (_,x) when Offs.is_definite x -> ReverseAddrSet.add (addr,rw) set
59+
| Some (_,x) when Offs.is_definite x -> add (addr,rw) set
6660
| _ -> set
6761

6862
let remove (addr,rw) set =
@@ -73,18 +67,9 @@ struct
7367
| None -> false
7468
in
7569
match (Addr.to_var_offset addr) with
76-
| Some (_,x) when Offs.is_definite x -> ReverseAddrSet.remove (addr,rw) set
77-
| Some x -> ReverseAddrSet.filter (collect_diff_varinfo_with x) set
78-
| _ -> AddrSet.top ()
79-
80-
let empty = ReverseAddrSet.empty
81-
let is_empty = ReverseAddrSet.is_empty
82-
83-
let filter = ReverseAddrSet.filter
84-
let fold = ReverseAddrSet.fold
85-
let singleton = ReverseAddrSet.singleton
86-
let mem = ReverseAddrSet.mem
87-
let exists = ReverseAddrSet.exists
70+
| Some (_,x) when Offs.is_definite x -> remove (addr,rw) set
71+
| Some x -> filter (collect_diff_varinfo_with x) set
72+
| _ -> top ()
8873

8974
let export_locks ls =
9075
let f (x,_) set = Mutexes.add x set in
@@ -101,6 +86,11 @@ struct
10186
let bot = Lockset.top
10287
end
10388

89+
module MayLocksetNoRW =
90+
struct
91+
include PreValueDomain.AD
92+
end
93+
10494
module Symbolic =
10595
struct
10696
(* TODO: use SetDomain.Reverse *)

src/cdomains/lval.ml

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -443,32 +443,38 @@ struct
443443
end
444444
end
445445

446-
(** Lvalue lattice with sublattice representatives for {!DisjointDomain}. *)
447-
module NormalLatRepr (Idx: IntDomain.Z) =
448-
struct
449-
include NormalLat (Idx)
450-
446+
(* Helper for offsets without abstract values for index offsets, i.e. with unit index offsets.*)
447+
module NoIdxOffsetBase = struct
451448
module UnitIdxDomain =
452449
struct
453450
include Lattice.Unit
454451
let equal_to _ _ = `Top
455452
let to_int _ = None
456453
end
454+
455+
let rec of_offs = function
456+
| `NoOffset -> `NoOffset
457+
| `Field (f,o) -> `Field (f, of_offs o)
458+
| `Index (i,o) -> `Index (UnitIdxDomain.top (), of_offs o)
459+
end
460+
461+
(** Lvalue lattice with sublattice representatives for {!DisjointDomain}. *)
462+
module NormalLatRepr (Idx: IntDomain.Z) =
463+
struct
464+
include NormalLat (Idx)
465+
457466
(** Representatives for lvalue sublattices as defined by {!NormalLat}. *)
458467
module R: DisjointDomain.Representative with type elt = t =
459468
struct
460469
type elt = t
470+
open NoIdxOffsetBase
461471

462472
(* Offset module for representative without abstract values for index offsets, i.e. with unit index offsets.
463473
Reason: The offset in the representative (used for buckets) should not depend on the integer domains,
464474
since different integer domains may be active at different program points. *)
465475
include Normal (UnitIdxDomain)
466476

467-
let rec of_elt_offset: (fieldinfo, Idx.t) offs -> (fieldinfo, UnitIdxDomain.t) offs =
468-
function
469-
| `NoOffset -> `NoOffset
470-
| `Field (f,o) -> `Field (f, of_elt_offset o)
471-
| `Index (_,o) -> `Index (UnitIdxDomain.top (), of_elt_offset o) (* all indices to same bucket *)
477+
let of_elt_offset: (fieldinfo, Idx.t) offs -> (fieldinfo, UnitIdxDomain.t) offs = of_offs
472478

473479
let of_elt (x: elt): t = match x with
474480
| Addr (v, o) -> Addr (v, of_elt_offset o) (* addrs grouped by var and part of offset *)
@@ -628,3 +634,11 @@ struct
628634
end
629635
)
630636
end
637+
638+
module OffsetNoIdx =
639+
struct
640+
include NoIdxOffsetBase
641+
include Offset(UnitIdxDomain)
642+
643+
let name () = "offset without index"
644+
end

0 commit comments

Comments
 (0)