-
Notifications
You must be signed in to change notification settings - Fork 76
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
Analysis of pthread_barrier
s
#1652
Draft
michael-schwarz
wants to merge
17
commits into
master
Choose a base branch
from
issue_1651
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
2fb5390
First steps
michael-schwarz 29a3900
Record capacity
michael-schwarz 60c1d72
Add first checks
michael-schwarz 041e544
Use MHP information
michael-schwarz db320ae
More involved MHP check
michael-schwarz 8b2ede3
Rm spurious variable
michael-schwarz f418b00
Document pthread barriers
michael-schwarz 93b7f0c
Add `NOMAC` option
michael-schwarz a990b5f
Remark that `os` gem is needed
michael-schwarz 27dd030
Make sound for multiprocess
michael-schwarz 24d61c0
Fix indentation
michael-schwarz 8fe2e16
Also consider locks
michael-schwarz 05d2f60
Minimal race handling
michael-schwarz 1b3a0d8
Fix pairwise MHP check
michael-schwarz 7e98213
Add case for more waiters
michael-schwarz 1b4beae
Get rid of overcomplicated logic
michael-schwarz 1bfb985
Cleanup
michael-schwarz 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 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 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 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 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 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 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 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 @@ | ||
(** Must have waited barriers for Pthread barriers ([pthreadBarriers]). *) | ||
|
||
open GoblintCil | ||
open Analyses | ||
module LF = LibraryFunctions | ||
|
||
module Spec = | ||
struct | ||
module Barriers = struct | ||
include SetDomain.ToppedSet (ValueDomain.Addr) (struct let topname = "All barriers" end) | ||
let name () = "mayBarriers" | ||
end | ||
|
||
module MustBarriers = struct | ||
include Lattice.Reverse (Barriers) | ||
let name () = "mustBarriers" | ||
end | ||
|
||
module Capacity = Queries.ID | ||
|
||
include Analyses.IdentitySpec | ||
module V = VarinfoV | ||
|
||
module TID = ThreadIdDomain.ThreadLifted | ||
|
||
module MHPplusLock = struct | ||
module Locks = LockDomain.MustLockset | ||
|
||
include Printable.Prod (MHP) (Locks) | ||
let name () = "MHPplusLock" | ||
|
||
let mhp (mhp1, l1) (mhp2, l2) = | ||
MHP.may_happen_in_parallel mhp1 mhp2 && Locks.is_empty (Locks.inter l1 l2) | ||
|
||
let tid ((mhp:MHP.t), _) = mhp.tid | ||
|
||
let may_be_non_unique_thread (mhp, _) = MHP.may_be_non_unique_thread mhp | ||
end | ||
|
||
module Waiters = SetDomain.ToppedSet (MHPplusLock) (struct let topname = "All MHP" end) | ||
module Multiprocess = BoolDomain.MayBool | ||
module G = Lattice.Prod3 (Multiprocess) (Capacity) (Waiters) | ||
|
||
let name () = "pthreadBarriers" | ||
|
||
module MustObserved = MapDomain.MapTop_LiftBot (TID) (MustBarriers) | ||
module D = Lattice.Prod (Barriers) (MustObserved) | ||
|
||
include Analyses.ValueContexts(D) | ||
|
||
let possible_vinfos (a: Queries.ask) barrier = | ||
Queries.AD.to_var_may (a.f (Queries.MayPointTo barrier)) | ||
|
||
let special man (lval: lval option) (f:varinfo) (arglist:exp list) : D.t = | ||
let desc = LF.find f in | ||
match desc.special arglist with | ||
| BarrierWait barrier -> | ||
let ask = Analyses.ask_of_man man in | ||
let may, must = man.local in | ||
let barriers = possible_vinfos ask barrier in | ||
let mhp = MHP.current ask in | ||
let handle_one b = | ||
try | ||
let locks = man.ask (Queries.MustLockset) in | ||
man.sideg b (Multiprocess.bot (), Capacity.bot (), Waiters.singleton (mhp, locks)); | ||
let addr = ValueDomain.Addr.of_var b in | ||
let (multiprocess, capacity, waiters) = man.global b in | ||
let may = Barriers.add addr may in | ||
if multiprocess then | ||
(may, must) | ||
else | ||
let relevant_waiters = Waiters.filter (fun other -> MHPplusLock.mhp (mhp, locks) other) waiters in | ||
if Waiters.exists MHPplusLock.may_be_non_unique_thread relevant_waiters then | ||
(may, must) | ||
else | ||
match capacity with | ||
| `Top | `Bot -> (may, must) | ||
| `Lifted c -> | ||
let count = Waiters.cardinal relevant_waiters in | ||
(* Add 1 as the thread calling wait at the moment will not be MHP with itself *) | ||
let min_cap = (BatOption.default Z.zero (Capacity.I.minimal c)) in | ||
if Z.leq min_cap Z.one then | ||
(may, must) | ||
else if Z.geq (Z.of_int (count + 1)) min_cap then | ||
(* This is quite a cute problem: Do (min_cap-1) elements exist in the set such that | ||
MHP is pairwise true? This solution is a sledgehammer, there should be something much | ||
better algorithmically (beyond just laziness) *) | ||
let must = | ||
let waiters = Waiters.elements relevant_waiters in | ||
let min_cap = Z.to_int min_cap in | ||
let lists = List.init (min_cap - 1) (fun _ -> waiters) in | ||
let candidates = BatList.n_cartesian_product lists in | ||
let can_proceed = List.exists (fun candidate -> | ||
let rec do_it = function | ||
| [] -> true | ||
| x::xs -> List.for_all (fun y -> MHPplusLock.mhp x y) xs && do_it xs | ||
in | ||
do_it candidate | ||
) candidates | ||
in | ||
if not can_proceed then raise Analyses.Deadcode; | ||
(* limit to this case to avoid having to construct all permutations above *) | ||
if List.length waiters = min_cap - 1 then | ||
List.fold_left (fun acc elem -> | ||
let tid = MHPplusLock.tid elem in | ||
let curr = MustObserved.find tid acc in | ||
let must' = MustObserved.add tid (Barriers.add addr curr) acc in | ||
must' | ||
) must waiters | ||
else | ||
must | ||
in | ||
(may, must) | ||
else | ||
raise Analyses.Deadcode; | ||
with Analyses.Deadcode -> D.bot () | ||
in | ||
let (may, must) = List.fold_left (fun acc b-> D.join acc (handle_one b)) (D.bot ()) barriers in | ||
if Barriers.is_empty may then raise Analyses.Deadcode; | ||
(may, must) | ||
| BarrierInit { barrier; attr; count } -> | ||
let multitprocess = not @@ Queries.AD.is_null @@ man.ask (Queries.MayPointTo attr) in | ||
if multitprocess then M.warn "Barrier initialized with a non-NULL attr argument. Handled as if PTHREAD_PROCESS_SHARED potentially set."; | ||
let count = man.ask (Queries.EvalInt count) in | ||
let publish_one b = man.sideg b (multitprocess, count, Waiters.bot ()) in | ||
let barriers = possible_vinfos (Analyses.ask_of_man man) barrier in | ||
List.iter publish_one barriers; | ||
man.local | ||
| _ -> man.local | ||
|
||
let startstate v = (Barriers.empty (), MustObserved.empty ()) | ||
let threadenter man ~multiple lval f args = [man.local] | ||
let exitstate v = (Barriers.empty (), MustObserved.empty ()) | ||
|
||
module A = | ||
struct | ||
include Lattice.Prod3 (Barriers) (MustObserved) (TID) | ||
let name () = "barriers" | ||
let may_race (may_await_t1, must_observed_by_t1, t1) (may_await_t2, must_observed_by_t2, t2) = | ||
let observed_from_t2 = MustObserved.find t2 must_observed_by_t1 in | ||
if not (Barriers.subset observed_from_t2 may_await_t2) then | ||
false | ||
else | ||
let observed_from_t1 = MustObserved.find t1 must_observed_by_t2 in | ||
Barriers.subset observed_from_t1 may_await_t1 | ||
let should_print f = true | ||
end | ||
|
||
let access man (a: Queries.access) = | ||
let (may,must) = man.local in | ||
let mhp = MHP.current (Analyses.ask_of_man man) in | ||
(may, must, mhp.tid) | ||
end | ||
|
||
let _ = | ||
MCP.register_analysis (module Spec : MCPSpec) |
This file contains 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 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 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 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 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,26 @@ | ||
// NOMAC PARAM: --set ana.activated[+] 'pthreadBarriers' | ||
#include<pthread.h> | ||
#include<stdio.h> | ||
#include<unistd.h> | ||
|
||
int g; | ||
|
||
pthread_barrier_t barrier; | ||
|
||
|
||
int main(int argc, char const *argv[]) | ||
{ | ||
int top; | ||
int i = 0; | ||
|
||
pthread_barrier_init(&barrier, NULL, 2); | ||
|
||
if(top) { | ||
pthread_barrier_wait(&barrier); | ||
// Unreachable | ||
i = 1; | ||
} | ||
|
||
__goblint_check(i == 0); | ||
return 0; | ||
} |
This file contains 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,45 @@ | ||
// NOMAC PARAM: --set ana.activated[+] 'pthreadBarriers' | ||
#include<pthread.h> | ||
#include<stdio.h> | ||
#include<unistd.h> | ||
|
||
int g; | ||
|
||
pthread_barrier_t barrier; | ||
|
||
void* f2(void* ptr) { | ||
pthread_barrier_wait(&barrier); | ||
return NULL; | ||
} | ||
|
||
void* f1(void* ptr) { | ||
pthread_barrier_wait(&barrier); | ||
|
||
// This is past the barrier, so it will not be reached | ||
pthread_t t2; | ||
pthread_create(&t2,NULL,f2,NULL); | ||
|
||
return NULL; | ||
} | ||
|
||
int main(int argc, char const *argv[]) | ||
{ | ||
int top; | ||
int i = 0; | ||
|
||
pthread_barrier_init(&barrier, NULL, 3); | ||
|
||
pthread_t t1; | ||
pthread_create(&t1,NULL,f1,NULL); | ||
|
||
if(top) { | ||
pthread_barrier_wait(&barrier); | ||
// Unreachable | ||
i = 1; | ||
} | ||
|
||
__goblint_check(i == 0); | ||
|
||
|
||
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.
Check warning
Code scanning / Semgrep OSS
computing list length is inefficient for length comparison, use compare_length_with instead Warning