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.
238 lines
8.3 KiB
238 lines
8.3 KiB
6 years ago
|
(*
|
||
|
* Copyright (c) 2019-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 AbstractAddress = PulseDomain.AbstractAddress
|
||
|
module Attributes = PulseDomain.Attributes
|
||
|
module BaseStack = PulseDomain.Stack
|
||
|
module BaseMemory = PulseDomain.Memory
|
||
|
|
||
|
(** signature common to the "normal" [Domain], representing the post at the current program point,
|
||
|
and the inverted [InvertedDomain], representing the inferred pre-condition*)
|
||
|
module type BaseDomain = sig
|
||
|
(* private because the lattice is not the same for preconditions and postconditions so we don't
|
||
|
want to confuse them *)
|
||
|
type t = private PulseDomain.t [@@deriving compare]
|
||
|
|
||
|
val empty : t
|
||
|
|
||
|
val make : BaseStack.t -> BaseMemory.t -> t
|
||
|
|
||
|
val update : ?stack:BaseStack.t -> ?heap:BaseMemory.t -> t -> t
|
||
|
|
||
|
include AbstractDomain.NoJoin with type t := t
|
||
|
end
|
||
|
|
||
|
(* just to expose the [heap] and [stack] record field names without having to type
|
||
|
[PulseDomain.heap] *)
|
||
|
type base_domain = PulseDomain.t = {heap: BaseMemory.t; stack: BaseStack.t}
|
||
|
|
||
|
(** operations common to [Domain] and [InvertedDomain], see also the [BaseDomain] signature *)
|
||
|
module BaseDomainCommon = struct
|
||
|
let make stack heap = {stack; heap}
|
||
|
|
||
|
let update ?stack ?heap foot =
|
||
|
let new_stack, new_heap =
|
||
|
(Option.value ~default:foot.stack stack, Option.value ~default:foot.heap heap)
|
||
|
in
|
||
|
if phys_equal new_stack foot.stack && phys_equal new_heap foot.heap then foot
|
||
|
else {stack= new_stack; heap= new_heap}
|
||
|
end
|
||
|
|
||
|
(** represents the post abstract state at each program point *)
|
||
|
module Domain : BaseDomain = struct
|
||
|
include BaseDomainCommon
|
||
|
include PulseDomain
|
||
|
end
|
||
|
|
||
|
(** represents the inferred pre-condition at each program point, biabduction style *)
|
||
|
module InvertedDomain : BaseDomain = struct
|
||
|
include BaseDomainCommon
|
||
|
|
||
|
type t = PulseDomain.t [@@deriving compare]
|
||
|
|
||
|
let empty = PulseDomain.empty
|
||
|
|
||
|
let pp = PulseDomain.pp
|
||
|
|
||
|
(** inverted lattice *)
|
||
|
let ( <= ) ~lhs ~rhs = PulseDomain.( <= ) ~rhs:lhs ~lhs:rhs
|
||
|
end
|
||
|
|
||
|
(** biabduction-style pre/post state *)
|
||
|
type t =
|
||
|
{ post: Domain.t (** state at the current program point*)
|
||
|
; pre: InvertedDomain.t (** inferred pre at the current program point *) }
|
||
|
[@@deriving compare]
|
||
|
|
||
|
let pp f {post; pre} = F.fprintf f "@[<v>%a@;[%a]@]" Domain.pp post InvertedDomain.pp pre
|
||
|
|
||
|
let ( <= ) ~lhs ~rhs =
|
||
|
match
|
||
|
PulseDomain.isograph_map PulseDomain.empty_mapping
|
||
|
~lhs:(rhs.pre :> PulseDomain.t)
|
||
|
~rhs:(lhs.pre :> PulseDomain.t)
|
||
|
with
|
||
|
| NotIsomorphic ->
|
||
|
false
|
||
|
| IsomorphicUpTo foot_mapping ->
|
||
|
PulseDomain.is_isograph foot_mapping
|
||
|
~lhs:(lhs.post :> PulseDomain.t)
|
||
|
~rhs:(rhs.post :> PulseDomain.t)
|
||
|
|
||
|
|
||
|
module Stack = struct
|
||
|
let is_abducible _var =
|
||
|
(* TODO: need to keep only formals + return variable + globals in the pre *) true
|
||
|
|
||
|
|
||
|
(** [astate] with [astate.post.stack = f astate.post.stack] *)
|
||
|
let map_post_stack ~f astate =
|
||
|
let new_post = Domain.update astate.post ~stack:(f (astate.post :> base_domain).stack) in
|
||
|
if phys_equal new_post astate.post then astate else {astate with post= new_post}
|
||
|
|
||
|
|
||
|
let materialize var astate =
|
||
|
match BaseStack.find_opt var (astate.post :> base_domain).stack with
|
||
|
| Some addr_loc_opt ->
|
||
|
(astate, addr_loc_opt)
|
||
|
| None ->
|
||
|
let addr_loc_opt' = (AbstractAddress.mk_fresh (), None) in
|
||
|
let post_stack = BaseStack.add var addr_loc_opt' (astate.post :> base_domain).stack in
|
||
|
let pre =
|
||
|
if is_abducible var then
|
||
|
let foot_stack = BaseStack.add var addr_loc_opt' (astate.pre :> base_domain).stack in
|
||
|
let foot_heap =
|
||
|
BaseMemory.register_address (fst addr_loc_opt') (astate.pre :> base_domain).heap
|
||
|
in
|
||
|
InvertedDomain.make foot_stack foot_heap
|
||
|
else astate.pre
|
||
|
in
|
||
|
({post= Domain.update astate.post ~stack:post_stack; pre}, addr_loc_opt')
|
||
|
|
||
|
|
||
|
let add var addr_loc_opt astate =
|
||
|
map_post_stack astate ~f:(fun stack -> BaseStack.add var addr_loc_opt stack)
|
||
|
|
||
|
|
||
|
let remove_vars vars astate =
|
||
|
map_post_stack astate ~f:(fun stack ->
|
||
|
BaseStack.filter (fun var _ -> not (List.mem ~equal:Var.equal vars var)) stack )
|
||
|
|
||
|
|
||
|
let fold f astate accum = BaseStack.fold f (astate.post :> base_domain).stack accum
|
||
|
|
||
|
let find_opt var astate = BaseStack.find_opt var (astate.post :> base_domain).stack
|
||
|
end
|
||
|
|
||
|
module Memory = struct
|
||
|
open Result.Monad_infix
|
||
|
module Access = BaseMemory.Access
|
||
|
|
||
|
(** [astate] with [astate.post.heap = f astate.post.heap] *)
|
||
|
let map_post_heap ~f astate =
|
||
|
let new_post = Domain.update astate.post ~heap:(f (astate.post :> base_domain).heap) in
|
||
|
if phys_equal new_post astate.post then astate else {astate with post= new_post}
|
||
|
|
||
|
|
||
|
(** if [address] is in [pre] and it should be valid then that fact goes in the precondition *)
|
||
|
let record_must_be_valid actor address (pre : InvertedDomain.t) =
|
||
|
if BaseMemory.mem_edges address (pre :> base_domain).heap then
|
||
|
InvertedDomain.update pre
|
||
|
~heap:
|
||
|
(BaseMemory.add_attributes address
|
||
|
(Attributes.singleton (MustBeValid actor))
|
||
|
(pre :> base_domain).heap)
|
||
|
else pre
|
||
|
|
||
|
|
||
|
let check_valid actor addr ({post; pre} as astate) =
|
||
|
BaseMemory.check_valid addr (post :> base_domain).heap
|
||
|
>>| fun () ->
|
||
|
let new_pre = record_must_be_valid actor addr pre in
|
||
|
if phys_equal new_pre pre then astate else {astate with pre= new_pre}
|
||
|
|
||
|
|
||
|
let add_edge addr access new_addr_trace astate =
|
||
|
map_post_heap astate ~f:(fun heap -> BaseMemory.add_edge addr access new_addr_trace heap)
|
||
|
|
||
|
|
||
|
let add_edge_and_back_edge addr access new_addr_trace astate =
|
||
|
map_post_heap astate ~f:(fun heap ->
|
||
|
BaseMemory.add_edge_and_back_edge addr access new_addr_trace heap )
|
||
|
|
||
|
|
||
|
let materialize_edge addr access astate =
|
||
|
match BaseMemory.find_edge_opt addr access (astate.post :> base_domain).heap with
|
||
|
| Some addr_trace' ->
|
||
|
(astate, addr_trace')
|
||
|
| None ->
|
||
|
let addr_trace' = (AbstractAddress.mk_fresh (), []) in
|
||
|
let post_heap =
|
||
|
BaseMemory.add_edge_and_back_edge addr access addr_trace'
|
||
|
(astate.post :> base_domain).heap
|
||
|
in
|
||
|
let foot_heap =
|
||
|
if BaseMemory.mem_edges addr (astate.pre :> base_domain).heap then
|
||
|
BaseMemory.add_edge_and_back_edge addr access addr_trace'
|
||
|
(astate.pre :> base_domain).heap
|
||
|
|> BaseMemory.register_address (fst addr_trace')
|
||
|
else (astate.pre :> base_domain).heap
|
||
|
in
|
||
|
( { post= Domain.update astate.post ~heap:post_heap
|
||
|
; pre= InvertedDomain.update astate.pre ~heap:foot_heap }
|
||
|
, addr_trace' )
|
||
|
|
||
|
|
||
|
let invalidate address actor astate =
|
||
|
map_post_heap astate ~f:(fun heap -> BaseMemory.invalidate address actor heap)
|
||
|
|
||
|
|
||
|
let add_attributes address attributes astate =
|
||
|
map_post_heap astate ~f:(fun heap -> BaseMemory.add_attributes address attributes heap)
|
||
|
|
||
|
|
||
|
let std_vector_reserve addr astate =
|
||
|
map_post_heap astate ~f:(fun heap -> BaseMemory.std_vector_reserve addr heap)
|
||
|
|
||
|
|
||
|
let is_std_vector_reserved addr astate =
|
||
|
BaseMemory.is_std_vector_reserved addr (astate.post :> base_domain).heap
|
||
|
|
||
|
|
||
|
let find_opt address astate = BaseMemory.find_opt address (astate.post :> base_domain).heap
|
||
|
|
||
|
let set_cell addr cell astate =
|
||
|
map_post_heap astate ~f:(fun heap -> BaseMemory.set_cell addr cell heap)
|
||
|
|
||
|
|
||
|
module Edges = BaseMemory.Edges
|
||
|
end
|
||
|
|
||
|
let empty = {post= Domain.empty; pre= InvertedDomain.empty}
|
||
|
|
||
|
let discard_unreachable ({pre; post} as astate) =
|
||
|
let pre_addresses = PulseDomain.visit (pre :> PulseDomain.t) in
|
||
|
let pre_old_heap = (pre :> PulseDomain.t).heap in
|
||
|
let pre_new_heap =
|
||
|
PulseDomain.Memory.filter
|
||
|
(fun address -> PulseDomain.AbstractAddressSet.mem address pre_addresses)
|
||
|
pre_old_heap
|
||
|
in
|
||
|
let post_addresses = PulseDomain.visit (post :> PulseDomain.t) in
|
||
|
let all_addresses = PulseDomain.AbstractAddressSet.union pre_addresses post_addresses in
|
||
|
let post_old_heap = (post :> PulseDomain.t).heap in
|
||
|
let post_new_heap =
|
||
|
PulseDomain.Memory.filter
|
||
|
(fun address -> PulseDomain.AbstractAddressSet.mem address all_addresses)
|
||
|
post_old_heap
|
||
|
in
|
||
|
if phys_equal pre_new_heap pre_old_heap && phys_equal post_new_heap post_old_heap then astate
|
||
|
else
|
||
|
{ pre= InvertedDomain.make (pre :> PulseDomain.t).stack pre_new_heap
|
||
|
; post= Domain.make (post :> PulseDomain.t).stack post_new_heap }
|