[sledge] Handle whether to follow exceptional control flow at model compilation

Summary:
Currently there is a symbolic execution option to ignore exceptional
control flow. This hack does not fit well, and it is unclear how much
backend functionality should take it into consideration. This diff
removes this option and replaces it with an option during model
compilation. This has the advantage of clarifying and simplifying the
backend, with the disadvantage of no longer supporting switching
between exceptions and no-exceptions modes at analysis time. Since the
possibility of ignoring exceptional control flow is due to it not being
ready yet, this is a good trade to make.

Reviewed By: jvillard

Differential Revision: D25146148

fbshipit-source-id: 1f1299ee1
master
Josh Berdine 4 years ago committed by Facebook GitHub Bot
parent c9185ae607
commit 2726079a63

@ -83,7 +83,6 @@ let used_globals pgm preanalyze : Domain_used_globals.r =
let summary_table = let summary_table =
Used_globals_executor.compute_summaries Used_globals_executor.compute_summaries
{ bound= 1 { bound= 1
; skip_throw= false
; function_summaries= true ; function_summaries= true
; entry_points ; entry_points
; globals= Declared Llair.Global.Set.empty } ; globals= Declared Llair.Global.Set.empty }
@ -101,9 +100,6 @@ let analyze =
flag "bound" flag "bound"
(optional_with_default 1 int) (optional_with_default 1 int)
~doc:"<int> stop execution exploration at depth <int>" ~doc:"<int> stop execution exploration at depth <int>"
and exceptions =
flag "exceptions" no_arg
~doc:"explore executions that throw and handle exceptions"
and function_summaries = and function_summaries =
flag "function-summaries" no_arg flag "function-summaries" no_arg
~doc:"use function summaries (in development)" ~doc:"use function summaries (in development)"
@ -131,10 +127,9 @@ let analyze =
fun program () -> fun program () ->
let pgm = program () in let pgm = program () in
let globals = used_globals pgm preanalyze_globals in let globals = used_globals pgm preanalyze_globals in
let skip_throw = not exceptions in
Domain_sh.simplify_states := not no_simplify_states ; Domain_sh.simplify_states := not no_simplify_states ;
Timer.enabled := stats ; Timer.enabled := stats ;
exec {bound; skip_throw; function_summaries; entry_points; globals} pgm ; exec {bound; function_summaries; entry_points; globals} pgm ;
Report.coverage pgm ; Report.coverage pgm ;
Report.safe_or_unsafe () Report.safe_or_unsafe ()

@ -6,19 +6,34 @@
*/ */
#include "cxa_default_handlers.cpp" #include "cxa_default_handlers.cpp"
#include "cxa_exception.cpp"
#include "cxa_exception_storage.cpp" #include "cxa_exception_storage.cpp"
#include "cxa_guard.cpp" #include "cxa_guard.cpp"
#include "cxa_handlers.cpp" #include "cxa_handlers.cpp"
#include "llair_intrinsics.h" #include "llair_intrinsics.h"
#ifdef EXCEPTIONS
#include "cxa_exception.cpp"
#endif
extern "C" { extern "C" {
void abort_message(const char* format, ...) { abort(); } void abort_message(const char* format, ...) { abort(); }
#ifndef EXCEPTIONS
__attribute__((noreturn)) void __llair_throw(void* thrown_exception) {
__llair_unreachable();
}
void* __cxa_allocate_exception(size_t thrown_size) throw() {
__llair_unreachable();
}
#else
__attribute__((always_inline)) _Unwind_Reason_Code __attribute__((always_inline)) _Unwind_Reason_Code
_Unwind_RaiseException(_Unwind_Exception* unwind_exception) { _Unwind_RaiseException(_Unwind_Exception* unwind_exception) {
__llair_throw(thrown_object_from_cxa_exception( __llair_throw(thrown_object_from_cxa_exception(
abi::cxa_exception_from_exception_unwind_exception(unwind_exception))); abi::cxa_exception_from_exception_unwind_exception(unwind_exception)));
} }
#endif
} }

@ -11,17 +11,18 @@
extern "C" { extern "C" {
#endif #endif
__attribute__((noreturn)) void __llair_throw(void* thrown_exception); /* allocation that cannot fail */
/* express assumption that an execution is not possible */
__attribute__((noreturn)) void __llair_unreachable();
/* allocation that cannot fail. */
void* __llair_alloc(unsigned size); void* __llair_alloc(unsigned size);
/* non-deterministic choice */ /* non-deterministic choice */
int __llair_choice(); int __llair_choice();
/* throw an exception */
__attribute__((noreturn)) void __llair_throw(void* thrown_exception);
/* executions that call __llair_unreachable are assumed to be impossible */
__attribute__((noreturn)) void __llair_unreachable();
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

@ -53,7 +53,6 @@ Analyze code in a buck target. This is a convenience wrapper for the sequence `s
[-domain <string>] select abstract domain; must be one of "sh" (default, [-domain <string>] select abstract domain; must be one of "sh" (default,
symbolic heap domain), "globals" (used-globals domain), symbolic heap domain), "globals" (used-globals domain),
or "unit" (unit domain) or "unit" (unit domain)
[-exceptions] explore executions that throw and handle exceptions
[-function-summaries] use function summaries (in development) [-function-summaries] use function summaries (in development)
[-fuzzer] add a harness for libFuzzer targets [-fuzzer] add a harness for libFuzzer targets
[-margin <cols>] wrap debug tracing at <cols> columns [-margin <cols>] wrap debug tracing at <cols> columns
@ -151,7 +150,6 @@ Analyze code in one or more LLVM bitcode files. This is a convenience wrapper fo
[-domain <string>] select abstract domain; must be one of "sh" (default, [-domain <string>] select abstract domain; must be one of "sh" (default,
symbolic heap domain), "globals" (used-globals domain), symbolic heap domain), "globals" (used-globals domain),
or "unit" (unit domain) or "unit" (unit domain)
[-exceptions] explore executions that throw and handle exceptions
[-function-summaries] use function summaries (in development) [-function-summaries] use function summaries (in development)
[-fuzzer] add a harness for libFuzzer targets [-fuzzer] add a harness for libFuzzer targets
[-margin <cols>] wrap debug tracing at <cols> columns [-margin <cols>] wrap debug tracing at <cols> columns
@ -238,7 +236,6 @@ The <input> file must be binary LLAIR, such as produced by `sledge translate`.
[-domain <string>] select abstract domain; must be one of "sh" (default, [-domain <string>] select abstract domain; must be one of "sh" (default,
symbolic heap domain), "globals" (used-globals domain), symbolic heap domain), "globals" (used-globals domain),
or "unit" (unit domain) or "unit" (unit domain)
[-exceptions] explore executions that throw and handle exceptions
[-function-summaries] use function summaries (in development) [-function-summaries] use function summaries (in development)
[-margin <cols>] wrap debug tracing at <cols> columns [-margin <cols>] wrap debug tracing at <cols> columns
[-no-simplify-states] do not simplify states during symbolic execution [-no-simplify-states] do not simplify states during symbolic execution

@ -11,7 +11,6 @@
type exec_opts = type exec_opts =
{ bound: int { bound: int
; skip_throw: bool
; function_summaries: bool ; function_summaries: bool
; entry_points: string list ; entry_points: string list
; globals: Domain_used_globals.r } ; globals: Domain_used_globals.r }
@ -446,9 +445,7 @@ module Make (Dom : Domain_intf.Dom) = struct
(Domain_used_globals.by_function opts.globals callee.name) (Domain_used_globals.by_function opts.globals callee.name)
|> Work.seq x ) ) |> Work.seq x ) )
| Return {exp} -> exec_return ~opts stk state block exp | Return {exp} -> exec_return ~opts stk state block exp
| Throw {exc} -> | Throw {exc} -> exec_throw stk state block exc
if opts.skip_throw then Work.skip
else exec_throw stk state block exc
| Unreachable -> Work.skip | Unreachable -> Work.skip
let exec_inst : Llair.inst -> Dom.t -> (Dom.t, Dom.t * Llair.inst) result let exec_inst : Llair.inst -> Dom.t -> (Dom.t, Dom.t * Llair.inst) result

@ -9,7 +9,6 @@
type exec_opts = type exec_opts =
{ bound: int (** Loop/recursion unrolling bound *) { bound: int (** Loop/recursion unrolling bound *)
; skip_throw: bool (** Treat throw as unreachable *)
; function_summaries: bool (** Use function summarisation *) ; function_summaries: bool (** Use function summarisation *)
; entry_points: string list (** C linkage names of entry points *) ; entry_points: string list (** C linkage names of entry points *)
; globals: Domain_used_globals.r } ; globals: Domain_used_globals.r }

@ -68,7 +68,7 @@ let exec_inst inst pre =
| Intrinsic {reg; name; args; _} -> | Intrinsic {reg; name; args; _} ->
let areturn = Option.map ~f:X.reg reg in let areturn = Option.map ~f:X.reg reg in
let actuals = IArray.map ~f:X.term args in let actuals = IArray.map ~f:X.term args in
Exec.intrinsic ~skip_throw:true pre areturn name actuals ) Exec.intrinsic pre areturn name actuals )
|> Option.map ~f:simplify |> Option.map ~f:simplify
let value_determined_by ctx us a = let value_determined_by ctx us a =

@ -734,7 +734,7 @@ let free pre ~ptr = exec_spec pre (free_spec ptr)
let nondet pre = function Some reg -> kill pre reg | None -> pre let nondet pre = function Some reg -> kill pre reg | None -> pre
let abort _ = None let abort _ = None
let intrinsic ~skip_throw : let intrinsic :
Sh.t Sh.t
-> Var.t option -> Var.t option
-> Llair.Intrinsic.t -> Llair.Intrinsic.t
@ -809,11 +809,6 @@ let intrinsic ~skip_throw :
*) *)
(* size_t strlen (const char* ptr) *) (* size_t strlen (const char* ptr) *)
| Some reg, `strlen, [|ptr|] -> exec_spec pre (strlen_spec reg ptr) | Some reg, `strlen, [|ptr|] -> exec_spec pre (strlen_spec reg ptr)
(*
* cxxabi
*)
| Some _, `__cxa_allocate_exception, [|_|] when skip_throw ->
Some (Sh.false_ pre.us)
(* (*
* folly * folly
*) *)
@ -827,7 +822,7 @@ let intrinsic ~skip_throw :
| `posix_memalign | `realloc | `mallocx | `rallocx | `xallocx | `posix_memalign | `realloc | `mallocx | `rallocx | `xallocx
| `sallocx | `dallocx | `sdallocx | `nallocx | `malloc_usable_size | `sallocx | `dallocx | `sdallocx | `nallocx | `malloc_usable_size
| `mallctl | `mallctlnametomib | `mallctlbymib | `strlen | `mallctl | `mallctlnametomib | `mallctlbymib | `strlen
| `__cxa_allocate_exception | `_ZN5folly13usingJEMallocEv ) | `_ZN5folly13usingJEMallocEv )
, _ ) -> , _ ) ->
fail "%aintrinsic %a%a;" fail "%aintrinsic %a%a;"
(Option.pp "%a := " Var.pp) (Option.pp "%a := " Var.pp)

@ -20,9 +20,4 @@ val nondet : Sh.t -> Var.t option -> Sh.t
val abort : Sh.t -> Sh.t option val abort : Sh.t -> Sh.t option
val intrinsic : val intrinsic :
skip_throw:bool Sh.t -> Var.t option -> Llair.Intrinsic.t -> Term.t iarray -> Sh.t option
-> Sh.t
-> Var.t option
-> Llair.Intrinsic.t
-> Term.t iarray
-> Sh.t option

@ -33,8 +33,6 @@ type t =
| `malloc_stats_print | `malloc_stats_print
| (* cstring *) | (* cstring *)
`strlen `strlen
| (* cxxabi *)
`__cxa_allocate_exception
| (* folly *) | (* folly *)
`_ZN5folly13usingJEMallocEv ] `_ZN5folly13usingJEMallocEv ]
[@@deriving compare, equal, hash, sexp, enumerate, variants] [@@deriving compare, equal, hash, sexp, enumerate, variants]

@ -572,9 +572,14 @@ module BlockQ = HashQueue.Make (Block_label)
module Func = struct module Func = struct
type t = func [@@deriving compare, equal, hash, sexp_of] type t = func [@@deriving compare, equal, hash, sexp_of]
let is_undefined = function let undefined_entry =
| {entry= {cmnd; term= Unreachable; _}; _} -> IArray.is_empty cmnd Block.mk ~lbl:"undefined" ~cmnd:IArray.empty ~term:Term.unreachable
| _ -> false
let mk_undefined ~name ~formals ~freturn ~fthrow ~loc =
let locals = Reg.Set.empty in
{name; formals; freturn; fthrow; locals; entry= undefined_entry; loc}
let is_undefined func = func.entry == undefined_entry
let fold_cfg func s ~f = let fold_cfg func s ~f =
let seen = BlockS.create 0 in let seen = BlockS.create 0 in
@ -672,13 +677,6 @@ module Func = struct
resolve_parent_and_jumps entry ; resolve_parent_and_jumps entry ;
IArray.iter cfg ~f:resolve_parent_and_jumps ; IArray.iter cfg ~f:resolve_parent_and_jumps ;
func |> check invariant func |> check invariant
let mk_undefined ~name ~formals ~freturn ~fthrow =
let entry =
Block.mk ~lbl:"" ~cmnd:IArray.empty ~term:Term.unreachable
in
let cfg = IArray.empty in
mk ~name ~entry ~formals ~freturn ~fthrow ~cfg
end end
(** Derived meta-data *) (** Derived meta-data *)

Loading…
Cancel
Save