Skip to content
Draft
49 changes: 21 additions & 28 deletions src/analyses/base.ml
Original file line number Diff line number Diff line change
Expand Up @@ -585,9 +585,9 @@ struct
if not (VD.is_immediate_type t) then M.info ~category:Unsound "Unknown value in %s could be an escaped pointer address!" description; empty
| Bot -> (*M.debug ~category:Analyzer "A bottom value when computing reachable addresses!";*) empty
| Address adrs when AD.is_top adrs ->
M.info ~category:Unsound "Unknown address in %s has escaped." description; AD.remove Addr.NullPtr adrs (* return known addresses still to be a bit more sane (but still unsound) *)
M.info ~category:Unsound "Unknown address in %s has escaped." description; adrs (* return known addresses still to be a bit more sane (but still unsound) *)
(* The main thing is to track where pointers go: *)
| Address adrs -> AD.remove Addr.NullPtr adrs
| Address adrs -> adrs
(* Unions are easy, I just ingore the type info. *)
| Union (f,e) -> reachable_from_value ask e t description
(* For arrays, we ask to read from an unknown index, this will cause it
Expand Down Expand Up @@ -1567,13 +1567,9 @@ struct
| Top -> Queries.Result.top q
| Bot -> Queries.Result.bot q (* TODO: remove *)
| Address a ->
let a' = AD.remove Addr.UnknownPtr a in (* run reachable_vars without unknown just to be safe: TODO why? *)
let addrs = reachable_vars ~man man.local [a'] in
let addrs' = List.fold_left (AD.join) (AD.empty ()) addrs in
if AD.may_be_unknown a then
AD.add UnknownPtr addrs' (* add unknown back *)
else
addrs'
[a]
|> reachable_vars ~man man.local
|> List.fold_left (AD.join) (AD.empty ())
| Int i ->
begin match Cilfacade.typeOf e with
| t when Cil.isPointerType t -> AD.of_int i (* integer used as pointer *)
Expand Down Expand Up @@ -1814,23 +1810,20 @@ struct
let set ~(man: _ man) ?invariant ?blob_destructive ?lval_raw ?rval_raw ?t_override (st: store) (lval: AD.t) (lval_type: Cil.typ) (value: value) : store =
let lval_raw = (Option.map (fun x -> Lval x) lval_raw) in
if M.tracing then M.tracel "set" "lval: %a\nvalue: %a\nstate: %a" AD.pretty lval VD.pretty value CPA.pretty st.cpa;
let update_one x store =
Option.map_default (fun x -> set_mval ~man ?invariant ?blob_destructive ?lval_raw ?rval_raw ?t_override store x lval_type value) store (Addr.to_mval x)
in try
(* We start from the current state and an empty list of global deltas,
* and we assign to all the the different possible places: *)
let nst = AD.fold update_one lval st in
(* if M.tracing then M.tracel "set" "new state1 %a" CPA.pretty nst; *)
(* If the address was definite, then we just return it. If the address
* was ambiguous, we have to join it with the initial state. *)
let nst = if AD.cardinal lval > 1 then D.join st nst else nst in
(* if M.tracing then M.tracel "set" "new state2 %a" CPA.pretty nst; *)
nst
with
(* If any of the addresses are unknown, we ignore it!?! *)
| SetDomain.Unsupported x ->
(* if M.tracing then M.tracel "set" "set got an exception '%s'" x; *)
M.info ~category:Unsound "Assignment to unknown address, assuming no write happened."; st
let update_one (x : Addr.t) store =
match x with
| Addr x -> set_mval ~man ?invariant ?blob_destructive ?lval_raw ?rval_raw ?t_override store x lval_type value
| NullPtr ->
begin match get_string "sem.null-pointer.dereference" with
| "assume_none" -> D.bot ()
| "assume_top" -> store
| _ -> assert false
end
| UnknownPtr
| StrPtr _ -> store
in
assert (not @@ AD.is_empty lval);
AD.fold (fun addr acc -> D.join (update_one addr st) acc) lval (D.bot ())
Comment on lines +1825 to +1826
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[...] Not sure about the test failures though.

The old CI runs seem to be gone, but I now merged master into this and locally at least two malloc_null tests fail.
The path-sensitivity of malloc_null analysis causes a scenario where a path has the malloc-ed pointer as {NULL}. Writing into that pointer, the assert doesn't fail because the points-to set is non-empty, but due to assume_none we end up returning D.bot ().
That bottom is a strange value: it appears live, but has lost all local variables state of base.

I suppose turning a D.bot () result into raise Deadcode could work here.
It does reveal a certain asymmetry with assume_none on NULL pointer reading: the latter doesn't raise Deadcode I think, but just assumes nothing changed.


let set_many ~man (st: store) lval_value_list: store =
(* Maybe this can be done with a simple fold *)
Expand Down Expand Up @@ -2111,14 +2104,14 @@ struct
collect_funargs ~man ~warn st exps
else (
let mpt e = match eval_rv_address ~man st e with
| Address a -> AD.remove NullPtr a
| Address a -> a
| _ -> AD.empty ()
in
List.map mpt exps
)

let invalidate ~(must: bool) ?(deep=true) ~man (st:store) (exps: exp list): store =
if M.tracing && exps <> [] then M.tracel "invalidate" "Will invalidate expressions [%a]" (d_list ", " d_plainexp) exps;
if M.tracing && exps <> [] then M.tracel "invalidate" "Will invalidate expressions [%a]" (d_list ", " d_exp) exps;
if exps <> [] then M.info ~category:Imprecise "Invalidating expressions: %a" (d_list ", " d_exp) exps;
(* To invalidate a single address, we create a pair with its corresponding
* top value. *)
Expand Down
2 changes: 1 addition & 1 deletion src/config/options.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1630,7 +1630,7 @@
"properties": {
"dereference": {
"title": "sem.null-pointer.dereference",
"description": "NULL pointer dereference handling. assume_top: assume it results in a top value, assume_none: assume it doesn't happen",
"description": "NULL pointer dereference handling. assume_top: reading returns top value, writing is ignored; assume_none: assumes that reading or writing to NULL pointer does not happen",
"type": "string",
"enum": ["assume_top", "assume_none"],
"default": "assume_none"
Expand Down
2 changes: 1 addition & 1 deletion tests/regression/05-lval_ls/17-per_elem_simp.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ struct t {
void* f(struct t* in) {
start_unpack(in);
pthread_mutex_lock(&(in->lock));
in->data++; // NOWARN
in->data++; // NORACE
pthread_mutex_unlock(&(in->lock));
end_unpack(in);
return 0;
Expand Down
1 change: 1 addition & 0 deletions tests/regression/41-stdlib/05-more.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include<goblint.h>
#include <wchar.h>
#include <stdio.h>
#include <stdlib.h>

int g = 8;

Expand Down
Loading