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.
211 lines
8.1 KiB
211 lines
8.1 KiB
(*
|
|
* Copyright (c) 2018-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*)
|
|
open! IStd
|
|
module F = Format
|
|
module L = Logging
|
|
open Result.Monad_infix
|
|
module AbstractAddress = PulseDomain.AbstractAddress
|
|
|
|
include (* ocaml ignores the warning suppression at toplevel, hence the [include struct ... end] trick *)
|
|
struct
|
|
[@@@warning "-60"]
|
|
|
|
(** Do not use {!PulseDomain} directly as it could result in bypassing abduction mechanisms in
|
|
{!PulseOperations} and {!PulseAbductiveDomain} that take care of propagating facts to the
|
|
precondition. *)
|
|
module PulseDomain = struct end
|
|
[@@deprecated "Use PulseAbductiveDomain or PulseOperations instead."]
|
|
end
|
|
|
|
let report summary diagnostic =
|
|
let open PulseDiagnostic in
|
|
Reporting.log_error summary ~loc:(get_location diagnostic) ~ltr:(get_trace diagnostic)
|
|
(get_issue_type diagnostic) (get_message diagnostic)
|
|
|
|
|
|
let check_error summary = function
|
|
| Ok ok ->
|
|
ok
|
|
| Error diagnostic ->
|
|
report summary diagnostic ;
|
|
(* We can also continue the analysis by returning {!PulseDomain.initial} here but there might
|
|
be a risk we would get nonsense. This seems safer for now but TODO. *)
|
|
raise_notrace AbstractDomain.Stop_analysis
|
|
|
|
|
|
module PulseTransferFunctions = struct
|
|
module CFG = ProcCfg.Exceptional
|
|
module Domain = PulseAbductiveDomain
|
|
|
|
type extras = Summary.t
|
|
|
|
let is_destructor = function
|
|
| Typ.Procname.ObjC_Cpp clang_pname ->
|
|
Typ.Procname.ObjC_Cpp.is_destructor clang_pname
|
|
&& not
|
|
(* Our frontend generates synthetic inner destructors to model invocation of base class
|
|
destructors correctly; see D5834555/D7189239 *)
|
|
(Typ.Procname.ObjC_Cpp.is_inner_destructor clang_pname)
|
|
| _ ->
|
|
false
|
|
|
|
|
|
let rec exec_assign summary (lhs_access : HilExp.AccessExpression.t) (rhs_exp : HilExp.t) loc
|
|
astate =
|
|
(* try to evaluate [rhs_exp] down to an abstract address or generate a fresh one *)
|
|
let crumb = PulseTrace.Assignment {lhs= lhs_access; location= loc} in
|
|
match rhs_exp with
|
|
| AccessExpression rhs_access -> (
|
|
PulseOperations.read loc rhs_access astate
|
|
>>= fun (astate, (rhs_addr, rhs_trace)) ->
|
|
let return_addr_trace = (rhs_addr, crumb :: rhs_trace) in
|
|
PulseOperations.write loc lhs_access return_addr_trace astate
|
|
>>= fun astate ->
|
|
match lhs_access with
|
|
| Base (var, _) when Var.is_return var ->
|
|
PulseOperations.check_address_of_local_variable summary.Summary.proc_desc rhs_addr
|
|
astate
|
|
| _ ->
|
|
Ok astate )
|
|
| Closure (pname, captured) ->
|
|
PulseOperations.Closures.record loc lhs_access pname captured astate
|
|
| Cast (_, e) ->
|
|
exec_assign summary lhs_access e loc astate
|
|
| _ ->
|
|
PulseOperations.read_all loc (HilExp.get_access_exprs rhs_exp) astate
|
|
>>= PulseOperations.havoc [crumb] loc lhs_access
|
|
|
|
|
|
let exec_call summary _ret (call : HilInstr.call) (actuals : HilExp.t list) _flags call_loc
|
|
astate =
|
|
let read_all args astate =
|
|
PulseOperations.read_all call_loc (List.concat_map args ~f:HilExp.get_access_exprs) astate
|
|
in
|
|
let crumb = PulseTrace.Call {f= `HilCall call; actuals; location= call_loc} in
|
|
match call with
|
|
| Direct callee_pname when is_destructor callee_pname -> (
|
|
match actuals with
|
|
| [AccessExpression (Base (destroyed_var, _))] when Var.is_this destroyed_var ->
|
|
(* do not invalidate [this] when it is destroyed by calls to [this->~Obj()] *)
|
|
Ok astate
|
|
| [AccessExpression destroyed_access] ->
|
|
let destroyed_object = HilExp.AccessExpression.dereference destroyed_access in
|
|
PulseOperations.invalidate
|
|
(CppDestructor (callee_pname, destroyed_object, call_loc))
|
|
call_loc destroyed_object astate
|
|
| _ ->
|
|
Ok astate )
|
|
| Direct callee_pname when Typ.Procname.is_constructor callee_pname -> (
|
|
L.d_printfln "constructor call detected@." ;
|
|
match actuals with
|
|
| AccessExpression constructor_access :: rest ->
|
|
let constructed_object = HilExp.AccessExpression.dereference constructor_access in
|
|
PulseOperations.havoc [crumb] call_loc constructed_object astate >>= read_all rest
|
|
| _ ->
|
|
Ok astate )
|
|
| Direct (Typ.Procname.ObjC_Cpp callee_pname)
|
|
when Typ.Procname.ObjC_Cpp.is_operator_equal callee_pname -> (
|
|
L.d_printfln "operator= detected@." ;
|
|
match actuals with
|
|
(* We want to assign *lhs to *rhs when rhs is materialized temporary created in constructor *)
|
|
| [AccessExpression lhs; HilExp.AccessExpression (AddressOf (Base rhs_base as rhs_exp))]
|
|
when Var.is_cpp_temporary (fst rhs_base) ->
|
|
let lhs_deref = HilExp.AccessExpression.dereference lhs in
|
|
exec_assign summary lhs_deref (HilExp.AccessExpression rhs_exp) call_loc astate
|
|
(* copy assignment *)
|
|
| [AccessExpression lhs; HilExp.AccessExpression rhs] ->
|
|
let lhs_deref = HilExp.AccessExpression.dereference lhs in
|
|
let rhs_deref = HilExp.AccessExpression.dereference rhs in
|
|
PulseOperations.havoc [crumb] call_loc lhs_deref astate
|
|
>>= fun astate -> PulseOperations.read call_loc rhs_deref astate >>| fst
|
|
| _ ->
|
|
read_all actuals astate )
|
|
| _ ->
|
|
L.d_printfln "skipping unknown procedure@." ;
|
|
read_all actuals astate
|
|
|
|
|
|
let dispatch_call summary ret (call : HilInstr.call) (actuals : HilExp.t list) flags call_loc
|
|
astate =
|
|
let model =
|
|
match call with
|
|
| Indirect _ ->
|
|
(* function pointer, etc.: skip for now *)
|
|
None
|
|
| Direct callee_pname ->
|
|
PulseModels.dispatch callee_pname flags
|
|
in
|
|
match model with
|
|
| None ->
|
|
exec_call summary ret call actuals flags call_loc astate
|
|
>>| PulseOperations.havoc_var
|
|
[PulseTrace.Call {f= `HilCall call; actuals; location= call_loc}]
|
|
(fst ret)
|
|
| Some model ->
|
|
model call_loc ~ret ~actuals astate
|
|
|
|
|
|
let exec_instr (astate : Domain.t) {ProcData.extras= summary} _cfg_node (instr : HilInstr.t) =
|
|
match instr with
|
|
| Assign (lhs_access, rhs_exp, loc) ->
|
|
let post = exec_assign summary lhs_access rhs_exp loc astate |> check_error summary in
|
|
[post]
|
|
| Assume (condition, _, _, loc) ->
|
|
let post =
|
|
PulseOperations.read_all loc (HilExp.get_access_exprs condition) astate
|
|
|> check_error summary
|
|
in
|
|
[post]
|
|
| Call (ret, call, actuals, flags, loc) ->
|
|
let post =
|
|
dispatch_call summary ret call actuals flags loc astate |> check_error summary
|
|
in
|
|
[post]
|
|
| Metadata (ExitScope (vars, _)) ->
|
|
let post = PulseOperations.remove_vars vars astate in
|
|
[post]
|
|
| Metadata (VariableLifetimeBegins (pvar, _, location)) ->
|
|
let var = Var.of_pvar pvar in
|
|
let post =
|
|
PulseOperations.havoc_var [PulseTrace.VariableDeclaration location] var astate
|
|
|> PulseOperations.record_var_decl_location location var
|
|
in
|
|
[post]
|
|
| Metadata (Abstract _ | Nullify _ | Skip) ->
|
|
[astate]
|
|
|
|
|
|
let pp_session_name _node fmt = F.pp_print_string fmt "Pulse"
|
|
end
|
|
|
|
module HilConfig = LowerHil.DefaultConfig
|
|
|
|
module DisjunctiveTransferFunctions =
|
|
TransferFunctions.MakeHILDisjunctive
|
|
(PulseTransferFunctions)
|
|
(struct
|
|
let join_policy =
|
|
match Config.pulse_max_disjuncts with 0 -> `NeverJoin | n -> `UnderApproximateAfter n
|
|
|
|
|
|
let widen_policy = `UnderApproximateAfterNumIterations Config.pulse_widen_threshold
|
|
end)
|
|
|
|
module DisjunctiveAnalyzer =
|
|
LowerHil.MakeAbstractInterpreterWithConfig (AbstractInterpreter.MakeWTO) (HilConfig)
|
|
(DisjunctiveTransferFunctions)
|
|
|
|
let checker {Callbacks.proc_desc; tenv; summary} =
|
|
let proc_data = ProcData.make proc_desc tenv summary in
|
|
AbstractAddress.init () ;
|
|
( try
|
|
ignore
|
|
(DisjunctiveAnalyzer.compute_post proc_data
|
|
~initial:(DisjunctiveTransferFunctions.Disjuncts.singleton PulseAbductiveDomain.empty))
|
|
with AbstractDomain.Stop_analysis -> () ) ;
|
|
summary
|