Open michael-schwarz opened 1 month ago
It does not have anything to do with logic to exclude temporaries, the problem is still there after renaming the variable.
Adding the assertion as a __goblint_check
also can not be verified, so it seems the shortcoming may be on the generation side?
Maybe this has to do with the long long
casts that we blindly add to all Apron invariants?
Nevertheless, it should still hold even after making this cast.
Actually claiming that this holds is in fact wrong! long long
and long
have the same length on LP64
machines, so this supposed invariant actually isn't one!
I now have the following minimal program
#include<pthread.h>
int *b;
pthread_mutex_t e;
void main() {
int g;
int a;
b = &g;
pthread_mutex_lock(&e);
}
where some 4 invariants cannot be shown after locking e
, such as (4294967294LL - (long long )a) - (long long )g >= 0LL
. Enabling the interval domain makes us able to show the assertions.
However, the bounds seem to be correct, they result from interval arithmetic. If I don't assign the address of g
to b
, verification also succeeds without intervals.
This minimal program locks a mutex in single-threaded mode which should be fine, but maybe we handle something incorrectly in such case? Or is the locking there just as a place for after-lock
invariant generation?
It also misbehaves if the program starts a thread right at the beginning. I just figured it might be easier to debug if there's no concurrency happening.
I haven't tried whether we have the same issue if I generate witnesses at all program points and remove the mutex, I'll try that tomorrow.
The problem also occurs when dumping invariants at all program points and removing the locking of the mutex.
The last remaining issue can be observed in this program:
#include<pthread.h>
int *b;
pthread_mutex_t e;
void* other(void* arg) {
return NULL;
}
void main() {
pthread_t t;
pthread_create(&t, NULL, other, NULL);
int g = 8;
int a;
if(a) {
g = 4383648;
}
b = &g;
// Invariant generated: (2143100000LL + (long long )a) + (long long )g >= 0LL
pthread_mutex_lock(&e);
}
This invariant is an interesting case: At first glance it seems wrong, as it appears as if the case where g is 8 seems to violate the invariant.
However, the invariant is actually correct as one can see by case distinction over a
:
0
, we have g is 8
and 2143100000LL + 8LL >= 0LL
holds.0
, we have g 4383648
and 2143100000LL + [–2147483648,2147483647] + 4383648 >= 0
and min(2143100000LL + [–2147483648,2147483647] + 4383648) = 0
and the invariant thus holds. If we destroy the relationship about a
before, this invariant is no longer produced.
It also seems to be related to escaping somehow:
To establish the invariant, we attempt to show a contradiction with (2143100000LL + (long long )a) + (long long )g#in < 0L
in the state we have a#994+g#993+2143100000 >= 0
which refers to a local copy of g somehow, and not the flow-insensitive invariant which appears to be stored in g
for which we know it is equal with g#in
(-g+g#in#>=0
and g-g#in#>=0
) such a property does not hold.
Complete state:
%%% gurki: assert_constraint (2143100000LL + (long long )a) + (long long )g#in < 0LL 0 -_i,n (2143100000 +_i,n a#994 +_i,n g#in#) > 0
%%% gurki: assert_constraint st: [|a#994+2147483648.>=0; -a#994+2147483647.>=0; -a#994+g+2147483639.>=0;
a#994+g+2147483640.>=0; g-8.>=0; -a#994-g+2151867295.>=0;
a#994-g+2151867296.>=0; -g+4383648.>=0; -a#994+g#993+2143099999.>=0;
a#994+g#993+2143100000.>=0; -g+g#993+4383640.>=0; g+g#993-16.>=0;
g#993-8.>=0; -a#994-g#993+2151867295.>=0; a#994-g#993+2151867296.>=0;
-g-g#993+8767296.>=0; g-g#993+4383640.>=0; -g#993+4383648.>=0;
-a#994+g#in#+2147483639.>=0;
a#994+g#in#+2147483640.>=0; -g+g#in#>=0;
g+g#in#-16.>=0; -g#993+g#in#+4383640.>=0;
g#993+g#in#-16.>=0; g#in#-8.>=0;
-a#994-g#in#+2151867295.>=0;
a#994-g#in#+2151867296.>=0; -g-g#in#+8767296.>=0;
g-g#in#>=0; -g#993-g#in#+8767296.>=0;
g#993-g#in#+4383640.>=0; -g#in#+4383648.>=0|] (env: [|
0> __daylight:int; 1> __timezone:int; 2> a#994:int; 3> daylight:int;
4> g:int; 5> g#993:int; 6> g#in#:int; 7> timezone:int|])
Consider the following program extracted from
libaco
which is one of the programs I am considering for #1417:Creating a YAML witness from it (via
./goblint --conf conf/traces-rel.json --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --enable allglobs -v --enable witness.yaml.enabled notlibaco.c --set witness.yaml.path notlibaco.yml
) yieldsHowever, subsequent self-validation with
./goblint --conf conf/traces-rel.json --set ana.activated[+] apron --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --enable allglobs -v --set witness.yaml.validate notlibaco.yml notlibaco.c
yields the message that only 2/5 invariants can be confirmed.One of the invariants that cannot be re-confirmed, is
(long long )tmp_gl_ct >= 0LL
. This is correct, as this invariant may not hold (if we assume uninitialized variables are top), aslong long
andunsigned long
have the same bit-length but different ranges.Wrong reasoning I used earlier:
Invaraint holds vacuously astmp_gl_ct
has typeunsigned long
.