package bap-primus-promiscuous
Enables the promiscuous mode of execution
Install
Dune Dependency
Authors
Maintainers
Sources
v2.5.0.tar.gz
sha256=9c126781385d2fa9b8edab22e62b25c70bf2f99f6ec78abb7e5e36d63cfa4174
md5=5abd9b3628b43f797326034f31ca574f
doc/src/bap-plugin-primus_promiscuous/primus_promiscuous_main.ml.html
Source file primus_promiscuous_main.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
open Core_kernel[@@warning "-D"] open Bap.Std open Monads.Std open Bap_primus.Std open Format include Self() let package = "bap" (* In a general case a block is terminated by a sequence of jumps: {v when C1 jmp D1 when C2 jmp D2 ... when Cm jmp Dm v} The IR requires the following invariant: {v C1 \/ C2 \/ ... \/ Cm v}. The infeasible interpreter is a non-deterministic interperter, that for every block B that is terminated with m jumps, will fork m-1 context after the last definition, so that under the n-th context the Dn destination will be taken by the interpreter. For the Dm-th destination to be taken, the following condition must hold: {v ~C1 /\ ~C2 /\ ... ~C(n-1) /\ Cn v}, where [~] symbol denotes logical negation. However, we would require an SMT solver to find such contexts. So, the interpreter requires a program to be in a trivial condition form (TCF). In TCF every jmp condition must be a single variable or a constant true. *) type assn = { use : tid; var : var; res : bool; } type id = Primus.Machine.id type state = { conflicts : assn list; visited : Tid.Set.t; } let inspect_assn {use; var; res} = let var = sprintf "%s: %s" (Tid.to_string use)(Var.to_string var) in Sexp.List [Sexp.Atom var; sexp_of_bool res] let inspect_conflict (assn) = Sexp.List (List.map assn ~f:inspect_assn) let inspect_conflicts cs = Sexp.List (List.map ~f:inspect_conflict cs) let init_visited prog = (object inherit [Tid.Set.t] Term.visitor method! enter_blk blk visited = if Term.has_attr blk Term.visited then Set.add visited (Term.tid blk) else visited end)#run prog Tid.Set.empty let state = Primus.Machine.State.declare ~name:"conflicts" ~uuid:"58bb35f4-f259-4712-8d15-bdde1be3caa8" @@ fun proj -> { conflicts=[]; visited = init_visited (Project.program proj) } let neg = List.map ~f:(fun assn -> {assn with res = not assn.res}) let assumptions blk = Term.enum jmp_t blk |> Seq.fold ~init:([],[]) ~f:(fun (assns, assms) jmp -> match Jmp.cond jmp with | Bil.Var c -> let assn = {use=Term.tid jmp; var=c; res=true} in assn :: assns, (assn :: neg assns) :: assms | Bil.Int _ -> assns, (neg assns) :: assms | _ -> failwith "Not in TCF") |> snd module TrapPageFault(Machine : Primus.Machine.S) = struct module Code = Primus.Linker.Make(Machine) let exec = Code.unlink (`symbol Primus.Interpreter.pagefault_handler) end module DoNothing(Machine : Primus.Machine.S) = struct let exec = Machine.return () end module Id = Monad.State.Multi.Id module Forker(Machine : Primus.Machine.S) = struct open Machine.Syntax module Eval = Primus.Interpreter.Make(Machine) module Env = Primus.Env.Make(Machine) module Mem = Primus.Memory.Make(Machine) module Linker = Primus.Linker.Make(Machine) let assume assns = Machine.List.iter assns ~f:(fun assn -> Eval.const (Word.of_bool assn.res) >>= fun r -> Eval.get assn.var >>= fun r' -> let op = if assn.res then Bil.OR else Bil.AND in Eval.binop op r r' >>= Eval.set assn.var) let unsat_assumptions blk = Machine.List.map (assumptions blk) ~f:(Machine.List.filter ~f:(fun {var;res} -> Env.get var >>| Primus.Value.to_word >>| fun r -> Word.(r <> of_bool res))) let pp_id = Monad.State.Multi.Id.pp let do_fork blk ~child = Machine.current () >>= fun pid -> Machine.Global.get state >>= fun t -> if Set.mem t.visited (Term.tid blk) then Machine.return pid else Machine.fork () >>= fun () -> Machine.current () >>= fun id -> if Id.(pid = id) then Machine.return id else child () >>= fun () -> Machine.switch pid >>| fun () -> id let fork blk = unsat_assumptions blk >>= Machine.List.iter ~f:(function | [] -> Machine.return () | conflicts -> Machine.ignore_m @@ do_fork blk ~child:(fun () -> assume conflicts >>= fun () -> Machine.Local.update state ~f:(fun t -> { t with conflicts}))) let assume_returns blk call = match Call.return call with | Some (Direct dst) -> Machine.current () >>= fun pid -> do_fork blk ~child:Machine.return >>= fun id -> if Id.(id = pid) then Machine.return () else Machine.Global.get state >>= fun {visited} -> if Set.mem visited dst then Eval.halt >>= never_returns else Linker.exec (`tid dst) >>= fun () -> Eval.halt >>= never_returns | _ -> Machine.return () let fork_on_calls blk jmp = match Jmp.kind jmp with | Call c -> assume_returns blk c | _ -> Machine.return () let is_last blk def = match Term.last def_t blk with | None -> true | Some last -> Term.same def last let has_no_def blk = Term.length def_t blk = 0 let step level = let open Primus.Pos in match level with | Blk {me=blk} when has_no_def blk -> fork blk | Def {up={me=blk}; me=def} when is_last blk def -> fork blk | Jmp {up={me=blk}; me=jmp} -> fork_on_calls blk jmp | _ -> Machine.return () let mark_visited blk = Machine.Global.update state ~f:(fun t -> { t with visited = Set.add t.visited (Term.tid blk) }) let init () = Machine.sequence [ Primus.Interpreter.leave_pos >>> step; Primus.Interpreter.leave_blk >>> mark_visited; ] end module EnableDivisionByZero(Machine : Primus.Machine.S) = struct module Linker = Primus.Linker.Make(Machine) let init () = Linker.link ~name:Primus.Interpreter.division_by_zero_handler (module DoNothing) end let legacy_promiscous_mode_components = [ "var-randomizer"; "mem-randomizer"; "arg-randomizer"; "promiscuous-path-explorer"; "division-by-zero-handler"; "limit"; ] let enable_legacy_promiscuous_mode () = Primus.System.Repository.update ~package "legacy-main" ~f:(fun init -> List.fold legacy_promiscous_mode_components ~init ~f:(fun system component -> Primus.System.add_component system ~package component)) open Config;; let desc = "When this mode is enabled the Primus Machine will venture into \ paths with unsatisfied constraints. Basically, it means that on \ every branch the state is duplicated." ;; manpage [ `S "DESCRIPTION"; `P desc ; `P "The program will be translated into the Trivial Condition Form, where each compound condition expression is trivialized to a variable, that is bound earlier in the block." ] let enabled = flag "mode" ~doc:"(DEPRECATED) Enable the mode." let () = when_ready (fun {get=(!!)} -> Primus.Components.register_generic "promiscuous-path-explorer" (module Forker) ~package ~desc:"Forces execution of all linearly independent paths \ by forcefully flipping the branch conditions."; Primus.Components.register_generic "division-by-zero-handler" (module EnableDivisionByZero) ~package ~desc:"Disables division by zero errors."; if !!enabled then enable_legacy_promiscuous_mode ());
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>