Skip to content

Commit 7261e66

Browse files
authored
Merge pull request #1788 from goblint/mutex-refactor-readwrite
Fix variables protected by mutex calculation
2 parents e27c85d + bbe22f2 commit 7261e66

File tree

7 files changed

+146
-66
lines changed

7 files changed

+146
-66
lines changed

src/analyses/basePriv.ml

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ struct
370370
if atomic || ask.f (GhostVarAvailable (Locked m')) then (
371371
let cpa = get_m_with_mutex_inits ask getg m' in (* Could be more precise if mutex_inits invariant is added by disjunction instead of joining abstract values. *)
372372
let inv = CPA.fold (fun v _ acc ->
373-
if ask.f (MustBeProtectedBy {mutex = m'; global = v; write = true; protection = Strong}) then
373+
if ask.f (MustBeProtectedBy {mutex = m'; global = v; kind = Write; protection = Strong}) then
374374
let inv = ValueDomain.invariant_global (fun g -> CPA.find g cpa) v in
375375
Invariant.(acc && inv)
376376
else
@@ -706,7 +706,7 @@ struct
706706

707707
let threadspawn (ask:Queries.ask) get set (st: BaseComponents (D).t) =
708708
let is_recovered_st = ask.f (Queries.MustBeSingleThreaded {since_start = false}) && not @@ ask.f (Queries.MustBeSingleThreaded {since_start = true}) in
709-
let unprotected_after x = ask.f (Q.MayBePublic {global=x; write=true; protection=Weak}) in
709+
let unprotected_after x = ask.f (Q.MayBePublic {global=x; kind=Write; protection=Weak}) in
710710
if is_recovered_st then
711711
(* Remove all things that are now unprotected *)
712712
let cpa' = CPA.fold (fun x v cpa ->
@@ -750,14 +750,14 @@ struct
750750
let startstate () = ()
751751

752752
let read_global (ask: Queries.ask) getg (st: BaseComponents (D).t) x =
753-
if is_unprotected ask ~write:false x then
753+
if is_unprotected ask ~kind:ReadWrite x then
754754
VD.join (CPA.find x st.cpa) (getg x)
755755
else
756756
CPA.find x st.cpa
757757

758758
let write_global ?(invariant=false) (ask: Queries.ask) getg sideg (st: BaseComponents (D).t) x v =
759759
if not invariant then (
760-
if is_unprotected ask ~write:false x then
760+
if is_unprotected ask ~kind:ReadWrite x then
761761
sideg x v;
762762
if !earlyglobs then (* earlyglobs workaround for 13/60 *)
763763
sideg x v (* TODO: is this needed for anything? 13/60 doesn't work for other reasons *)
@@ -766,7 +766,7 @@ struct
766766

767767
let lock ask getg (st: BaseComponents (D).t) m =
768768
let cpa' = CPA.mapi (fun x v ->
769-
if is_protected_by ask ~write:false m x && is_unprotected ask ~write:false x then (* is_in_Gm *)
769+
if is_protected_by ask ~kind:ReadWrite m x && is_unprotected ask ~kind:ReadWrite x then (* is_in_Gm *)
770770
VD.join (CPA.find x st.cpa) (getg x)
771771
else
772772
v
@@ -776,8 +776,8 @@ struct
776776

777777
let unlock ask getg sideg (st: BaseComponents (D).t) m =
778778
CPA.iter (fun x v ->
779-
if is_protected_by ask ~write:false m x then ( (* is_in_Gm *)
780-
if is_unprotected_without ask ~write:false x m then (* is_in_V' *)
779+
if is_protected_by ask ~kind:ReadWrite m x then ( (* is_in_Gm *)
780+
if is_unprotected_without ask ~kind:ReadWrite x m then (* is_in_V' *)
781781
sideg x v
782782
)
783783
) st.cpa;
@@ -787,7 +787,7 @@ struct
787787
let branched_sync () =
788788
(* required for branched thread creation *)
789789
CPA.iter (fun x v ->
790-
if is_global ask x && is_unprotected ask ~write:false x then
790+
if is_global ask x && is_unprotected ask ~kind:ReadWrite x then
791791
sideg x v
792792
) st.cpa;
793793
st
@@ -832,7 +832,7 @@ struct
832832
| _ -> ()
833833

834834
let invariant_global (ask: Q.ask) getg g =
835-
let locks = ask.f (Q.MustProtectingLocks {global = g; write = false}) in
835+
let locks = ask.f (Q.MustProtectingLocks {global = g; kind = ReadWrite}) in
836836
if LockDomain.MustLockset.is_all locks then
837837
Invariant.none
838838
else (
@@ -988,7 +988,7 @@ struct
988988
(* Extra precision in implementation to pass tests:
989989
If global is read-protected by multiple locks,
990990
then inner unlock shouldn't yet publish. *)
991-
if not Param.check_read_unprotected || is_unprotected_without ask ~write:false x m then
991+
if not Param.check_read_unprotected || is_unprotected_without ask ~kind:ReadWrite x m then
992992
sideg (V.protected x) v;
993993
if atomic then
994994
sideg (V.unprotected x) v; (* Publish delayed unprotected write as if it were protected by the atomic section. *)
@@ -1076,7 +1076,7 @@ struct
10761076
| `Left g' -> (* unprotected *)
10771077
ValueDomain.invariant_global (fun g -> getg (V.unprotected g)) g'
10781078
| `Right g' -> (* protected *)
1079-
let locks = ask.f (Q.MustProtectingLocks {global = g'; write = true}) in
1079+
let locks = ask.f (Q.MustProtectingLocks {global = g'; kind = Write}) in
10801080
if LockDomain.MustLockset.is_all locks || LockDomain.MustLockset.is_empty locks then
10811081
Invariant.none
10821082
else if VD.equal (getg (V.protected g')) (getg (V.unprotected g')) then

src/analyses/commonPriv.ml

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,26 +82,28 @@ end
8282
module Protection =
8383
struct
8484
open Q.Protection
85-
let is_unprotected ask ?(write=true) ?(protection=Strong) x: bool =
85+
open Q.ProtectionKind
86+
87+
let is_unprotected ask ?(kind=Write) ?(protection=Strong) x: bool =
8688
let multi = if protection = Weak then ThreadFlag.is_currently_multi ask else ThreadFlag.has_ever_been_multi ask in
8789
(!GobConfig.earlyglobs && not multi && not (is_excluded_from_earlyglobs x)) ||
8890
(
8991
multi &&
90-
ask.f (Q.MayBePublic {global=x; write; protection})
92+
ask.f (Q.MayBePublic {global=x; kind; protection})
9193
)
9294

93-
let is_unprotected_without ask ?(write=true) ?(protection=Strong) x m: bool =
95+
let is_unprotected_without ask ?(kind=Write) ?(protection=Strong) x m: bool =
9496
(if protection = Weak then ThreadFlag.is_currently_multi ask else ThreadFlag.has_ever_been_multi ask) &&
95-
ask.f (Q.MayBePublicWithout {global=x; write; without_mutex=m; protection})
97+
ask.f (Q.MayBePublicWithout {global=x; kind; without_mutex=m; protection})
9698

97-
let is_protected_by ask ?(write=true) ?(protection=Strong) m x: bool =
99+
let is_protected_by ask ?(kind=Write) ?(protection=Strong) m x: bool =
98100
is_global ask x &&
99101
not (VD.is_immediate_type x.vtype) &&
100-
ask.f (Q.MustBeProtectedBy {mutex=m; global=x; write; protection})
102+
ask.f (Q.MustBeProtectedBy {mutex=m; global=x; kind; protection})
101103

102104
let protected_vars (ask: Q.ask): varinfo list =
103105
LockDomain.MustLockset.fold (fun ml acc ->
104-
Q.VS.join (ask.f (Q.MustProtectedVars {mutex = ml; write = true})) acc
106+
Q.VS.join (ask.f (Q.MustProtectedVars {mutex = ml; kind = Write})) acc
105107
) (ask.f Q.MustLockset) (Q.VS.empty ())
106108
|> Q.VS.elements
107109
end

src/analyses/mutexAnalysis.ml

Lines changed: 58 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ open Analyses
1313

1414
module VarSet = SetDomain.Make (Basetype.Variables)
1515

16+
type access_kind = Read | Write
17+
1618
module Spec =
1719
struct
1820
module Arg =
@@ -59,47 +61,51 @@ struct
5961

6062
let name () = "strong protection * weak protection"
6163

62-
let get ~write protection (s,w) =
64+
let get ~kind protection (s,w) =
6365
let (rw, w) = match protection with
6466
| Queries.Protection.Strong -> s
6567
| Weak -> w
6668
in
67-
if write then w else rw
69+
match kind with
70+
| Queries.ProtectionKind.Write -> w
71+
| ReadWrite -> rw
6872
end
6973

7074
(** Collects information about which variables are protected by which mutexes *)
7175
module GProtecting: sig
7276
include Lattice.S
73-
val make: write:bool -> recovered:bool -> MustLockset.t -> t
74-
val get: write:bool -> Queries.Protection.t -> t -> MustLockset.t
77+
val make: kind:access_kind -> recovered:bool -> MustLockset.t -> t
78+
val get: kind:Queries.ProtectionKind.t -> Queries.Protection.t -> t -> MustLockset.t
7579
end = struct
7680
include MakeP (MustLockset)
7781

78-
let make ~write ~recovered locks =
79-
(* If the access is not a write, set to T so intersection with current write-protecting is identity *)
80-
let wlocks = if write then locks else MustLockset.all () in
82+
let make ~(kind: access_kind) ~recovered locks =
83+
let locks =
84+
match kind with
85+
| Write -> (locks, locks)
86+
| Read -> (locks, MustLockset.all ()) (* If the access is not a write, set to T so intersection with current write-protecting is identity *)
87+
in
8188
if recovered then
8289
(* If we are in single-threaded mode again, this does not need to be added to set of mutexes protecting in mt-mode only *)
83-
((locks, wlocks), (MustLockset.all (), MustLockset.all ()))
90+
(locks, (MustLockset.all (), MustLockset.all ()))
8491
else
85-
((locks, wlocks), (locks, wlocks))
92+
(locks, locks)
8693
end
8794

8895

8996
(** Collects information about which mutex protects which variable *)
9097
module GProtected: sig
9198
include Lattice.S
92-
val make: write:bool -> VarSet.t -> t
93-
val get: write:bool -> Queries.Protection.t -> t -> VarSet.t
99+
val make: kind:Queries.ProtectionKind.t -> VarSet.t -> t
100+
val get: kind:Queries.ProtectionKind.t -> Queries.Protection.t -> t -> VarSet.t
94101
end = struct
95102
include MakeP (VarSet)
96103

97-
let make ~write vs =
104+
let make ~kind vs =
98105
let vs_empty = VarSet.empty () in
99-
if write then
100-
((vs_empty, vs), (vs_empty, vs))
101-
else
102-
((vs, vs_empty), (vs, vs_empty))
106+
match kind with
107+
| Queries.ProtectionKind.Write -> ((vs_empty, vs), (vs_empty, vs))
108+
| ReadWrite -> ((vs, vs_empty), (vs, vs_empty))
103109
end
104110

105111
module G =
@@ -198,43 +204,43 @@ struct
198204
let query (man: (D.t, _, _, V.t) man) (type a) (q: a Queries.t): a Queries.result =
199205
let ls, m = man.local in
200206
(* get the set of mutexes protecting the variable v in the given mode *)
201-
let protecting ~write mode v = GProtecting.get ~write mode (G.protecting (man.global (V.protecting v))) in
207+
let protecting ~kind mode v = GProtecting.get ~kind mode (G.protecting (man.global (V.protecting v))) in
202208
match q with
203209
| Queries.MayBePublic _ when MustLocksetRW.is_all ls -> false
204-
| Queries.MayBePublic {global=v; write; protection} ->
210+
| Queries.MayBePublic {global=v; kind; protection} ->
205211
let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in
206-
let protecting = protecting ~write protection v in
212+
let protecting = protecting ~kind protection v in
207213
(* TODO: unsound in 29/24, why did we do this before? *)
208214
(* if Mutexes.mem verifier_atomic (Lockset.export_locks man.local) then
209215
false
210216
else *)
211217
MustLockset.disjoint held_locks protecting
212218
| Queries.MayBePublicWithout _ when MustLocksetRW.is_all ls -> false
213-
| Queries.MayBePublicWithout {global=v; write; without_mutex; protection} ->
219+
| Queries.MayBePublicWithout {global=v; kind; without_mutex; protection} ->
214220
let held_locks = MustLockset.remove without_mutex (MustLocksetRW.to_must_lockset ls) in
215-
let protecting = protecting ~write protection v in
221+
let protecting = protecting ~kind protection v in
216222
(* TODO: unsound in 29/24, why did we do this before? *)
217223
(* if Mutexes.mem verifier_atomic (Lockset.export_locks (Lockset.remove (without_mutex, true) man.local)) then
218224
false
219225
else *)
220226
MustLockset.disjoint held_locks protecting
221-
| Queries.MustBeProtectedBy {mutex = ml; global=v; write; protection} ->
222-
let protecting = protecting ~write protection v in
227+
| Queries.MustBeProtectedBy {mutex = ml; global=v; kind; protection} ->
228+
let protecting = protecting ~kind protection v in
223229
(* TODO: unsound in 29/24, why did we do this before? *)
224230
(* if LockDomain.Addr.equal mutex (LockDomain.Addr.of_var LF.verifier_atomic_var) then
225231
true
226232
else *)
227233
MustLockset.mem ml protecting
228-
| Queries.MustProtectingLocks {global; write} ->
229-
protecting ~write Strong global
234+
| Queries.MustProtectingLocks {global; kind} ->
235+
protecting ~kind Strong global
230236
| Queries.MustLockset ->
231237
let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in
232238
held_locks
233239
| Queries.MustBeAtomic ->
234240
let held_locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd ls) in
235241
MustLockset.mem (LF.verifier_atomic_var, `NoOffset) held_locks (* TODO: Mval.of_var *)
236-
| Queries.MustProtectedVars {mutex; write} ->
237-
let protected = GProtected.get ~write Strong (G.protected (man.global (V.protected mutex))) in
242+
| Queries.MustProtectedVars {mutex; kind} ->
243+
let protected = GProtected.get ~kind Strong (G.protected (man.global (V.protected mutex))) in
238244
VarSet.fold (fun v acc ->
239245
Queries.VS.add v acc
240246
) protected (Queries.VS.empty ())
@@ -245,13 +251,13 @@ struct
245251
begin match g with
246252
| `Left g' -> (* protecting *)
247253
if GobConfig.get_bool "dbg.print_protection" then (
248-
let protecting = GProtecting.get ~write:false Strong (G.protecting (man.global g)) in (* readwrite protecting *)
254+
let protecting = GProtecting.get ~kind:ReadWrite Strong (G.protecting (man.global g)) in (* readwrite protecting *)
249255
let s = MustLockset.cardinal protecting in
250256
M.info_noloc ~category:Race "Variable %a read-write protected by %d mutex(es): %a" CilType.Varinfo.pretty g' s MustLockset.pretty protecting
251257
)
252258
| `Right m -> (* protected *)
253259
if GobConfig.get_bool "dbg.print_protection" then (
254-
let protected = GProtected.get ~write:false Strong (G.protected (man.global g)) in (* readwrite protected *)
260+
let protected = GProtected.get ~kind:ReadWrite Strong (G.protected (man.global g)) in (* readwrite protected *)
255261
let s = VarSet.cardinal protected in
256262
max_protected := max !max_protected s;
257263
sum_protected := !sum_protected + s;
@@ -293,25 +299,34 @@ struct
293299
| Some v ->
294300
if not (MustLocksetRW.is_all (fst oman.local)) then
295301
let locks = MustLocksetRW.to_must_lockset (MustLocksetRW.filter snd (fst oman.local)) in
296-
let write = match kind with
297-
| Write | Free -> true
298-
| Read -> false
302+
let kind = match kind with
303+
| Write | Free -> Write
304+
| Read -> Read
299305
| Call
300-
| Spawn -> false (* TODO: nonsense? *)
306+
| Spawn -> Read (* TODO: nonsense? *)
301307
in
302-
let s = GProtecting.make ~write ~recovered:is_recovered_to_st locks in
308+
let s = GProtecting.make ~kind ~recovered:is_recovered_to_st locks in
303309
man.sideg (V.protecting v) (G.create_protecting s);
304310

305311
if !AnalysisState.postsolving then (
306-
let protecting mode = GProtecting.get ~write mode (G.protecting (man.global (V.protecting v))) in
307-
let held_strong = protecting Strong in
308-
let held_weak = protecting Weak in
309-
let vs = VarSet.singleton v in
310-
let protected = G.create_protected @@ GProtected.make ~write vs in
311-
MustLockset.iter (fun ml -> man.sideg (V.protected ml) protected) held_strong;
312-
(* If the mutex set here is top, it is actually not accessed *)
313-
if is_recovered_to_st && not @@ MustLockset.is_all held_weak then
314-
MustLockset.iter (fun ml -> man.sideg (V.protected ml) protected) held_weak;
312+
let side_protected kind mode =
313+
let protecting = GProtecting.get ~kind mode (G.protecting (man.global (V.protecting v))) in
314+
(* If the mutex set here is top, it is actually not accessed *)
315+
if not @@ MustLockset.is_all protecting then (
316+
let vs = VarSet.singleton v in
317+
let protected = G.create_protected @@ GProtected.make ~kind vs in
318+
MustLockset.iter (fun ml -> man.sideg (V.protected ml) protected) protecting
319+
)
320+
in
321+
let side_protected kind =
322+
side_protected kind Strong;
323+
if is_recovered_to_st then
324+
side_protected kind Weak
325+
in
326+
side_protected Queries.ProtectionKind.ReadWrite;
327+
match kind with
328+
| Write -> side_protected Queries.ProtectionKind.Write
329+
| Read -> ()
315330
)
316331
| None -> M.info ~category:Unsound "Write to unknown address: privatization is unsound."
317332
in

src/domains/queries.ml

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ module MustBool = BoolDomain.MustBool
3939

4040
module Unit = Lattice.Unit
4141

42+
module ProtectionKind =
43+
struct
44+
type t = ReadWrite | Write [@@deriving ord, hash]
45+
end
46+
4247
(** Different notions of protection for a global variables g by a mutex m
4348
m protects g strongly if:
4449
- whenever g is accessed after the program went multi-threaded for the first time, m is held
@@ -55,11 +60,11 @@ module AllocationLocation = struct
5560
end
5661

5762
(* Helper definitions for deriving complex parts of Any.compare below. *)
58-
type maybepublic = {global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash]
59-
type maybepublicwithout = {global: CilType.Varinfo.t; write: bool; without_mutex: LockDomain.MustLock.t; protection: Protection.t} [@@deriving ord, hash]
60-
type mustbeprotectedby = {mutex: LockDomain.MustLock.t; global: CilType.Varinfo.t; write: bool; protection: Protection.t} [@@deriving ord, hash]
61-
type mustprotectedvars = {mutex: LockDomain.MustLock.t; write: bool} [@@deriving ord, hash]
62-
type mustprotectinglocks = {global: CilType.Varinfo.t; write: bool} [@@deriving ord, hash]
63+
type maybepublic = {global: CilType.Varinfo.t; kind: ProtectionKind.t; protection: Protection.t} [@@deriving ord, hash]
64+
type maybepublicwithout = {global: CilType.Varinfo.t; kind: ProtectionKind.t; without_mutex: LockDomain.MustLock.t; protection: Protection.t} [@@deriving ord, hash]
65+
type mustbeprotectedby = {mutex: LockDomain.MustLock.t; global: CilType.Varinfo.t; kind: ProtectionKind.t; protection: Protection.t} [@@deriving ord, hash]
66+
type mustprotectedvars = {mutex: LockDomain.MustLock.t; kind: ProtectionKind.t} [@@deriving ord, hash]
67+
type mustprotectinglocks = {global: CilType.Varinfo.t; kind: ProtectionKind.t} [@@deriving ord, hash]
6368
type access =
6469
| Memory of {exp: CilType.Exp.t; var_opt: CilType.Varinfo.t option; kind: AccessKind.t} (** Memory location access (race). *)
6570
| Point (** Program point and state access (MHP), independent of memory location. *)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
$ goblint --enable dbg.print_protection --enable warn.deterministic 19-publish-precision.c
2+
[Warning][Assert] Assertion "glob1 == 0" is unknown. (19-publish-precision.c:30:3-30:30)
3+
[Warning][Assert] Assertion "glob1 == 5" is unknown. (19-publish-precision.c:31:3-31:30)
4+
[Success][Assert] Assertion "glob1 == 5" will succeed (19-publish-precision.c:17:3-17:30)
5+
[Success][Assert] Assertion "glob1 == 0" will succeed (19-publish-precision.c:27:3-27:30)
6+
[Info][Race] Mutex mutex1 read-write protects 0 variable(s): {}
7+
[Info][Race] Mutex mutex2 read-write protects 1 variable(s): {glob1}
8+
[Info][Race] Variable glob1 read-write protected by 1 mutex(es): {mutex2}
9+
[Info][Race] Memory locations race summary:
10+
safe: 1
11+
vulnerable: 0
12+
unsafe: 0
13+
total memory locations: 1
14+
[Info][Race] Mutex read-write protection summary:
15+
Number of mutexes: 2
16+
Max number variables of protected by a mutex: 1
17+
Total number of protected variables (including duplicates): 1
18+
[Info][Deadcode] Logical lines of code (LLoC) summary:
19+
live: 20
20+
dead: 0
21+
total lines: 20
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
$ goblint --enable dbg.print_protection --enable warn.deterministic 20-publish-regression.c
2+
[Success][Assert] Assertion "glob1 == 5" will succeed (20-publish-regression.c:20:3-20:30)
3+
[Success][Assert] Assertion "glob1 == 0" will succeed (20-publish-regression.c:29:3-29:30)
4+
[Success][Assert] Assertion "glob1 == 0" will succeed (20-publish-regression.c:32:3-32:30)
5+
[Info][Race] Mutex mutex1 read-write protects 1 variable(s): {glob1}
6+
[Info][Race] Mutex mutex2 read-write protects 0 variable(s): {}
7+
[Info][Race] Variable glob1 read-write protected by 1 mutex(es): {mutex1}
8+
[Info][Race] Memory locations race summary:
9+
safe: 1
10+
vulnerable: 0
11+
unsafe: 0
12+
total memory locations: 1
13+
[Info][Race] Mutex read-write protection summary:
14+
Number of mutexes: 2
15+
Max number variables of protected by a mutex: 1
16+
Total number of protected variables (including duplicates): 1
17+
[Info][Deadcode] Logical lines of code (LLoC) summary:
18+
live: 19
19+
dead: 0
20+
total lines: 19

0 commit comments

Comments
 (0)