Skip to content

Commit d81fcc4

Browse files
Merge pull request #1646 from goblint/issue_1535
Relational: Use same invalidation strategy as base
2 parents a1e5006 + 44a2862 commit d81fcc4

File tree

6 files changed

+142
-60
lines changed

6 files changed

+142
-60
lines changed

src/analyses/apron/relationAnalysis.apron.ml

Lines changed: 49 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -466,32 +466,23 @@ struct
466466

467467
(* Give the set of reachables from argument. *)
468468
let reachables (ask: Queries.ask) es =
469-
let reachable acc e =
470-
Option.bind acc (fun st ->
471-
let ad = ask.f (Queries.ReachableFrom e) in
472-
if Queries.AD.is_top ad then
473-
None
474-
else
475-
Some (Queries.AD.join ad st))
469+
let reachable st e =
470+
let ad = ask.f (Queries.ReachableFrom e) in
471+
Queries.AD.join ad st
476472
in
477-
List.fold_left reachable (Some (Queries.AD.empty ())) es
473+
List.fold_left reachable (Queries.AD.empty ()) es
478474

479475

480476
let forget_reachable man st es =
481477
let ask = Analyses.ask_of_man man in
482478
let rs =
483-
match reachables ask es with
484-
| None ->
485-
(* top reachable, so try to invalidate everything *)
486-
let to_cil_lval x = Option.map Cil.var @@ RV.to_cil_varinfo x in
487-
RD.vars st.rel |> List.filter_map to_cil_lval
488-
| Some ad ->
489-
let to_cil addr rs =
490-
match addr with
491-
| Queries.AD.Addr.Addr mval -> (ValueDomain.Addr.Mval.to_cil mval) :: rs
492-
| _ -> rs
493-
in
494-
Queries.AD.fold to_cil ad []
479+
let ad = reachables ask es in
480+
let to_cil addr rs =
481+
match addr with
482+
| Queries.AD.Addr.Addr mval -> (ValueDomain.Addr.Mval.to_cil mval) :: rs
483+
| _ -> rs
484+
in
485+
Queries.AD.fold to_cil ad []
495486
in
496487
List.fold_left (fun st lval ->
497488
invalidate_one ask man st lval
@@ -512,6 +503,36 @@ struct
512503
if RD.is_bot_env res then raise Deadcode;
513504
{st with rel = res}
514505

506+
let special_unknown_invalidate man f args =
507+
(* No warning here, base already produces the appropriate warnings *)
508+
let desc = LibraryFunctions.find f in
509+
let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in
510+
let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } args in
511+
let deep_addrs =
512+
if List.mem LibraryDesc.InvalidateGlobals desc.attrs then (
513+
foldGlobals !Cilfacade.current_file (fun acc global ->
514+
match global with
515+
| GVar (vi, _, _) when not (BaseUtil.is_static vi) ->
516+
mkAddrOf (Var vi, NoOffset) :: acc
517+
(* TODO: what about GVarDecl? *)
518+
| _ -> acc
519+
) deep_addrs
520+
)
521+
else
522+
deep_addrs
523+
in
524+
let lvallist e =
525+
match man.ask (Queries.MayPointTo e) with
526+
| ad when Queries.AD.is_top ad -> []
527+
| ad ->
528+
Queries.AD.to_mval ad
529+
|> List.map ValueDomain.Addr.Mval.to_cil
530+
in
531+
let st' = forget_reachable man man.local deep_addrs in
532+
let shallow_lvals = List.concat_map lvallist shallow_addrs in
533+
List.fold_left (invalidate_one (Analyses.ask_of_man man) man) st' shallow_lvals
534+
535+
515536
let special man r f args =
516537
let ask = Analyses.ask_of_man man in
517538
let st = man.local in
@@ -542,31 +563,7 @@ struct
542563
assert_fn {man with local = st} (BinOp (Ge, Lval lv, zero, intType)) true
543564
) st r
544565
| _, _ ->
545-
let lvallist e =
546-
match ask.f (Queries.MayPointTo e) with
547-
| ad when Queries.AD.is_top ad -> []
548-
| ad ->
549-
Queries.AD.to_mval ad
550-
|> List.map ValueDomain.Addr.Mval.to_cil
551-
in
552-
let shallow_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = false } args in
553-
let deep_addrs = LibraryDesc.Accesses.find desc.accs { kind = Write; deep = true } args in
554-
let deep_addrs =
555-
if List.mem LibraryDesc.InvalidateGlobals desc.attrs then (
556-
foldGlobals !Cilfacade.current_file (fun acc global ->
557-
match global with
558-
| GVar (vi, _, _) when not (BaseUtil.is_static vi) ->
559-
mkAddrOf (Var vi, NoOffset) :: acc
560-
(* TODO: what about GVarDecl? *)
561-
| _ -> acc
562-
) deep_addrs
563-
)
564-
else
565-
deep_addrs
566-
in
567-
let st' = forget_reachable man st deep_addrs in
568-
let shallow_lvals = List.concat_map lvallist shallow_addrs in
569-
let st' = List.fold_left (invalidate_one ask man) st' shallow_lvals in
566+
let st' = special_unknown_invalidate man f args in
570567
(* invalidate lval if present *)
571568
Option.map_default (invalidate_one ask man st') st' r
572569

@@ -669,21 +666,19 @@ struct
669666

670667
let threadenter man ~multiple lval f args =
671668
let st = man.local in
669+
(* TODO: HACK: Simulate enter_multithreaded for first entering thread to publish global inits before analyzing thread.
670+
Otherwise thread is analyzed with no global inits, reading globals gives bot, which turns into top, which might get published...
671+
sync `Thread doesn't help us here, it's not specific to entering multithreaded mode.
672+
EnterMultithreaded events only execute after threadenter and threadspawn. *)
673+
if not (ThreadFlag.has_ever_been_multi (Analyses.ask_of_man man)) then
674+
ignore (Priv.enter_multithreaded (Analyses.ask_of_man man) man.global man.sideg st);
672675
match Cilfacade.find_varinfo_fundec f with
673676
| fd ->
674-
(* TODO: HACK: Simulate enter_multithreaded for first entering thread to publish global inits before analyzing thread.
675-
Otherwise thread is analyzed with no global inits, reading globals gives bot, which turns into top, which might get published...
676-
sync `Thread doesn't help us here, it's not specific to entering multithreaded mode.
677-
EnterMultithreaded events only execute after threadenter and threadspawn. *)
678-
if not (ThreadFlag.has_ever_been_multi (Analyses.ask_of_man man)) then
679-
ignore (Priv.enter_multithreaded (Analyses.ask_of_man man) man.global man.sideg st);
680677
let st' = Priv.threadenter (Analyses.ask_of_man man) man.global st in
681678
let new_rel = make_callee_rel ~thread:true man fd args in
682679
[{st' with rel = new_rel}]
683680
| exception Not_found ->
684-
(* Unknown functions *)
685-
(* TODO: do something like base? *)
686-
failwith "relation.threadenter: unknown function"
681+
[special_unknown_invalidate man f args]
687682

688683
let threadspawn man ~multiple lval f args fman =
689684
man.local

src/analyses/base.ml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2150,12 +2150,6 @@ struct
21502150
(* TODO: make this is_private PrivParam dependent? PerMutexOplusPriv should keep *)
21512151
let st' =
21522152
if thread then (
2153-
(* TODO: HACK: Simulate enter_multithreaded for first entering thread to publish global inits before analyzing thread.
2154-
Otherwise thread is analyzed with no global inits, reading globals gives bot, which turns into top, which might get published...
2155-
sync `Thread doesn't help us here, it's not specific to entering multithreaded mode.
2156-
EnterMultithreaded events only execute after threadenter and threadspawn. *)
2157-
if not (ThreadFlag.has_ever_been_multi ask) then
2158-
ignore (Priv.enter_multithreaded ask (priv_getg man.global) (priv_sideg man.sideg) st);
21592153
Priv.threadenter ask st
21602154
) else
21612155
(* use is_global to account for values that became globals because they were saved into global variables *)
@@ -2967,6 +2961,13 @@ struct
29672961
combine_one man.local after
29682962

29692963
let threadenter man ~multiple (lval: lval option) (f: varinfo) (args: exp list): D.t list =
2964+
(* TODO: HACK: Simulate enter_multithreaded for first entering thread to publish global inits before analyzing thread.
2965+
Otherwise thread is analyzed with no global inits, reading globals gives bot, which turns into top, which might get published...
2966+
sync `Thread doesn't help us here, it's not specific to entering multithreaded mode.
2967+
EnterMultithreaded events only execute after threadenter and threadspawn. *)
2968+
let st = man.local in
2969+
if not (ThreadFlag.has_ever_been_multi (Analyses.ask_of_man man)) then
2970+
ignore (Priv.enter_multithreaded (Analyses.ask_of_man man) (priv_getg man.global) (priv_sideg man.sideg) st);
29702971
match Cilfacade.find_varinfo_fundec f with
29712972
| fd ->
29722973
[make_entry ~thread:true man fd args]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// SKIP PARAM: --set ana.activated[+] apron --set sem.int.signed_overflow assume_none
2+
#include <pthread.h>
3+
#include <goblint.h>
4+
#include <stdio.h>
5+
6+
int debug;
7+
int other;
8+
9+
int main() {
10+
int top;
11+
12+
// Needed so Base & DefExc doesn't find this information because it invalidates less
13+
if(top) {
14+
debug = 3;
15+
}
16+
17+
fscanf(stdin, "%d", &other);
18+
19+
// Use to fail as debug was invalidated
20+
__goblint_check(debug <= 3);
21+
return 0;
22+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// CRAM SKIP
2+
#include<pthread.h>
3+
pthread_mutex_t c;
4+
int d, e, f;
5+
6+
void b(void* arg);
7+
8+
void main(int argc, char *argv) {
9+
pthread_t t;
10+
e = pthread_create(&t, 0, b, &f);
11+
pthread_mutex_lock(&c);
12+
d++;
13+
pthread_mutex_unlock(&c);
14+
pthread_mutex_lock(&c);
15+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Check that the invariant (long long )f + 2147483648LL >= (long long )e is not confirmed, as it presumes information about f and e which are supposed to be invalidated
2+
$ goblint --set dbg.level warning --disable warn.imprecise --disable warn.race --set ana.activated[+] apron --enable witness.invariant.after-lock --disable witness.invariant.other --disable witness.invariant.loop-head --disable sem.unknown_function.invalidate.globals --set ana.path_sens[+] threadflag --set ana.relation.privatization mutex-meet-tid-cluster12 --set witness.yaml.entry-types[*] invariant_set --set witness.yaml.validate 94-weird.yml 94-weird.c
3+
[Error][Imprecise][Unsound] Function definition missing for b (94-weird.c:10:3-10:35)
4+
[Error][Imprecise][Unsound] Created a thread from unknown function b (94-weird.c:10:3-10:35)
5+
[Info][Deadcode] Logical lines of code (LLoC) summary:
6+
live: 7
7+
dead: 0
8+
total lines: 7
9+
[Warning][Witness] invariant unconfirmed: (long long )f + 2147483648LL >= (long long )e (94-weird.c:11:3)
10+
[Info][Witness] witness validation summary:
11+
confirmed: 0
12+
unconfirmed: 1
13+
refuted: 0
14+
error: 0
15+
unchecked: 0
16+
unsupported: 0
17+
disabled: 0
18+
total validation entries: 1
19+
[Error][Imprecise][Unsound] Function definition missing
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
- entry_type: invariant_set
2+
metadata:
3+
format_version: "2.0"
4+
uuid: 271d28bc-9fe6-4b18-9b7c-6bb68c7d5dcf
5+
creation_time: 2025-07-15T14:56:13Z
6+
producer:
7+
name: Goblint
8+
version: heads/issue_1535-0-g2accba23c-dirty
9+
command_line: '''./goblint'' ''--conf'' ''conf/traces-rel.json'' ''--set'' ''ana.activated[+]''
10+
''threadJoins'' ''--set'' ''ana.activated[+]'' ''apron'' ''--set'' ''ana.path_sens[+]''
11+
''threadflag'' ''--set'' ''ana.relation.privatization'' ''mutex-meet-tid''
12+
''2.c'' ''--sets'' ''exp.relation.prec-dump'' ''cluster.prec'' ''--html''
13+
''--enable'' ''witness.yaml.enabled'' ''--set'' ''witness.yaml.path'' ''1.yml'''
14+
task:
15+
input_files:
16+
- 94-weird.c
17+
input_file_hashes:
18+
94-weird.c: c67808107b2da394e33439894ea1ae3c5e5c3628a08c9371eaa31ee3efbc8414
19+
data_model: LP64
20+
language: C
21+
content:
22+
- invariant:
23+
type: location_invariant
24+
location:
25+
file_name: 94-weird.c
26+
line: 11
27+
column: 3
28+
function: main
29+
value: (long long )f + 2147483648LL >= (long long )e
30+
format: c_expression

0 commit comments

Comments
 (0)