[prover] do destructive normalization to prove more

Summary:
In some cases we normalize expressions to check some facts about them. In these
cases, trying to keep as much information as possible in the expression, such
as the fact it comes from a `sizeof()` expression, is not needed. Doing
destructive normalization allows us to replace `sizeof()` by its
statically-known value.

closes #706

Reviewed By: mbouaziz

Differential Revision: D5536685

fbshipit-source-id: cc3d731
master
Jules Villard 7 years ago committed by Facebook Github Bot
parent 91d518979b
commit b2ee1152fe

@ -685,7 +685,7 @@ module Normalize = struct
let ( ++ ) = IntLit.add let ( ++ ) = IntLit.add
let sym_eval tenv abs e = let sym_eval ?(destructive= false) tenv abs e =
let lookup = Tenv.lookup tenv in let lookup = Tenv.lookup tenv in
let rec eval (e: Exp.t) : Exp.t = let rec eval (e: Exp.t) : Exp.t =
(* L.d_str " ["; Sil.d_exp e; L.d_str"] "; *) (* L.d_str " ["; Sil.d_exp e; L.d_str"] "; *)
@ -699,6 +699,8 @@ module Normalize = struct
Closure {c with captured_vars} Closure {c with captured_vars}
| Const _ | Const _
-> e -> e
| Sizeof {nbytes= Some n} when destructive
-> Exp.Const (Const.Cint (IntLit.of_int n))
| Sizeof {typ= {desc= Tarray ({desc= Tint ik}, _, _)}; dynamic_length= Some l} | Sizeof {typ= {desc= Tarray ({desc= Tint ik}, _, _)}; dynamic_length= Some l}
when Typ.ikind_is_char ik && Config.curr_language_is Config.Clang when Typ.ikind_is_char ik && Config.curr_language_is Config.Clang
-> eval l -> eval l
@ -1084,9 +1086,10 @@ module Normalize = struct
(* L.d_str "sym_eval "; Sil.d_exp e; L.d_str" --> "; Sil.d_exp e'; L.d_ln (); *) (* L.d_str "sym_eval "; Sil.d_exp e; L.d_str" --> "; Sil.d_exp e'; L.d_ln (); *)
e' e'
let exp_normalize tenv sub exp = let exp_normalize ?destructive tenv sub exp =
let exp' = Sil.exp_sub sub exp in let exp' = Sil.exp_sub sub exp in
if !Config.abs_val >= 1 then sym_eval tenv true exp' else sym_eval tenv false exp' let abstract_expressions = !Config.abs_val >= 1 in
sym_eval ?destructive tenv abstract_expressions exp'
let texp_normalize tenv sub (exp: Exp.t) : Exp.t = let texp_normalize tenv sub (exp: Exp.t) : Exp.t =
match exp with match exp with
@ -1675,8 +1678,10 @@ end
(* End of module Normalize *) (* End of module Normalize *)
let exp_normalize_prop tenv prop exp = let exp_normalize_prop ?destructive tenv prop exp =
Config.run_with_abs_val_equal_zero (Normalize.exp_normalize tenv (`Exp prop.sub)) exp Config.run_with_abs_val_equal_zero
(Normalize.exp_normalize ?destructive tenv (`Exp prop.sub))
exp
let lexp_normalize_prop tenv p lexp = let lexp_normalize_prop tenv p lexp =
let root = Exp.root_of_lexp lexp in let root = Exp.root_of_lexp lexp in

@ -158,10 +158,12 @@ val atom_exp_le_const : Sil.atom -> (Exp.t * IntLit.t) option
val atom_const_lt_exp : Sil.atom -> (IntLit.t * Exp.t) option val atom_const_lt_exp : Sil.atom -> (IntLit.t * Exp.t) option
(** If the atom is [n<e] return [n,e] *) (** If the atom is [n<e] return [n,e] *)
val exp_normalize_prop : Tenv.t -> 'a t -> Exp.t -> Exp.t val exp_normalize_prop : ?destructive:bool -> Tenv.t -> 'a t -> Exp.t -> Exp.t
(** Normalize [exp] using the pure part of [prop]. Later, we should (** Normalize [exp] using the pure part of [prop]. Later, we should change this such that the
change this such that the normalization exposes offsets of [exp] normalization exposes offsets of [exp] as much as possible.
as much as possible. *)
If [destructive] is true then normalize more aggressively, which may lose some useful structure
or types. *)
val exp_normalize_noabs : Tenv.t -> Sil.subst -> Exp.t -> Exp.t val exp_normalize_noabs : Tenv.t -> Sil.subst -> Exp.t -> Exp.t
(** Normalize the expression without abstracting complex subexpressions *) (** Normalize the expression without abstracting complex subexpressions *)

@ -473,11 +473,11 @@ end = struct
match (e1, e2) with match (e1, e2) with
| Exp.Const Const.Cint n1, Exp.Const Const.Cint n2 | Exp.Const Const.Cint n1, Exp.Const Const.Cint n2
-> IntLit.leq n1 n2 -> IntLit.leq n1 n2
| ( Exp.BinOp | ( Exp.BinOp (Binop.MinusA, Exp.Sizeof {nbytes= Some nb1}, Exp.Sizeof {nbytes= Some nb2})
( Binop.MinusA
, Exp.Sizeof {typ= t1; dynamic_length= None}
, Exp.Sizeof {typ= t2; dynamic_length= None} )
, Exp.Const Const.Cint n2 ) , Exp.Const Const.Cint n2 )
-> (* [ sizeof(t1) - sizeof(t2) <= n2 ] *)
IntLit.(leq (sub (of_int nb1) (of_int nb2)) n2)
| Exp.BinOp (Binop.MinusA, Exp.Sizeof {typ= t1}, Exp.Sizeof {typ= t2}), Exp.Const Const.Cint n2
when IntLit.isminusone n2 && type_size_comparable t1 t2 when IntLit.isminusone n2 && type_size_comparable t1 t2
-> (* [ sizeof(t1) - sizeof(t2) <= -1 ] *) -> (* [ sizeof(t1) - sizeof(t2) <= -1 ] *)
check_type_size_lt t1 t2 check_type_size_lt t1 t2
@ -550,6 +550,8 @@ end = struct
match e1 with match e1 with
| Exp.Const Const.Cint n1 | Exp.Const Const.Cint n1
-> Some (n1 -- IntLit.one) -> Some (n1 -- IntLit.one)
| Exp.Sizeof {nbytes= Some n1}
-> Some (IntLit.of_int n1 -- IntLit.one)
| Exp.Sizeof _ | Exp.Sizeof _
-> Some IntLit.zero -> Some IntLit.zero
| _ | _
@ -597,9 +599,9 @@ end
(* End of module Inequalities *) (* End of module Inequalities *)
(** Check [prop |- e1=e2]. Result [false] means "don't know". *) (** Check [prop |- e1=e2]. Result [false] means "don't know". *)
let check_equal tenv prop e1 e2 = let check_equal tenv prop e1_0 e2_0 =
let n_e1 = Prop.exp_normalize_prop tenv prop e1 in let n_e1 = Prop.exp_normalize_prop ~destructive:true tenv prop e1_0 in
let n_e2 = Prop.exp_normalize_prop tenv prop e2 in let n_e2 = Prop.exp_normalize_prop ~destructive:true tenv prop e2_0 in
let check_equal () = Exp.equal n_e1 n_e2 in let check_equal () = Exp.equal n_e1 n_e2 in
let check_equal_const () = let check_equal_const () =
match (n_e1, n_e2) with match (n_e1, n_e2) with
@ -651,8 +653,8 @@ let is_root tenv prop base_exp exp =
f [] exp f [] exp
(** Get upper and lower bounds of an expression, if any *) (** Get upper and lower bounds of an expression, if any *)
let get_bounds tenv prop _e = let get_bounds tenv prop e0 =
let e_norm = Prop.exp_normalize_prop tenv prop _e in let e_norm = Prop.exp_normalize_prop ~destructive:true tenv prop e0 in
let e_root, off = let e_root, off =
match e_norm with match e_norm with
| Exp.BinOp (Binop.PlusA, e, Exp.Const Const.Cint n1) | Exp.BinOp (Binop.PlusA, e, Exp.Const Const.Cint n1)
@ -671,8 +673,8 @@ let get_bounds tenv prop _e =
(** Check whether [prop |- e1!=e2]. *) (** Check whether [prop |- e1!=e2]. *)
let check_disequal tenv prop e1 e2 = let check_disequal tenv prop e1 e2 =
let spatial_part = prop.Prop.sigma in let spatial_part = prop.Prop.sigma in
let n_e1 = Prop.exp_normalize_prop tenv prop e1 in let n_e1 = Prop.exp_normalize_prop ~destructive:true tenv prop e1 in
let n_e2 = Prop.exp_normalize_prop tenv prop e2 in let n_e2 = Prop.exp_normalize_prop ~destructive:true tenv prop e2 in
let rec check_expr_disequal ce1 ce2 = let rec check_expr_disequal ce1 ce2 =
match (ce1, ce2) with match (ce1, ce2) with
| Exp.Const c1, Exp.Const c2 | Exp.Const c1, Exp.Const c2
@ -874,7 +876,7 @@ let check_le tenv prop e1 e2 =
(** Check whether [prop |- allocated(e)]. *) (** Check whether [prop |- allocated(e)]. *)
let check_allocatedness tenv prop e = let check_allocatedness tenv prop e =
let n_e = Prop.exp_normalize_prop tenv prop e in let n_e = Prop.exp_normalize_prop ~destructive:true tenv prop e in
let spatial_part = prop.Prop.sigma in let spatial_part = prop.Prop.sigma in
let f = function let f = function
| Sil.Hpointsto (base, _, _) | Sil.Hpointsto (base, _, _)
@ -915,7 +917,7 @@ let check_inconsistency_two_hpreds tenv prop =
let prop' = Prop.normalize tenv (Prop.from_sigma (sigma_seen @ sigma_rest)) in let prop' = Prop.normalize tenv (Prop.from_sigma (sigma_seen @ sigma_rest)) in
let prop_new = Prop.conjoin_eq tenv e1 e2 prop' in let prop_new = Prop.conjoin_eq tenv e1 e2 prop' in
let sigma_new = prop_new.Prop.sigma in let sigma_new = prop_new.Prop.sigma in
let e_new = Prop.exp_normalize_prop tenv prop_new e in let e_new = Prop.exp_normalize_prop ~destructive:true tenv prop_new e in
f e_new [] sigma_new f e_new [] sigma_new
else f e (hpred :: sigma_seen) sigma_rest else f e (hpred :: sigma_seen) sigma_rest
| (Sil.Hdllseg (Sil.Lseg_PE, _, e1, _, Exp.Const Const.Cint i, _, _) as hpred) :: sigma_rest | (Sil.Hdllseg (Sil.Lseg_PE, _, e1, _, Exp.Const Const.Cint i, _, _) as hpred) :: sigma_rest
@ -926,7 +928,7 @@ let check_inconsistency_two_hpreds tenv prop =
let prop' = Prop.normalize tenv (Prop.from_sigma (sigma_seen @ sigma_rest)) in let prop' = Prop.normalize tenv (Prop.from_sigma (sigma_seen @ sigma_rest)) in
let prop_new = Prop.conjoin_eq tenv e1 e3 prop' in let prop_new = Prop.conjoin_eq tenv e1 e3 prop' in
let sigma_new = prop_new.Prop.sigma in let sigma_new = prop_new.Prop.sigma in
let e_new = Prop.exp_normalize_prop tenv prop_new e in let e_new = Prop.exp_normalize_prop ~destructive:true tenv prop_new e in
f e_new [] sigma_new f e_new [] sigma_new
else f e (hpred :: sigma_seen) sigma_rest else f e (hpred :: sigma_seen) sigma_rest
in in
@ -1319,7 +1321,9 @@ let exp_imply tenv calc_missing (subs: subst2) e1_in e2_in : subst2 =
-> let occurs_check v e = -> let occurs_check v e =
(* check whether [v] occurs in normalized [e] *) (* check whether [v] occurs in normalized [e] *)
if Sil.fav_mem (Sil.exp_fav e) v if Sil.fav_mem (Sil.exp_fav e) v
&& Sil.fav_mem (Sil.exp_fav (Prop.exp_normalize_prop tenv Prop.prop_emp e)) v && Sil.fav_mem
(Sil.exp_fav (Prop.exp_normalize_prop ~destructive:true tenv Prop.prop_emp e))
v
then raise (IMPL_EXC ("occurs check", subs, EXC_FALSE_EXPS (e1, e2))) then raise (IMPL_EXC ("occurs check", subs, EXC_FALSE_EXPS (e1, e2)))
in in
if Ident.is_primed v2 then if Ident.is_primed v2 then

@ -331,7 +331,7 @@ let prune_ineq tenv ~is_strict ~positive prop e1 e2 =
Propset.singleton tenv prop_with_ineq Propset.singleton tenv prop_with_ineq
let rec prune tenv ~positive condition prop = let rec prune tenv ~positive condition prop =
match condition with match Prop.exp_normalize_prop ~destructive:true tenv prop condition with
| Exp.Var _ | Exp.Lvar _ | Exp.Var _ | Exp.Lvar _
-> prune_ne tenv ~positive condition Exp.zero prop -> prune_ne tenv ~positive condition Exp.zero prop
| Exp.Const Const.Cint i when IntLit.iszero i | Exp.Const Const.Cint i when IntLit.iszero i

@ -91,6 +91,7 @@ codetoanalyze/c/errors/null_dereference/short.c, l_error, 2, NULL_DEREFERENCE
codetoanalyze/c/errors/null_dereference/short.c, m, 2, NULL_TEST_AFTER_DEREFERENCE codetoanalyze/c/errors/null_dereference/short.c, m, 2, NULL_TEST_AFTER_DEREFERENCE
codetoanalyze/c/errors/resource_leaks/leak.c, fileNotClosed, 5, RESOURCE_LEAK codetoanalyze/c/errors/resource_leaks/leak.c, fileNotClosed, 5, RESOURCE_LEAK
codetoanalyze/c/errors/resource_leaks/leak.c, socketNotClosed, 5, RESOURCE_LEAK codetoanalyze/c/errors/resource_leaks/leak.c, socketNotClosed, 5, RESOURCE_LEAK
codetoanalyze/c/errors/sizeof/sizeof_706.c, sentinel_bad, 0, DIVIDE_BY_ZERO
codetoanalyze/c/frontend/enumeration/other_enum.c, other_enum_test, 4, DIVIDE_BY_ZERO codetoanalyze/c/frontend/enumeration/other_enum.c, other_enum_test, 4, DIVIDE_BY_ZERO
codetoanalyze/c/frontend/offsetof_expr/offsetof_expr.c, test_offsetof_expr, 3, DIVIDE_BY_ZERO codetoanalyze/c/frontend/offsetof_expr/offsetof_expr.c, test_offsetof_expr, 3, DIVIDE_BY_ZERO
codetoanalyze/c/frontend/offsetof_expr/offsetof_expr.c, test_offsetof_expr, 5, DIVIDE_BY_ZERO codetoanalyze/c/frontend/offsetof_expr/offsetof_expr.c, test_offsetof_expr, 5, DIVIDE_BY_ZERO

@ -0,0 +1,29 @@
/*
* Copyright (c) 2017 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
int sizeof_eval_good(void) {
int a = 4;
int b = sizeof(a);
char c[2];
if (a % 4) {
return a / 0;
}
if (b % sizeof(a)) {
return a / 0;
}
if (sizeof(c) > 2) {
return a / 0;
}
if ((sizeof(c) / sizeof(c[0])) != 2) {
return a / 0;
}
return 0;
}
void sentinel_bad(void) { return 1 / 0; }
Loading…
Cancel
Save