-
Notifications
You must be signed in to change notification settings - Fork 75
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
Generate flow-insensitive YAML witness invariants with ghosts for privatized variables #1394
base: master
Are you sure you want to change the base?
Conversation
This also means that the global variable is (lazily) not accessed when the condition isn't met.
Happened on 13-privatized/01-priv_nr
…utexes Happened on 13-privatized/02-priv_rc.
…utexes Happened on 13-privatized/02-priv_rc.
5694838
to
7951588
Compare
Avoids unsound rwlock struct content invariants in witnesses.
let invs = WitnessUtil.InvariantExp.process_exp inv in | ||
List.fold_left (fun acc inv -> | ||
let invariant = Entry.invariant (CilType.Exp.show inv) in | ||
let entry = Entry.flow_insensitive_invariant ~task ~invariant in | ||
entry :: acc | ||
) acc invs | ||
| `Bot | `Top -> (* global bot might only be possible for alloc variables, if at all, so emit nothing *) | ||
| `Lifted inv, true -> | ||
(* TODO: or do at location_invariant loop for each node and query if should also do global invariants there? *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happened with this TODO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be possible to do this without the InvariantGlobalNodes
query by querying InvariantGlobal
for each location_invariant
location. It might be more confusing though (the same query can be made both globally or locally) and possibly less efficient (although that might depend on how many nodes InvariantGlobalNodes
returns and how big the invariants are).
else if VD.equal (getg (V.protected g')) (getg (V.unprotected g')) then | ||
Invariant.none (* don't output protected invariant because it's the same as unprotected *) | ||
else ( | ||
let inv = ValueDomain.invariant_global (fun g -> getg (V.protected g)) g' in (* TODO: this takes protected values of everything *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's up with this TODO?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem here is that it can be unsound I think. If a global pointer is protected but what it points to isn't, then this invariant will assume both are protected.
Surprisingly, Ultimate hasn't found any of such to be wrong. Maybe because if the pointee is jointly protected, then it's fine again?
We cannot just delegate to read_global
here because this is in flow-sensitive context, so there's no notion of current lockset, etc.
(* Very conservative about multiple (write-)protecting mutexes: invariant is not claimed when any of them is held. | ||
It should be possible to be more precise because writes only happen with all of them held, | ||
but conjunction is unsound when one of the mutexes is temporarily unlocked. | ||
Hypothetical read-protection is also somehow relevant. *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this sentence mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We decided not to come up with more precise invariants here, so it's hard to say anything more precisely about it. Simply that read-protection also plays into it (if nobody reads it with fewer locks then we can claim a stronger invariant, kind of like the protection-read
privatization).
@@ -250,7 +250,7 @@ struct | |||
|
|||
let invariant_global ask getg = function | |||
| `Right g' -> (* global *) | |||
ValueDomain.invariant_global (read_unprotected_global getg) g' | |||
ValueDomain.invariant_global (read_unprotected_global getg) g' (* TODO: disjunct with mutex_inits instead of join? *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the plan with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using duplicate versions of read_unprotected_global
and get_mutex_global_x_with_mutex_inits
which operate on invariant expressions rather than ValueDomain
, we could make these invariants maybe more precise.
I just don't know if it's worth it though. Currently the logic is shared with the usual read_global
used by the analysis. Duplicating would require them to be maintained in sync.
if !earlyglobs && not (ThreadFlag.is_currently_multi ask) then | ||
sideg V.mutex_inits side_cpa (* Also side to inits because with earlyglobs enter_multithreaded does not side everything to inits *) | ||
(* Unlock after invariant will still side effect refined value (if protected) from CPA, because cannot distinguish from non-invariant write. *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed and what effect does it have?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you check whether this causes any precision regressions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Without it
goblint --enable ana.sv-comp.functions --set ana.base.privatization mutex-meet --enable exp.earlyglobs 74-mutex.c
does not pass because the variable will be missing from mutex_inits
, so we'll read bottom from the map and turn it into top I think. So this is actually to increase precision with earlyglobs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A similar thing is already in protection privatization. I think we never really tested mutex-meet with earlyglobs.
else | ||
Invariant.none | ||
| g -> (* global *) | ||
Invariant.none (* TODO: ? *) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Plan?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be possible to emit unprotected invariants from relational mutex-meet, but these will be inherently non-relational, so this might be rather useless.
It is now enabled by default and default mutex type is assumed non-recursive now.
…r flow-insensitive location invariants to work
… location_invariant-s in YAML witnesses
This is a quick implementation of the idea we had a long time ago for exporting our protected invariants into YAML witnesses with ghosts.
protection privatization
For the Freiburg
mutex.c
example, we essentially generate the same witness with ghosts as the example:m_locked
is declared and updated to match lock and unlock operations of the mutexm
.used == 0 || m_locked
is generated using the ghost variable.This is still our special
flow_insensitive_invariant
because there's the question where one would place thelocation_invariant
when our result in fact holds at every point.Currently tested only manually with:
./goblint ~/Documents/concurrency-witnesses/examples/VEWIT2023/mutex.c --enable ana.sv-comp.functions --set pre.cppflags[+] "-DGOBLINT_NO_PTHREAD_ONCE" --html --set ana.activated[+] mutexGhosts --enable witness.yaml.enabled --set witness.yaml.entry-types[+] flow_insensitive_invariant
mutex-meet privatization
For this example, the result is the same, but one invariant is generated per-mutex with a conjunction for all the protected variables. Also for the relational privatization.
TODO
(alloc@...)
locks.earlyglobs
.e235ba7 broke OSX onlyghost_variable
andghost_update
entry types.__VERIFIER_atomic
invariants unconditionally: they should not be observable anyway, but those ghost updates can be technically tricky to work with.