[inferbo] Limit depth of abstract location

Summary:
This diff limits the depth of abstract location by a constant.

problem: Inferbo generated too many of abstract locations, especially when struct types had many pointer fields and Inferbo was not able to analyze the objects precisely. Since the number of generated abstract locations were exponential to the number of fields, it resulted in OOM in the end.

(reported by zyh1121 in https://github.com/facebook/infer/issues/1246)

Reviewed By: jvillard

Differential Revision: D20818471

fbshipit-source-id: f8af27e5c
master
Sungkeun Cho 5 years ago committed by Facebook GitHub Bot
parent d2276c4975
commit cfe4c62e47

@ -336,6 +336,10 @@ BUFFER OVERRUN OPTIONS
--bo-debug int --bo-debug int
Debug level for buffer-overrun checker (0-4) Debug level for buffer-overrun checker (0-4)
--bo-field-depth-limit int
Limit of field depth of abstract location in buffer-overrun
checker
--bo-service-handler-request --bo-service-handler-request
Activates: [EXPERIMENTAL] Use taint flow of service handler Activates: [EXPERIMENTAL] Use taint flow of service handler
requests in buffer overflow checking. (Conversely: requests in buffer overflow checking. (Conversely:

@ -129,6 +129,10 @@ OPTIONS
--bo-debug int --bo-debug int
Debug level for buffer-overrun checker (0-4) See also infer-analyze(1). Debug level for buffer-overrun checker (0-4) See also infer-analyze(1).
--bo-field-depth-limit int
Limit of field depth of abstract location in buffer-overrun
checker See also infer-analyze(1).
--bo-service-handler-request --bo-service-handler-request
Activates: [EXPERIMENTAL] Use taint flow of service handler Activates: [EXPERIMENTAL] Use taint flow of service handler
requests in buffer overflow checking. (Conversely: requests in buffer overflow checking. (Conversely:
@ -1161,6 +1165,9 @@ INTERNAL OPTIONS
Activates: Mode for analyzing the biabduction models (Conversely: Activates: Mode for analyzing the biabduction models (Conversely:
--no-biabduction-models-mode) --no-biabduction-models-mode)
--bo-field-depth-limit-reset
Cancel the effect of --bo-field-depth-limit.
--bootclasspath-reset --bootclasspath-reset
Cancel the effect of --bootclasspath. Cancel the effect of --bootclasspath.

@ -129,6 +129,10 @@ OPTIONS
--bo-debug int --bo-debug int
Debug level for buffer-overrun checker (0-4) See also infer-analyze(1). Debug level for buffer-overrun checker (0-4) See also infer-analyze(1).
--bo-field-depth-limit int
Limit of field depth of abstract location in buffer-overrun
checker See also infer-analyze(1).
--bo-service-handler-request --bo-service-handler-request
Activates: [EXPERIMENTAL] Use taint flow of service handler Activates: [EXPERIMENTAL] Use taint flow of service handler
requests in buffer overflow checking. (Conversely: requests in buffer overflow checking. (Conversely:

@ -1003,6 +1003,7 @@ and custom_symbols =
and ( biabduction_models_mode and ( biabduction_models_mode
, bo_debug , bo_debug
, bo_field_depth_limit
, bo_service_handler_request , bo_service_handler_request
, deduplicate , deduplicate
, developer_mode , developer_mode
@ -1037,6 +1038,10 @@ and ( biabduction_models_mode
CLOpt.mk_int ~default:0 ~long:"bo-debug" CLOpt.mk_int ~default:0 ~long:"bo-debug"
~in_help:InferCommand.[(Analyze, manual_buffer_overrun)] ~in_help:InferCommand.[(Analyze, manual_buffer_overrun)]
"Debug level for buffer-overrun checker (0-4)" "Debug level for buffer-overrun checker (0-4)"
and bo_field_depth_limit =
CLOpt.mk_int_opt ~long:"bo-field-depth-limit"
~in_help:InferCommand.[(Analyze, manual_buffer_overrun)]
"Limit of field depth of abstract location in buffer-overrun checker"
and bo_service_handler_request = and bo_service_handler_request =
CLOpt.mk_bool ~long:"bo-service-handler-request" CLOpt.mk_bool ~long:"bo-service-handler-request"
~in_help:InferCommand.[(Analyze, manual_buffer_overrun)] ~in_help:InferCommand.[(Analyze, manual_buffer_overrun)]
@ -1163,6 +1168,7 @@ and ( biabduction_models_mode
in in
( biabduction_models_mode ( biabduction_models_mode
, bo_debug , bo_debug
, bo_field_depth_limit
, bo_service_handler_request , bo_service_handler_request
, deduplicate , deduplicate
, developer_mode , developer_mode
@ -2583,6 +2589,8 @@ and bootclasspath = !bootclasspath
and bo_debug = !bo_debug and bo_debug = !bo_debug
and bo_field_depth_limit = !bo_field_depth_limit
and bo_service_handler_request = !bo_service_handler_request and bo_service_handler_request = !bo_service_handler_request
and buck = !buck and buck = !buck

@ -221,6 +221,8 @@ val biabduction_models_mode : bool
val bo_debug : int val bo_debug : int
val bo_field_depth_limit : int option
val bo_service_handler_request : bool val bo_service_handler_request : bool
val bootclasspath : string option val bootclasspath : string option

@ -142,34 +142,37 @@ module Loc = struct
let of_allocsite a = Allocsite a let of_allocsite a = Allocsite a
let append_field ?typ l0 ~fn = let append_star_field l0 ~fn =
let rec aux = function let rec aux = function
| Var _ | Allocsite _ -> | Var _ | Allocsite _ ->
Field {prefix= l0; fn; typ} StarField {prefix= l0; last_field= fn}
| StarField {last_field} as l when Fieldname.equal fn last_field -> | StarField {last_field} as l when Fieldname.equal fn last_field ->
l l
| StarField {prefix} -> | StarField {prefix} ->
StarField {prefix; last_field= fn} StarField {prefix; last_field= fn}
| Field {fn= fn'} when Fieldname.equal fn fn' ->
StarField {prefix= l0; last_field= fn}
| Field {prefix= l} -> | Field {prefix= l} ->
aux l aux l
in in
aux l0 aux l0
let append_star_field l0 ~fn = let append_field ?typ l0 ~fn =
let rec aux = function let rec aux ~depth l =
if Symb.SymbolPath.is_field_depth_beyond_limit depth then append_star_field l0 ~fn
else
match l with
| Var _ | Allocsite _ -> | Var _ | Allocsite _ ->
StarField {prefix= l0; last_field= fn} Field {prefix= l0; fn; typ}
| StarField {last_field} as l when Fieldname.equal fn last_field -> | StarField {last_field} as l when Fieldname.equal fn last_field ->
l l
| StarField {prefix} -> | StarField {prefix} ->
StarField {prefix; last_field= fn} StarField {prefix; last_field= fn}
| Field {fn= fn'} when Fieldname.equal fn fn' ->
StarField {prefix= l0; last_field= fn}
| Field {prefix= l} -> | Field {prefix= l} ->
aux l aux ~depth:(depth + 1) l
in in
aux l0 aux ~depth:0 l0
end : end :
sig sig
type t = private type t = private

@ -27,6 +27,14 @@ module SymbolPath = struct
let compare_field_typ _ _ = 0 let compare_field_typ _ _ = 0
let is_field_depth_beyond_limit =
match Config.bo_field_depth_limit with
| None ->
fun _depth -> false
| Some limit ->
fun depth -> depth > limit
include (* Enforce invariants on Field and StarField *) ( include (* Enforce invariants on Field and StarField *) (
struct struct
type partial = type partial =
@ -58,19 +66,22 @@ module SymbolPath = struct
let field ?typ p0 fn = let field ?typ p0 fn =
let rec aux = function let rec aux ~depth p =
if is_field_depth_beyond_limit depth then star_field p0 fn
else
match p with
| Pvar _ | Callsite _ -> | Pvar _ | Callsite _ ->
Field {fn; prefix= p0; typ} Field {fn; prefix= p0; typ}
| Field {fn= fn'} when Fieldname.equal fn fn' -> | Field {fn= fn'} when Fieldname.equal fn fn' ->
StarField {last_field= fn; prefix= p0} StarField {last_field= fn; prefix= p0}
| Field {prefix= p} | Deref (_, p) -> | Field {prefix= p} | Deref (_, p) ->
aux p aux ~depth:(depth + 1) p
| StarField {last_field} as p when Fieldname.equal fn last_field -> | StarField {last_field} as p when Fieldname.equal fn last_field ->
p p
| StarField {prefix} -> | StarField {prefix} ->
StarField {last_field= fn; prefix} StarField {last_field= fn; prefix}
in in
aux p0 aux ~depth:0 p0
end : end :
sig sig
type partial = private type partial = private

@ -88,6 +88,8 @@ module SymbolPath : sig
val is_cpp_vector_elem : partial -> bool val is_cpp_vector_elem : partial -> bool
val is_global_partial : partial -> bool val is_global_partial : partial -> bool
val is_field_depth_beyond_limit : int -> bool
end end
module Symbol : sig module Symbol : sig

@ -8,7 +8,8 @@
TESTS_DIR = ../../.. TESTS_DIR = ../../..
CLANG_OPTIONS = -c CLANG_OPTIONS = -c
INFER_OPTIONS = -F --project-root $(TESTS_DIR) --bufferoverrun-only --function-pointer-specialization INFER_OPTIONS = -F --project-root $(TESTS_DIR) --bufferoverrun-only \
--function-pointer-specialization --bo-field-depth-limit 6
INFERPRINT_OPTIONS = --issues-tests INFERPRINT_OPTIONS = --issues-tests
SOURCES = $(wildcard *.c) SOURCES = $(wildcard *.c)

@ -89,3 +89,56 @@ int call_get_v2_Bad() {
next->v = 0; next->v = 0;
a[get_v2(l)] = 0; a[get_v2(l)] = 0;
} }
struct t1 {
struct t1* a;
struct t1* b;
struct t1* c;
struct t1* d;
struct t1* e;
struct t1* f;
struct t1* g;
struct t1* h;
struct t1* i;
struct t1* j;
};
int unknown;
// The analysis should be terminated within a reasonable amount of time.
void make_many_locations(struct t1* x) {
while (1) {
switch (unknown) {
case 0:
x = x->a;
break;
case 1:
x = x->b;
break;
case 2:
x = x->c;
break;
case 3:
x = x->d;
break;
case 4:
x = x->e;
break;
case 5:
x = x->f;
break;
case 6:
x = x->g;
break;
case 7:
x = x->h;
break;
case 8:
x = x->i;
break;
default:
x = x->j;
break;
}
}
}

@ -126,6 +126,7 @@ codetoanalyze/c/bufferoverrun/get_field.c, call_get_field_cond_Bad, 6, BUFFER_OV
codetoanalyze/c/bufferoverrun/get_field.c, call_get_v2_Bad, 8, BUFFER_OVERRUN_L5, no_bucket, ERROR, [<Offset trace>,Call,Assignment,<Length trace>,Array declaration,Array access: Offset: [-oo, +oo] Size: 10] codetoanalyze/c/bufferoverrun/get_field.c, call_get_v2_Bad, 8, BUFFER_OVERRUN_L5, no_bucket, ERROR, [<Offset trace>,Call,Assignment,<Length trace>,Array declaration,Array access: Offset: [-oo, +oo] Size: 10]
codetoanalyze/c/bufferoverrun/get_field.c, call_get_v2_Good_FP, 8, BUFFER_OVERRUN_L5, no_bucket, ERROR, [<Offset trace>,Call,Assignment,<Length trace>,Array declaration,Array access: Offset: [-oo, +oo] Size: 10] codetoanalyze/c/bufferoverrun/get_field.c, call_get_v2_Good_FP, 8, BUFFER_OVERRUN_L5, no_bucket, ERROR, [<Offset trace>,Call,Assignment,<Length trace>,Array declaration,Array access: Offset: [-oo, +oo] Size: 10]
codetoanalyze/c/bufferoverrun/get_field.c, call_get_v_Bad, 8, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Offset trace>,Assignment,Call,Parameter `l->next->prev->v`,Assignment,<Length trace>,Array declaration,Array access: Offset: 10 Size: 10] codetoanalyze/c/bufferoverrun/get_field.c, call_get_v_Bad, 8, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Offset trace>,Assignment,Call,Parameter `l->next->prev->v`,Assignment,<Length trace>,Array declaration,Array access: Offset: 10 Size: 10]
codetoanalyze/c/bufferoverrun/get_field.c, make_many_locations, 1, CONDITION_ALWAYS_TRUE, no_bucket, WARNING, [Here]
codetoanalyze/c/bufferoverrun/global.c, compare_global_const_enum_Bad, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 10] codetoanalyze/c/bufferoverrun/global.c, compare_global_const_enum_Bad, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 10]
codetoanalyze/c/bufferoverrun/global.c, compare_global_const_enum_Good_FP, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 10] codetoanalyze/c/bufferoverrun/global.c, compare_global_const_enum_Good_FP, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 10]
codetoanalyze/c/bufferoverrun/global.c, compare_global_variable_bad, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 10] codetoanalyze/c/bufferoverrun/global.c, compare_global_variable_bad, 3, BUFFER_OVERRUN_L1, no_bucket, ERROR, [<Length trace>,Array declaration,Array access: Offset: 10 Size: 10]

Loading…
Cancel
Save