[inferbo] Conditions should not have bottom intervals

Summary: A bottom interval in a safety condition doesn't make sense. Let's not allow it at all.

Reviewed By: skcho

Differential Revision: D5941552

fbshipit-source-id: 6bd2a65
master
Mehdi Bouaziz 7 years ago committed by Facebook Github Bot
parent 39ff045569
commit 26f847f381

@ -437,7 +437,8 @@ module Report = struct
in in
match array_access with match array_access with
| Some (arr, traces_arr, idx, traces_idx, is_plus) | Some (arr, traces_arr, idx, traces_idx, is_plus)
-> let site = Sem.get_allocsite pname node 0 0 in -> (
let site = Sem.get_allocsite pname node 0 0 in
let size = ArrayBlk.sizeof arr in let size = ArrayBlk.sizeof arr in
let offset = ArrayBlk.offsetof arr in let offset = ArrayBlk.offsetof arr in
let idx = (if is_plus then Itv.plus else Itv.minus) offset idx in let idx = (if is_plus then Itv.plus else Itv.minus) offset idx in
@ -445,10 +446,12 @@ module Report = struct
L.(debug BufferOverrun Verbose) "array: %a@," ArrayBlk.pp arr ; L.(debug BufferOverrun Verbose) "array: %a@," ArrayBlk.pp arr ;
L.(debug BufferOverrun Verbose) " idx: %a@," Itv.pp idx ; L.(debug BufferOverrun Verbose) " idx: %a@," Itv.pp idx ;
L.(debug BufferOverrun Verbose) "@]@." ; L.(debug BufferOverrun Verbose) "@]@." ;
if size <> Itv.bot && idx <> Itv.bot then match (size, idx) with
let traces = TraceSet.merge ~traces_arr ~traces_idx loc in | NonBottom size, NonBottom idx
-> let traces = TraceSet.merge ~traces_arr ~traces_idx loc in
Dom.ConditionSet.add_bo_safety pname loc site ~size ~idx traces cond_set Dom.ConditionSet.add_bo_safety pname loc site ~size ~idx traces cond_set
else cond_set | _
-> cond_set )
| None | None
-> cond_set -> cond_set

@ -15,6 +15,7 @@ open AbsLoc
open! AbstractDomain.Types open! AbstractDomain.Types
module F = Format module F = Format
module L = Logging module L = Logging
module ItvPure = Itv.ItvPure
module MF = MarkupFormatter module MF = MarkupFormatter
module Trace = BufferOverrunTrace module Trace = BufferOverrunTrace
module TraceSet = Trace.Set module TraceSet = Trace.Set
@ -34,8 +35,8 @@ module Condition = struct
; loc: Location.t ; loc: Location.t
; id: string ; id: string
; cond_trace: cond_trace ; cond_trace: cond_trace
; idx: Itv.astate ; idx: ItvPure.astate
; size: Itv.astate ; size: ItvPure.astate
; traces: TraceSet.t } ; traces: TraceSet.t }
[@@deriving compare] [@@deriving compare]
@ -43,9 +44,8 @@ module Condition = struct
let set_size_pos : t -> t = let set_size_pos : t -> t =
fun c -> fun c ->
if Itv.Bound.lt (Itv.lb c.size) Itv.Bound.zero then let size' = ItvPure.make_positive c.size in
{c with size= Itv.make Itv.Bound.zero (Itv.ub c.size)} if phys_equal size' c.size then c else {c with size= size'}
else c
let pp_location : F.formatter -> t -> unit = fun fmt c -> Location.pp_file_pos fmt c.loc let pp_location : F.formatter -> t -> unit = fun fmt c -> Location.pp_file_pos fmt c.loc
@ -53,16 +53,16 @@ module Condition = struct
fun fmt c -> fun fmt c ->
let c = set_size_pos c in let c = set_size_pos c in
if Config.bo_debug <= 1 then if Config.bo_debug <= 1 then
F.fprintf fmt "%a < %a at %a" Itv.pp c.idx Itv.pp c.size pp_location c F.fprintf fmt "%a < %a at %a" ItvPure.pp c.idx ItvPure.pp c.size pp_location c
else else
match c.cond_trace with match c.cond_trace with
| Inter (_, pname, loc) | Inter (_, pname, loc)
-> let pname = Typ.Procname.to_string pname in -> let pname = Typ.Procname.to_string pname in
F.fprintf fmt "%a < %a at %a by call %s() at %a (%a)" Itv.pp c.idx Itv.pp c.size F.fprintf fmt "%a < %a at %a by call %s() at %a (%a)" ItvPure.pp c.idx ItvPure.pp
pp_location c pname Location.pp_file_pos loc TraceSet.pp c.traces c.size pp_location c pname Location.pp_file_pos loc TraceSet.pp c.traces
| Intra _ | Intra _
-> F.fprintf fmt "%a < %a at %a (%a)" Itv.pp c.idx Itv.pp c.size pp_location c TraceSet.pp -> F.fprintf fmt "%a < %a at %a (%a)" ItvPure.pp c.idx ItvPure.pp c.size pp_location c
c.traces TraceSet.pp c.traces
let get_location : t -> Location.t = fun c -> c.loc let get_location : t -> Location.t = fun c -> c.loc
@ -70,37 +70,40 @@ module Condition = struct
let get_proc_name : t -> Typ.Procname.t = fun c -> c.proc_name let get_proc_name : t -> Typ.Procname.t = fun c -> c.proc_name
let make : Typ.Procname.t -> Location.t -> string -> idx:Itv.t -> size:Itv.t -> TraceSet.t -> t = let make
: Typ.Procname.t -> Location.t -> string -> idx:ItvPure.t -> size:ItvPure.t -> TraceSet.t
-> t =
fun proc_name loc id ~idx ~size traces -> fun proc_name loc id ~idx ~size traces ->
{proc_name; idx; size; loc; id; cond_trace= Intra proc_name; traces} {proc_name; idx; size; loc; id; cond_trace= Intra proc_name; traces}
let filter1 : t -> bool = let filter1 : t -> bool =
fun c -> fun c ->
Itv.eq c.idx Itv.top || Itv.eq c.size Itv.top || Itv.Bound.eq (Itv.lb c.idx) Itv.Bound.MInf ItvPure.is_top c.idx || ItvPure.is_top c.size
|| Itv.Bound.eq (Itv.lb c.size) Itv.Bound.MInf || Itv.Bound.eq (ItvPure.lb c.idx) Itv.Bound.MInf
|| Itv.eq c.idx Itv.nat && Itv.eq c.size Itv.nat || Itv.Bound.eq (ItvPure.lb c.size) Itv.Bound.MInf
|| ItvPure.is_nat c.idx && ItvPure.is_nat c.size
let filter2 : t -> bool = let filter2 : t -> bool =
fun c -> fun c ->
(* basically, alarms involving infinity are filtered *) (* basically, alarms involving infinity are filtered *)
(not (Itv.is_finite c.idx) || not (Itv.is_finite c.size)) (not (ItvPure.is_finite c.idx) || not (ItvPure.is_finite c.size))
&& (* except the following cases *) && (* except the following cases *)
not not
( Itv.Bound.is_not_infty (Itv.lb c.idx) ( Itv.Bound.is_not_infty (ItvPure.lb c.idx)
&& (* idx non-infty lb < 0 *) && (* idx non-infty lb < 0 *)
Itv.Bound.lt (Itv.lb c.idx) Itv.Bound.zero Itv.Bound.lt (ItvPure.lb c.idx) Itv.Bound.zero
|| Itv.Bound.is_not_infty (Itv.lb c.idx) || Itv.Bound.is_not_infty (ItvPure.lb c.idx)
&& (* idx non-infty lb > size lb *) && (* idx non-infty lb > size lb *)
Itv.Bound.gt (Itv.lb c.idx) (Itv.lb c.size) Itv.Bound.gt (ItvPure.lb c.idx) (ItvPure.lb c.size)
|| Itv.Bound.is_not_infty (Itv.lb c.idx) || Itv.Bound.is_not_infty (ItvPure.lb c.idx)
&& (* idx non-infty lb > size ub *) && (* idx non-infty lb > size ub *)
Itv.Bound.gt (Itv.lb c.idx) (Itv.ub c.size) Itv.Bound.gt (ItvPure.lb c.idx) (ItvPure.ub c.size)
|| Itv.Bound.is_not_infty (Itv.ub c.idx) || Itv.Bound.is_not_infty (ItvPure.ub c.idx)
&& (* idx non-infty ub > size lb *) && (* idx non-infty ub > size lb *)
Itv.Bound.gt (Itv.ub c.idx) (Itv.lb c.size) Itv.Bound.gt (ItvPure.ub c.idx) (ItvPure.lb c.size)
|| Itv.Bound.is_not_infty (Itv.ub c.idx) || Itv.Bound.is_not_infty (ItvPure.ub c.idx)
&& (* idx non-infty ub > size ub *) && (* idx non-infty ub > size ub *)
Itv.Bound.gt (Itv.ub c.idx) (Itv.ub c.size) ) Itv.Bound.gt (ItvPure.ub c.idx) (ItvPure.ub c.size) )
(* check buffer overrun and return its confidence *) (* check buffer overrun and return its confidence *)
let check : t -> string option = let check : t -> string option =
@ -108,23 +111,26 @@ module Condition = struct
(* idx = [il, iu], size = [sl, su], we want to check that 0 <= idx < size *) (* idx = [il, iu], size = [sl, su], we want to check that 0 <= idx < size *)
let c' = set_size_pos c in let c' = set_size_pos c in
(* if sl < 0, use sl' = 0 *) (* if sl < 0, use sl' = 0 *)
let not_overrun = Itv.lt_sem c'.idx c'.size in let not_overrun = ItvPure.lt_sem c'.idx c'.size in
let not_underrun = Itv.le_sem Itv.zero c'.idx in let not_underrun = ItvPure.le_sem ItvPure.zero c'.idx in
(* il >= 0 and iu < sl, definitely not an error *) (* il >= 0 and iu < sl, definitely not an error *)
if Itv.eq not_overrun Itv.one && Itv.eq not_underrun Itv.one then None if ItvPure.is_one not_overrun && ItvPure.is_one not_underrun then None
(* iu < 0 or il >= su, definitely an error *) (* iu < 0 or il >= su, definitely an error *)
else if Itv.eq not_overrun Itv.zero || Itv.eq not_underrun Itv.zero then else if ItvPure.is_zero not_overrun || ItvPure.is_zero not_underrun then
Some Localise.BucketLevel.b1 (* su <= iu < +oo, most probably an error *) Some Localise.BucketLevel.b1 (* su <= iu < +oo, most probably an error *)
else if Itv.Bound.is_not_infty (Itv.ub c.idx) && Itv.Bound.le (Itv.ub c.size) (Itv.ub c.idx) else if Itv.Bound.is_not_infty (ItvPure.ub c.idx)
&& Itv.Bound.le (ItvPure.ub c.size) (ItvPure.ub c.idx)
then Some Localise.BucketLevel.b2 (* symbolic il >= sl, probably an error *) then Some Localise.BucketLevel.b2 (* symbolic il >= sl, probably an error *)
else if Itv.Bound.is_symbolic (Itv.lb c.idx) && Itv.Bound.le (Itv.lb c'.size) (Itv.lb c.idx) else if Itv.Bound.is_symbolic (ItvPure.lb c.idx)
&& Itv.Bound.le (ItvPure.lb c'.size) (ItvPure.lb c.idx)
then Some Localise.BucketLevel.b3 (* other symbolic bounds are probably too noisy *) then Some Localise.BucketLevel.b3 (* other symbolic bounds are probably too noisy *)
else if Config.bo_debug <= 3 && (Itv.is_symbolic c.idx || Itv.is_symbolic c.size) then None else if Config.bo_debug <= 3 && (ItvPure.is_symbolic c.idx || ItvPure.is_symbolic c.size)
then None
else if filter1 c then Some Localise.BucketLevel.b5 else if filter1 c then Some Localise.BucketLevel.b5
else if filter2 c then Some Localise.BucketLevel.b3 else if filter2 c then Some Localise.BucketLevel.b3
else Some Localise.BucketLevel.b2 else Some Localise.BucketLevel.b2
let invalid : t -> bool = fun x -> Itv.invalid x.idx || Itv.invalid x.size let invalid : t -> bool = fun x -> ItvPure.invalid x.idx || ItvPure.invalid x.size
let pp_trace : F.formatter -> t -> unit = let pp_trace : F.formatter -> t -> unit =
fun fmt c -> fun fmt c ->
@ -139,16 +145,22 @@ module Condition = struct
let pp_description : F.formatter -> t -> unit = let pp_description : F.formatter -> t -> unit =
fun fmt c -> fun fmt c ->
let c = set_size_pos c in let c = set_size_pos c in
F.fprintf fmt "Offset: %a Size: %a%a" Itv.pp c.idx Itv.pp c.size pp_trace c F.fprintf fmt "Offset: %a Size: %a%a" ItvPure.pp c.idx ItvPure.pp c.size pp_trace c
let description : t -> string = fun c -> Format.asprintf "%a" pp_description c let description : t -> string = fun c -> Format.asprintf "%a" pp_description c
let subst let subst
: t -> Itv.Bound.t Itv.SubstMap.t * TraceSet.t Itv.SubstMap.t -> Typ.Procname.t : t -> Itv.Bound.t Itv.SubstMap.t * TraceSet.t Itv.SubstMap.t -> Typ.Procname.t
-> Typ.Procname.t -> Location.t -> t = -> Typ.Procname.t -> Location.t -> t option =
fun c (bound_map, trace_map) caller_pname callee_pname loc -> fun c (bound_map, trace_map) caller_pname callee_pname loc ->
if Itv.is_symbolic c.idx || Itv.is_symbolic c.size then match ItvPure.get_symbols c.idx @ ItvPure.get_symbols c.size with
let symbols = Itv.get_symbols c.idx @ Itv.get_symbols c.size in | []
-> Some c
| symbols
-> let idx = ItvPure.subst c.idx bound_map in
let size = ItvPure.subst c.size bound_map in
if ItvPure.has_bnd_bot idx || ItvPure.has_bnd_bot size then None
else
let traces_caller = let traces_caller =
List.fold symbols ~init:TraceSet.empty ~f:(fun traces symbol -> List.fold symbols ~init:TraceSet.empty ~f:(fun traces symbol ->
match Itv.SubstMap.find symbol trace_map with match Itv.SubstMap.find symbol trace_map with
@ -158,12 +170,8 @@ module Condition = struct
-> traces ) -> traces )
in in
let traces = TraceSet.instantiate ~traces_caller ~traces_callee:c.traces loc in let traces = TraceSet.instantiate ~traces_caller ~traces_callee:c.traces loc in
{ c with let cond_trace = Inter (caller_pname, callee_pname, loc) in
idx= Itv.subst c.idx bound_map Some {c with idx; size; cond_trace; traces}
; size= Itv.subst c.size bound_map
; cond_trace= Inter (caller_pname, callee_pname, loc)
; traces }
else c
end end
module ConditionSet = struct module ConditionSet = struct
@ -171,7 +179,8 @@ module ConditionSet = struct
module Map = Caml.Map.Make (Location) module Map = Caml.Map.Make (Location)
let add_bo_safety let add_bo_safety
: Typ.Procname.t -> Location.t -> string -> idx:Itv.t -> size:Itv.t -> TraceSet.t -> t -> t = : Typ.Procname.t -> Location.t -> string -> idx:ItvPure.t -> size:ItvPure.t -> TraceSet.t
-> t -> t =
fun pname loc id ~idx ~size traces cond -> fun pname loc id ~idx ~size traces cond ->
add (Condition.make pname loc id ~idx ~size traces) cond add (Condition.make pname loc id ~idx ~size traces) cond
@ -179,7 +188,14 @@ module ConditionSet = struct
: t -> Itv.Bound.t Itv.SubstMap.t * TraceSet.t Itv.SubstMap.t -> Typ.Procname.t : t -> Itv.Bound.t Itv.SubstMap.t * TraceSet.t Itv.SubstMap.t -> Typ.Procname.t
-> Typ.Procname.t -> Location.t -> t = -> Typ.Procname.t -> Location.t -> t =
fun x subst_map caller_pname callee_pname loc -> fun x subst_map caller_pname callee_pname loc ->
fold (fun e -> add (Condition.subst e subst_map caller_pname callee_pname loc)) x empty fold
(fun e x ->
match Condition.subst e subst_map caller_pname callee_pname loc with
| Some c
-> add c x
| None
-> x)
x empty
let group : t -> t Map.t = let group : t -> t Map.t =
fun x -> fun x ->

@ -528,10 +528,14 @@ module Bound = struct
let mone : t = Linear (-1, SymLinear.zero) let mone : t = Linear (-1, SymLinear.zero)
let is_zero : t -> bool = let is_some_const : int -> t -> bool =
fun x -> fun c x ->
assert (x <> Bot) ; assert (x <> Bot) ;
match x with Linear (c, y) -> Int.equal c 0 && SymLinear.is_zero y | _ -> false match x with Linear (c', y) -> Int.equal c c' && SymLinear.is_zero y | _ -> false
let is_zero : t -> bool = is_some_const 0
let is_one : t -> bool = is_some_const 1
let is_const : t -> int option = let is_const : t -> int option =
fun x -> fun x ->
@ -712,10 +716,9 @@ module ItvPure = struct
let unknown_bool = join false_sem true_sem let unknown_bool = join false_sem true_sem
let is_true : t -> bool = let is_top : t -> bool = function Bound.MInf, Bound.PInf -> true | _ -> false
fun (l, u) -> Bound.le (Bound.of_int 1) l || Bound.le u (Bound.of_int (-1))
let is_false : t -> bool = fun (l, u) -> Bound.is_zero l && Bound.is_zero u let is_nat : t -> bool = function l, Bound.PInf -> Bound.is_zero l | _ -> false
let is_const : t -> int option = let is_const : t -> int option =
fun (l, u) -> fun (l, u) ->
@ -725,6 +728,15 @@ module ItvPure = struct
| _, _ | _, _
-> None -> None
let is_one : t -> bool = fun (l, u) -> Bound.is_one l && Bound.is_one u
let is_zero : t -> bool = fun (l, u) -> Bound.is_zero l && Bound.is_zero u
let is_true : t -> bool =
fun (l, u) -> Bound.le (Bound.of_int 1) l || Bound.le u (Bound.of_int (-1))
let is_false : t -> bool = is_zero
let is_symbolic : t -> bool = fun (lb, ub) -> Bound.is_symbolic lb || Bound.is_symbolic ub let is_symbolic : t -> bool = fun (lb, ub) -> Bound.is_symbolic lb || Bound.is_symbolic ub
let is_ge_zero : t -> bool = let is_ge_zero : t -> bool =
@ -955,6 +967,9 @@ module ItvPure = struct
let get_symbols : t -> Symbol.t list = let get_symbols : t -> Symbol.t list =
fun (l, u) -> List.append (Bound.get_symbols l) (Bound.get_symbols u) fun (l, u) -> List.append (Bound.get_symbols l) (Bound.get_symbols u)
let make_positive : t -> t =
fun (l, u as x) -> if Bound.lt l Bound.zero then (Bound.zero, u) else x
let normalize : t -> t option = fun (l, u) -> if invalid (l, u) then None else Some (l, u) let normalize : t -> t option = fun (l, u) -> if invalid (l, u) then None else Some (l, u)
let has_bnd_bot : t -> bool = fun (l, u) -> Bound.equal l Bound.Bot || Bound.equal u Bound.Bot let has_bnd_bot : t -> bool = fun (l, u) -> Bound.equal l Bound.Bot || Bound.equal u Bound.Bot

Loading…
Cancel
Save