-
Notifications
You must be signed in to change notification settings - Fork 88
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
Merged
+783
−10
Merged
Changes from 71 commits
Commits
Show all changes
72 commits
Select commit
Hold shift + click to select a range
c28716d
add first creation lockset test files
dabund24 3a10eb6
initial version of creationLockset analysis
dabund24 deca788
AncestorLocksetSpec as common base module
dabund24 0b9f875
initial version of TaintedCreationLockset analysis
dabund24 189ac84
use thread domain instead of lifted thread domain
dabund24 4a7e92c
add threadJoins as dependency for TaintedCreationLockset analysis
dabund24 163f871
initial version of transitive descendants analysis
dabund24 f1ddc30
query for descendant analysis
dabund24 f6e65f6
get rid of unnecessary match expression
dabund24 d65759c
MayCreationLockset query
dabund24 0221ada
InterThreadedLockset query
dabund24 803a9f0
cartesian product helper functions
dabund24 e893c59
remove unused function from TaintedCreationLocksetSpec
dabund24 41d739b
correct comment in tainted lockset analysis
dabund24 56b8dfa
replace threadset and lockset module references with shorthand
dabund24 89eddc1
function for getting currently running tids
dabund24 0ab55bc
inter-threaded lockset A module
dabund24 8dde95f
use topped set for global domain in AncestorLocksetSpec
dabund24 f067a8f
replace comparison operators with equals function of domains
dabund24 34c9b39
add creationLockset analysis to dependencies of taintedCreationLockse…
dabund24 6206b0a
remove unused inter threaded lockset query
dabund24 9690c9f
hash descendant thread query param
dabund24 d0abcce
transitive version of (tainted) creation locksets
dabund24 6a6fcab
add race and transitive descendants analyses as dependencies to creat…
dabund24 ab9b59c
regression tests for transitive creation locksets
dabund24 cb7de4f
add thread param to MayCreationLockset query
dabund24 9ef79c2
handle unlock of unknown mutex
dabund24 0343db5
edit some comments
dabund24 20c65f0
Merge branch 'goblint:master' into master
dabund24 44e64fc
remove redundant must-ancestor check
dabund24 38aa7ec
minimize contributions to tcl when unlock of unknown thread is encoun…
dabund24 da825ce
align naming style of A module with other modules
dabund24 eabd9ec
Merge branch 'master' into master
dabund24 a6fb3b1
add new modules to goblint_lib
dabund24 80b83d9
add params to tests
dabund24 a731908
remove comment/question concerning contexts
dabund24 eb6c151
align hash calls for threads in queries with other hash calls
dabund24 a943f15
move address to must lock conversion from mutex ghosts/creation locks…
dabund24 47dd71b
should_print in A module
dabund24 8615223
ambiguous thread creation test cases
dabund24 1ccc286
more racefree test cases
dabund24 6e966b6
more racing test cases
dabund24 7ac184e
Merge branch 'master' into master
dabund24 f88dde5
remove unused LibraryFunctions import
dabund24 0868f78
add query parameters to result of pretty function
dabund24 5a97553
remove redundant `;;`
dabund24 597d061
add ambiguous context regression test
dabund24 944a862
change global domain for creation lockset analysis
dabund24 b15e5d6
remove creation lockset query
dabund24 8386f0e
remove query function from creation lockset analysis
dabund24 8171412
enforce must-ancestor property in unlock transfer function
dabund24 ab18883
fix comments for test files
dabund24 9dd79e4
reorder some statements in event transition function
dabund24 de5b73e
move domain type definition back from queries to analysis
dabund24 676d07d
move comment explaining the analysis to top of file
dabund24 6d5e82c
rename descendants analysis
dabund24 9bc8766
make threadspawn transfer function in descendants analysis a little l…
dabund24 df8725e
Merge branch 'master' into master
dabund24 7ecf8bd
compare functions for queries
dabund24 ec062ca
one more norace test case
dabund24 b45b2c6
fix dependency analyses names
dabund24 7822842
Merge branch 'master' into master
dabund24 c62e44e
norace -> racefree aligning test names with those of other tests
dabund24 03c9752
LID -> Lock and LIDs -> Lockset
dabund24 438fd89
compute thread descendants hull in `query` rather than `threadspawn`
dabund24 98574cc
Merge branch 'master' into master
dabund24 bf831e7
Merge branch 'goblint:master' into master
dabund24 d2e1d7a
move regression tests to other test group
dabund24 9ff2446
partially rename functions and variables: unique -> must_ancestor
dabund24 71b1ec4
use man.ask instead of ask.f
dabund24 55912b3
re-use function in A module
dabund24 524d91b
Merge branch 'master' into master
dabund24 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| (** creation lockset analysis [creationLockset] | ||
| constructs edges on the graph over all threads, where the edges are labelled with must-locksets: | ||
| (t_1) ---L--> (t_0) is represented by global t_1 t_0 = L and means that t_1 is protected by all members of L from t_0 | ||
|
|
||
| @see https://github.com/goblint/analyzer/pull/1865 | ||
| *) | ||
|
|
||
| open Analyses | ||
| module TID = ThreadIdDomain.Thread | ||
| module TIDs = ConcDomain.ThreadSet | ||
| module Lock = LockDomain.MustLock | ||
| module Lockset = LockDomain.MustLockset | ||
|
|
||
| module Spec = struct | ||
| include IdentityUnitContextsSpec | ||
| module D = Lattice.Unit | ||
|
|
||
| module V = struct | ||
| include TID | ||
| include StdV | ||
| end | ||
|
|
||
| module G = MapDomain.MapBot (TID) (Lockset) | ||
|
|
||
| let name () = "creationLockset" | ||
| let startstate _ = D.bot () | ||
| let exitstate _ = D.bot () | ||
|
|
||
| (** register a global contribution: global.[child_tid] \supseteq [to_contribute] | ||
| @param man man at program point | ||
| @param to_contribute new edges from [child_tid] to ego thread to register | ||
| @param child_tid | ||
| *) | ||
| let contribute_locks man to_contribute child_tid = man.sideg child_tid to_contribute | ||
|
|
||
| (** reflexive-transitive closure of child relation applied to [tid] | ||
| and filtered to only include threads, where [tid] is a must-ancestor | ||
| @param man any man | ||
| @param tid | ||
| *) | ||
| let must_ancestor_descendants_closure man tid = | ||
| let descendants = man.ask @@ Queries.DescendantThreads tid in | ||
| let must_ancestors_descendants = TIDs.filter (TID.must_be_ancestor tid) descendants in | ||
| TIDs.add tid must_ancestors_descendants | ||
|
|
||
| let threadspawn man ~multiple lval f args fman = | ||
| let tid_lifted = man.ask Queries.CurrentThreadId in | ||
| let child_tid_lifted = fman.ask Queries.CurrentThreadId in | ||
| match tid_lifted, child_tid_lifted with | ||
| | `Lifted tid, `Lifted child_tid when TID.must_be_ancestor tid child_tid -> | ||
| let must_ancestor_descendants = must_ancestor_descendants_closure fman child_tid in | ||
| let lockset = man.ask Queries.MustLockset in | ||
| let to_contribute = G.singleton tid lockset in | ||
| TIDs.iter (contribute_locks man to_contribute) must_ancestor_descendants | ||
| | _ -> () | ||
|
|
||
| (** compute all descendant threads that may run along with the ego thread at a program point. | ||
| for all of them, tid must be an ancestor | ||
| @param man man of ego thread at the program point | ||
| @param tid ego thread id | ||
| *) | ||
| let get_must_ancestor_running_descendants man tid = | ||
| let may_created_tids = man.ask Queries.CreatedThreads in | ||
| let may_must_ancestor_created_tids = | ||
| TIDs.filter (TID.must_be_ancestor tid) may_created_tids | ||
| in | ||
| let may_transitively_created_tids = | ||
| TIDs.fold | ||
| (fun child_tid acc -> TIDs.union acc (must_ancestor_descendants_closure man child_tid)) | ||
| may_must_ancestor_created_tids | ||
| (TIDs.empty ()) | ||
| in | ||
| let must_joined_tids = man.ask Queries.MustJoinedThreads in | ||
| TIDs.diff may_transitively_created_tids must_joined_tids | ||
|
|
||
| (** handle unlock of mutex [lock] *) | ||
| let unlock man tid possibly_running_tids lock = | ||
| let shrink_locksets des_tid = | ||
| let old_creation_lockset = G.find tid (man.global des_tid) in | ||
| (* Bot - {something} = Bot. This is exactly what we want in this case! *) | ||
| let updated_creation_lockset = Lockset.remove lock old_creation_lockset in | ||
| let to_contribute = G.singleton tid updated_creation_lockset in | ||
| man.sideg des_tid to_contribute | ||
| in | ||
| TIDs.iter shrink_locksets possibly_running_tids | ||
|
|
||
| (** handle unlock of an unknown mutex. Assumes that any mutex could have been unlocked *) | ||
| let unknown_unlock man tid possibly_running_tids = | ||
| let evaporate_locksets des_tid = | ||
| let to_contribute = G.singleton tid (Lockset.empty ()) in | ||
| man.sideg des_tid to_contribute | ||
| in | ||
| TIDs.iter evaporate_locksets possibly_running_tids | ||
|
|
||
| let event man e _ = | ||
| match e with | ||
| | Events.Unlock addr -> | ||
| let tid_lifted = man.ask Queries.CurrentThreadId in | ||
| (match tid_lifted with | ||
| | `Lifted tid -> | ||
| let possibly_running_tids = get_must_ancestor_running_descendants man tid in | ||
| let lock_opt = LockDomain.MustLock.of_addr addr in | ||
| (match lock_opt with | ||
| | Some lock -> unlock man tid possibly_running_tids lock | ||
| | None -> unknown_unlock man tid possibly_running_tids) | ||
| | _ -> ()) | ||
| | _ -> () | ||
|
|
||
| module A = struct | ||
| (** ego tid * must-lockset * creation-lockset *) | ||
| include Printable.Prod3 (TID) (Lockset) (G) | ||
|
|
||
| let name () = "creationLockset" | ||
|
|
||
| (** checks if [cl1] has a mapping ([tp1] |-> [ls1]) | ||
| such that [ls1] and [ls2] are not disjoint and [tp1] != [t2] | ||
| @param cl1 creation-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 cl1 t2 ls2 = | ||
| G.exists (fun tp1 ls1 -> not (Lockset.disjoint ls1 ls2 || TID.equal tp1 t2)) cl1 | ||
dabund24 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| (** checks if [cl1] has a member ([tp1] |-> [ls1]) and [cl2] has a member ([tp2] |-> [ls2]) | ||
| such that [ls1] and [ls2] are not disjoint and [tp1] != [tp2] | ||
| @param cl1 creation-lockset of first thread [t1] | ||
| @param cl2 creation-lockset of second thread [t2] | ||
| @returns whether [t1] and [t2] must be running mutually exclusive | ||
| *) | ||
| let both_protected_inter_threaded cl1 cl2 = | ||
| G.exists (one_protected_inter_threaded_other_intra_threaded cl1) cl2 | ||
|
|
||
| let may_race (t1, ls1, cl1) (t2, ls2, cl2) = | ||
| not | ||
| (both_protected_inter_threaded cl1 cl2 | ||
| || one_protected_inter_threaded_other_intra_threaded cl1 t2 ls2 | ||
| || one_protected_inter_threaded_other_intra_threaded cl2 t1 ls1) | ||
|
|
||
| let should_print (_t, _ls, cl) = not @@ G.is_empty cl | ||
| end | ||
|
|
||
| let access man _ = | ||
| let tid_lifted = man.ask Queries.CurrentThreadId in | ||
| match tid_lifted with | ||
| | `Lifted tid -> | ||
| let lockset = man.ask Queries.MustLockset in | ||
| let creation_lockset = man.global tid in | ||
| tid, lockset, creation_lockset | ||
| | _ -> ThreadIdDomain.UnknownThread, Lockset.empty (), G.empty () | ||
| end | ||
|
|
||
| let _ = | ||
| MCP.register_analysis | ||
| ~dep:[ "threadid"; "mutex"; "threadJoins"; "threadDescendants" ] | ||
| (module Spec : MCPSpec) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| (** thread descendants analysis [threadDescendants] | ||
| flow-insensitive construction of descendants may-set for every thread | ||
| *) | ||
|
|
||
| open Analyses | ||
| module TID = ThreadIdDomain.Thread | ||
|
|
||
| module Spec = struct | ||
| include IdentityUnitContextsSpec | ||
| module D = Lattice.Unit | ||
|
|
||
| module V = struct | ||
| include TID | ||
| include StdV | ||
| end | ||
|
|
||
| module G = ConcDomain.ThreadSet | ||
|
|
||
| let name () = "threadDescendants" | ||
| 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 -> | ||
| let children = man.global t in | ||
| (G.fold | ||
| (fun e acc -> G.union (man.ask @@ Queries.DescendantThreads e) acc) | ||
| children | ||
| children | ||
| : G.t) | ||
| | _ -> Queries.Result.top x | ||
|
|
||
| let threadspawn man ~multiple lval f args fman = | ||
| let tid_lifted = man.ask Queries.CurrentThreadId in | ||
| let child_tid_lifted = fman.ask Queries.CurrentThreadId in | ||
| match tid_lifted, child_tid_lifted with | ||
| | `Lifted tid, `Lifted child_tid -> man.sideg tid (G.singleton child_tid) | ||
| | _ -> () | ||
| end | ||
|
|
||
| let _ = MCP.register_analysis ~dep:[ "threadid" ] (module Spec : MCPSpec) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
tests/regression/53-races-mhp/10-lockset_inter_threaded_lock_racefree.c
dabund24 marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // PARAM: --set ana.activated[+] threadJoins --set ana.activated[+] threadDescendants --set ana.activated[+] creationLockset | ||
| #include <pthread.h> | ||
|
|
||
| int global = 0; | ||
| pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; | ||
| pthread_t id1, id2; | ||
|
|
||
| void *t1(void *arg) { | ||
| pthread_mutex_lock(&mutex); | ||
| global++; // NORACE | ||
| pthread_mutex_unlock(&mutex); | ||
| return NULL; | ||
| } | ||
|
|
||
| void *t2(void *arg) { // t2 is protected by mutex locked in main thread | ||
| global++; // NORACE | ||
| return NULL; | ||
| } | ||
|
|
||
| int main(void) { | ||
| pthread_create(&id1, NULL, t1, NULL); | ||
| pthread_mutex_lock(&mutex); | ||
| pthread_create(&id2, NULL, t2, NULL); | ||
| pthread_join(id2, NULL); | ||
| pthread_mutex_unlock(&mutex); | ||
| return 0; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.