-
Notifications
You must be signed in to change notification settings - Fork 85
Improve MHP precision using ancestor locksets #1865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 56 commits
7831cbf
3ef7082
9d88584
b7d0d35
93a513a
946536b
a78e44c
8ba9094
ead4843
73e6d9c
f212c45
4633ec7
3f3e2f3
691cdfd
61d5c3f
e1719b2
8720b23
7c6e5d9
61a48dc
31ceff8
450d349
7d1fa1a
3c52c76
8b1727f
b0751dc
09f28ba
ea5a72a
0b0fae8
3a5513d
cecf244
55184fe
611a91e
c1ad680
70dabb7
0e63731
3514a37
19ce960
61b9609
52ce9f0
7f4e531
2158f23
e531002
85aad48
4b9c2c7
2b62168
dbf73f1
e1299ea
87dde05
b8416ef
13443f9
f1efd32
6ec28ca
1879e7e
5b97ec4
fc22ec0
4ac477e
695081c
5bf35ca
8d5928e
873ff0c
b495fa9
3c45dff
6c2c849
1db14cb
099f742
850e1cb
76c14dd
1f3cdef
72afe4a
3b494d9
583a41f
744f584
648de9a
772cf03
a8479fe
14d195f
a0c2446
048ca71
a677314
97a6967
2c9c39d
c36d6d1
3c1a51f
32c76cd
34f4e9f
5403dfd
7770141
169b60f
fe3c133
edda5d8
b011914
8aa1490
57cf4a8
82ecd93
135ce02
2054f59
6b8c3f3
db4cdf9
0df9c76
00ca4df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,210 @@ | ||
| (** Ancestor-lock analysis. See https://github.com/goblint/analyzer/pull/1865 *) | ||
|
|
||
| open Analyses | ||
| module LF = LibraryFunctions | ||
| module TID = ThreadIdDomain.Thread | ||
| module TIDs = ConcDomain.ThreadSet | ||
| module LID = LockDomain.MustLock | ||
| module LIDs = LockDomain.MustLockset | ||
|
|
||
| (** common base for [CreationLocksetSpec] and [TaintedCreationLocksetSpec] *) | ||
| module AncestorLocksetSpec = struct | ||
| include IdentityUnitContextsSpec | ||
| module D = Lattice.Unit | ||
|
|
||
| module V = struct | ||
| include TID | ||
| include StdV | ||
| end | ||
|
|
||
| (** 2 ^ { [TID] \times [LID] } *) | ||
| module G = Queries.ALS | ||
sim642 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| let startstate _ = D.bot () | ||
| let exitstate _ = D.bot () | ||
|
|
||
| (** register a contribution to a global: global.[child_tid] \supseteq [to_contribute] | ||
| @param man man at program point | ||
| @param to_contribute new edges from [child_tid] to register | ||
| @param child_tid | ||
| *) | ||
| let contribute_lock man to_contribute child_tid = man.sideg child_tid to_contribute | ||
|
|
||
| (** compute [tids] \times \{[lock]\} *) | ||
| let singleton_cartesian_prod tids lock = | ||
| TIDs.fold (fun tid acc -> G.add (tid, lock) acc) tids (G.empty ()) | ||
| ;; | ||
dabund24 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| (** compute the cartesian product [tids] \times [locks] *) | ||
| let cartesian_prod tids locks = | ||
| LIDs.fold | ||
| (fun lock acc -> | ||
| let tids_times_lock = singleton_cartesian_prod tids lock in | ||
| G.union tids_times_lock acc) | ||
| locks | ||
| (G.empty ()) | ||
| ;; | ||
|
|
||
| (** reflexive-transitive closure of child relation applied to [tid] | ||
| @param ask any ask | ||
| @param tid | ||
| @returns [{ tid }] \cup DES([tid]) | ||
| *) | ||
| let descendants_closure (ask : Queries.ask) tid = | ||
| let transitive_descendants = ask.f @@ Queries.DescendantThreads tid in | ||
| TIDs.add tid transitive_descendants | ||
| ;; | ||
| end | ||
|
|
||
| (** collects for each thread t_n pairs of ancestors and locks (t_0,l): | ||
| when t_n or an ancestor t_1 of t_n was created, the creating thread t_0 must have held l. | ||
| *) | ||
| module CreationLocksetSpec = struct | ||
| include AncestorLocksetSpec | ||
|
|
||
| let name () = "creationLockset" | ||
|
|
||
| (** create(t_1) in t_0 with lockset L *) | ||
| let threadspawn man ~multiple lval f args fman = | ||
| let ask = Analyses.ask_of_man man in | ||
| let tid_lifted = ask.f Queries.CurrentThreadId in | ||
| let child_ask = Analyses.ask_of_man fman in | ||
| let child_tid_lifted = child_ask.f Queries.CurrentThreadId in | ||
| match tid_lifted, child_tid_lifted with | ||
| | `Lifted tid, `Lifted child_tid -> | ||
| let descendants = descendants_closure child_ask child_tid in | ||
| let lockset = ask.f Queries.MustLockset in | ||
| let to_contribute = cartesian_prod (TIDs.singleton tid) lockset in | ||
| TIDs.iter (contribute_lock man to_contribute) descendants | ||
| | _ -> (* TODO deal with top or bottom? *) () | ||
|
||
| ;; | ||
|
|
||
| let query man (type a) (x : a Queries.t) : a Queries.result = | ||
| match x with | ||
| | Queries.MayCreationLockset tid -> (man.global tid : G.t) | ||
| | _ -> Queries.Result.top x | ||
| ;; | ||
| end | ||
|
|
||
| (** collects for each thread t_n pairs of ancestors and locks (t_0,l): | ||
| l may be unlocked in t_0 while t_n could be running. | ||
| *) | ||
| module TaintedCreationLocksetSpec = struct | ||
| include AncestorLocksetSpec | ||
|
|
||
| let name () = "taintedCreationLockset" | ||
|
|
||
| (** compute all threads that may run along with the ego thread at a program point | ||
| @param ask ask of ego thread at the program point | ||
| *) | ||
| let get_possibly_running_tids (ask : Queries.ask) = | ||
| let may_created_tids = ask.f Queries.CreatedThreads in | ||
| let may_transitively_created_tids = | ||
| TIDs.fold | ||
| (fun child_tid acc -> TIDs.union acc (descendants_closure ask child_tid)) | ||
| may_created_tids | ||
| (TIDs.empty ()) | ||
| in | ||
| let must_joined_tids = ask.f Queries.MustJoinedThreads in | ||
| TIDs.diff may_transitively_created_tids must_joined_tids | ||
| ;; | ||
|
|
||
| let event man e _ = | ||
| match e with | ||
| | Events.Unlock addr -> | ||
| let ask = Analyses.ask_of_man man in | ||
| let tid_lifted = ask.f Queries.CurrentThreadId in | ||
| (match tid_lifted with | ||
| | `Top | `Bot -> () | ||
| | `Lifted tid -> | ||
| let possibly_running_tids = get_possibly_running_tids ask in | ||
| let lock_opt = LockDomain.MustLock.of_addr addr in | ||
| (match lock_opt with | ||
| | Some lock -> | ||
| (* contribute for all possibly_running_tids: (tid, lock) *) | ||
| let to_contribute = G.singleton (tid, lock) in | ||
| TIDs.iter (contribute_lock man to_contribute) possibly_running_tids | ||
| | None -> | ||
| (* any lock could have been unlocked. Contribute for all possibly_running_tids all members of their CreationLocksets with the ego thread to invalidate them!! *) | ||
| let contribute_creation_lockset des_tid = | ||
| let full_creation_lockset = ask.f @@ Queries.MayCreationLockset des_tid in | ||
| let filtered_creation_lockset = | ||
| G.filter (fun (t, _l) -> t = tid) full_creation_lockset | ||
| in | ||
| man.sideg des_tid filtered_creation_lockset | ||
| in | ||
| TIDs.iter contribute_creation_lockset possibly_running_tids)) | ||
| | _ -> () | ||
| ;; | ||
|
|
||
| module A = struct | ||
| (** ego tid * lockset * inter-threaded lockset *) | ||
| include Printable.Prod3 (TID) (LIDs) (G) | ||
|
|
||
| let name () = "interThreadedLockset" | ||
|
|
||
| (** checks if [itls1] has a member ([tp1], [l]) such that [itls2] has a member ([tp2], [l]) with [tp1] != [tp2] | ||
| @param itls1 inter-threaded lockset of first thread [t1] | ||
| @param itls2 inter-threaded lockset of second thread [t2] | ||
| @returns whether [t1] and [t2] must be running mutually exclusive | ||
| *) | ||
| let both_protected_inter_threaded itls1 itls2 = | ||
| let itls2_has_same_lock_other_tid (tp1, l1) = | ||
| G.exists (fun (tp2, l2) -> LID.equal l1 l2 && (not @@ TID.equal tp1 tp2)) itls2 | ||
| in | ||
| G.exists itls2_has_same_lock_other_tid itls1 | ||
| ;; | ||
|
|
||
| (** checks if [itls1] has a member ([tp1], [l1]) such that [l1] is in [ls2] and [tp1] != [t2] | ||
| @param itls1 inter-threaded lockset of thread [t1] at first program point | ||
| @param t2 thread id at second program point | ||
| @param ls2 lockset at second program point | ||
| @returns whether [t1] must be running mutually exclusive with second program point | ||
| *) | ||
| let one_protected_inter_threaded_other_intra_threaded itls1 t2 ls2 = | ||
| G.exists (fun (tp1, l1) -> LIDs.mem l1 ls2 && (not @@ TID.equal tp1 t2)) itls1 | ||
| ;; | ||
|
|
||
| let may_race (t1, ls1, itls1) (t2, ls2, itls2) = | ||
| not | ||
| (both_protected_inter_threaded itls1 itls2 | ||
| || one_protected_inter_threaded_other_intra_threaded itls1 t2 ls2 | ||
| || one_protected_inter_threaded_other_intra_threaded itls2 t1 ls1) | ||
| ;; | ||
|
|
||
| let should_print (_t, _ls, itls) = not @@ G.is_empty itls | ||
| end | ||
|
|
||
| let access man _ = | ||
| let ask = Analyses.ask_of_man man in | ||
| let tid_lifted = ask.f Queries.CurrentThreadId in | ||
| match tid_lifted with | ||
| | `Lifted tid -> | ||
| let lockset = ask.f Queries.MustLockset in | ||
| let creation_lockset = ask.f @@ Queries.MayCreationLockset tid in | ||
| let tainted_creation_lockset = man.global tid in | ||
| (* all values in creation lockset, but not in tainted creation lockset *) | ||
| let inter_threaded_lockset = G.diff creation_lockset tainted_creation_lockset in | ||
| tid, lockset, inter_threaded_lockset | ||
| | _ -> ThreadIdDomain.UnknownThread, LIDs.empty (), G.empty () | ||
| ;; | ||
| end | ||
|
|
||
| let _ = | ||
| MCP.register_analysis | ||
| ~dep:[ "threadid"; "mutex"; "race"; "transitiveDescendants" ] | ||
| (module CreationLocksetSpec : MCPSpec) | ||
| ;; | ||
|
|
||
| let _ = | ||
| MCP.register_analysis | ||
| ~dep: | ||
| [ "threadid" | ||
| ; "mutex" | ||
| ; "threadJoins" | ||
| ; "race" | ||
| ; "transitiveDescendants" | ||
| ; "creationLockset" | ||
| ] | ||
| (module TaintedCreationLocksetSpec : MCPSpec) | ||
| ;; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| open Analyses | ||
| module TID = ThreadIdDomain.Thread | ||
|
|
||
| (** flow-insensitive analysis mapping threads to may-sets of descendants *) | ||
| module TransitiveDescendants = struct | ||
| include IdentityUnitContextsSpec | ||
| module D = Lattice.Unit | ||
|
|
||
| module V = struct | ||
| include TID | ||
| include StdV | ||
| end | ||
|
|
||
| module G = ConcDomain.ThreadSet | ||
|
|
||
| let name () = "transitiveDescendants" | ||
| let startstate _ = D.bot () | ||
| let exitstate _ = D.bot () | ||
|
|
||
| let query man (type a) (x : a Queries.t) : a Queries.result = | ||
| match x with | ||
| | Queries.DescendantThreads t -> (man.global t : G.t) | ||
| | _ -> Queries.Result.top x | ||
| ;; | ||
|
|
||
| let threadspawn man ~multiple lval f args fman = | ||
| let ask = Analyses.ask_of_man man in | ||
| let tid_lifted = ask.f Queries.CurrentThreadId in | ||
| match tid_lifted with | ||
| | `Top | `Bot -> () | ||
| | `Lifted tid -> | ||
| let child_ask = Analyses.ask_of_man fman in | ||
| let child_tid_lifted = child_ask.f Queries.CurrentThreadId in | ||
| (match child_tid_lifted with | ||
| | `Top | `Bot -> () | ||
| | `Lifted child_tid -> | ||
| (* contribute new child *) | ||
| let _ = man.sideg tid (G.singleton child_tid) in | ||
| (* transitive closure *) | ||
| let child_descendants = man.global child_tid in | ||
| man.sideg tid child_descendants) | ||
| ;; | ||
| end | ||
|
|
||
| let _ = MCP.register_analysis ~dep:[ "threadid" ] (module TransitiveDescendants : MCPSpec) |
Uh oh!
There was an error while loading. Please reload this page.