You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

214 lines
7.6 KiB

/*
* vim: set ft=rust:
* vim: set ft=reason:
*
* Copyright (c) 2016 - 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.
*/
open! Utils;
let get_name_of_local (curr_f: Cfg.Procdesc.t) (x, _) =>
Pvar.mk x (Cfg.Procdesc.get_proc_name curr_f);
/* returns a list of local static variables (ie local variables defined static) in a proposition */
let get_name_of_objc_static_locals (curr_f: Cfg.Procdesc.t) p => {
let pname = Procname.to_string (Cfg.Procdesc.get_proc_name curr_f);
let local_static e =>
switch e {
/* is a local static if it's a global and it has a static local name */
| Exp.Lvar pvar when Pvar.is_global pvar && Sil.is_static_local_name pname pvar => [pvar]
| _ => []
};
let hpred_local_static hpred =>
switch hpred {
| Sil.Hpointsto e _ _ => [local_static e]
| _ => []
};
let vars_sigma = IList.map hpred_local_static p.Prop.sigma;
IList.flatten (IList.flatten vars_sigma)
};
/* returns a list of local variables that points to an objc block in a proposition */
let get_name_of_objc_block_locals p => {
let local_blocks e =>
switch e {
| Exp.Lvar pvar when Sil.is_block_pvar pvar => [pvar]
| _ => []
};
let hpred_local_blocks hpred =>
switch hpred {
| Sil.Hpointsto e _ _ => [local_blocks e]
| _ => []
};
let vars_sigma = IList.map hpred_local_blocks p.Prop.sigma;
IList.flatten (IList.flatten vars_sigma)
};
let remove_abduced_retvars tenv p =>
/* compute the hpreds and pure atoms reachable from the set of seed expressions in [exps] */
{
let compute_reachable p seed_exps => {
let (sigma, pi) = (p.Prop.sigma, p.Prop.pi);
let rec collect_exps exps =>
fun
| Sil.Eexp (Exp.Exn e) _ => Exp.Set.add e exps
| Sil.Eexp e _ => Exp.Set.add e exps
| Sil.Estruct flds _ =>
IList.fold_left (fun exps (_, strexp) => collect_exps exps strexp) exps flds
| Sil.Earray _ elems _ =>
IList.fold_left (fun exps (_, strexp) => collect_exps exps strexp) exps elems;
let rec compute_reachable_hpreds_rec sigma (reach, exps) => {
let add_hpred_if_reachable (reach, exps) =>
fun
| Sil.Hpointsto lhs rhs _ as hpred when Exp.Set.mem lhs exps => {
let reach' = Sil.HpredSet.add hpred reach;
let exps' = collect_exps exps rhs;
(reach', exps')
}
| Sil.Hlseg _ _ exp1 exp2 exp_l as hpred => {
let reach' = Sil.HpredSet.add hpred reach;
let exps' =
IList.fold_left
(fun exps_acc exp => Exp.Set.add exp exps_acc) exps [exp1, exp2, ...exp_l];
(reach', exps')
}
| Sil.Hdllseg _ _ exp1 exp2 exp3 exp4 exp_l as hpred => {
let reach' = Sil.HpredSet.add hpred reach;
let exps' =
IList.fold_left
(fun exps_acc exp => Exp.Set.add exp exps_acc)
exps
[exp1, exp2, exp3, exp4, ...exp_l];
(reach', exps')
}
| _ => (reach, exps);
let (reach', exps') = IList.fold_left add_hpred_if_reachable (reach, exps) sigma;
if (Sil.HpredSet.cardinal reach == Sil.HpredSet.cardinal reach') {
(reach, exps)
} else {
compute_reachable_hpreds_rec sigma (reach', exps')
}
};
let (reach_hpreds, reach_exps) =
compute_reachable_hpreds_rec sigma (Sil.HpredSet.empty, seed_exps);
/* filter away the pure atoms without reachable exps */
let reach_pi = {
let rec exp_contains =
fun
| exp when Exp.Set.mem exp reach_exps => true
| Exp.UnOp _ e _
| Exp.Cast _ e
| Exp.Lfield e _ _ => exp_contains e
| Exp.BinOp _ e0 e1
| Exp.Lindex e0 e1 => exp_contains e0 || exp_contains e1
| _ => false;
IList.filter
(
fun
| Sil.Aeq lhs rhs
| Sil.Aneq lhs rhs => exp_contains lhs || exp_contains rhs
| Sil.Apred _ es
| Sil.Anpred _ es => IList.exists exp_contains es
)
pi
};
(Sil.HpredSet.elements reach_hpreds, reach_pi)
};
/* separate the abduced pvars from the normal ones, deallocate the abduced ones*/
let (abduceds, normal_pvars) =
IList.fold_left
(
fun pvars hpred =>
switch hpred {
| Sil.Hpointsto (Exp.Lvar pvar) _ _ =>
let (abduceds, normal_pvars) = pvars;
if (Pvar.is_abduced pvar) {
([pvar, ...abduceds], normal_pvars)
} else {
(abduceds, [pvar, ...normal_pvars])
}
| _ => pvars
}
)
([], [])
p.Prop.sigma;
let (_, p') = Attribute.deallocate_stack_vars tenv p abduceds;
let normal_pvar_set =
IList.fold_left
(fun normal_pvar_set pvar => Exp.Set.add (Exp.Lvar pvar) normal_pvar_set)
Exp.Set.empty
normal_pvars;
/* walk forward from non-abduced pvars, keep everything reachable. remove everything else */
let (sigma_reach, pi_reach) = compute_reachable p' normal_pvar_set;
Prop.normalize tenv (Prop.set p' pi::pi_reach sigma::sigma_reach)
};
let remove_locals tenv (curr_f: Cfg.Procdesc.t) p => {
let names_of_locals = IList.map (get_name_of_local curr_f) (Cfg.Procdesc.get_locals curr_f);
let names_of_locals' =
switch !Config.curr_language {
| Config.Clang =>
/* in ObjC to deal with block we need to remove static locals */
let names_of_static_locals = get_name_of_objc_static_locals curr_f p;
let names_of_block_locals = get_name_of_objc_block_locals p;
names_of_block_locals @ names_of_locals @ names_of_static_locals
| _ => names_of_locals
};
let (removed, p') = Attribute.deallocate_stack_vars tenv p names_of_locals';
(
removed,
if Config.angelic_execution {
remove_abduced_retvars tenv p'
} else {
p'
}
)
};
let remove_formals tenv (curr_f: Cfg.Procdesc.t) p => {
let pname = Cfg.Procdesc.get_proc_name curr_f;
let formal_vars = IList.map (fun (n, _) => Pvar.mk n pname) (Cfg.Procdesc.get_formals curr_f);
Attribute.deallocate_stack_vars tenv p formal_vars
};
/** remove the return variable from the prop */
let remove_ret tenv (curr_f: Cfg.Procdesc.t) (p: Prop.t Prop.normal) => {
let pname = Cfg.Procdesc.get_proc_name curr_f;
let name_of_ret = Cfg.Procdesc.get_ret_var curr_f;
let (_, p') = Attribute.deallocate_stack_vars tenv p [Pvar.to_callee pname name_of_ret];
p'
};
/** remove locals and return variable from the prop */
let remove_locals_ret tenv (curr_f: Cfg.Procdesc.t) p => snd (
remove_locals tenv curr_f (remove_ret tenv curr_f p)
);
/** Remove locals and formal parameters from the prop.
Return the list of stack variables whose address was still present after deallocation. */
let remove_locals_formals tenv (curr_f: Cfg.Procdesc.t) p => {
let (pvars1, p1) = remove_formals tenv curr_f p;
let (pvars2, p2) = remove_locals tenv curr_f p1;
(pvars1 @ pvars2, p2)
};
/** remove seed vars from a prop */
let remove_seed_vars tenv (prop: Prop.t 'a) :Prop.t Prop.normal => {
let hpred_not_seed =
fun
| Sil.Hpointsto (Exp.Lvar pv) _ _ => not (Pvar.is_seed pv)
| _ => true;
let sigma = prop.sigma;
let sigma' = IList.filter hpred_not_seed sigma;
Prop.normalize tenv (Prop.set prop sigma::sigma')
};