[inferbo] Add an allocsite type for literal strings

Reviewed By: mbouaziz

Differential Revision: D13689870

fbshipit-source-id: 658884b9c
master
Sungkeun Cho 6 years ago committed by Facebook Github Bot
parent f4b4f68e6f
commit 5aa26dc32e

@ -21,6 +21,7 @@ module Allocsite = struct
; dimension: int
; represents_multiple_values: bool
; path: Symb.SymbolPath.partial option }
| LiteralString of string
[@@deriving compare]
let eq as1 as2 =
@ -35,7 +36,9 @@ module Allocsite = struct
Boolean.False
| Known {path= None}, Known {path= None} ->
Boolean.of_bool ([%compare.equal: t] as1 as2)
| Known _, Symbol _ | Symbol _, Known _ ->
| LiteralString s1, LiteralString s2 ->
Boolean.of_bool (String.equal s1 s2)
| _, _ ->
Boolean.False
@ -50,12 +53,16 @@ module Allocsite = struct
F.fprintf fmt "%s-%d-%d-%d" proc_name node_hash inst_num dimension ;
Option.iter path ~f:(fun path ->
F.fprintf fmt "(%a)" (Symb.SymbolPath.pp_partial_paren ~paren:false) path )
| LiteralString s ->
F.fprintf fmt "%S" s
let pp = pp_paren ~paren:false
let is_pretty = function Symbol _ | Known {path= Some _} -> true | _ -> false
let is_literal_string = function LiteralString s -> Some s | _ -> None
let to_string x = F.asprintf "%a" pp x
let make :
@ -80,12 +87,21 @@ module Allocsite = struct
let unknown = Unknown
let get_path = function Unknown -> None | Symbol path -> Some path | Known {path} -> path
let literal_string s = LiteralString s
let get_path = function
| Unknown | LiteralString _ ->
None
| Symbol path ->
Some path
| Known {path} ->
path
let get_param_path = function
| Symbol path ->
Option.some_if (not (Symb.SymbolPath.represents_callsite_sound_partial path)) path
| Unknown | Known _ ->
| Unknown | Known _ | LiteralString _ ->
None
@ -97,6 +113,8 @@ module Allocsite = struct
| Known {path; represents_multiple_values} ->
represents_multiple_values
|| Option.value_map path ~default:false ~f:Symb.SymbolPath.represents_multiple_values
| LiteralString _ ->
true
end
module Loc = struct
@ -182,6 +200,8 @@ module Loc = struct
let is_field_of ~loc ~field_loc = match field_loc with Field (l, _) -> equal loc l | _ -> false
let is_literal_string = function Allocsite a -> Allocsite.is_literal_string a | _ -> None
let rec get_path = function
| Var (LogicalVar _) ->
None

@ -164,6 +164,20 @@ module Val = struct
; traces }
let of_literal_string : Typ.IntegerWidths.t -> string -> t =
fun integer_type_widths s ->
let allocsite = Allocsite.literal_string s in
let stride = Some (integer_type_widths.char_width / 8) in
let offset = Itv.zero in
let size = Itv.of_int (String.length s + 1) in
of_c_array_alloc allocsite ~stride ~offset ~size ~traces:TraceSet.empty
let deref_of_literal_string s =
let max_char = String.fold s ~init:0 ~f:(fun acc c -> max acc (Char.to_int c)) in
of_itv (Itv.set_lb_zero (Itv.of_int max_char))
let modify_itv : Itv.t -> t -> t = fun i x -> {x with itv= i}
let unknown_bit : t -> t = fun x -> {x with itv= Itv.top; sym= Relation.Sym.top}
@ -470,20 +484,24 @@ module Val = struct
let on_demand : default:t -> OndemandEnv.t -> Loc.t -> t =
fun ~default {tenv; typ_of_param_path; may_last_field; entry_location; integer_type_widths} l ->
match Loc.get_path l with
| None ->
L.d_printfln_escaped "Val.on_demand for %a -> no path" Loc.pp l ;
default
| Some path -> (
match typ_of_param_path path with
match Loc.is_literal_string l with
| Some s ->
deref_of_literal_string s
| None -> (
match Loc.get_path l with
| None ->
L.d_printfln_escaped "Val.on_demand for %a -> no type" Loc.pp l ;
L.d_printfln_escaped "Val.on_demand for %a -> no path" Loc.pp l ;
default
| Some typ ->
L.d_printfln_escaped "Val.on_demand for %a" Loc.pp l ;
let may_last_field = may_last_field path in
let path = OndemandEnv.canonical_path typ_of_param_path path in
of_path tenv ~may_last_field integer_type_widths entry_location typ path )
| Some path -> (
match typ_of_param_path path with
| None ->
L.d_printfln_escaped "Val.on_demand for %a -> no type" Loc.pp l ;
default
| Some typ ->
L.d_printfln_escaped "Val.on_demand for %a" Loc.pp l ;
let may_last_field = may_last_field path in
let path = OndemandEnv.canonical_path typ_of_param_path path in
of_path tenv ~may_last_field integer_type_widths entry_location typ path ) )
module Itv = struct

@ -23,8 +23,6 @@ type check_fun = model_env -> Dom.Mem.t -> PO.ConditionSet.checked_t -> PO.Condi
type model = {exec: exec_fun; check: check_fun}
let no_exec _model_env ~ret:_ mem = mem
let no_check _model_env _mem cond_set = cond_set
let no_model =
@ -179,9 +177,19 @@ let inferbo_set_size e1 e2 =
{exec; check}
let model_by_value value (id, _) mem = Dom.Mem.add_stack (Loc.of_id id) value mem
let variable_initialization (e, typ) =
let exec model_env ~ret:_ mem =
match e with
| Exp.Lvar x when Pvar.is_global x ->
let mem, _ = BoUtils.Exec.decl_local model_env (mem, 1) (Loc.of_pvar x, typ) in
mem
| _ ->
mem
in
{exec; check= no_check}
let nop = {exec= no_exec; check= no_check}
let model_by_value value (id, _) mem = Dom.Mem.add_stack (Loc.of_id id) value mem
let by_value =
let exec ~value _ ~ret mem = model_by_value value ret mem in
@ -524,7 +532,7 @@ module Call = struct
make_dispatcher
[ -"__inferbo_min" <>$ capt_exp $+ capt_exp $!--> inferbo_min
; -"__inferbo_set_size" <>$ capt_exp $+ capt_exp $!--> inferbo_set_size
; -"__variable_initialization" <>--> nop
; -"__variable_initialization" <>$ capt_arg $!--> variable_initialization
; -"__exit" <>--> bottom
; -"exit" <>--> bottom
; -"fgetc" <>--> by_value Dom.Val.Itv.m1_255

@ -12,11 +12,14 @@ open AbsLoc
open! AbstractDomain.Types
open BufferOverrunDomain
let eval_const : Const.t -> Val.t = function
let eval_const : Typ.IntegerWidths.t -> Const.t -> Val.t =
fun integer_type_widths -> function
| Const.Cint intlit ->
Val.of_big_int (IntLit.to_big_int intlit)
| Const.Cfloat f ->
f |> int_of_float |> Val.of_int
| Const.Cstr s ->
Val.of_literal_string integer_type_widths s
| _ ->
Val.Itv.top
@ -155,7 +158,7 @@ let rec eval : Typ.IntegerWidths.t -> Exp.t -> Mem.t -> Val.t =
| Exp.BinOp (bop, e1, e2) ->
eval_binop integer_type_widths bop e1 e2 mem
| Exp.Const c ->
eval_const c
eval_const integer_type_widths c
| Exp.Cast (t, e) ->
let v = eval integer_type_widths e mem in
let v = set_array_stride integer_type_widths t v in

@ -123,6 +123,8 @@ module ItvPure = struct
let pos = (Bound.one, Bound.PInf)
let set_lb_zero (_, ub) = (Bound.zero, ub)
let top = (Bound.MInf, Bound.PInf)
let zero = of_bound Bound.zero
@ -550,6 +552,8 @@ let incr = plus one
let decr x = minus x one
let set_lb_zero = lift1 ItvPure.set_lb_zero
let get_iterator_itv : t -> t = lift1 ItvPure.get_iterator_itv
let is_const : t -> Z.t option = bind1zo ItvPure.is_const

@ -152,6 +152,8 @@ val decr : t -> t
val incr : t -> t
val set_lb_zero : t -> t
val neg : t -> t
val normalize : t -> t

@ -123,3 +123,44 @@ void literal_string_bad() {
}
}
}
void literal_string2_Good() {
int a[1];
char s[] = "hello";
for (int i = 0; i < 5; i++) {
if (s[i] > 'o') {
a[s[i]] = 0;
}
}
}
void literal_string2_Bad_FN() {
int a[1];
char s[] = "hello";
for (int i = 0; i < 5; i++) {
if (s[i] > 'n') {
a[s[i]] = 0;
}
}
}
void literal_string_parameter(char* s) {
int a[112]; // 'o' is 111, 'p' is 112
a[s[0]] = 0;
}
void call_literal_string_parameter1_Good() {
char* s = "hello";
literal_string_parameter(s);
}
void call_literal_string_parameter1_Bad() {
char* s = "hellp";
literal_string_parameter(s);
}
void call_literal_string_parameter2_Good() {
literal_string_parameter("hello");
}
void call_literal_string_parameter2_Bad() { literal_string_parameter("hellp"); }

@ -53,3 +53,5 @@ static void copyfilter_Good_FP(const char* s, const char* z, int b) {
}
}
}
static const char* global_string_array[] = {"a", "b", "c", "d", "e", "f"};

@ -47,6 +47,8 @@ codetoanalyze/c/bufferoverrun/arith.c, unused_integer_underflow_Good_FP, 2, INTE
codetoanalyze/c/bufferoverrun/arith.c, use_int64_max_Bad, 4, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Offset trace>,Assignment,<Length trace>,Array declaration,Array access: Offset: 15 Size: 10]
codetoanalyze/c/bufferoverrun/arith.c, use_uint64_max_Bad, 4, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Offset trace>,Assignment,<Length trace>,Array declaration,Array access: Offset: 15 Size: 10]
codetoanalyze/c/bufferoverrun/array_content.c, array_min_index_from_one_FP, 3, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here]
codetoanalyze/c/bufferoverrun/array_content.c, call_literal_string_parameter1_Bad, 2, BUFFER_OVERRUN_L2, no_bucket, ERROR, [Call,<Offset trace>,Parameter `*s`,<Length trace>,Array declaration,Array access: Offset: [0, 112] Size: 112 by call to `literal_string_parameter` ]
codetoanalyze/c/bufferoverrun/array_content.c, call_literal_string_parameter2_Bad, 0, BUFFER_OVERRUN_L2, no_bucket, ERROR, [Call,<Offset trace>,Parameter `*s`,<Length trace>,Array declaration,Array access: Offset: [0, 112] Size: 112 by call to `literal_string_parameter` ]
codetoanalyze/c/bufferoverrun/array_content.c, check_sorted_arr10_good_FP, 2, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here]
codetoanalyze/c/bufferoverrun/array_content.c, check_sorted_arr_good_FP, 2, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here]
codetoanalyze/c/bufferoverrun/array_content.c, check_sorted_ptr_good_FP, 2, CONDITION_ALWAYS_FALSE, no_bucket, WARNING, [Here]

@ -81,6 +81,7 @@ codetoanalyze/cpp/bufferoverrun/std_array.cpp, new_int3_Bad, 3, INTEGER_OVERFLOW
codetoanalyze/cpp/bufferoverrun/std_array.cpp, normal_array_bo, 2, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 42 Size: 42]
codetoanalyze/cpp/bufferoverrun/std_array.cpp, std_array_bo_Bad, 2, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 42 Size: 42]
codetoanalyze/cpp/bufferoverrun/std_string.cpp, call_length4_1_Bad, 2, BUFFER_OVERRUN_L1, no_bucket, ERROR, [Array declaration,Call,<Length trace>,Array declaration,Array access: Offset: 11 Size: 10 by call to `length4` ]
codetoanalyze/cpp/bufferoverrun/std_string.cpp, call_length4_2_Bad, 0, BUFFER_OVERRUN_L1, no_bucket, ERROR, [Call,<Length trace>,Array declaration,Array access: Offset: 11 Size: 10 by call to `length4` ]
codetoanalyze/cpp/bufferoverrun/std_string.cpp, compare_Bad, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 10]
codetoanalyze/cpp/bufferoverrun/std_string.cpp, compare_Good_FP, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 10]
codetoanalyze/cpp/bufferoverrun/std_string.cpp, empty_Bad, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 10]

@ -161,7 +161,7 @@ void call_length4_1_Bad() {
void call_length4_2_Good() { length4("hello"); }
void call_length4_2_Bad_FN() { length4("hellohello"); }
void call_length4_2_Bad() { length4("hellohello"); }
void size_Good() {
std::string s("hello");

Loading…
Cancel
Save