Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
cdcc21c
In eval_rv: Assume varinfo of void type is valid target of dereferenc…
michael-schwarz Nov 12, 2020
1e859cf
Revert "In eval_rv: Assume varinfo of void type is valid target of de…
michael-schwarz Nov 13, 2020
22279a7
In cast_addr also allow offsets when replacing a TVoid with the prope…
michael-schwarz Nov 13, 2020
0b89e3e
Add an origin of the allocated memory: malloc or calloc
Nov 16, 2020
863ae4f
Store the origin of allocated data as bool
Nov 17, 2020
62ceb77
Return type argument to VD.update_offset
Nov 17, 2020
f1d1e24
Add zero_init_value to make a zero-initiliazed abstract value of type…
michael-schwarz Nov 5, 2020
cca3adb
Join works differently for calloc and malloc
Nov 18, 2020
c350a60
Merge branch 'master' of github.com:goblint/analyzer into fixes/calloc
Nov 18, 2020
c3f2e77
Fix the bug that the array is uknown for calloc
Nov 18, 2020
a782ae0
Change the names of values for AllocOrigin to Malloc and Calloc
Nov 18, 2020
3d27f41
Separate n and size for calloc, it now takes two arguments
Nov 19, 2020
60fcbe9
Create tests for calloc
Nov 19, 2020
577ac0d
Add the type as a parameter to the eval_offset function
Nov 20, 2020
3cd7c04
Deduplicate top_value in the value domain
Nov 20, 2020
c4ece20
Add partition arrays parameter to the calloc tests
Nov 23, 2020
2c6f158
Small changes suggested by comments
Nov 24, 2020
a1c5fd9
Add a helper function to deduplicate the zero initialising of calloce…
Nov 24, 2020
bf12f78
New type for booleans was added to Basetype to keep track in value do…
Nov 24, 2020
a17736a
Add explanation to the regression test 02 44
Nov 24, 2020
72d3d27
Mark the comment in a proper C style
Nov 24, 2020
e08bc4b
Merge branch 'master' into fixes/calloc
michael-schwarz Nov 25, 2020
06c2d1b
Fixed issues with wrong types passed to zero_init_calloced_memory
michael-schwarz Nov 25, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 22 additions & 86 deletions src/analyses/base.ml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,6 @@ module Offs = ValueDomain.Offs
module LF = LibraryFunctions
module CArrays = ValueDomain.CArrays

let is_mutex_type (t: typ): bool = match t with
| TNamed (info, attr) -> info.tname = "pthread_mutex_t" || info.tname = "spinlock_t"
| TInt (IInt, attr) -> hasAttribute "mutex" attr
| _ -> false

let is_immediate_type t = is_mutex_type t || isFunctionType t

let is_global (a: Q.ask) (v: varinfo): bool =
v.vglob || match a (Q.MayEscape v) with `Bool tv -> tv | _ -> false

Expand Down Expand Up @@ -383,10 +376,10 @@ struct
end
in

let v = VD.eval_offset a (fun x -> get a gs (st,fl,dep) x exp) var offs exp (Some (Var x, Offs.to_cil_offset offs)) in
let v = VD.eval_offset a (fun x -> get a gs (st,fl,dep) x exp) var offs exp (Some (Var x, Offs.to_cil_offset offs)) x.vtype in
if M.tracing then M.tracec "get" "var = %a, %a = %a\n" VD.pretty var AD.pretty (AD.from_var_offset (x, offs)) VD.pretty v;
if full then v else match v with
| `Blob (c, s) -> c
| `Blob (c,s,_) -> c
| x -> x
in
let f x =
Expand Down Expand Up @@ -443,7 +436,7 @@ struct
| `Top ->
let typ = AD.get_type adr in
let warning = "Unknown value in " ^ AD.short 40 adr ^ " could be an escaped pointer address!" in
if is_immediate_type typ then () else M.warn_each warning; empty
if VD.is_immediate_type typ then () else M.warn_each warning; empty
| `Bot -> (*M.debug "A bottom value when computing reachable addresses!";*) empty
| `Address adrs when AD.is_top adrs ->
let warning = "Unknown address in " ^ AD.short 40 adr ^ " has escaped." in
Expand All @@ -455,7 +448,7 @@ struct
(* For arrays, we ask to read from an unknown index, this will cause it
* join all its values. *)
| `Array a -> reachable_from_value (ValueDomain.CArrays.get ask a (ExpDomain.top (), ValueDomain.ArrIdxDomain.top ()))
| `Blob (e,_) -> reachable_from_value e
| `Blob (e,_,_) -> reachable_from_value e
| `List e -> reachable_from_value (`Address (ValueDomain.Lists.entry_rand e))
| `Struct s -> ValueDomain.Structs.fold (fun k v acc -> AD.join (reachable_from_value v) acc) s empty
| `Int _ -> empty
Expand Down Expand Up @@ -494,11 +487,11 @@ struct
if CPA.is_top st then st else
let rec replace_val = function
| `Address _ as v -> v
| `Blob (v,s) ->
| `Blob (v,s,o) ->
begin match replace_val v with
| `Blob (`Top, _)
| `Blob (`Top,_,_)
| `Top -> `Top
| t -> `Blob (t, s)
| t -> `Blob (t,s,o)
end
| `Struct s ->
let one_field fl vl st =
Expand All @@ -518,7 +511,7 @@ struct
| `Array n -> `Array (ValueDomain.CArrays.map replace_val n)
| `Struct n -> `Struct (ValueDomain.Structs.map replace_val n)
| `Union (f,v) -> `Union (f,replace_val v)
| `Blob (n,s) -> `Blob (replace_val n,s)
| `Blob (n,s,o) -> `Blob (replace_val n,s,o)
| `Address x -> `Address (ValueDomain.AD.map ValueDomain.Addr.drop_ints x)
| x -> x
in
Expand Down Expand Up @@ -586,7 +579,7 @@ struct
| `Address adrs -> (adrs,TS.bot (), AD.has_unknown adrs)
| `Union (t,e) -> with_field (reachable_from_value e) t
| `Array a -> reachable_from_value (ValueDomain.CArrays.get ctx.ask a (ExpDomain.top(), ValueDomain.ArrIdxDomain.top ()))
| `Blob (e,_) -> reachable_from_value e
| `Blob (e,_,_) -> reachable_from_value e
| `List e -> reachable_from_value (`Address (ValueDomain.Lists.entry_rand e))
| `Struct s ->
let join_tr (a1,t1,_) (a2,t2,_) = AD.join a1 a2, TS.join t1 t2, false in
Expand Down Expand Up @@ -690,7 +683,7 @@ struct
in
let v' = VD.cast t v in (* cast to the expected type (the abstract type might be something other than t since we don't change addresses upon casts!) *)
M.tracel "cast" "Ptr-Deref: cast %a to %a = %a!\n" VD.pretty v d_type t VD.pretty v';
let v' = VD.eval_offset a (fun x -> get a gs st x (Some exp)) v' (convert_offset a gs st ofs) (Some exp) None in (* handle offset *)
let v' = VD.eval_offset a (fun x -> get a gs st x (Some exp)) v' (convert_offset a gs st ofs) (Some exp) None t in (* handle offset *)
let v' = do_offs v' ofs in (* handle blessed fields? *)
v'
(* Binary operators *)
Expand Down Expand Up @@ -796,64 +789,6 @@ struct
M.debug ("Failed evaluating "^str^" to lvalue"); do_offs AD.unknown_ptr ofs
end

let rec bot_value a (gs:glob_fun) (st: store) (t: typ): value =
let bot_comp compinfo: ValueDomain.Structs.t =
let nstruct = ValueDomain.Structs.top () in
let bot_field nstruct fd = ValueDomain.Structs.replace nstruct fd (bot_value a gs st fd.ftype) in
List.fold_left bot_field nstruct compinfo.cfields
in
match t with
| TInt _ -> `Bot (*`Int (ID.bot ()) -- should be lower than any int or address*)
| TPtr _ -> `Address (AD.bot ())
| TComp ({cstruct=true; _} as ci,_) -> `Struct (bot_comp ci)
| TComp ({cstruct=false; _},_) -> `Union (ValueDomain.Unions.bot ())
| TArray (ai, None, _) ->
`Array (ValueDomain.CArrays.make (IdxDom.bot ()) (bot_value a gs st ai))
| TArray (ai, Some exp, _) ->
let l = Cil.isInteger (Cil.constFold true exp) in
`Array (ValueDomain.CArrays.make (BatOption.map_default (IdxDom.of_int) (IdxDom.bot ()) l) (bot_value a gs st ai))
| TNamed ({ttype=t; _}, _) -> bot_value a gs st t
| _ -> `Bot

let rec init_value a (gs:glob_fun) (st: store) (t: typ): value = (* TODO why is VD.top_value not used here? *)
let init_comp compinfo: ValueDomain.Structs.t =
let nstruct = ValueDomain.Structs.top () in
let init_field nstruct fd = ValueDomain.Structs.replace nstruct fd (init_value a gs st fd.ftype) in
List.fold_left init_field nstruct compinfo.cfields
in
match t with
| t when is_mutex_type t -> `Top
| TInt (ik,_) -> `Int (ID.(cast_to ik (top ())))
| TPtr _ -> `Address (if get_bool "exp.uninit-ptr-safe" then AD.(join null_ptr safe_ptr) else AD.top_ptr)
| TComp ({cstruct=true; _} as ci,_) -> `Struct (init_comp ci)
| TComp ({cstruct=false; _},_) -> `Union (ValueDomain.Unions.top ())
| TArray (ai, None, _) ->
`Array (ValueDomain.CArrays.make (IdxDom.bot ()) (if get_bool "exp.partition-arrays.enabled" then (init_value a gs st ai) else (bot_value a gs st ai)))
| TArray (ai, Some exp, _) ->
let l = Cil.isInteger (Cil.constFold true exp) in
`Array (ValueDomain.CArrays.make (BatOption.map_default (IdxDom.of_int) (IdxDom.bot ()) l) (if get_bool "exp.partition-arrays.enabled" then (init_value a gs st ai) else (bot_value a gs st ai)))
| TNamed ({ttype=t; _}, _) -> init_value a gs st t
| _ -> `Top

let rec top_value a (gs:glob_fun) (st: store) (t: typ): value =
let top_comp compinfo: ValueDomain.Structs.t =
let nstruct = ValueDomain.Structs.top () in
let top_field nstruct fd = ValueDomain.Structs.replace nstruct fd (top_value a gs st fd.ftype) in
List.fold_left top_field nstruct compinfo.cfields
in
match t with
| TInt _ -> `Int (ID.top ())
| TPtr _ -> `Address AD.top_ptr
| TComp ({cstruct=true; _} as ci,_) -> `Struct (top_comp ci)
| TComp ({cstruct=false; _},_) -> `Union (ValueDomain.Unions.top ())
| TArray (ai, None, _) ->
`Array (ValueDomain.CArrays.make (IdxDom.top ()) (if get_bool "exp.partition-arrays.enabled" then (top_value a gs st ai) else (bot_value a gs st ai)))
| TArray (ai, Some exp, _) ->
let l = Cil.isInteger (Cil.constFold true exp) in
`Array (ValueDomain.CArrays.make (BatOption.map_default (IdxDom.of_int) (IdxDom.top ()) l) (if get_bool "exp.partition-arrays.enabled" then (top_value a gs st ai) else (bot_value a gs st ai)))
| TNamed ({ttype=t; _}, _) -> top_value a gs st t
| _ -> `Top

(* run eval_rv from above and keep a result that is bottom *)
(* this is needed for global variables *)
let eval_rv_keep_bot = eval_rv
Expand All @@ -864,9 +799,9 @@ struct
try
let r = eval_rv a gs st exp in
if M.tracing then M.tracel "eval" "eval_rv %a = %a\n" d_exp exp VD.pretty r;
if VD.is_bot r then top_value a gs st (typeOf exp) else r
if VD.is_bot r then VD.top_value (typeOf exp) else r
with IntDomain.ArithmeticOnIntegerBot _ ->
top_value a gs st (typeOf exp)
ValueDomain.Compound.top_value (typeOf exp)

(* Evaluate an expression containing only locals. This is needed for smart joining the partitioned arrays where ctx is not accessible. *)
(* This will yield `Top for expressions containing any access to globals, and does not make use of the query system. *)
Expand Down Expand Up @@ -932,7 +867,7 @@ struct
let r = get ~full:true ctx.ask ctx.global ctx.local a None in
(* ignore @@ printf "BlobSize %a = %a\n" d_plainexp e VD.pretty r; *)
(match r with
| `Blob (_,s) -> (match ID.to_int s with Some i -> `Int i | None -> `Top)
| `Blob (_,s,_) -> (match ID.to_int s with Some i -> `Int i | None -> `Top)
| _ -> `Top)
| _ -> `Top
end
Expand Down Expand Up @@ -1619,7 +1554,7 @@ struct

let set_savetop ?lval_raw ?rval_raw ask (gs:glob_fun) st adr lval_t v : store =
match v with
| `Top -> set ask gs st adr lval_t (top_value ask gs st (AD.get_type adr)) ?lval_raw ?rval_raw
| `Top -> set ask gs st adr lval_t (VD.top_value (AD.get_type adr)) ?lval_raw ?rval_raw
| v -> set ask gs st adr lval_t v ?lval_raw ?rval_raw


Expand Down Expand Up @@ -1704,7 +1639,7 @@ struct
(match Addr.to_var_offset (AD.choose lval_val) with
| [(x,offs)] ->
let t = v.vtype in
let iv = bot_value ctx.ask ctx.global ctx.local v.vtype in (* correct bottom value for top level variable *)
let iv = VD.bot_value t in (* correct bottom value for top level variable *)
let nv = VD.update_offset ctx.ask iv offs rval_val (Some (Lval lval)) lval t in (* do desired update to value *)
set_savetop ctx.ask ctx.global ctx.local (AD.from_var v) lval_t nv (* set top-level variable to updated value *)
| _ ->
Expand Down Expand Up @@ -1778,7 +1713,7 @@ struct

let body ctx f =
(* First we create a variable-initvalue pair for each variable *)
let init_var v = (AD.from_var v, v.vtype, init_value ctx.ask ctx.global ctx.local v.vtype) in
let init_var v = (AD.from_var v, v.vtype, VD.init_value v.vtype) in
(* Apply it to all the locals and then assign them all *)
let inits = List.map init_var f.slocals in
set_many ctx.ask ctx.global ctx.local inits
Expand Down Expand Up @@ -1982,7 +1917,7 @@ struct
let expected = if string_match (regexp ".+//.*\\(FAIL\\|UNKNOWN\\).*") line 0 then Some (matched_group 1 line) else None in
if expected <> annot then (
let result = if annot = None && (expected = Some ("NOWARN") || (expected = Some ("UNKNOWN") && not (String.exists line "UNKNOWN!"))) then "improved" else "failed" in
(* Expressions with logical connectives like a && b are calculated in temporary variables by CIL. Instead of the original expression, we then see something like tmp___0. So we replace expr in msg by the orginal source if this is the case. *)
(* Expressions with logical connectives like a && b are calculated in temporary variables by CIL. Instead of the original expression, we then see something like tmp___0. So we replace expr in msg by the original source if this is the case. *)
let assert_expr = if string_match (regexp ".*assert(\\(.+\\));.*") line 0 then matched_group 1 line else expr in
let msg = if expr <> assert_expr then String.nreplace msg expr assert_expr else msg in
M.warn_each ~ctx:ctx.control_context (msg ^ " Expected: " ^ (expected |? "SUCCESS") ^ " -> " ^ result)
Expand Down Expand Up @@ -2138,19 +2073,20 @@ struct
else AD.from_var (heap_var ctx)
in
(* ignore @@ printf "malloc will allocate %a bytes\n" ID.pretty (eval_int ctx.ask gs st size); *)
set_many ctx.ask gs st [(heap_var, TVoid [], `Blob (VD.bot (), eval_int ctx.ask gs st size));
set_many ctx.ask gs st [(heap_var, TVoid [], `Blob (VD.bot (), eval_int ctx.ask gs st size, true));
(eval_lv ctx.ask gs st lv, (Cil.typeOfLval lv), `Address heap_var)]
| _ -> st
end
| `Calloc size ->
| `Calloc (n, size) ->
begin match lv with
| Some lv -> (* array length is set to one, as num*size is done when turning into `Calloc *)
let heap_var = heap_var ctx in
let add_null addr =
if get_bool "exp.malloc.fail"
then AD.join addr AD.null_ptr (* calloc can fail and return NULL *)
else addr in
set_many ctx.ask gs st [(add_null (AD.from_var heap_var), TVoid [], `Array (CArrays.make (IdxDom.of_int Int64.one) (`Blob (VD.bot (), eval_int ctx.ask gs st size)))); (* TODO why? should be zero-initialized *)
(* the memory that was allocated by calloc is set to bottom, but we keep track that it originated from calloc, so when bottom is read from memory allocated by calloc it is turned to zero *)
set_many ctx.ask gs st [(add_null (AD.from_var heap_var), TVoid [], `Array (CArrays.make (IdxDom.of_int Int64.one) (`Blob (VD.bot (), eval_int ctx.ask gs st size, false))));
(eval_lv ctx.ask gs st lv, (Cil.typeOfLval lv), `Address (add_null (AD.from_var_offset (heap_var, `Index (IdxDom.of_int 0L, `NoOffset)))))]
| _ -> st
end
Expand Down Expand Up @@ -2261,7 +2197,7 @@ struct
| TPtr (t, attr), `Address a
when (not (AD.is_top a))
&& List.length (AD.to_var_may a) = 1
&& not (is_immediate_type t)
&& not (VD.is_immediate_type t)
->
let cv = List.hd (AD.to_var_may a) in
"ref " ^ VD.short 26 (CPA.find cv es)
Expand Down
6 changes: 3 additions & 3 deletions src/analyses/libraryFunctions.ml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ module M = Messages

type categories = [
| `Malloc of exp
| `Calloc of exp
| `Calloc of exp * exp
| `Realloc of exp * exp
| `Assert of exp
| `Lock of bool * bool * bool (* try? * write? * return on success *)
Expand Down Expand Up @@ -37,12 +37,12 @@ let classify' fn exps =
end
| "kzalloc" ->
begin match exps with
| size::_ -> `Calloc size
| size::_ -> `Calloc (Cil.one, size)
| _ -> M.bailwith (fn^" arguments are strange!")
end
| "calloc" ->
begin match exps with
| n::size::_ -> `Calloc Cil.(BinOp (Mult, n, size, typeOf one))
| n::size::_ -> `Calloc (n, size)
| _ -> M.bailwith (fn^" arguments are strange!")
end
| "realloc" ->
Expand Down
2 changes: 1 addition & 1 deletion src/analyses/libraryFunctions.mli
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ open Prelude.Ana

type categories = [
| `Malloc of exp
| `Calloc of exp
| `Calloc of exp * exp
| `Realloc of exp * exp
| `Assert of exp
| `Lock of bool * bool * bool (* try? * write? *)
Expand Down
2 changes: 1 addition & 1 deletion src/analyses/osek.ml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ let is_atomic lval =

let is_ignorable lval =
(* ignore (printf "Var %a\n" d_lval lval);*)
try Base.is_immediate_type (Cilfacade.typeOfLval lval) || is_atomic lval
try ValueDomain.Compound.is_immediate_type (Cilfacade.typeOfLval lval) || is_atomic lval
with Not_found -> false

module Spec =
Expand Down
4 changes: 2 additions & 2 deletions src/cdomains/arrayDomain.ml
Original file line number Diff line number Diff line change
Expand Up @@ -429,9 +429,9 @@ struct

let make i v =
if Idx.to_int i = Some Int64.one then
(`Lifted (Cil.integer 0), (Val.bot (), v, Val.bot ()))
(`Lifted (Cil.integer 0), (v, v, v))
else if Val.is_bot v then
(Expp.top(), (Val.top(), Val.top(), Val.top()))
(Expp.top(), (Val.bot(), Val.bot(), Val.bot()))
else
(Expp.top(), (v, v, v))

Expand Down
22 changes: 22 additions & 0 deletions src/cdomains/basetype.ml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,28 @@ module Strings: Lattice.S with type t = [`Bot | `Lifted of string | `Top] =
let bot_name = "-"
end)

module RawBools: Printable.S with type t = bool =
struct
include Printable.Std
open Pretty
type t = bool [@@deriving to_yojson]
let hash (x:t) = Hashtbl.hash x
let equal (x:t) (y:t) = x=y
let isSimple _ = true
let short _ (x:t) = if x then "\" true \"" else "\" false \""
let pretty_f sf () x = text (if x then "true" else "false")
let pretty () x = text (short () x)
let name () = "raw bools"
let pretty_diff () (x,y) = dprintf "%s: %a not leq %a" (name ()) pretty x pretty y
let printXml f x = BatPrintf.fprintf f "<value>\n<data>\n%s\n</data>\n</value>\n" (short () x)
end

module Bools: Lattice.S with type t = [`Bot | `Lifted of bool | `Top] =
Lattice.Flat (RawBools) (struct
let top_name = "?"
let bot_name = "-"
end)

module CilExp =
struct
include Printable.Std
Expand Down
Loading