Skip to content
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

Add some hacky atomic privatizations #1216

Merged
merged 37 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1d05f0f
Add hacky atomic protection privatization
sim642 Oct 12, 2023
b60e836
Add hacky atomic relation mutex-meet privatization
sim642 Oct 12, 2023
a5f4bff
Use relational unprotected invariant for mutex-meet
sim642 Oct 13, 2023
f846894
Fix relational mutex-meet atomic unlock
sim642 Oct 13, 2023
f27b098
Add Freiburg case_distinction_with_ghosts example
sim642 Oct 13, 2023
4e7312b
Add simpler case_distinction tests
sim642 Oct 13, 2023
4c54140
Add Freiburg nondet_inc_with_ghosts examples
sim642 Oct 20, 2023
e1e0813
Use threshold widening in Freiburg nondet_inc_with_ghosts
sim642 Oct 20, 2023
bd533c0
Use relational unprotected invariant for mutex-meet-tid
sim642 Oct 20, 2023
2beea6e
Merge branch 'master' into priv-atomic
sim642 Nov 27, 2023
fa1f6fa
Use strengthening in 36-apron/98-loc
sim642 Nov 27, 2023
21a4a98
Try to make two Mukerjee tests more precise with strengthening
sim642 Nov 28, 2023
b3af798
Fix unsoundness in per-mutex-tid which is revealed by strengthening
sim642 Nov 28, 2023
f2b2a39
Split relational mutex-meet-tid unprotected invariant by global
sim642 Nov 29, 2023
e4c575b
Revert "Use strengthening in 36-apron/98-loc"
sim642 Nov 29, 2023
a3f531b
Revert "Try to make two Mukerjee tests more precise with strengthening"
sim642 Nov 29, 2023
73c0690
Add missing relationpriv tracing
sim642 Nov 29, 2023
9680cc5
Fix relational mutex-meet-tid atomic unlock
sim642 Nov 29, 2023
69dd788
Add nondet_inc_with_ghosts with globalization
sim642 Nov 29, 2023
f47e596
Add mutex_with_ghosts Freiburg example with variants
sim642 Dec 13, 2023
392bc5b
Revert "Split relational mutex-meet-tid unprotected invariant by global"
sim642 Dec 14, 2023
28bc738
Update mutex_with_ghosts tests with mutex-meet-tid
sim642 Dec 14, 2023
a322785
Add names to mutex-meet-tid components
sim642 Dec 14, 2023
43552fa
Fix duplicate test ID
sim642 Dec 15, 2023
f754362
Separate base privatization with atomic support
sim642 Jan 11, 2024
105fd3a
Separate relation mutex-meet with atomic support
sim642 Jan 11, 2024
d4a1fe4
Separate relation mutex-meet-tid with atomic support
sim642 Jan 11, 2024
b12b6e8
Delete duplicate priv-atomic tests
sim642 Jan 11, 2024
92eac6d
Add __goblint_globalize special function
sim642 Jan 11, 2024
696a35f
Comment out atomic mutex protecting everything again
sim642 Jan 11, 2024
0308c25
Merge branch 'master' into priv-atomic
sim642 Jan 11, 2024
45555ad
Add case_distinction_with_ghosts variant where reads publish to unpro…
sim642 Jan 25, 2024
67558f4
Make mutex-meet-tid-atomic more precise for no-write unlocks
sim642 Jan 25, 2024
3bafccb
Update __goblint_globalize documentation
sim642 Jan 29, 2024
ae5c42e
Remove commented out code in RelationPriv.PerMutexMeetPrivTID.write_g…
sim642 Jan 29, 2024
6a2d078
Comment atomic privatizations
sim642 Feb 15, 2024
c0da7a7
Merge branch 'master' into priv-atomic
sim642 Feb 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/user-guide/annotating.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,4 @@ Include `goblint.h` when using these.
* `__goblint_assume_join(id)` is like `pthread_join(id, NULL)`, but considers the given thread IDs must-joined even if Goblint cannot, e.g. due to non-uniqueness.
Notably, this annotation can be used after a threads joining loop to make the assumption that the loop correctly joined all those threads.
_Misuse of this annotation can cause unsoundness._
* `__goblint_globalize(ptr)` forces all data potentially pointed to by `ptr` to be treated as global by simulating it escaping the thread.
1 change: 1 addition & 0 deletions lib/goblint/runtime/include/goblint.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ void __goblint_assume(int exp);
void __goblint_assert(int exp);

void __goblint_assume_join(/* pthread_t thread */); // undeclared argument to avoid pthread.h interfering with Linux kernel headers
void __goblint_globalize(void *ptr);

void __goblint_split_begin(int exp);
void __goblint_split_end(int exp);
Expand Down
345 changes: 271 additions & 74 deletions src/analyses/apron/relationPriv.apron.ml

Large diffs are not rendered by default.

24 changes: 18 additions & 6 deletions src/analyses/basePriv.ml
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,8 @@ module type PerGlobalPrivParam =
sig
(** Whether to also check unprotectedness by reads for extra precision. *)
val check_read_unprotected: bool

include AtomicParam
end

(** Protection-Based Reading. *)
Expand Down Expand Up @@ -671,29 +673,35 @@ struct

let startstate () = P.empty ()

let read_global ask getg (st: BaseComponents (D).t) x =
let read_global (ask: Queries.ask) getg (st: BaseComponents (D).t) x =
if P.mem x st.priv then
CPA.find x st.cpa
else if Param.handle_atomic && ask.f MustBeAtomic then
VD.join (CPA.find x st.cpa) (getg (V.unprotected x)) (* Account for previous unpublished unprotected writes in current atomic section. *)
else if is_unprotected ask x then
getg (V.unprotected x) (* CPA unnecessary because all values in GUnprot anyway *)
else
VD.join (CPA.find x st.cpa) (getg (V.protected x))

let write_global ?(invariant=false) ask getg sideg (st: BaseComponents (D).t) x v =
let write_global ?(invariant=false) (ask: Queries.ask) getg sideg (st: BaseComponents (D).t) x v =
if not invariant then (
sideg (V.unprotected x) v;
if not (Param.handle_atomic && ask.f MustBeAtomic) then
sideg (V.unprotected x) v; (* Delay publishing unprotected write in the atomic section. *)
if !earlyglobs then (* earlyglobs workaround for 13/60 *)
sideg (V.protected x) v
(* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write since W is implicit. *)
);
if is_unprotected ask x then
if Param.handle_atomic && ask.f MustBeAtomic then
{st with cpa = CPA.add x v st.cpa; priv = P.add x st.priv} (* Keep write local as if it were protected by the atomic section. *)
else if is_unprotected ask x then
st
else
{st with cpa = CPA.add x v st.cpa; priv = P.add x st.priv}

let lock ask getg st m = st

let unlock ask getg sideg (st: BaseComponents (D).t) m =
let atomic = Param.handle_atomic && LockDomain.Addr.equal m (LockDomain.Addr.of_var LibraryFunctions.verifier_atomic_var) in
(* TODO: what about G_m globals in cpa that weren't actually written? *)
CPA.fold (fun x v (st: BaseComponents (D).t) ->
if is_protected_by ask m x then ( (* is_in_Gm *)
Expand All @@ -702,6 +710,8 @@ struct
then inner unlock shouldn't yet publish. *)
if not Param.check_read_unprotected || is_unprotected_without ask ~write:false x m then
sideg (V.protected x) v;
if atomic then
sideg (V.unprotected x) v; (* Publish delayed unprotected write as if it were protected by the atomic section. *)

if is_unprotected_without ask x m then (* is_in_V' *)
{st with cpa = CPA.remove x st.cpa; priv = P.remove x st.priv}
Expand Down Expand Up @@ -1779,8 +1789,10 @@ let priv_module: (module S) Lazy.t =
| "mutex-oplus" -> (module PerMutexOplusPriv)
| "mutex-meet" -> (module PerMutexMeetPriv)
| "mutex-meet-tid" -> (module PerMutexMeetTIDPriv (ThreadDigest))
| "protection" -> (module ProtectionBasedPriv (struct let check_read_unprotected = false end))
| "protection-read" -> (module ProtectionBasedPriv (struct let check_read_unprotected = true end))
| "protection" -> (module ProtectionBasedPriv (struct let check_read_unprotected = false let handle_atomic = false end))
| "protection-atomic" -> (module ProtectionBasedPriv (struct let check_read_unprotected = false let handle_atomic = true end)) (* experimental *)
| "protection-read" -> (module ProtectionBasedPriv (struct let check_read_unprotected = true let handle_atomic = false end))
| "protection-read-atomic" -> (module ProtectionBasedPriv (struct let check_read_unprotected = true let handle_atomic = true end)) (* experimental *)
| "mine" -> (module MinePriv)
| "mine-nothread" -> (module MineNoThreadPriv)
| "mine-W" -> (module MineWPriv (struct let side_effect_global_init = true end))
Expand Down
25 changes: 22 additions & 3 deletions src/analyses/commonPriv.ml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ module Q = Queries
module IdxDom = ValueDomain.IndexDomain
module VD = BaseDomain.VD

module type AtomicParam =
sig
val handle_atomic: bool
(** Whether to handle SV-COMP atomic blocks (experimental). *)
end

module NoAtomic: AtomicParam =
struct
let handle_atomic = false
end

module ConfCheck =
struct
module RequireMutexActivatedInit =
Expand Down Expand Up @@ -110,7 +121,11 @@ end

module Locksets =
struct
module Lock = LockDomain.Addr
module Lock =
struct
include LockDomain.Addr
let name () = "lock"
end

module Lockset = SetDomain.ToppedSet (Lock) (struct let topname = "All locks" end)

Expand Down Expand Up @@ -206,7 +221,7 @@ struct

module LLock =
struct
include Printable.Either (Locksets.Lock) (CilType.Varinfo)
include Printable.Either (Locksets.Lock) (struct include CilType.Varinfo let name () = "global" end)
let mutex m = `Left m
let global x = `Right x
end
Expand All @@ -218,7 +233,11 @@ struct
end

(* Map from locks to last written values thread-locally *)
module L = MapDomain.MapBot_LiftTop (LLock) (LD)
module L =
struct
include MapDomain.MapBot_LiftTop (LLock) (LD)
let name () = "L"
end
module GMutex = MapDomain.MapBot_LiftTop (Digest) (LD)
module GThread = Lattice.Prod (LMust) (L)

Expand Down
2 changes: 1 addition & 1 deletion src/analyses/mutexAnalysis.ml
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ struct
let mutex_lockset = Lockset.export_locks @@ Lockset.singleton (mutex, true) in
let protecting = protecting ~write protection v in
(* TODO: unsound in 29/24, why did we do this before? *)
(* if LockDomain.Addr.equal mutex verifier_atomic then
(* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then
true
else *)
Mutexes.leq mutex_lockset protecting
Expand Down
3 changes: 3 additions & 0 deletions src/analyses/threadEscape.ml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ struct
let special ctx (lval: lval option) (f:varinfo) (args:exp list) : D.t =
let desc = LibraryFunctions.find f in
match desc.special args, f.vname, args with
| Globalize ptr, _, _ ->
let escaped = escape_rval ctx (Analyses.ask_of_ctx ctx) ptr in
D.join ctx.local escaped
| _, "pthread_setspecific" , [key; pt_value] ->
let escaped = escape_rval ctx (Analyses.ask_of_ctx ctx) pt_value in
D.join ctx.local escaped
Expand Down
4 changes: 2 additions & 2 deletions src/config/options.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@
"description":
"Which privatization to use? none/mutex-oplus/mutex-meet/mutex-meet-tid/protection/protection-read/mine/mine-nothread/mine-W/mine-W-noinit/lock/write/write+lock",
"type": "string",
"enum": ["none", "mutex-oplus", "mutex-meet", "protection", "protection-read", "mine", "mine-nothread", "mine-W", "mine-W-noinit", "lock", "write", "write+lock","mutex-meet-tid"],
"enum": ["none", "mutex-oplus", "mutex-meet", "protection", "protection-atomic", "protection-read", "protection-read-atomic", "mine", "mine-nothread", "mine-W", "mine-W-noinit", "lock", "write", "write+lock","mutex-meet-tid"],
"default": "protection-read"
},
"priv": {
Expand Down Expand Up @@ -901,7 +901,7 @@
"description":
"Which relation privatization to use? top/protection/protection-path/mutex-meet/mutex-meet-tid/mutex-meet-tid-cluster12/mutex-meet-tid-cluster2/mutex-meet-tid-cluster-max/mutex-meet-tid-cluster-power",
"type": "string",
"enum": ["top", "protection", "protection-path", "mutex-meet", "mutex-meet-tid", "mutex-meet-tid-cluster12", "mutex-meet-tid-cluster2", "mutex-meet-tid-cluster-max", "mutex-meet-tid-cluster-power"],
"enum": ["top", "protection", "protection-path", "mutex-meet", "mutex-meet-atomic", "mutex-meet-tid", "mutex-meet-tid-atomic", "mutex-meet-tid-cluster12", "mutex-meet-tid-cluster2", "mutex-meet-tid-cluster-max", "mutex-meet-tid-cluster-power"],
"default": "mutex-meet"
},
"priv": {
Expand Down
1 change: 1 addition & 0 deletions src/util/library/libraryDesc.ml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ type special =
| ThreadCreate of { thread: Cil.exp; start_routine: Cil.exp; arg: Cil.exp; multiple: bool }
| ThreadJoin of { thread: Cil.exp; ret_var: Cil.exp; }
| ThreadExit of { ret_val: Cil.exp; }
| Globalize of Cil.exp
| Signal of Cil.exp
| Broadcast of Cil.exp
| MutexAttrSetType of { attr:Cil.exp; typ: Cil.exp; }
Expand Down
1 change: 1 addition & 0 deletions src/util/library/libraryFunctions.ml
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,7 @@ let goblint_descs_list: (string * LibraryDesc.t) list = LibraryDsl.[
("__goblint_check", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = false });
("__goblint_assume", special [__ "exp" []] @@ fun exp -> Assert { exp; check = false; refine = true });
("__goblint_assert", special [__ "exp" []] @@ fun exp -> Assert { exp; check = true; refine = get_bool "sem.assert.refine" });
("__goblint_globalize", special [__ "ptr" []] @@ fun ptr -> Globalize ptr);
("__goblint_split_begin", unknown [drop "exp" []]);
("__goblint_split_end", unknown [drop "exp" []]);
("__goblint_bounded", special [__ "exp"[]] @@ fun exp -> Bounded { exp });
Expand Down
42 changes: 42 additions & 0 deletions tests/regression/13-privatized/74-mutex.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// PARAM: --enable ana.sv-comp.functions
/*-----------------------------------------------------------------------------
* mutex.c - Concurrent program using locking to access a shared variable
*-----------------------------------------------------------------------------
* Author: Frank Schüssele
* Date: 2023-07-11
*---------------------------------------------------------------------------*/
#include <pthread.h>
#include <goblint.h>

extern void __VERIFIER_atomic_begin();
extern void __VERIFIER_atomic_end();

int used;
pthread_mutex_t m;

void* producer()
{
while (1) {
pthread_mutex_lock(&m);
used++;
used--;
pthread_mutex_unlock(&m);
}

return 0;
}

int main()
{
pthread_t tid;

pthread_mutex_init(&m, 0);
pthread_create(&tid, 0, producer, 0);

pthread_mutex_lock(&m);
__goblint_check(used == 0);
pthread_mutex_unlock(&m);

pthread_mutex_destroy(&m);
return 0;
}
4 changes: 2 additions & 2 deletions tests/regression/29-svcomp/16-atomic_priv.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// PARAM: --enable ana.sv-comp.functions
// PARAM: --enable ana.sv-comp.functions --set ana.base.privatization protection-atomic
#include <pthread.h>
#include <goblint.h>

Expand All @@ -21,7 +21,7 @@ void *t_fun(void *arg) {
int main(void) {
pthread_t id;
pthread_create(&id, NULL, t_fun, NULL);
__goblint_check(myglobal == 5); // TODO
__goblint_check(myglobal == 5);
__VERIFIER_atomic_begin();
__goblint_check(myglobal == 5);
__VERIFIER_atomic_end();
Expand Down
4 changes: 2 additions & 2 deletions tests/regression/29-svcomp/18-atomic_fun_priv.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// PARAM: --enable ana.sv-comp.functions
// PARAM: --enable ana.sv-comp.functions --set ana.base.privatization protection-atomic
#include <pthread.h>
#include <goblint.h>

Expand All @@ -21,7 +21,7 @@ void *t_fun(void *arg) {
int main(void) {
pthread_t id;
pthread_create(&id, NULL, t_fun, NULL);
__goblint_check(myglobal == 5); // TODO
__goblint_check(myglobal == 5);
__VERIFIER_atomic_begin();
__goblint_check(myglobal == 5);
__VERIFIER_atomic_end();
Expand Down
30 changes: 30 additions & 0 deletions tests/regression/46-apron2/61-atomic_priv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-atomic --set ana.base.privatization none
#include <pthread.h>
#include <goblint.h>

extern void __VERIFIER_atomic_begin();
extern void __VERIFIER_atomic_end();

int myglobal = 5;

void *t_fun(void *arg) {
__VERIFIER_atomic_begin();
__goblint_check(myglobal == 5);
myglobal++;
__goblint_check(myglobal == 6);
myglobal--;
__goblint_check(myglobal == 5);
__VERIFIER_atomic_end();
return NULL;
}

int main(void) {
pthread_t id;
pthread_create(&id, NULL, t_fun, NULL);
__goblint_check(myglobal == 5);
__VERIFIER_atomic_begin();
__goblint_check(myglobal == 5);
__VERIFIER_atomic_end();
pthread_join (id, NULL);
return 0;
}
30 changes: 30 additions & 0 deletions tests/regression/46-apron2/62-atomic_fun_priv.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-atomic --set ana.base.privatization none
#include <pthread.h>
#include <goblint.h>

int myglobal = 5;

// atomic by function name prefix
void __VERIFIER_atomic_fun() {
__goblint_check(myglobal == 5);
myglobal++;
__goblint_check(myglobal == 6);
myglobal--;
__goblint_check(myglobal == 5);
}

void *t_fun(void *arg) {
__VERIFIER_atomic_fun();
return NULL;
}

int main(void) {
pthread_t id;
pthread_create(&id, NULL, t_fun, NULL);
__goblint_check(myglobal == 5);
__VERIFIER_atomic_begin();
__goblint_check(myglobal == 5);
__VERIFIER_atomic_end();
pthread_join (id, NULL);
return 0;
}
28 changes: 28 additions & 0 deletions tests/regression/46-apron2/63-atomic_priv_sound.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-atomic --set ana.base.privatization none
#include <pthread.h>
#include <goblint.h>

extern void __VERIFIER_atomic_begin();
extern void __VERIFIER_atomic_end();

int myglobal = 5;

void *t_fun(void *arg) {
__VERIFIER_atomic_begin();
__goblint_check(myglobal == 5); // TODO
myglobal++;
__goblint_check(myglobal == 6); // TODO
__VERIFIER_atomic_end();
return NULL;
}

int main(void) {
pthread_t id;
pthread_create(&id, NULL, t_fun, NULL);
__goblint_check(myglobal == 5); // UNKNOWN!
__VERIFIER_atomic_begin();
__goblint_check(myglobal == 5); // UNKNOWN!
__VERIFIER_atomic_end();
pthread_join (id, NULL);
return 0;
}
43 changes: 43 additions & 0 deletions tests/regression/46-apron2/64-atomic_priv_sound2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// PARAM: --enable ana.sv-comp.functions --set ana.activated[+] apron --set ana.relation.privatization mutex-meet-atomic --set ana.base.privatization none
#include <pthread.h>
#include <goblint.h>

extern void __VERIFIER_atomic_begin();
extern void __VERIFIER_atomic_end();

int myglobal = 0;
int myglobal2 = 0;
int myglobal3 = 0;

void *t_fun(void *arg) {
__VERIFIER_atomic_begin();
myglobal2++;
__VERIFIER_atomic_end();
__VERIFIER_atomic_begin();
myglobal++;
__VERIFIER_atomic_end();
return NULL;
}

void *t2_fun(void *arg) {
__VERIFIER_atomic_begin();
myglobal3++;
__VERIFIER_atomic_end();
__VERIFIER_atomic_begin();
myglobal++;
__VERIFIER_atomic_end();
return NULL;
}

int main(void) {
pthread_t id, id2;
pthread_create(&id, NULL, t_fun, NULL);
pthread_create(&id2, NULL, t2_fun, NULL);
__goblint_check(myglobal == 2); // UNKNOWN!
__VERIFIER_atomic_begin();
__goblint_check(myglobal == 2); // UNKNOWN!
__VERIFIER_atomic_end();
pthread_join (id, NULL);
pthread_join (id2, NULL);
return 0;
}
Loading
Loading