diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 0000000..54e0888 --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 9ecb4aafd5f1795cb5d85c68c54ada02 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/_sources/autoapi/cozy/__main__/index.rst.txt b/_sources/autoapi/cozy/__main__/index.rst.txt new file mode 100644 index 0000000..9a30a62 --- /dev/null +++ b/_sources/autoapi/cozy/__main__/index.rst.txt @@ -0,0 +1,319 @@ +cozy.__main__ +============= + +.. py:module:: cozy.__main__ + + +Classes +------- + +.. autoapisummary:: + + cozy.__main__.Results + cozy.__main__.Stage + cozy.__main__.Wizard + + +Module Contents +--------------- + +.. py:class:: Results + + .. py:method:: to_string() + + +.. py:class:: Stage + + Bases: :py:obj:`enum.Enum` + + + Generic enumeration. + + Derive from this class to define new enumerations. + + + .. py:attribute:: confirm_start + :value: 1 + + + + .. py:attribute:: request_script_name + :value: 2 + + + + .. py:attribute:: confirm_script_clobber + :value: 3 + + + + .. py:attribute:: request_prepatched + :value: 4 + + + + .. py:attribute:: request_postpatched + :value: 5 + + + + .. py:attribute:: request_function_name + :value: 6 + + + + .. py:attribute:: request_signature + :value: 7 + + + + .. py:attribute:: request_concolic + :value: 8 + + + + .. py:attribute:: request_concolic_complete + :value: 9 + + + + .. py:attribute:: request_hooks + :value: 10 + + + + .. py:attribute:: request_textual_report + :value: 11 + + + + .. py:attribute:: request_visualization + :value: 12 + + + + .. py:attribute:: request_dump + :value: 13 + + + + .. py:attribute:: request_dump_name + :value: 14 + + + + .. py:attribute:: complete + :value: 15 + + + +.. py:class:: Wizard + + Bases: :py:obj:`textual.app.App` + + + .. py:attribute:: CSS + :value: Multiline-String + + .. raw:: html + +
Show Value + + .. code-block:: python + + """ + Screen { + padding:1 + } + """ + + .. raw:: html + +
+ + + + + .. py:method:: compose() -> textual.app.ComposeResult + + + .. py:method:: ask_yes_no(text) + :async: + + + + .. py:method:: ask_string(text, placeholder='') + :async: + + + + .. py:method:: ask_file(text) + :async: + + + + .. py:method:: set_stage(stage) + :async: + + + + .. py:method:: set_confirm_start() + :async: + + + + .. py:method:: handle_confirm_start(message) + :async: + + + + .. py:method:: set_confirm_script_clobber() + :async: + + + + .. py:method:: handle_confirm_script_clobber(message) + :async: + + + + .. py:method:: set_request_script_name() + :async: + + + + .. py:method:: handle_request_script_name(message) + :async: + + + + .. py:method:: set_request_prepatched() + :async: + + + + .. py:method:: handle_request_prepatched(message) + :async: + + + + .. py:method:: set_request_postpatched() + :async: + + + + .. py:method:: handle_request_postpatched(message) + :async: + + + + .. py:method:: set_request_function_name() + :async: + + + + .. py:method:: handle_request_function_name(message) + :async: + + + + .. py:method:: set_request_signature() + :async: + + + + .. py:method:: handle_request_signature(message) + :async: + + + + .. py:method:: set_request_hooks() + :async: + + + + .. py:method:: handle_request_hooks(message) + :async: + + + + .. py:method:: set_request_concolic() + :async: + + + + .. py:method:: handle_request_concolic(message) + :async: + + + + .. py:method:: set_request_concolic_complete() + :async: + + + + .. py:method:: handle_request_concolic_complete(message) + :async: + + + + .. py:method:: set_request_textual_report() + :async: + + + + .. py:method:: handle_request_textual_report(message) + :async: + + + + .. py:method:: set_request_dump() + :async: + + + + .. py:method:: handle_request_dump(message) + :async: + + + + .. py:method:: set_request_dump_name() + :async: + + + + .. py:method:: handle_request_dump_name(message) + :async: + + + + .. py:method:: set_request_visualization() + :async: + + + + .. py:method:: handle_request_visualization(message) + :async: + + + + .. py:method:: complete() + :async: + + + + .. py:method:: on_input_submitted(message: textual.widgets.Input.Submitted) -> None + :async: + + + + .. py:method:: on_option_list_option_selected(message: textual.widgets.OptionList.OptionSelected) -> None + :async: + + + + .. py:method:: on_directory_tree_file_selected(message: textual.widgets.DirectoryTree.FileSelected) -> None + :async: + + + diff --git a/_sources/autoapi/cozy/analysis/index.rst.txt b/_sources/autoapi/cozy/analysis/index.rst.txt new file mode 100644 index 0000000..87bb16e --- /dev/null +++ b/_sources/autoapi/cozy/analysis/index.rst.txt @@ -0,0 +1,224 @@ +cozy.analysis +============= + +.. py:module:: cozy.analysis + + +Classes +------- + +.. autoapisummary:: + + cozy.analysis.FieldDiff + cozy.analysis.EqFieldDiff + cozy.analysis.NotEqLeaf + cozy.analysis.NotEqFieldDiff + cozy.analysis.DiffResult + cozy.analysis.StateDiff + cozy.analysis.CompatiblePair + cozy.analysis.Comparison + + +Functions +--------- + +.. autoapisummary:: + + cozy.analysis._invalid_stack_addrs + cozy.analysis._invalid_stack_overlap + cozy.analysis._stack_addrs + cozy.analysis.nice_name + cozy.analysis.compare_side_effect + cozy.analysis.hexify + + +Module Contents +--------------- + +.. py:function:: _invalid_stack_addrs(st: angr.SimState) -> range + +.. py:function:: _invalid_stack_overlap(invalid_stack_left: range, invalid_stack_right: range, stack_change: int) + +.. py:function:: _stack_addrs(st: angr.SimState) -> range + +.. py:function:: nice_name(state: angr.SimState, malloced_names: portion.IntervalDict[tuple[str, portion.Interval]], addr: int) -> str | None + + This function attempts to create a human understandable name for an address, or returns None if it can't figure + one out. + + +.. py:class:: FieldDiff + +.. py:class:: EqFieldDiff(left_body, right_body) + + Bases: :py:obj:`FieldDiff` + + + For a field to be equal, all subcomponents of the body must be equal. In this case, left_body and right_body + should not hold any further FieldDiffs within themselves. Rather left_body and right_body should be the entire + fields for which differencing was checked (and it was determined that all subfields are equal). + + +.. py:class:: NotEqLeaf(left_leaf, right_leaf) + + Bases: :py:obj:`FieldDiff` + + + A not equal leaf is a field that cannot be further unpacked/traversed. + + +.. py:class:: NotEqFieldDiff(body_diff) + + Bases: :py:obj:`FieldDiff` + + + For a field to be not equal, there must be at least one subcomponent of the body that was not equal. In this case, + body_diff will hold further FieldDiffs within itself. Equal subfields of the bodies will be + represented by EqFieldDiff, whereas unequal subfields will be represented by further nested NotEqFieldDiff. + + +.. py:function:: compare_side_effect(joint_solver, left_se, right_se) -> FieldDiff + +.. py:class:: DiffResult(mem_diff: dict[range, tuple[claripy.ast.bits, claripy.ast.bits]], reg_diff: dict[str, tuple[claripy.ast.bits, claripy.ast.bits]], side_effect_diff: dict[str, list[tuple[cozy.side_effect.PerformedSideEffect | None, cozy.side_effect.PerformedSideEffect | None, FieldDiff]]]) + +.. py:class:: StateDiff + + StateDiff encapsulates the memoized state used by the difference method. This class is used internally by Comparison and is typically not for external use. + + + .. py:method:: difference(sl: angr.SimState, sr: angr.SimState, ignore_addrs: collections.abc.Iterable[range] | None = None, compute_mem_diff=True, compute_reg_diff=True, compute_side_effect_diff=True, use_unsat_core=True, simplify=False) -> DiffResult | None + + Compares two states to find differences in memory. This function will return None if the two states have non-intersecting inputs. Otherwise, it will return a dict of addresses and a dict of registers which are different between the two. This function is based off of angr.analyses.congruency_check.CongruencyCheck().compare_states, but has been customized for our purposes. Note that this function may use memoization to enhance performance. + + :param SimState sl: The first state to compare + :param SimState sr: The second state to compare + :param collections.abc.Iterable[range] | None ignore_addrs: Memory addresses to ignore when doing the memory diffing. This representation is more efficient than a set of integers since the ranges involved can be quite large. + :param bool compute_mem_diff: If this flag is True, then we will diff the memory. If this is false, then the first element of the return tuple will be None. + :param bool compute_reg_diff: If this flag is True, then we will diff the registers. If this is false, then the second element of the return tuple will be None. + :param bool use_unsat_core: If this flag is True, then we will use unsat core optimization to speed up comparison of pairs of states. This option may cause errors in Z3, so disable if this occurs. + :return: None if the two states are not compatible, otherwise returns an object containing the memory, register differences, and side effect differences. + :rtype: DiffResult | None + + + +.. py:function:: hexify(val0) + + Recursively transforms all integers in a Python datastructure (that is mappable with functools_ext.fmap) to hex strings. + + :param val0: The datastructure to traverse. + :return: A deep copy of the datastructure, with all integers converted to hex strings. + + +.. py:class:: CompatiblePair(state_left: cozy.terminal_state.TerminalState, state_right: cozy.terminal_state.TerminalState, mem_diff: dict[range, tuple[claripy.ast.Base, claripy.ast.Base]], reg_diff: dict[str, tuple[claripy.ast.Base, claripy.ast.Base]], side_effect_diff: dict[str, list[tuple[cozy.side_effect.PerformedSideEffect | None, cozy.side_effect.PerformedSideEffect | None, FieldDiff]]], mem_diff_ip: dict[int, tuple[frozenset[claripy.ast.Base]], frozenset[claripy.ast.Base]], compare_std_out: bool, compare_std_err: bool) + + Stores information about comparing two compatible states. + + :ivar TerminalState state_left: Information pertaining specifically to the pre-patched state being compared. + :ivar TerminalState state_right: Information pertaining specifically to the post-patched state being compared. + :ivar dict[range, tuple[claripy.ast.Base, claripy.ast.Base]] mem_diff: Maps memory addresses to pairs of claripy ASTs, where the left element of the tuple is the data in memory for state_left, and the right element of the tuple is what was found in memory for state_right. Only memory locations that are different are saved in this dict. + :ivar dict[str, tuple[claripy.ast.Base, claripy.ast.Base]] reg_diff: Similar to mem_diff, except that the dict is keyed by register names. Note that some registers may be subparts of another. For example in x64, EAX is a subregister of RAX. + :ivar dict[str, list[tuple[PerformedSideEffect | None, PerformedSideEffect | None, FieldDiff]]] side_effect_diff: Maps side effect channels to a list of 3 element tuples, where the first element is the performed side effect from the left binary, the second element is the performed side effect from the right binary, and the third element is the diff between the body of the side effects. + :ivar dict[int, tuple[frozenset[claripy.ast.Base]], frozenset[claripy.ast.Base]] mem_diff_ip: Maps memory addresses to a set of instruction pointers that the program was at when it wrote that byte in memory. In most cases the frozensets will have a single element, but this may not be the case in the scenario where a symbolic value determined the write address. + :ivar bool compare_std_out: If True then we should consider stdout when checking if the two input states are equal. + :ivar bool compare_std_err: If True then we should consider stderr when checking if the two input states are equal. + + + .. py:method:: equal_side_effects() -> bool + + + .. py:method:: equal() -> bool + + Determines if the two compatible states are observationally equal. That is, they contain the same memory contents, registers, stdout, and stderr after execution. + + :return: True if the two compatible states are observationally equal, and False otherwise. + :rtype: bool + + + + .. py:method:: concrete_examples(args: any, num_examples=3) -> list[cozy.concrete.CompatiblePairInput] + + Concretizes the arguments used to put the program in these states by jointly using the constraints attached to the compatible states. + + :param any args: The input arguments to concretize. This argument may be a Python datastructure, the concretizer will make a deep copy with claripy symbolic variables replaced with concrete values. + :param int num_examples: The maximum number of concrete examples to generate for this particular pair. + :return: A list of concrete inputs that satisfy both constraints attached to the states. + :rtype: list[CompatiblePairInput] + + + +.. py:class:: Comparison(pre_patched: cozy.session.RunResult, post_patched: cozy.session.RunResult, ignore_addrs: list[range] | None = None, ignore_invalid_stack=True, compare_memory=True, compare_registers=True, compare_side_effects=True, compare_std_out=False, compare_std_err=False, use_unsat_core=True, simplify=False) + + This class stores all compatible pairs and orphaned states. An orphan state is one in which there is no compatible state in the other execution tree. In most scenarios there will be no orphaned states. + + :ivar dict[tuple[SimState, SimState], CompatiblePair] pairs: pairs stores a dictionary that maps a pair of (pre_patch_state, post_patch_state) compatible states to their comparison information + :ivar set[TerminalState] orphans_left: Pre-patched states for which there are 0 corresponding compatible states in the post-patch + :ivar set[TerminalState] orphans_right: Post-patched states for which there are 0 corresponding compatible states in the pre-patch + + Compares a bundle of pre-patched states with a bundle of post-patched states. + + :param project.RunResult pre_patched: The pre-patched state bundle + :param project.RunResult post_patched: The post-patched state bundle + :param list[range] | None ignore_addrs: A list of addresses ranges to ignore when comparing memory. + :param bool ignore_invalid_stack: If this flag is True, then memory differences in locations previously occupied by the stack are ignored. + :param bool compare_memory: If True, then the analysis will compare locations in the program memory. + :param bool compare_registers: If True, then the analysis will compare registers used by the program. + :param bool compare_side_effects: If True, then the analysis will compare side effects outputted by the program. + :param bool compare_std_out: If True, then the analysis will save stdout written by the program in the results. Note that angr currently concretizes values written to stdout, so these values will be binary strings. + :param bool compare_std_err: If True, then the analysis will save stderr written by the program in the results. + :param bool use_unsat_core: If this flag is True, then we will use unsat core optimization to speed up comparison of pairs of states. This option may cause errors in Z3, so disable if this occurs. + :param bool simplify: If this flag is True, then symbolic memory and register differences will be simplified as much as possible. This flag is typically only necessary if you want to do some deep inspection of symbolic contents. simplify can speed things down a lot, and symbolic expressions are usually very complex to the point where they are not easily understandable. This is why in most scenarios the flag should be left as False. + + + .. py:method:: get_pair(state_left: angr.SimState, state_right: angr.SimState) -> CompatiblePair + + Retrieves a CompatiblePair given two compatible input states. + + :param SimState state_left: The pre-patched state + :param SimState state_right: The post-patched state + :return: The CompatiblePair object corresponding to this compatible state pair. + :rtype: CompatiblePair + + + + .. py:method:: is_compatible(state_left: angr.SimState, state_right: angr.SimState) -> bool + + Returns True when the two input states are compatible based on the pairs stored in this object, and False otherwise. + + :param SimState state_left: The pre-patched state + :param SimState state_right: The post-patched state + :return: True if the input states are compatible, and False otherwise + :rtype: bool + + + + .. py:method:: __iter__() -> collections.abc.Iterator[CompatiblePair] + + Iterates over compatible pairs stored in the comparison. + + :return: An iterator over compatible pairs. + :rtype: Iterator[CompatiblePair] + + + + .. py:method:: verify(verification_assertion: Callable[[CompatiblePair], claripy.ast.Base | bool]) -> list[CompatiblePair] + + Determines what compatible state pairs are valid with respect to a verification assertion. Note that the comparison results are verified with respect to the verification_assertion if the returned list is empty (has length 0). + + :param Callable[[CompatiblePair], claripy.ast.Base | bool] verification_assertion: A function which takes in a compatible pair and returns a claripy expression which must be satisfiable for all inputs while under the joint constraints of the state pair. Alternatively the function can return a bool. If the return value is False, this will be considered a verification failure. If the return value is True, this will be considered a verification success. + :return: A list of all compatible pairs for which there was a concrete input that caused the verification assertion to fail. + :rtype: list[CompatiblePair] + + + + .. py:method:: report(args: any, concrete_post_processor: Callable[[any], any] | None = None, num_examples: int = 3) -> str + + Generates a human-readable report of the result object, saved as a string. This string is suitable for printing. + + :param any args: The symbolic/concolic arguments used during exeuction, here these args are concretized so that we can give examples of concrete input. + :param Callable[[any], any] | None concrete_post_processor: This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two's complement, or slicing off parts of the argument based on another part of the input arguments. + :param int num_examples: The number of concrete examples to show the user. + :return: A human-readable summary of the comparison. + :rtype: str + + + diff --git a/_sources/autoapi/cozy/claripy_ext/index.rst.txt b/_sources/autoapi/cozy/claripy_ext/index.rst.txt new file mode 100644 index 0000000..58c4adb --- /dev/null +++ b/_sources/autoapi/cozy/claripy_ext/index.rst.txt @@ -0,0 +1,33 @@ +cozy.claripy_ext +================ + +.. py:module:: cozy.claripy_ext + + +Functions +--------- + +.. autoapisummary:: + + cozy.claripy_ext.simplify_kb + cozy.claripy_ext.get_symbol_name + cozy.claripy_ext.model + + +Module Contents +--------------- + +.. py:function:: simplify_kb(expr: claripy.ast.bits, kb: claripy.ast.Bool) -> claripy.ast.bits + + Simplifies a claripy AST expression, given some knowledge base (kb) of information + + :param claripy.ast.bits expr: The expression to simplify + :param claripy.ast.Bool kb: The knowledge base which is used to simplify the expr. This is typically a series of equalities conjoined together. + :return: A simplified version of the input expression, or the original expression if no simplification occurred. + :rtype: claripy.ast.bits + + +.. py:function:: get_symbol_name(sym) + +.. py:function:: model(constraints, extra_symbols: set[Union[claripy.BVS, claripy.FPS]] | frozenset[Union[claripy.BVS, claripy.FPS]] = frozenset(), n=1, **kwargs) -> list[dict[Union[claripy.BVS, claripy.FPS], Union[claripy.BVV, claripy.FPV]]] + diff --git a/_sources/autoapi/cozy/concolic/exploration/index.rst.txt b/_sources/autoapi/cozy/concolic/exploration/index.rst.txt new file mode 100644 index 0000000..781211b --- /dev/null +++ b/_sources/autoapi/cozy/concolic/exploration/index.rst.txt @@ -0,0 +1,146 @@ +cozy.concolic.exploration +========================= + +.. py:module:: cozy.concolic.exploration + + +Classes +------- + +.. autoapisummary:: + + cozy.concolic.exploration.ConcolicSim + cozy.concolic.exploration._ExploreMode + cozy.concolic.exploration.JointConcolicSim + + +Module Contents +--------------- + +.. py:class:: ConcolicSim(concrete_init: dict[claripy.BVS, claripy.BVV] | set[claripy.BVS] | frozenset[claripy.BVS], deferred_stash='deferred', check_only_recent_constraints=True) + + Bases: :py:obj:`angr.ExplorationTechnique` + + + This class implements concolic execution without using an external emulator + like QEMU or Unicorn. This class functions by storing a concrete assignment + for each symbolic variable. When the state branches, the assignment is + substituted into both children's constraints. If the substitution results + in the constraints evaluating to false, that child is placed in the deferred + stash. By querying the simulation manager's active stash length, we can + tell when the concrete execution ends. When concrete execution ends, we + can either choose a new concrete substitution ourselves and set it with + the :py:meth:`ConcolicDeferred.set_concrete` method. Alternatively we + can take one of the deferred states and generate and autogenerate a new + concrete substitution by finding a satisfying assignment for that state's + constraints. + + ConcolicSim constructor + + :param dict[claripy.BVS, claripy.BVV] | set[claripy.BVS] | frozenset[claripy.BVS] concrete_init: Used to initialize the concrete value. If this value is a substitution dictionary, then that dictionary is used as our concrete input. If this value is a set or frozenset, then a substituting dictionary is autogenerated, subject to the initial state's constraints. + :param string deferred_stash: The name of the deferred stash + :param bool check_only_recent_constraints: If this value is true, then whenever child states are created, only the new constraints are checked with respect to the concrete substitution. Here we assume that the parent of the child already satisfied the concrete input on a previous iteration, so it's safe to only check with respect to the new constraints. + + + .. py:method:: setup(simgr) + + + .. py:method:: is_satisfied(constraints: list[claripy.ast.bool]) -> bool + + Substitutes the current concrete input into the constraints, and returns True if the constraints are True after + the substitution is made. + + :param list[claripy.ast.bool] constraints: The constraints in which the concrete solution will be substituted. + :rtype: bool + :return: If the constraints are True after substitution, then True is returned. Otherwise returns False. + + + + .. py:method:: _set_replacement_dict(concrete) + + + .. py:method:: set_concrete(simgr, concrete: dict[Union[claripy.BVS, claripy.FPS], Union[claripy.BVV, claripy.FPV]]) + + Sets the concrete input via a substitution dictionary. All the symbols used by the program should have concrete + values provided for them. The active and deferred stash will be mutated to ensure that only states which + are satisfied by the concrete substitution are active. + + :param dict[claripy.BVS, claripy.BVV] concrete: A dictionary mapping each symbol to its concrete value. + + + + .. py:method:: _generate_concrete(simgr: angr.SimulationManager, from_stash: list[angr.SimState], symbols: set[claripy.BVS] | frozenset[claripy.BVS], candidate_heuristic: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None) + + + .. py:method:: generate_concrete(simgr: angr.SimulationManager, symbols: set[claripy.BVS] | frozenset[claripy.BVS], candidate_heuristic: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None) + + Autogenerates a new concrete input by choosing a deferred state and finding a satisfying assignment + with respect to that state's constraints. The symbols provided will be used to internally generate + a substitution dictionary. The candidate_heuristic is used to choose a state from the current stash + of deferred states. If no heuristic is provided, the last state in the deferred stash will be chosen next. + If there are any active states in the simulation manager, a ValueError will be thrown. + + :param angr.SimulationManager simgr: The simulation manager + :param set[claripy.BVS] | frozenset[claripy.BVS] symbols: The symbols that we will generate a substitution for. + :param Callable[[list[angr.SimState]], angr.SimState] | None candidate_heuristic: The heuristic that should be used to choose the deferred state that should be explored further. Note that this function should mutate its input list (ie, remove the desired state), and return that desired state. + + + + .. py:method:: filter(simgr, state, **kwargs) + + +.. py:class:: _ExploreMode + + Bases: :py:obj:`enum.Enum` + + + Generic enumeration. + + Derive from this class to define new enumerations. + + + .. py:attribute:: EXPLORE_LEFT + :value: 0 + + + + .. py:attribute:: EXPLORE_RIGHT + :value: 1 + + + +.. py:class:: JointConcolicSim(simgr_left: angr.SimulationManager, simgr_right: angr.SimulationManager, symbols: set[claripy.BVS] | frozenset[claripy.BVS], left_explorer: ConcolicSim, right_explorer: ConcolicSim, candidate_heuristic_left: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None, candidate_heuristic_right: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None) + + Jointly runs two SimulationManager objects by concretizing symbols, then running the left and right simulations + with the same concrete input. This joint simulator alternates between the left and right simulations + when generating new concrete inputs. + + :param SimulationManager simgr_left: The first simulation manager to run in concolic execution + :param SimulationManager simgr_right: The second simulation manager to run in concolic execution. + :param ConcolicSim left_explorer: The first exploration method to use for concolic execution. Note that this exploration technique will be attached to the left simulation manager. + :param ConcolicSim right_explorer: The second exploration method to use for concolic execution. Note that this exploration technique will be attached to the right simulation manager. + :param Callable[[list[angr.SimState]], angr.SimState] | None candidate_heuristic_left: The heuristic that should be used to choose the deferred state that should be explored further. Note that this function should mutate its input list (ie, remove the desired state), and return that desired state. Note that some pre-made candidate heuristic techniques can be found in the :py:mod:`cozy.concolic.heuristics` module. + :param Callable[[list[angr.SimState]], angr.SimState] | None candidate_heuristic_right: The heuristic that should be used to choose the deferred state that should be explored further. Note that this function should mutate its input list (ie, remove the desired state), and return that desired state. Note that some pre-made candidate heuristic techniques can be found in the :py:mod:`cozy.concolic.heuristics` module. + + + .. py:method:: _swap_explore_mode() + + + .. py:method:: _generate_concrete(from_stash_left, from_stash_right) + + + .. py:method:: explore(explore_fun_left: collections.abc.Callable[[angr.SimulationManager], None] | None = None, explore_fun_right: collections.abc.Callable[[angr.SimulationManager], None] | None = None, termination_fun_left: collections.abc.Callable[[angr.SimulationManager], bool] | None = None, termination_fun_right: collections.abc.Callable[[angr.SimulationManager], bool] | None = None, loop_bound_left: int | None = None, loop_bound_right: int | None = None) -> None + + Explores the simulations given in the left and right simulation manager. + + :param Callable[[SimulationManager], None] | None explore_fun_left: If this parameter is not None, then instead of :py:meth:`SimulationManager.explore` being called to do the exploration, we call explore_fun_left instead. + :param Callable[[SimulationManager], None] | None explore_fun_right: If this parameter is not None, then instead of :py:meth:`SimulationManager.explore` being called to do the exploration, we call explore_fun_right instead. + :param Callable[[SimulationManager], bool] | None termination_fun_left: Every time we finish exploring one concrete input, this function is called to determine if the exploration should terminate. If both termination functions return True, then exploration is halted and this function returns. If this parameter is None, then the left simulation manager will terminate only when no further exploration is possible (ie, execution is complete). Pre-made termination functions can be found in the :py:mod:`cozy.concolic.heuristics` module. + :param Callable[[SimulationManager], bool] | None termination_fun_right: Every time we finish exploring one concrete input, this function is called to determine if the exploration should terminate. If both termination functions return True, then exploration is halted and this function returns. If this parameter is None, then the right simulation manager will terminate only when no further exploration is possible (ie, execution is complete). Pre-made termination functions can be found in the :py:mod:`cozy.concolic.heuristics` module. + :param int | None loop_bound_left: Sets an upper bound on loop iteration count for the left session. Useful for programs with non-terminating loops. + :param int | None loop_bound_right: Sets an upper bound on loop iteration count for the right session. Useful for programs with non-terminating loops. + :return: None + :rtype: None + + + diff --git a/_sources/autoapi/cozy/concolic/heuristics/index.rst.txt b/_sources/autoapi/cozy/concolic/heuristics/index.rst.txt new file mode 100644 index 0000000..1b7b4db --- /dev/null +++ b/_sources/autoapi/cozy/concolic/heuristics/index.rst.txt @@ -0,0 +1,98 @@ +cozy.concolic.heuristics +======================== + +.. py:module:: cozy.concolic.heuristics + + +Classes +------- + +.. autoapisummary:: + + cozy.concolic.heuristics.ArbitraryCandidate + cozy.concolic.heuristics.BBTransitionCandidate + cozy.concolic.heuristics.CompleteTermination + cozy.concolic.heuristics.CoverageTermination + cozy.concolic.heuristics.CyclomaticComplexityTermination + + +Module Contents +--------------- + +.. py:class:: ArbitraryCandidate + + For use as the candidate heuristic in :py:meth:`cozy.exploration.ConcolicSim.generate_concrete` + This heuristic will choose the next exploration candidate by popping the last element off the candidate's list. + + + .. py:method:: __call__(candidate_states: list[angr.SimState]) + + +.. py:class:: BBTransitionCandidate(lookback: int = 2) + + For use as the candidate heuristic in :py:meth:`cozy.exploration.ConcolicSim.generate_concrete` + This heuristic will select a candidate whose basic block history has been seen least frequently in the past. This + class keeps an internal record of candidates it chose in the past to compute this metric. + + :param int lookback: The number of basic blocks we should look back to when computing a candidate's transition history. This should be a small integer, somewhere in the range 1 to 6. This number should in general only be increased if the total number of states we search goes up. The candidate state with the most unique transition history will be chosen by this heuristic. + + + .. py:method:: __call__(candidate_states: list[angr.SimState]) + + +.. py:class:: CompleteTermination + + This termination heuristic tells the concolic execution to explore until all states are deadended. + + + .. py:method:: __call__(simgr) + + +.. py:class:: CoverageTermination(fun: angr.knowledge_plugins.Function, coverage_fraction: float = 0.9) + + This termination heuristic tells the concolic execution to explore until a certain fraction of a + function's basic blocks have been visited at least once. + + :param Function fun: The function that we are seeking a specific coverage over. + :param float coverage_fraction: A number in the range [0, 1] that determines what fraction of basic blocks need to be visited before termination is reached. + + + .. py:method:: from_session(sess: cozy.project.Session, coverage_fraction: float = 0.9) -> CoverageTermination + :staticmethod: + + + Constructs a CoverageTermination object from an unrun session. + + :param Session sess: The session which is set to call some specific function, but has not yet been run. + :param float coverage_fraction: A number in the range [0, 1] that determines what fraction of basic blocks need to be visited before termination is reached. + + + + .. py:method:: __call__(simgr) + + +.. py:class:: CyclomaticComplexityTermination(fun: angr.knowledge_plugins.Function, fun_manager: angr.knowledge_plugins.FunctionManager, add_callees=True, multiplier: int | float = 1) + + This termination heuristic tells the concolic execution to explore until a certain number of terminated + states are reached. If add_callees is False, then this value is equal to the cyclomatic complexity of the function. + Otherwise, it is equal to the cyclomatic complexity of the function plus the cyclomatic complexity of all callees + of the function (recursively). + + :param bool add_callees: If this parameter is True, the cyclomatic complexity of all functions deeper in the call graph will be summed to determine the maximum number of states to explore. If False, the upper bound will be the cyclomatic complexity of the session. + :param int | float multiplier: The computed cyclomatic complexity sum will be multiplied by this value to determine the number of states to explore + + + .. py:method:: from_session(sess: cozy.project.Session, add_callees=True, multiplier: int | float = 1) -> CyclomaticComplexityTermination + :staticmethod: + + + Constructs an object from a session. The session must be started from a specific function. + + :param bool add_callees: If this parameter is True, the cyclomatic complexity of all functions deeper in the call graph will be summed to determine the maximum number of states to explore. If False, the upper bound will be the cyclomatic complexity of the session. + :param int | float multiplier: The computed cyclomatic complexity sum will be multiplied by this value to determine the number of states to explore + + + + .. py:method:: __call__(simgr) + + diff --git a/_sources/autoapi/cozy/concolic/index.rst.txt b/_sources/autoapi/cozy/concolic/index.rst.txt new file mode 100644 index 0000000..7344269 --- /dev/null +++ b/_sources/autoapi/cozy/concolic/index.rst.txt @@ -0,0 +1,17 @@ +cozy.concolic +============= + +.. py:module:: cozy.concolic + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/cozy/concolic/exploration/index + /autoapi/cozy/concolic/heuristics/index + /autoapi/cozy/concolic/session/index + + diff --git a/_sources/autoapi/cozy/concolic/session/index.rst.txt b/_sources/autoapi/cozy/concolic/session/index.rst.txt new file mode 100644 index 0000000..79b5f2e --- /dev/null +++ b/_sources/autoapi/cozy/concolic/session/index.rst.txt @@ -0,0 +1,42 @@ +cozy.concolic.session +===================== + +.. py:module:: cozy.concolic.session + + +Classes +------- + +.. autoapisummary:: + + cozy.concolic.session.JointConcolicSession + + +Module Contents +--------------- + +.. py:class:: JointConcolicSession(sess_left: cozy.session.Session, sess_right: cozy.session.Session, candidate_heuristic_left: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None, candidate_heuristic_right: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None, termination_heuristic_left: collections.abc.Callable[[angr.sim_manager.SimulationManager], bool] | None = None, termination_heuristic_right: collections.abc.Callable[[angr.sim_manager.SimulationManager], bool] | None = None) + + This class is used for jointly running two sessions using concolic execution. The sessions need to be run + jointly because the concrete values generated during concolic execution need to be fed to both programs. + + + .. py:method:: run(args_left: list[claripy.ast.bits], args_right: list[claripy.ast.bits], symbols: set[claripy.BVS] | frozenset[claripy.BVS], cache_intermediate_info: bool = True, ret_addr_left: int | None = None, ret_addr_right: int | None = None, loop_bound_left: int | None = None, loop_bound_right: int | None = None) -> tuple[cozy.session.RunResult, cozy.session.RunResult] + + Jointly run two sessions. + + :param list[claripy.ast.bits] args_left: The arguments to pass to the left session. + :param list[claripy.ast.bits] args_right: The arguments to pass to the right session. + :param set[claripy.BVS] | frozenset[claripy.BVS] symbols: All symbolic values used in the two sessions. These symbols may be passed as arguments, or may have been pre-stored in the memory of the session before this method was called. This set is required during the concretization step where we need to generate concrete values for all symbolic values in the program. + :param bool cache_intermediate_info: If this flag is True, then information about intermediate states will be + cached. This is required for dumping the execution graph which is used in visualization. + :param int | None ret_addr_left: What address to return to if calling as a function + :param int | None ret_addr_right: What address to return to if calling as a function + :param int | None loop_bound_left: Sets an upper bound on loop iteration count for the left session. Useful for programs with non-terminating loops. + :param int | None loop_bound_right: Sets an upper bound on loop iteration count for the right session. Useful for programs with non-terminating loops. + + :return: The result of running the two sessions, where the first element of the return tuple being the left session's result, and the second element being the right session's result. + :rtype: tuple[RunResult, RunResult] + + + diff --git a/_sources/autoapi/cozy/concrete/index.rst.txt b/_sources/autoapi/cozy/concrete/index.rst.txt new file mode 100644 index 0000000..fb43ae0 --- /dev/null +++ b/_sources/autoapi/cozy/concrete/index.rst.txt @@ -0,0 +1,47 @@ +cozy.concrete +============= + +.. py:module:: cozy.concrete + + +Classes +------- + +.. autoapisummary:: + + cozy.concrete.CompatiblePairInput + cozy.concrete.TerminalStateInput + + +Functions +--------- + +.. autoapisummary:: + + cozy.concrete._concretize + + +Module Contents +--------------- + +.. py:function:: _concretize(solver, state_bundle, n=1) + +.. py:class:: CompatiblePairInput(args, mem_diff: dict[range, tuple[int, int]], reg_diff: dict[str, tuple[int, int]], left_side_effects: dict[str, list[cozy.side_effect.ConcretePerformedSideEffect]], right_side_effects: dict[str, list[cozy.side_effect.ConcretePerformedSideEffect]]) + + Stores information about the concretization of a compatible state pair. + + :ivar any args: The same Python datastructures as the arguments passed to concrete_examples, except that all claripy symbolic variables are replaced with concrete values. + :ivar dict[range, tuple[int, int]] mem_diff: Concretized version of memory difference. Each key is a memory address range, and each value is a concretized version of the data stored at that location for the prepatched, postpatched runs. + :ivar dict[str, tuple[int, int]] reg_diff: Concretized version of register difference. Each key is a register name, and each value is a concretized version of the data stored at that register for the prepatched, postpatched runs. + :ivar dict[str, list[ConcretePerformedSideEffect]] left_side_effects: Concretized versions of side effects made by the prepatched state. + :ivar dict[str, list[ConcretePerformedSideEffect]] right_side_effects: Concretized versions of side_effects made by the postpatched state. + + +.. py:class:: TerminalStateInput(args, side_effects: dict[str, list[cozy.side_effect.ConcretePerformedSideEffect]]) + + Stores information about the concretization of a TerminalState. + + :ivar any args: The same Python datastructures as the arguments passed to concrete_examples, except that all claripy symbolic variables are replaced with concrete values. + :ivar dict[str, list[PerformedSideEffect]] side_effects: Concretized side effects outputted by the singleton state. + + diff --git a/_sources/autoapi/cozy/constants/index.rst.txt b/_sources/autoapi/cozy/constants/index.rst.txt new file mode 100644 index 0000000..660a5c7 --- /dev/null +++ b/_sources/autoapi/cozy/constants/index.rst.txt @@ -0,0 +1,26 @@ +cozy.constants +============== + +.. py:module:: cozy.constants + + +Attributes +---------- + +.. autoapisummary:: + + cozy.constants.NULL_PTR + cozy.constants.INT_SIZE + + +Module Contents +--------------- + +.. py:data:: NULL_PTR + :value: 0 + + +.. py:data:: INT_SIZE + :value: 4 + + diff --git a/_sources/autoapi/cozy/directive/index.rst.txt b/_sources/autoapi/cozy/directive/index.rst.txt new file mode 100644 index 0000000..5682296 --- /dev/null +++ b/_sources/autoapi/cozy/directive/index.rst.txt @@ -0,0 +1,264 @@ +cozy.directive +============== + +.. py:module:: cozy.directive + + +Classes +------- + +.. autoapisummary:: + + cozy.directive.Directive + cozy.directive.AssertType + cozy.directive.Assert + cozy.directive.Assume + cozy.directive.VirtualPrint + cozy.directive.ErrorDirective + cozy.directive.Breakpoint + cozy.directive.Postcondition + + +Module Contents +--------------- + +.. py:class:: Directive + + Abstract base class for all directives. + + +.. py:class:: AssertType + + Bases: :py:obj:`enum.Enum` + + + An enum to determine the type of assertion. + + + .. py:attribute:: ASSERT_MUST + :value: 0 + + + This type of assert will be triggered if the assertion condition can be falsified. This assertion type replicates + the behaviour of assertions as used in a typical testing environment. More precisely, this assertion uses + universal quantification. The assertion fails if the following condition does not hold: forall x . P(x), where + x is the program input, and P is the assertion condition. + + + + .. py:attribute:: ASSERT_CAN + :value: 1 + + + This type of assert will be triggered if the assertion condition cannot be satisfied, under the constraints of + the local state. This assertion type is a dual to ASSERT_MUST, and an exact analogue does not exist from + typical testing environments. More precisely, this assertion uses existential quantification. The assertion + fails if the following condition does not hold: exists x . P(x), where x is the program input, P is the + assertion condition, and C is the state's constraints. + + + + .. py:attribute:: ASSERT_CAN_GLOBAL + :value: 2 + + + This is type of assert is like ASSERT_CAN, but is computed under a global setting. If on any path the local + assertion exists x . P(x) holds, then all cases where the assertion failed will be scrubbed from the output. + This is much the same E from computation tree logic, which is also a global property. Note that this assertion + type should only be used in cases where the exploration is complete - ie all states can be explored. + + + +.. py:class:: Assert(addr: int, condition_fun: collections.abc.Callable[[angr.SimState], claripy.ast.bool], info_str: str | None = None, assert_type: AssertType = AssertType.ASSERT_MUST) + + Bases: :py:obj:`Directive` + + + An assert directive sets a breakpoint at a certain address. + + :ivar int addr: The program address this assert is attached to. + :ivar Callable[[SimState], claripy.ast.bool] condition_fun: When the program reaches the desired address, the SimState will be passed to this function, and an assertion condition should be returned. This is then used internally by the SAT solver, along with the state's accumulated constraints. + :ivar str | None info_str: Human readable label for this assertion, printed to the user if the assert is triggered. + :ivar AssertType assert_type: The type of assert. + + Constructor for an Assert object. + + :param int addr: The address at which the assert will be triggered. + :param Callable[[SimState], claripy.ast.bool] condition_fun: When the program reaches the desired address, the SimState will be passed to this function, and an assertion condition should be returned. This is then used internally by the SAT solver, along with the state's accumulated constraints. + :param str | None info_str: Human readable label for this assertion, printed to the user if the assert is triggered. + :param AssertType assert_type: The type of assert to construct. + + + .. py:method:: from_fun_offset(project, fun_name: str, offset: int, condition_fun: collections.abc.Callable[[angr.SimState], claripy.ast.bool], info_str: str | None = None, assert_type: AssertType = AssertType.ASSERT_MUST) + :staticmethod: + + + Factory for an Assert object set at a certain offset from a function start. + + :param cozy.project.Project project: The project which this assert is attached to. The project is used to compute the address of the assert. + :param fun_name str: The name of the function in which this assert will be located. + :param offset int: The offset into the function in which this assert will be located. + :param Callable[[SimState], claripy.ast.bool] condition_fun: When the program reaches the desired address, the SimState will be passed to this function, and an assertion condition should be returned. This is then used internally by the SAT solver, along with the state's accumulated constraints. + :param str | None info_str: Human readable label for this assertion, printed to the user if the assert is triggered. + :param AssertType assert_type: The type of assert to construct. + :rtype: Assert + + + +.. py:class:: Assume(addr: int, condition_fun: collections.abc.Callable[[angr.SimState], claripy.ast.bool], info_str: str | None = None) + + Bases: :py:obj:`Directive` + + + An assume directive sets a breakpoint at a certain address. An assume simply adds an extra constraint to the state's accumulated constraints before resuming execution. An assume is useful for adding a precondition. + + :ivar int addr: The program address this assume is attached to. + :ivar Callable[[SimState], claripy.ast.bool] condition_fun: When the program reaches the desired address, the SimState will be passed to this function, and a condition should be returned. This condition is then attached to the state's set of constraints. + :ivar str | None info_str: Human readable label for this assume. + + Constructor for an Assume object. + + :param int addr: The address at which the assume will be triggered. + :param Callable[[SimState], claripy.ast.bool] condition_fun: When the program reaches the desired address, the SimState will be passed to this function, and an assumption should be returned. This assumption is attached to the state's constraints for future execution. + :param str | None info_str: Human readable label for this assume. + + + .. py:method:: from_fun_offset(project, fun_name: str, offset: int, condition_fun: collections.abc.Callable[[angr.SimState], claripy.ast.bool], info_str: str | None = None) + :staticmethod: + + + Factory for an Assume object set at a certain offset from a function start. + + :param cozy.project.Project project: The project this assume is attached to. + :param fun_name str: The name of the function in which this assume will be located. + :param offset int: The offset into the function in which this assume will be located. + :param Callable[[SimState], claripy.ast.bool] condition_fun: When the program reaches the desired address, the SimState will be passed to this function, and an assumption should be returned. This assumption is attached to the state's constraints for future execution. + :param str | None info_str: Human readable label for this assume. + :return: A new Assume object at the desired function offset. + :rtype: Assume + + + +.. py:class:: VirtualPrint(addr: int, log_fun: collections.abc.Callable[[angr.SimState], claripy.ast.Base], concrete_post_processor: collections.abc.Callable[[claripy.ast.Base], any] | None = None, info_str: str = 'Unknown Virtual Print: ', label=None) + + Bases: :py:obj:`Directive` + + + This directive is used to log some piece of information about the program in a list attached to the state. When execution reaches the desired address, the log function will be called, and the result will be saved inside the state's globals dictionary. + + :ivar int addr: The program address this virutal print is attached to. + :ivar Callable[[SimState], claripy.ast.Base] log_fun: This function takes in the current state and returns a claripy AST which should be logged. This value may be symbolic. + :ivar str info_str: Human readable label for this virtual print. + :ivar str label: Internal label used for side effect alignment. Side effects (including virtual prints) are diffed if they have the same label. + :ivar Callable[[claripy.ast.Base], any] | None concrete_post_processor: concrete_post_processor takes as input a concretized version of the output from log_fun and returns a result which is printed to the screen. For example, a log fun may return state.regs.eax to log the value of eax. But if eax represents a 32 bit signed value, we want to pretty print to negative number. This is where concrete_post_processor is useful. In this example concrete_post_processor would take a concrete bit vector representing a possible value of EAX and return a Python integer (which can be negative). This is what is shown to the user. + + Constructor for a VirtualPrint object. + + :param int addr: The program address this virutal print is attached to. + :param Callable[[SimState], claripy.ast.Base] log_fun: This function takes in the current state and returns a claripy AST which should be logged. This value may be symbolic. + :param Callable[[claripy.ast.Base], any] | None concrete_post_processor: concrete_post_processor takes as input a concretized version of the output from log_fun and returns a result which is printed to the screen. For example, a log fun may return state.regs.eax to log the value of eax. But if eax represents a 32 bit signed value, we want to pretty print to negative number. This is where concrete_post_processor is useful. In this example concrete_post_processor would take a concrete bit vector representing a possible value of EAX and return a Python integer (which can be negative). This is what is shown to the user. + :param str info_str: Human readable label for this virtual print. + :param str label: Internal label used for side effect alignment. Side effects (including virtual prints) are diffed if they have the same label. + + + .. py:method:: effect_concrete_post_processor(concrete_value) + + + .. py:method:: from_fun_offset(project, fun_name: str, offset: int, log_fun: collections.abc.Callable[[angr.SimState], claripy.ast.Base], concrete_post_processor: collections.abc.Callable[[claripy.ast.Base], any] | None = None, info_str: str | None = None, label=None) + :staticmethod: + + + Factory for VirtualPrint object set at a certain offset from a function start. + + :param cozy.project.Project project: The project which this virtual print is attached to. The project is used to compute the address of the virtual print. + :param str fun_name: The name of the function in which this virtual print will be located. + :param int offset: The offset into the function in which this virtual print will be located. + :param Callable[[SimState], claripy.ast.Base] log_fun: This function takes in the current state and returns a claripy AST which should be logged. The return value may be symbolic. + :param Callable[[claripy.ast.Base], any] | None concrete_post_processor: concrete_post_processor takes as input a concretized version of the output from log_fun and returns a result which is printed to the screen. For example, a log fun may return state.regs.eax to log the value of eax. But if eax represents a 32 bit signed value, we want to pretty print to negative number. This is where concrete_post_processor is useful. In this example concrete_post_processor would take a concrete bit vector representing a possible value of EAX and return a Python integer (which can be negative). This is what is shown to the user. + :param str info_str: Human readable label for this virtual print. + :param str label: Internal label used for side effect alignment. Side effects (including virtual prints) are diffed if they have the same label. + :return: A new VirtualPrint object at the desired function offset. + :rtype: VirtualPrint + + + +.. py:class:: ErrorDirective(addr: int, info_str: str | None = None) + + Bases: :py:obj:`Directive` + + + If the program execution reaches the desired address, the state will be considered to be in an errored state and will be moved to the errored cache. This state will have no further execution. + + :ivar int addr: The program address this error directive is attached to. + :ivar str: Human readable information for this error directive. + + Constructor for an ErrorDirective object. + + :param int addr: The program address this error directive is attached to. + :param str info_str: Human readable information for this error directive. + + + .. py:method:: from_fun_offset(project, fun_name: str, offset: int, info_str: str | None = None) + :staticmethod: + + + Factory for ErrorDirective object set at a certain offset from a function start. + + :param cozy.project.Project project: The project this error directive should be attached to. + :param str fun_name: The name of the function in which this error directive will be located. + :param int offset: The offset into the function in which this error directive will be located. + :param str | None info_str: Human readable information for this error directive. + :return: A new ErrorDirective object at the desired function offset. + :rtype: ErrorDirective + + + +.. py:class:: Breakpoint(addr: int, breakpoint_fun: collections.abc.Callable[[angr.SimState], None]) + + Bases: :py:obj:`Directive` + + + This directive is used to halt execution at some particular address, and pass the current state to the provided + breakpoint function, which can then either modify the state or do some other side effect (like executing a Python + print() call). + + :ivar int addr: The program address this breakpoint is attached to. + :ivar Callable[[SimState], None] breakpoint_fun: This function takes in the state reached by the program at the attachment point. + + Constructor for a VirtualPrint object. + + :param int addr: The program address this breakpoint is attached to. + :param Callable[[SimState], None] breakpoint_fun: This function takes in the state reached by the program at the attachment point. + + + .. py:method:: from_fun_offset(project, fun_name: str, offset: int, breakpoint_fun: collections.abc.Callable[[angr.SimState], None]) + :staticmethod: + + + Factory for VirtualPrint object set at a certain offset from a function start. + + :param cozy.project.Project project: The project which this virtual print is attached to. The project is used to compute the address of the virtual print. + :param str fun_name: The name of the function in which this virtual print will be located. + :param int offset: The offset into the function in which this virtual print will be located. + :param Callable[[SimState], None] breakpoint_fun: This function takes in the state reached by the program at the attachment point. + :return: A new Breakpoint object at the desired function offset. + :rtype: Breakpoint + + + +.. py:class:: Postcondition(condition_fun: collections.abc.Callable[[angr.SimState], claripy.ast.bool], info_str: str | None = None, assert_type=AssertType.ASSERT_MUST) + + Bases: :py:obj:`Directive` + + + A Postcondition is a special type of assertion that is executed on terminal states for which execution has been + completed. This is identical to attaching an ASSERT_MUST assertion to all return points. This type of property + is useful for verifying that a property holds in all terminal states. Note that if you are looking to add a + precondition, you can add your proposition to the session before the run via + :py:meth:`cozy.Session.add_constraints`. + + :param Callable[[SimState], claripy.ast.bool] condition_fun: When the program reaches a terminal state, the SimState will be passed to this function, and an assertion condition should be returned. This is then used internally by the SAT solver, along with the state's accumulated constraints. + :param str | None info_str: Human readable label for this postcondition assertion, printed to the user if the assert is triggered. + :param AssertType assert_type: The type of assert to construct. + + diff --git a/_sources/autoapi/cozy/execution_graph/index.rst.txt b/_sources/autoapi/cozy/execution_graph/index.rst.txt new file mode 100644 index 0000000..65a6060 --- /dev/null +++ b/_sources/autoapi/cozy/execution_graph/index.rst.txt @@ -0,0 +1,219 @@ +cozy.execution_graph +==================== + +.. py:module:: cozy.execution_graph + + +Classes +------- + +.. autoapisummary:: + + cozy.execution_graph.ExecutionGraph + + +Functions +--------- + +.. autoapisummary:: + + cozy.execution_graph._serialize_diff + cozy.execution_graph._serialized_field_diff + cozy.execution_graph.dump_comparison + cozy.execution_graph.visualize_comparison + cozy.execution_graph._generate_comparison + + +Module Contents +--------------- + +.. py:function:: _serialize_diff(diff, nice_name_a: collections.abc.Callable[[int], str | None] | None = None, nice_name_b: collections.abc.Callable[[int], str | None] | None = None) + +.. py:function:: _serialized_field_diff(diff: any) + +.. py:function:: dump_comparison(proj_a: cozy.project.Project, proj_b: cozy.project.Project, rslt_a: cozy.session.RunResult, rslt_b: cozy.session.RunResult, comparison_results: cozy.analysis.Comparison, file_name_a: str = 'prepatch', file_name_b: str = 'postpatch', output_file: str = 'cozy-result.json', concrete_post_processor: collections.abc.Callable[[any], any] | None = None, include_vex: bool = False, include_simprocs: bool = False, flag_syscalls: bool = False, include_actions: bool = False, include_debug: bool = False, include_side_effects: bool = True, args: any = [], num_examples: int = 0) -> None + + Generates and saves JSON data for Cozy-Viz. + + Generates JSON data for Cozy-Viz from the results of two symbolic + executions, and saves the result to two files, one for pre and one for post. + + :param Project proj_a: The project associated with the first execuction. + :param Project proj_b: The project associated with the second execuction. + :param RunResult rslt_a: The result of the first execution. + :param RunResult rslt_b: The result of the second execution. + :param analysis.Comparison comparison_results: The comparison we wish to dump. + :param str, optional file_name_a: A name for the prepatch binary, displayed in visualization. + Default "prepatch". + :param str, optional file_name_b: A name for the postpatch binary, displayed in visualization. + Default "postpatch" + :param str, optional output_file: A name for generated JSON file. Default "cozy-result.json". + :param Callable [[any],any] | None, optional concrete_post_processor: This function is used to + post-process concretized versions of args before they are added to the + return string. Some examples of this function include converting an integer + to a negative number due to use of two's complement, or slicing off parts of + the argument based on another part of the input arguments. Default None. + :param bool, optional include_vex: whether to, for each SimState, generate the + corresponding VEX IR and include the result in the JSON. Default False. + :param bool, optional include_simprocs: whether to, for each SimState, flag any + SimProcedure locations occurring in the corrsponding basic block. Default False. + :param bool, optional include_simprocs: whether to include a listing of + SimProcedures called in each basic block. Default False. + :param bool, optional include_actions: whether to include logging of + read/write operations on memory and registers. Default False. + :param bool, optional include_debug: whether to include debugging information + recovered from DWARF metadata. Default False. + :param bool, optional include_side_effects: whether to include cozy side effects, + like virtual prints, if present. Default True. + :param any, optional args: The input arguments to concretize. This argument + may be a Python datastructure, the concretizer will make a deep copy with + claripy symbolic variables replaced with concrete values. See + :class:`cozy.analysis.CompatiblePair`. Default = []. + :param int, optional num_examples: The number of concrete examples to + generate and incorporate into the JSON, for each dead-end state. Default 0. + + +.. py:function:: visualize_comparison(proj_a: cozy.project.Project, proj_b: cozy.project.Project, rslt_a: cozy.session.RunResult, rslt_b: cozy.session.RunResult, comparison_results: cozy.analysis.Comparison, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, include_vex: bool = False, include_simprocs: bool = False, flag_syscalls: bool = False, include_actions: bool = False, include_debug: bool = False, include_side_effects: bool = True, args: any = [], num_examples: int = 0, open_browser=False, port=8080) + + Generates and visualizes JSON data for Cozy-Viz. + + Generates JSON data suitable for visual comparison using Cozy-Viz from the results of two symbolic executions, and launches a server to view the data. + + :param Project proj_a: The project associated with the first execuction. + :param Project proj_b: The project associated with the second execuction. + :param RunResult rslt_a: The result of the first execution. + :param RunResult rslt_b: The result of the second execution. + :param analysis.Comparison comparison_results: The comparison we wish to dump. + :param Callable [[any],any] | None, optional concrete_post_processor: This function is used to + post-process concretized versions of args before they are added to the + return string. Some examples of this function include converting an integer + to a negative number due to use of two's complement, or slicing off parts of + the argument based on another part of the input arguments. Default None. + :param bool, optional include_vex: whether to, for each SimState, generate the + corresponding VEX IR and include the result in the JSON. Default False. + :param bool, optional include_simprocs: whether to include a listing of + SimProcedures called in each basic block. Default False. + :param bool, optional include_actions: whether to include logging of + read/write operations on memory and registers. Default False. + :param bool, optional include_debug: whether to include debugging information + recovered from DWARF metadata. Default False. + :param bool, optional include_side_effects: whether to include cozy side effects, + like virtual prints, if present. Default True. + :param any, optional args: The input arguments to concretize. This argument + may be a Python datastructure, the concretizer will make a deep copy with + claripy symbolic variables replaced with concrete values. See + :class:`cozy.analysis.CompatiblePair`. Default []. + :param int, optional num_examples: The number of concrete examples to + generate and incorporate into the JSON, for each dead-end state. Default 0. + :param bool, optional open_browser: Automatically open cozy-viz with the + comparison data loaded. Default False. + :param int, optional port: The port to serve cozy-viz on. Default 8080. + + +.. py:function:: _generate_comparison(proj_a: cozy.project.Project, proj_b: cozy.project.Project, rslt_a: cozy.session.RunResult, rslt_b: cozy.session.RunResult, comparison_results: cozy.analysis.Comparison, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, include_vex: bool = False, include_simprocs: bool = False, flag_syscalls: bool = False, include_actions: bool = False, include_debug: bool = False, include_side_effects: bool = True, args: any = [], num_examples: int = 0) -> tuple[networkx.DiGraph, networkx.DiGraph] + + Generates JSON data for Cozy-Viz. + + Generates JSON data suitable for visual comparison using Cozy-Viz from the results of two symbolic executions. + + :param Project proj_a: The project associated with the first execuction. + :param Project proj_b: The project associated with the second execuction. + :param RunResult rslt_a: The result of the first execution. + :param RunResult rslt_b: The result of the second execution. + :param Callable [[any],any] | None, optional concrete_post_processor: This function is used to + post-process concretized versions of args before they are added to the + return string. Some examples of this function include converting an integer + to a negative number due to use of two's complement, or slicing off parts of + the argument based on another part of the input arguments. Default None. + :param bool, optional include_vex: whether to, for each SimState, generate the + corresponding VEX IR and include the result in the JSON. Default False. + :param bool, optional include_simprocs: whether to include a listing of + SimProcedures called in each basic block. Default False. + :param bool, optional include_actions: whether to include logging of + read/write operations on memory and registers. Default False. + :param bool, optional include_debug: whether to include debugging information + recovered from DWARF metadata. Default False. + :param bool, optional include_side_effects: whether to include cozy side effects, + like virtual prints, if present. Default True. + :param any, optional args: The input arguments to concretize. This argument + may be a Python datastructure, the concretizer will make a deep copy with + claripy symbolic variables replaced with concrete values. See + :class:`cozy.analysis.CompatiblePair`. Default = []. + :param int, optional num_examples: The number of concrete examples to + generate and incorporate into the JSON, for each dead-end state. Default 0. + + :return (networkx.DiGraph, networkx.DiGraph): A pair of directed graphs + representing the two symbolic executions. + + +.. py:class:: ExecutionGraph(proj: cozy.project.Project, result: cozy.session.RunResult) + + This class is used to store a `networkx.DiGraph`, decorated with `SimStates`, representing the full history of a symbolic program execution. + It constructs an ExecutionGraph, from a project and the results of an + executed project session. + + :ivar Project proj: the project associated with the execution. + :ivar RunResult result: the result of the execution. + + + .. py:method:: _get_bbl_asm(b: angr.block.Block) + + An internal method which renders the assembly corresponding to a given basic block as a formatted string + + :param Block b: The block to render. + :return str: The rendered string. + + + + .. py:method:: _list_simprocs(b: angr.block.Block) + + An internal method which lists SimProcedure calls occuring in a block + + :param Block b: the block to scan + + + + .. py:method:: _has_syscall(b: angr.block.Block) + + An internal method which checks whether the jumpkind of a Block is + a syscall. + + :param Block b: the relevant Block + :return bool: Whether the jumpkind is a syscall + + + + .. py:method:: _list_actions(child: angr.SimState | angr.state_plugins.SimStateHistory, parent: angr.SimState) + + + .. py:method:: reconstruct_bbl_addr_graph() + + Convert the SimState-decorated graph into a graph decorated with + integers, carrying symbolic program execution data in the attributes + `stdout`, `stderr`, `contents` (this holds a basic block), + `constraints`, `actions` (optionally) and `state`. + + :return networkx.DiGraph: The resulting graph. + + + + .. py:method:: reconstruct_bbl_pp_graph() + + Convert the SimState-decorated graph into a graph decorated with + integers, carrying symbolic program execution data in the attributes + `stdout`, `stderr`, `contents`, `constraints` ,`vex` and `state`. The + difference from :func:`reconstruct_bbl_addr_graph` is that the data is + now pretty-printed and suitable for serialization. + + :return networkx.DiGraph: The resulting graph. + + + + .. py:method:: dump_bbp_pp_cytoscape(name: str) + + Dump the graph as cytoscapejs readable JSON. + + :param str name: The filename for the generated json. + + + diff --git a/_sources/autoapi/cozy/functools_ext/index.rst.txt b/_sources/autoapi/cozy/functools_ext/index.rst.txt new file mode 100644 index 0000000..4a6373e --- /dev/null +++ b/_sources/autoapi/cozy/functools_ext/index.rst.txt @@ -0,0 +1,83 @@ +cozy.functools_ext +================== + +.. py:module:: cozy.functools_ext + + +Attributes +---------- + +.. autoapisummary:: + + cozy.functools_ext.T + cozy.functools_ext.U + cozy.functools_ext.V + cozy.functools_ext.B + cozy.functools_ext.C + + +Functions +--------- + +.. autoapisummary:: + + cozy.functools_ext.preorder_mapfold + cozy.functools_ext.preorder_fold + cozy.functools_ext.fmap + cozy.functools_ext.compose + + +Module Contents +--------------- + +.. py:data:: T + +.. py:data:: U + +.. py:data:: V + +.. py:data:: B + +.. py:data:: C + +.. py:function:: preorder_mapfold(val0: any, f: collections.abc.Callable[[any, T], tuple[any, T]], accum0: T, sort=True) -> tuple[any, T] + + Simultaneously maps and folds over a nested Python datastructure in preorder traversal order. The datastructure may consist of arbitrarily nested lists, tuples, dictionaries and sets. Note that for dictionaries, both keys and values will be traversed. + + :param any val0: The datastructure to traverse. + :param Callable[[any, T], tuple[any, T]] f: This function takes as input a value inside the datastructure, the accumulated value and should return a mapped value and newly accumulated value. + :param T accum0: Initial accumulation parameter. + :return: The mapped datastructure and final accumulated value. + :rtype: tuple[any, T] + + +.. py:function:: preorder_fold(val0: any, f: collections.abc.Callable[[any, U], U], accum0: U) -> U + + Folds over a Python datastructure in preorder traversal. The datastructure may consist of arbitrarily nested lists, tuples, dictionaries and sets. Note that for dictionaries, both keys and values will be traversed. + + :param any val0: The datastructure to traverse. + :param Callable[[any, U], U] f: This function takes as input the value inside the datastructure, the accumulated value and should return a new accumulated value. + :param U accum0: Initial accumulation parameter. + :return: The final accumulated value. + :rtype: U + + +.. py:function:: fmap(val0: any, f: collections.abc.Callable[[any], any]) -> any + + Maps a Python datastructure. The datastructure may consist of arbitrarily nested lists, tuples, dictionaries and sets. + + :param any val0: The datastructure to map. Note that for dictionaries, both keys and values will be mapped. + :return: The mapped datastructure. + :rtype: any + + +.. py:function:: compose(f: collections.abc.Callable[[B], C], g: collections.abc.Callable[[Ellipsis], B]) -> collections.abc.Callable[[Ellipsis], C] + + Composes two functions, `f` and `g`, to create a new function h(\*a, \*\*kw) = f(g(\*a, \*\*kw)) + + :param Callable[[B], C] f: The first function to compose. + :param Callable[[...], B] g: The second function to compose. + :return: A newly composed function which takes in an arbitrary number of arguments and keyword arguments, and returns a C. + :rtype: Callable[[...], C] + + diff --git a/_sources/autoapi/cozy/hooks/index.rst.txt b/_sources/autoapi/cozy/hooks/index.rst.txt new file mode 100644 index 0000000..eeba2b9 --- /dev/null +++ b/_sources/autoapi/cozy/hooks/index.rst.txt @@ -0,0 +1,17 @@ +cozy.hooks +========== + +.. py:module:: cozy.hooks + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/cozy/hooks/strlen/index + /autoapi/cozy/hooks/strncmp/index + /autoapi/cozy/hooks/strtok_r/index + + diff --git a/_sources/autoapi/cozy/hooks/strlen/index.rst.txt b/_sources/autoapi/cozy/hooks/strlen/index.rst.txt new file mode 100644 index 0000000..5404b1e --- /dev/null +++ b/_sources/autoapi/cozy/hooks/strlen/index.rst.txt @@ -0,0 +1,40 @@ +cozy.hooks.strlen +================= + +.. py:module:: cozy.hooks.strlen + + +Attributes +---------- + +.. autoapisummary:: + + cozy.hooks.strlen.l + + +Classes +------- + +.. autoapisummary:: + + cozy.hooks.strlen.strlen + + +Module Contents +--------------- + +.. py:data:: l + +.. py:class:: strlen + + Bases: :py:obj:`angr.SimProcedure` + + + .. py:attribute:: max_null_index + :value: None + + + + .. py:method:: run(s, wchar=False, maxlen=None) + + diff --git a/_sources/autoapi/cozy/hooks/strncmp/index.rst.txt b/_sources/autoapi/cozy/hooks/strncmp/index.rst.txt new file mode 100644 index 0000000..819f8f6 --- /dev/null +++ b/_sources/autoapi/cozy/hooks/strncmp/index.rst.txt @@ -0,0 +1,35 @@ +cozy.hooks.strncmp +================== + +.. py:module:: cozy.hooks.strncmp + + +Attributes +---------- + +.. autoapisummary:: + + cozy.hooks.strncmp.l + + +Classes +------- + +.. autoapisummary:: + + cozy.hooks.strncmp.strncmp + + +Module Contents +--------------- + +.. py:data:: l + +.. py:class:: strncmp + + Bases: :py:obj:`angr.SimProcedure` + + + .. py:method:: run(a_addr, b_addr, limit, a_len=None, b_len=None, wchar=False, ignore_case=False) + + diff --git a/_sources/autoapi/cozy/hooks/strtok_r/index.rst.txt b/_sources/autoapi/cozy/hooks/strtok_r/index.rst.txt new file mode 100644 index 0000000..d101613 --- /dev/null +++ b/_sources/autoapi/cozy/hooks/strtok_r/index.rst.txt @@ -0,0 +1,35 @@ +cozy.hooks.strtok_r +=================== + +.. py:module:: cozy.hooks.strtok_r + + +Attributes +---------- + +.. autoapisummary:: + + cozy.hooks.strtok_r.l + + +Classes +------- + +.. autoapisummary:: + + cozy.hooks.strtok_r.strtok_r + + +Module Contents +--------------- + +.. py:data:: l + +.. py:class:: strtok_r + + Bases: :py:obj:`angr.SimProcedure` + + + .. py:method:: run(str_ptr, delim_ptr, save_ptr, str_strlen=None, delim_strlen=None) + + diff --git a/_sources/autoapi/cozy/index.rst.txt b/_sources/autoapi/cozy/index.rst.txt new file mode 100644 index 0000000..3807939 --- /dev/null +++ b/_sources/autoapi/cozy/index.rst.txt @@ -0,0 +1,41 @@ +cozy +==== + +.. py:module:: cozy + + +Subpackages +----------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/cozy/concolic/index + /autoapi/cozy/hooks/index + + +Submodules +---------- + +.. toctree:: + :maxdepth: 1 + + /autoapi/cozy/__main__/index + /autoapi/cozy/analysis/index + /autoapi/cozy/claripy_ext/index + /autoapi/cozy/concrete/index + /autoapi/cozy/constants/index + /autoapi/cozy/directive/index + /autoapi/cozy/execution_graph/index + /autoapi/cozy/functools_ext/index + /autoapi/cozy/log/index + /autoapi/cozy/primitives/index + /autoapi/cozy/project/index + /autoapi/cozy/server/index + /autoapi/cozy/session/index + /autoapi/cozy/side_effect/index + /autoapi/cozy/stubs/index + /autoapi/cozy/terminal_state/index + /autoapi/cozy/types/index + + diff --git a/_sources/autoapi/cozy/log/index.rst.txt b/_sources/autoapi/cozy/log/index.rst.txt new file mode 100644 index 0000000..6d9eed6 --- /dev/null +++ b/_sources/autoapi/cozy/log/index.rst.txt @@ -0,0 +1,68 @@ +cozy.log +======== + +.. py:module:: cozy.log + + +Attributes +---------- + +.. autoapisummary:: + + cozy.log.logger + + +Functions +--------- + +.. autoapisummary:: + + cozy.log.disable + cozy.log.set_level + cozy.log.info + cozy.log.warning + cozy.log.error + cozy.log.debug + cozy.log.critical + + +Module Contents +--------------- + +.. py:data:: logger + +.. py:function:: disable() + + Disable cozy logging + + +.. py:function:: set_level(level) + + Set the level of logging + + +.. py:function:: info(*args, **kwargs) + + Log at the info level + + +.. py:function:: warning(*args, **kwargs) + + Log at the warning level + + +.. py:function:: error(*args, **kwargs) + + Log at the error level + + +.. py:function:: debug(*args, **kwargs) + + Log at the debug level + + +.. py:function:: critical(*args, **kwargs) + + Log at the critical level + + diff --git a/_sources/autoapi/cozy/primitives/index.rst.txt b/_sources/autoapi/cozy/primitives/index.rst.txt new file mode 100644 index 0000000..0bf6b6b --- /dev/null +++ b/_sources/autoapi/cozy/primitives/index.rst.txt @@ -0,0 +1,73 @@ +cozy.primitives +=============== + +.. py:module:: cozy.primitives + + +Attributes +---------- + +.. autoapisummary:: + + cozy.primitives._malloc_name_ctr + + +Functions +--------- + +.. autoapisummary:: + + cozy.primitives.sym_ptr + cozy.primitives.sym_ptr_constraints + cozy.primitives.from_twos_comp + cozy.primitives.to_twos_comp + + +Module Contents +--------------- + +.. py:data:: _malloc_name_ctr + :value: 0 + + +.. py:function:: sym_ptr(arch: Type[archinfo.Arch], name: str | None = None) -> claripy.BVS + + Generates a fresh symbolic pointer for the input architecture. + + :param Type[Arch] arch: The architecture the pointer is for. For example in x64, the paramater passed should be archinfo.ArchAMD64, and a 64 bit symbolic pointer will be returned. + :param str | None name: A human readable name for the symbolic pointer. If None is passed an autogenerated symbolic_ptr_i name is used. + :return: A fresh symbolic bitvector whose size is appropriate for the input architecture. + :rtype: claripy.BVS + + +.. py:function:: sym_ptr_constraints(symbolic_ptr: claripy.ast.bits, concrete_addr: int, can_be_null: bool = True) -> claripy.ast.bool + + Generates claripy expressions for constraining the input symbolic pointer to a specific concrete value. + + :param claripy.ast.bits symbolic_ptr: The symbolic pointer to constrain. + :param int concrete_addr: The concrete address which the symbolic pointer should be equal to. + :param bool can_be_null: If this value is True, the returned constraints will contain a disjunction which allows the symbolic pointer to be NULL. + :return: A claripy proposition constraining the symbolic pointer. + :rtype: claripy.ast.bool + + +.. py:function:: from_twos_comp(val: int, num_bits: int) -> int + + Converts an integer from two's complement form back to an integer, assuming the value is stored in num_bits space. + + :param int val: The two's complement integer to convert. This number must be non-negative. + :param int num_bits: The number of bits used to store the number. + :return: A signed Python representation of the integer. + :rtype: int + + +.. py:function:: to_twos_comp(val: int, num_bits: int) -> int + + Converts an integer value to two's complement representation, assuming the value is stored in num_bits space. + + :param int val: The integer value to convert. + :param int num_bits: The number of bits used to store the integer. + :return: A two's complement representation of the value. This value is non-negative. + :rtype: int + + diff --git a/_sources/autoapi/cozy/project/index.rst.txt b/_sources/autoapi/cozy/project/index.rst.txt new file mode 100644 index 0000000..da1a97f --- /dev/null +++ b/_sources/autoapi/cozy/project/index.rst.txt @@ -0,0 +1,107 @@ +cozy.project +============ + +.. py:module:: cozy.project + + +Classes +------- + +.. autoapisummary:: + + cozy.project.Project + + +Module Contents +--------------- + +.. py:class:: Project(binary_path: str, fun_prototypes: dict[str | int, str] | None = None, load_debug_info: bool = False, **kwargs) + + Represents a project for a single executable + + :ivar angr.Project angr_proj: The angr project created for this cozy project. + :ivar dict[str | int, str] fun_prototypes: Maps function names or function addresses to their type signatures. + + Constructor for a project. + + :param str binary_path: The path to the binary to analyze. + :param dict[str | int, str] | None fun_prototypes: Initial dictionary that maps function names or addresses to their type signatures. If None is passed, fun_prototypes is initialized to the empty dictionary. + :param kwargs: Extra arguments to pass to angr.Project + + + .. py:method:: object_ranges(obj_filter: collections.abc.Callable[[cle.Backend], bool] | None = None) -> list[range] + + Returns the ranges of the objects stored in the executable (for example: ELF objects). If obj_filter is specified, only objects that pass the filter make it into the return list. + + :param Callable[[Backend], bool] | None obj_filter: Used to filter certain objects from the output list. + :return: A list of memory ranges. + :rtype: list[range] + + + + .. py:method:: find_symbol_addr(sym_name: str) -> int + + Finds the rebased addressed of a symbol. Functions are the most common symbol type. + + :param str sym_name: The symbol to lookup. + :return: The rebased symbol address + :rtype: int + + + + .. py:method:: add_prototype(fun: str | int, fun_prototype: str) -> None + + Adds a function prototype to this project. + + :param str | int fun: The function's name or address. + :param str fun_prototype: The function's type signature. + :return: None + :rtype: None + + + + .. py:method:: session(start_fun: str | int | None = None) -> cozy.session.Session + + Returns a new session derived from this project. + + :param str | int | None start_fun: The name or address of the function which this session will start with. If None is specified, then the program will start at the entry point (main function). + :return: The fresh session. + :rtype: Session + + + + .. py:property:: cfg + Returns the control flow graph for this project. This property will cache the cfg in a pickle file + to speed up future runs. This means if you change the underlying program you will need to delete the + .cfg.pickle file located in the same directory as your executable. + + + + .. py:property:: arch + Returns the underlying angr project architecture + + + + .. py:method:: hook_symbol(symbol_name: str, simproc_class: type[angr.SimProcedure], kwargs=None, replace: bool | None = None) -> int + + Hooks a symbol in the angr project. If the symbol is one from libc, this method will also replace + what is stored in :py:attr:`angr.SIM_PROCEDURES["libc"][symbol_name]`. + + :param str symbol_name: The name of the symbol to hook. + :param type[SimProcedure] simproc_class: The class to use to hook the symbol. Note that this is not an instance of SimProcedure, but is instead a reference to the class itself. + :param kwargs: These are the keyword arguments that will be passed to the procedure's `run` method eventually. + :param bool | None replace: Control the behavior on finding that the address is already hooked. If true, silently replace the hook. If false, warn and do not replace the hook. If none (default), warn and replace the hook. + :rtype: int + :return: The address of the new symbol. + + + + .. py:method:: hook_syscall(syscall_name: str, simproc_class: type[angr.SimProcedure]) + + Hooks a syscall in the angr project. + + :param str syscall_name: The name of the syscall to hook. + :param type[SimProcedure] simproc_class: The class to use to hook the symbol. Note that this is not an instance of SimProcedure, but is instead a reference to the class itself. + + + diff --git a/_sources/autoapi/cozy/server/index.rst.txt b/_sources/autoapi/cozy/server/index.rst.txt new file mode 100644 index 0000000..49a5cd0 --- /dev/null +++ b/_sources/autoapi/cozy/server/index.rst.txt @@ -0,0 +1,65 @@ +cozy.server +=========== + +.. py:module:: cozy.server + + +Classes +------- + +.. autoapisummary:: + + cozy.server.VizHandler + + +Functions +--------- + +.. autoapisummary:: + + cozy.server.get_vizroot + cozy.server.start_viz_server + + +Module Contents +--------------- + +.. py:function:: get_vizroot() + +.. py:class:: VizHandler(prepatch, postpatch, *args, **kwargs) + + Bases: :py:obj:`http.server.SimpleHTTPRequestHandler` + + + Simple HTTP request handler with GET and HEAD commands. + + This serves files from the current directory and any of its + subdirectories. The MIME type for files is determined by + calling the .guess_type() method. + + The GET and HEAD requests are identical except that the HEAD + request omits the actual contents of the file. + + + + .. py:method:: do_GET() + + Serve a GET request. + + + +.. py:function:: start_viz_server(pre={}, post={}, open_browser=False, port=8080) + + Serves Cozy-Viz on localhost:8080. + + Useful for visualization of information generated using + :func:`cozy.execution_graph.compare_and_dump`. + + To include comparison data, use the `pre` and `post` arguments, and add + a query string to the URL, like so: `localhost:8080?pre=/pre&post=/post`. + + :param dict, optional pre: served as JSON at `/pre` on the server. Default {}. + :param dict, optional post: served as JSON at `/post` on the server. Default {}. + :param int, optional port: An alternative port to serve on. Default 8080. + + diff --git a/_sources/autoapi/cozy/session/index.rst.txt b/_sources/autoapi/cozy/session/index.rst.txt new file mode 100644 index 0000000..f04949e --- /dev/null +++ b/_sources/autoapi/cozy/session/index.rst.txt @@ -0,0 +1,275 @@ +cozy.session +============ + +.. py:module:: cozy.session + + +Attributes +---------- + +.. autoapisummary:: + + cozy.session._mem_write_ctr + cozy.session._malloc_name_ctr + + +Classes +------- + +.. autoapisummary:: + + cozy.session.RunResult + cozy.session._SessionExploration + cozy.session._SessionDirectiveExploration + cozy.session._SessionBasicExploration + cozy.session.Session + + +Functions +--------- + +.. autoapisummary:: + + cozy.session._on_mem_write + cozy.session._on_simprocedure + cozy.session._save_states + + +Module Contents +--------------- + +.. py:class:: RunResult(deadended: list[cozy.terminal_state.DeadendedState], errored: list[cozy.terminal_state.ErrorState], asserts_failed: list[cozy.terminal_state.AssertFailedState], assume_warnings: list[tuple[cozy.directive.Assume, angr.SimState]], postconditions_failed: list[cozy.terminal_state.PostconditionFailedState], spinning: list[cozy.terminal_state.SpinningState]) + + This class is used for storing the results of running a session. + + :ivar list[DeadendedState] deadended: States that reached normal termination. + :ivar list[ErrorState] errored: States that reached an error state. This may be triggered for example by program errors such as division by 0, or by reaching a :py:class:`cozy.directive.ErrorDirective`. + :ivar list[AssertFailed] asserts_failed: States where an assertion was able to be falsified. + :ivar list[tuple[Assume, SimState]] assume_warnings: An assume warning occurs when a :py:class:`~cozy.directive.Assume` is reached, and the added assumption contradicts the constraints for that state. This means that due to the assumption, the new constraints are not satisfiable. + :ivar list[PostconditionFailedState] postconditions_failed: States where the function returned, and the assertion as part of the postcondition could be falsified. + :ivar list[SpinningState] spinning: States that were stashed due to a loop bound being breached. + + + .. py:property:: assertion_triggered + :type: bool + + Returns True if there were any assertions triggered during this run. + + :return: True if there were assertions triggered. + :rtype: bool + + + + .. py:property:: postcondition_triggered + :type: bool + + Returns True if there were any postcondition assertions triggered during this run. + + :return: True if there were postcondition assertions triggered. + :rtype: bool + + + + .. py:method:: __str__() + + Return str(self). + + + + .. py:method:: report(args: any, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, num_examples: int = 3) -> str + + Creates a composite human readable report with information about errored states, asserts failed, postconditions failed, and spinning states. + + :param any args: The arguments to concretize + :param Callable[[any], any] | None concrete_post_processor: This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two's complement, or slicing off parts of the argument based on another part of the input arguments. + :param int num_examples: The maximum number of concrete examples to show the user. + :return: The report as a string + :rtype: str + + + + .. py:method:: report_errored(args: any, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, num_examples: int = 3) -> str + + Creates a human readable report about a list of errored states. + + :param any args: The arguments to concretize + :param Callable[[any], any] | None concrete_post_processor: This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two's complement, or slicing off parts of the argument based on another part of the input arguments. + :param int num_examples: The maximum number of concrete examples to show the user for each errored state. + :return: The report as a string + :rtype: str + + + + .. py:method:: report_spinning(args: any, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, num_examples: int = 3) -> str + + Creates a human readable report about a list of failed postcondition assertions. + + :param any args: The arguments to concretize + :param Callable[[any], any] | None concrete_post_processor: This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two's complement, or slicing off parts of the argument based on another part of the input arguments. + :param int num_examples: The maximum number of concrete examples to show the user for each assertion failed state. + :return: The report as a string + :rtype: str + + + + .. py:method:: report_postconditions_failed(args: any, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, num_examples: int = 3) -> str + + Creates a human readable report about the failed postcondition assertions. + + :param any args: The arguments to concretize + :param Callable[[any], any] | None concrete_post_processor: This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two's complement, or slicing off parts of the argument based on another part of the input arguments. + :param int num_examples: The maximum number of concrete examples to show the user for each assertion failed state. + :return: The report as a string + :rtype: str + + + + .. py:method:: report_asserts_failed(args: any, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, num_examples: int = 3) -> str + + Creates a human readable report about any failed assertions. + + :param any args: The arguments to concretize + :param Callable[[any], any] | None concrete_post_processor: This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two's complement, or slicing off parts of the argument based on another part of the input arguments. + :param int num_examples: The maximum number of concrete examples to show the user for each assertion failed state. + :return: The report as a string + :rtype: str + + + +.. py:data:: _mem_write_ctr + :value: 0 + + +.. py:function:: _on_mem_write(state) + +.. py:data:: _malloc_name_ctr + :value: 0 + + +.. py:function:: _on_simprocedure(state) + +.. py:function:: _save_states(states) + +.. py:class:: _SessionExploration(session: Session, cache_intermediate_info: bool = True) + + .. py:method:: explore(simgr) + :abstractmethod: + + + +.. py:class:: _SessionDirectiveExploration(session: Session, cache_intermediate_info: bool = True) + + Bases: :py:obj:`_SessionExploration` + + + .. py:method:: check_postconditions(simgr) + + + .. py:method:: explore(simgr) + + +.. py:class:: _SessionBasicExploration(session: Session, cache_intermediate_info: bool = True) + + Bases: :py:obj:`_SessionExploration` + + + .. py:method:: explore(simgr) + + +.. py:class:: Session(proj, start_fun: str | int | None = None) + + A session is a particular run of a project, consisting of attached directives (asserts/assumes). You can malloc memory for storage prior to running the session. Once you are ready to run the session, use the run method. + :ivar angr.SimState state: The initial state tied to this particular session. You can access this member to modify properties of the state before a run. + :ivar cozy.project.Project proj: The Project tied to this session. + :ivar str | int | None start_fun: The starting function tied to this session. If start_fun is None, then the session starts in an entry state. + :ivar list[Directive] directives: The directives added to this session. + :ivar bool has_run: True if the :py:meth:`cozy.project.Session.run` method has been called, otherwise False. + + Constructs a session derived from a project. The :py:meth:`cozy.project.Project.session` is the preferred method for creating a session, not this constructor. + + + .. py:method:: store_fs(filename: str, simfile: angr.SimFile) -> None + + Stores a file in a virtual filesystem available during execution. This method simply forwards the arguments to state.fs.insert. + + :param str filename: The filename of the new file. + :param angr.SimFile simfile: The file to make available to the simulated program. + :return: None + :rtype: None + + + + .. py:method:: malloc(num_bytes: int, name=None) -> int + + Mallocs a fixed amount of memory using the angr heap simulation plugin. Useful for setting things up in memory before the :py:meth:`~cozy.project.Project.run` method is called. + + :param int num_bytes: The number of bytes to allocate. + :return: A pointer to the allocated memory block. + :rtype: int + + + + .. py:method:: store(addr: int, data: claripy.ast.bits, **kwargs) + + Stores data at some address. This method simply forwards the arguments to state.memory.store. + + :param int addr: Address to store the data at. + :param claripy.ast.bits data: The data to store in memory. + :param kwargs: Additional keyword arguments to pass to state.memory.store + + + + .. py:property:: memory + + + .. py:property:: mem + Access memory using a dict-like interface. This property simply forwards to state.mem + + + + .. py:method:: add_directives(*directives: cozy.directive.Directive) -> None + + Adds multiple directives to the session. + + :param Directive directives: The directives to add. + :return: None + :rtype: None + + + + .. py:method:: add_constraints(*constraints: claripy.ast.bool) -> None + + Adds multiple constraints to the session's state. + + :param claripy.ast.bool constraints: The constraints to add + :return: None + :rtype: None + + + + .. py:property:: start_fun_addr + + + .. py:method:: _call(args: list[claripy.ast.bits], cache_intermediate_info: bool = True, ret_addr: int | None = None) -> angr.sim_manager.SimulationManager + + + .. py:method:: _session_exploration(cache_intermediate_info: bool = True) -> _SessionExploration + + + .. py:method:: _run_result(simgr: angr.sim_manager.SimulationManager, sess_exploration: _SessionExploration) -> RunResult + + + .. py:method:: run(args: list[claripy.ast.bits], cache_intermediate_info: bool = True, ret_addr: int | None = None, loop_bound: int | None = None) -> RunResult + + Runs a session to completion, either starting from the start_fun used to create the session, or from the program start. Note that currently a session may be run only once. If run is called multiple times, a RuntimeError will be thrown. + + :param list[claripy.ast.bits] args: The arguments to pass to the function. angr will utilize the function's type signature to figure out the calling convention to use with the arguments. + :param bool cache_intermediate_info: If this flag is True, then information about intermediate states will be + cached. This is required for dumping the execution graph which is used in visualization. + :param int | None ret_addr: What address to return to if calling as a function + :param int | None loop_bound: Sets an upper bound on loop iteration count. Useful for programs with non-terminating loops. + :return: The result of running this session. + :rtype: RunResult + + + diff --git a/_sources/autoapi/cozy/side_effect/index.rst.txt b/_sources/autoapi/cozy/side_effect/index.rst.txt new file mode 100644 index 0000000..3c7f8a0 --- /dev/null +++ b/_sources/autoapi/cozy/side_effect/index.rst.txt @@ -0,0 +1,94 @@ +cozy.side_effect +================ + +.. py:module:: cozy.side_effect + + +Classes +------- + +.. autoapisummary:: + + cozy.side_effect.PerformedSideEffect + cozy.side_effect.ConcretePerformedSideEffect + + +Functions +--------- + +.. autoapisummary:: + + cozy.side_effect.perform + cozy.side_effect.get_effects + cozy.side_effect.get_channel + cozy.side_effect.levenshtein_alignment + cozy.side_effect.test_levenshtein_alignment + + +Module Contents +--------------- + +.. py:class:: PerformedSideEffect(state_history: angr.state_plugins.SimStateHistory, body, concrete_post_processor=None, label=None) + + This class encapsulates the idea of a side effect whose body may consist of mixed symbolic and concrete values. + + :param SimStateHistory state_history: The point in execution at which the side effect was performed. + :param body: The body must be a mixture of string-keyed Python dictionaries, Python lists, Python tuples, and claripy concrete and symbolic values. + :param concrete_post_processor: The optional post processing function to apply to concretized versions of the side effect's body if post processing is required. + :param label: The label to apply to the side effect, used to align instances of side effects when making comparisons. For example, if you have two call sites to a network send function, you would want different labels for the two different call locations. + + +.. py:class:: ConcretePerformedSideEffect(base_effect: PerformedSideEffect, state_history: angr.state_plugins.SimStateHistory, body, concrete_post_processor=None, label=None) + + This class encapsulates the idea of a side effect whose body previously consisted of mixed symbolic and concrete + values, but now consists of only concrete values (ie, BVV and FPV). At the point of the construction, this concrete + value has not yet been passed through the user provided concrete_post_processor, whose job is to take the concrete value + and transform the BVV values into ordinary Python values. The purpose of concrete_post_processor for instance could be + to transform a two's complement BVV that is negative into a negative Python integer. This will make the display + more readable to the user. Hence, the concrete_post_processor can be viewed as a post-processing function. + + :param PerformedSideEffect base_effect: The non-symbolic side effect that was concretized. + :param SimStateHistory state_history: The point in execution at which the side effect was performed. + :param body: The body must be a mixture of string-keyed Python dictionaries, Python lists, Python tuples, and claripy concrete values. + :param concrete_post_processor: The optional post processing function to apply to concretized versions of the side effect's body if post processing is required. + :param label: The label to apply to the side effect, used to align instances of side effects when making comparisons. For example, if you have two call sites to a network send function, you would want different labels for the two different call locations. + + + .. py:property:: mapped_body + + +.. py:function:: perform(state: angr.SimState, channel: str, body, concrete_post_processor=None, label=None) + + Attaches a side effect to the passed state. + + :param SimState state: The state in which the side effect should be performed and attached to. + :param str channel: The name of the channel in which the side effect should be performed. Different side effects should be sent down different channels. For example, the virtual print side effect channel is different from the networking side effect channel. + :param body: The body must be a mixture of string-keyed Python dictionaries, Python lists, Python tuples, claripy concrete values, and claripy symbolic values. This should represent the payload of the side effect. + :param concrete_post_processor: The optional post processing function to apply to concretized versions of the side effect's body if post processing is required. + :param label: The label to apply to the side effect, used to align instances of side effects when making comparisons. For example, if you have two call sites to a network send function, you would want different labels for the two different call locations. + + +.. py:function:: get_effects(state: angr.SimState) -> dict[str, list[PerformedSideEffect]] + + Gets the side effects attached to a specific state + + :param SimState state: The state from which we should retrieve the side effects. + :rtype: dict[str, list[PerformedSideEffect]] + :return: All side effects attached to this state. Each entry in the dictionary is a different channel. + + +.. py:function:: get_channel(state: angr.SimState, channel: str) -> list[PerformedSideEffect] + + Gets the side effects from the given channel attached to a specific state. An empty list is returned for channels + in which the channel has not yet been used. + + :param SimState state: The state from which we should retrieve the side effects channel. + :param str channel: The name of the channel + :rtype: list[PerformedSideEffect] + :return: A list of side effects for the requested channel. + + +.. py:function:: levenshtein_alignment(lst_a, lst_b, key=None) + +.. py:function:: test_levenshtein_alignment() + diff --git a/_sources/autoapi/cozy/stubs/index.rst.txt b/_sources/autoapi/cozy/stubs/index.rst.txt new file mode 100644 index 0000000..ed4bd16 --- /dev/null +++ b/_sources/autoapi/cozy/stubs/index.rst.txt @@ -0,0 +1,89 @@ +cozy.stubs +========== + +.. py:module:: cozy.stubs + + +Attributes +---------- + +.. autoapisummary:: + + cozy.stubs.unstripped_binary_path + + +Classes +------- + +.. autoapisummary:: + + cozy.stubs.Stubber + + +Module Contents +--------------- + +.. py:class:: Stubber(binary_path: str) + + A Stubber outputs Python source code that represents stubs for the callees of a given binary function. + + If `foo` is the function to be analyzed, and `foo` calls a two-argument function `bar`, then the following stub + will be among those generated for `foo`: + + .. code-block:: python + + class bar(angr.SimProcedure): + def run(self, arg0, arg1): + pass + + The stub can then be filled out and used during symbolic execution. + + :param str binary_path: Path for the binary under analysis. + :ivar angr.analyses.cfg.cfg_fast.CFGFast cfg: CFG for the binary. + :ivar networkx.classes.multidigraph.MultiDiGraph cg: Call graph for the binary. + + + .. py:method:: extract_func(func_name: str) -> angr.knowledge_plugins.functions.function.Function + + Returns the function with the given name from the CFG. + + :param str func_name: Name of the function to extract. + :return: Function with the given name. + :rtype: angr.knowledge_plugins.functions.function.Function + + + + .. py:method:: get_callees(func_name: str) -> list[angr.knowledge_plugins.functions.function.Function] + + Returns the list of functions called by function `func_name`. + + :param str func_name: Name of the caller function. + :return: The list of functions called by `func_name`. + :rtype: list[angr.knowledge_plugins.functions.function.Function] + + + + .. py:method:: make_stub(func: angr.knowledge_plugins.functions.function.Function) -> str + + Returns an empty Python class definition (in string form) named after `func` that inherits from `angr.SimProcedure`. + + :param angr.knowledge_plugins.functions.function.Function func: Function to be stubbed. + :return: Empty Python class definition representing a symbolic execution stub for function `func`. + :rtype: str + + + + .. py:method:: make_callee_stubs(func_name: str) -> list[str] + + Returns a list of stubs for the callees of function `func_name`. + + :param str func_name: Name of the caller function. + :return: Stubs for the callees of function `func_name`. + :rtype: list[str] + + + +.. py:data:: unstripped_binary_path + :value: '../test_programs/GridIDPS/build/amp_challenge_arm.ino_unstripped.elf' + + diff --git a/_sources/autoapi/cozy/terminal_state/index.rst.txt b/_sources/autoapi/cozy/terminal_state/index.rst.txt new file mode 100644 index 0000000..bba2a93 --- /dev/null +++ b/_sources/autoapi/cozy/terminal_state/index.rst.txt @@ -0,0 +1,171 @@ +cozy.terminal_state +=================== + +.. py:module:: cozy.terminal_state + + +Classes +------- + +.. autoapisummary:: + + cozy.terminal_state.TerminalState + cozy.terminal_state.DeadendedState + cozy.terminal_state.SpinningState + cozy.terminal_state.AssertFailedState + cozy.terminal_state.PostconditionFailedState + cozy.terminal_state.ErrorState + + +Module Contents +--------------- + +.. py:class:: TerminalState(state: angr.SimState, state_id: int, state_type_str: str) + + Stores information pertaining specifically to a single SimState. + + :ivar SimState state: The state we are storing information about. + :ivar int state_id: The index of this particular state in the corresponding list in RunResult. Note that errored states have separate state_ids from deadended states. Therefore a particular input state here is uniquely identified by the pair (state_id, state_tag), not just state_id by itself. + :ivar str state_type_str: A string representation of the state's type + + + .. py:property:: std_out + :type: bytes + + The data that has been written to stdout when the program is in this state. + + :getter: The data written to stdout + :type: bytes + + + + .. py:property:: std_err + :type: bytes + + The data that has been written to stderr when the program is in this state. + + :getter: The data written to stderr + :type: bytes + + + + .. py:property:: side_effects + :type: dict[str, list[cozy.side_effect.PerformedSideEffect]] + + + + .. py:property:: virtual_prints + :type: list[cozy.side_effect.PerformedSideEffect] + + Returns the output of the virtual prints that occurred while reaching this state. + + :getter: A list of VirtualPrint directives, along with the values they produced. + :type: list[tuple[VirtualPrint, claripy.ast.Base]] + + + + .. py:property:: mem_writes + :type: portion.IntervalDict + + The memory writes that occurred while reaching this state. + + :getter: An interval dictionary, with the keys being ranges and the values being tuple[int, frozenset[int]]. The first element of the tuple is a unique placeholder, the second element of the tuple are the possible instruction pointer values that wrote to this memory. + :type: P.IntervalDict + + + + .. py:property:: malloced_names + :type: portion.IntervalDict + + + + .. py:method:: concrete_examples(args: any, num_examples=3) -> list[cozy.concrete.TerminalStateInput] + + Concretizes the arguments used to put the program in this singleton state. + + :param any args: The input arguments to concretize. This argument may be a Python datastructure, the concretizer will make a deep copy with claripy symbolic variables replaced with concrete values. + :param int num_examples: The maximum number of concrete examples to generate for this singleton state. + :return: A list of concrete inputs that satisfies the constraints attached to the state. + :rtype: list[TerminalStateInput] + + + +.. py:class:: DeadendedState(state: angr.SimState, state_id: int) + + Bases: :py:obj:`TerminalState` + + + This class is used to indicate that execution terminated normally in the contained state. + + Constructor for DeadendedState + + :ivar SimState state: The state that terminated normally. + :ivar int state_id: The identifer of the state, determined by its position in the list :py:obj:`cozy.project.RunResult.deadended` + + +.. py:class:: SpinningState(state: angr.SimState, state_id: int) + + Bases: :py:obj:`TerminalState` + + + This class is used to indicate that the contained state was killed by the LocalLoopSeer, indicating that an upper + bound on number of loop iterations was reached. + + Constructor for SpinningState + + :ivar SimState state: The state that was spinning + :ivar int state_id: The identifer of the state, determined by its position in the list :py:obj:`cozy.project.RunResult.spinning` + + +.. py:class:: AssertFailedState(assertion: cozy.directive.Assert, cond: claripy.ast.bool, failure_state: angr.SimState, state_id: int) + + Bases: :py:obj:`TerminalState` + + + This class is used to indicate that execution failed due to an :py:class:`~cozy.directive.Assert` being satisfiable. + + :ivar Assert assertion: The assertion that was triggered. + :ivar claripy.ast.bool cond: The condition that caused the assertion to trigger + + Constructor for AssertFailedState + + :param Assert assertion: The assertion that was triggered. + :param claripy.ast.bool: The condition which if falsified will trigger the assertion. + :param SimState failure_state: The state that was created to test the assertion. + :param int state_id: The identifier of the state, determined by its position in the list :py:obj:`cozy.project.RunResult.asserts_failed` + + +.. py:class:: PostconditionFailedState(postcondition: cozy.directive.Postcondition, cond: claripy.ast.bool, failure_state: angr.SimState, state_id: int) + + Bases: :py:obj:`TerminalState` + + + This class is used to indicate that execution failed due to an :py:class:`~cozy.directive.Assert` being satisfiable. + + :ivar Assert assertion: The assertion that was triggered. + :ivar claripy.ast.bool cond: The condition that caused the assertion to trigger + + Constructor for AssertFailedState + + :param Postcondition assertion: The postcondition that was triggered. + :param claripy.ast.bool: The condition which if falsified will trigger the postcondition assertion. + :param SimState failure_state: The state that was created to test the postcondition assertion. + :param int state_id: The identifier of the state, determined by its position in the list :py:obj:`cozy.project.RunResult.postconditions_failed` + + +.. py:class:: ErrorState(error_record: angr.sim_manager.ErrorRecord, state_id: int) + + Bases: :py:obj:`TerminalState` + + + This class is used to indicate a state that resulted in an error (either my an execution error or :py:class:`~cozy.directive.ErrorDirective`). + + :ivar SimError error: The error that was thrown. + :ivar traceback: The traceback attached to the error. + + Constructor for ErrorState + + :param ErrorRecord error_record: The error thrown for this state. + :param int state_id: The identifier of the state, determined by it's position in the list :py:obj:`cozy.project.RunResult.errored` + + diff --git a/_sources/autoapi/cozy/types/index.rst.txt b/_sources/autoapi/cozy/types/index.rst.txt new file mode 100644 index 0000000..c787195 --- /dev/null +++ b/_sources/autoapi/cozy/types/index.rst.txt @@ -0,0 +1,38 @@ +cozy.types +========== + +.. py:module:: cozy.types + + +Functions +--------- + +.. autoapisummary:: + + cozy.types.register_type + cozy.types.register_types + + +Module Contents +--------------- + +.. py:function:: register_type(type_definition: str, arch: archinfo.Arch) -> angr.sim_type.SimType + + Parses a C-style type definition given in the input string, assuming the given architecture. Registers this type with angr. + + :param str type_definition: The type definition, given in C-style format. + :param Arch arch: The architecture this type should be used with. + :return: The parsed typed. + :rtype: SimType + + +.. py:function:: register_types(type_definition: str) -> angr.sim_type.SimType + + Parses a series of type definition given in the input string. Registers this type with angr. + + :param str type_definition: The type definition, given in C-style format. + :param Arch arch: The architecture this type should be used with. + :return: The parsed typed. + :rtype: SimType + + diff --git a/_sources/autoapi/index.rst.txt b/_sources/autoapi/index.rst.txt new file mode 100644 index 0000000..e0cf3eb --- /dev/null +++ b/_sources/autoapi/index.rst.txt @@ -0,0 +1,11 @@ +API Reference +============= + +This page contains auto-generated API reference documentation [#f1]_. + +.. toctree:: + :titlesonly: + + /autoapi/cozy/index + +.. [#f1] Created with `sphinx-autoapi `_ \ No newline at end of file diff --git a/_sources/concolic.rst.txt b/_sources/concolic.rst.txt new file mode 100644 index 0000000..310015f --- /dev/null +++ b/_sources/concolic.rst.txt @@ -0,0 +1,83 @@ +Using Concolic Execution +================================= + +Concolic execution is a state exploration strategy that uses concrete values to +guide symbolic execution. cozy performs concolic execution slightly differently +than what you might be used to with angr, including angr's Unicorn engine. In +our implementation of concolic execution, concrete values for each symbol are +chosen, and symbolic execution proceeds as normal. When a branch is created in +the symbolic execution, the concrete values are substituted into the constraints +of both children, and the children that evaluate to false are placed in a deferred +stash. Once execution reaches a terminal state, a deferred child is selected using +some heuristic, and its constraints are used to generate a new concrete input. +This is equivalent to negating a portion of the path constraint that you might +typically see in literature on concolic execution. Our core implementation of +concolic execution can be used outside of the cozy workflow as a standalone +exploration technique. The relevant classes if you wish to do this can be found +in the :py:mod:`cozy.concolic.exploration` module. + +Since cozy is a comparative framework, we implement an additional strategy +called joint concolic execution. In joint concolic execution, we alternate +between the two programs when generating concrete values. After a concrete value +is implemented, we run both programs on the same concrete values, which automatically +leads to compatible state pairs being generated. + +Note that like typical symbolic execution, concolic execution can be complete +if so desired. The execution is complete if there are no deferred states in either +program. However the primary benefit of concolic execution is that we can explore +promising paths at the expense of an incomplete analysis. The promising paths in cozy +are determined by heuristics. There are two different heuristics required for +concolic execution: + +1. A termination heuristic, which determines when to halt concolic execution. +2. A candidate heuristic, which determines which deferred state to explore next. + +Some pre-made heuristics can be found in :py:mod:`cozy.concolic.heuristics`. Let's +walk through an example of using a joint concolic session to explore how to use +:py:class:`cozy.concolic.session.JointConcolicSession`. + +Let's assume that we already have a prepatched and postpatched cozy project set up:: + + sess_prepatched = proj_prepatched.session('process_sensor_data') + add_directives(sess_prepatched) + initialize_state(sess_orig) + + sess_postpatched = proj_postpatched.session('process_sensor_data') + add_directives(sess_postpatched) + initialize_state(sess_postpatched) + +We are now ready to create and run a joint concolic session. We must remember to pass +a set of symbols used in the program to the +:py:meth:`~cozy.concolic.session.JoinConcolicSession.run` method, as we need to assign +concrete values to every symbolic value. We also construct the candidate and termination +heuristics:: + + joint_sess = JointConcolicSession(sess_prepatched, sess_postpatched, + candidate_heuristic_left=BBTransitionCandidate(), + candidate_heuristic_right=BBTransitionCandidate(), + termination_heuristic_left=CyclomaticComplexityTermination.from_session(sess_prepatched), + termination_heuristic_right=CyclomaticComplexityTermination.from_session(sess_postpatched)) + (prepatched_results, postpatched_results) = joint_sess.run([], [], symbols) + +Here we are setting heuristics so that we do not explore every state. Instead, our candidate +heuristic will pick states with the most unique basic block edge transitions in their history, +and the exploration will be terminated once the number of terminal states exceeds the +cyclomatic complexity of the session's function. The return result from the +:py:meth:`~cozy.concolic.session.JoinConcolicSession.run` method gives two +:py:class:`~cozy.session.RunResult` objects, which can be directly be used by +:py:class:`cozy.analysis.Comparison`:: + + comparison_results = analysis.Comparison(prepatched_results, postpatched_results) + +We can of course visualize the results in the browser:: + + # Here args is not the function arguments, but rather the contents of the memory + # mutated by initialize_state + execution_graph.visualize_comparison(proj_prepatched, proj_postpatched, + prepatched_results, postpatched_results, + comparison_results, + concrete_arg_mapper=concrete_mapper, args=args, + num_examples=2, open_browser=True) + +The full implementation used in this guide can be found at +https://github.com/draperlaboratory/cozy/blob/main/examples/cmp_weather_v5_concolic.py \ No newline at end of file diff --git a/_sources/gettingstarted.rst.txt b/_sources/gettingstarted.rst.txt new file mode 100644 index 0000000..2aeb61a --- /dev/null +++ b/_sources/gettingstarted.rst.txt @@ -0,0 +1,357 @@ +Getting Started +================================= + +On this page we will cover the architecture of cozy and how you can use +it to compare two binary programs. cozy is based on the angr symbolic +execution framework, so we support the same architectures as angr. We +will be following the null_deref example, which can be found in the +examples and test_programs folder in the cozy repository. The null_deref +source code is a very simple C program which writes the integer 42 to +some location in memory: + +**Prepatched null_deref (first program being compared)**:: + + #include + + void my_fun(int *num) { + *num = 42; + } + + int main(int argc, char *argv[]) { + int my_num; + my_fun(&my_num); + printf("my_num: %d\n", my_num); + return 0; + } + +**Postpatched null_deref_patched (second program being compared)**:: + + #include + + void my_fun(int *num) { + if (num != NULL) { + *num = 42; + } + } + + int main(int argc, char *argv[]) { + int my_num; + my_fun(&my_num); + printf("my_num: %d\n", my_num); + return 0; + } + +Let's assume that we are compiling for x64 architecture. In this case we +are interested in comparing my_fun between the two programs. Much like angr, +cozy can be used interactively through a Python REPL or through a Python script. + +========================== +How cozy makes comparisons +========================== + +To make comparisons between two programs with different function +implementations, cozy uses symbolic execution. Both programs are fed +the same symbolic input, and cozy runs symbolic execution until all states +terminate. At the end of execution, we have a list of deadended (terminated) +states from the prepatched program, and a list of deadended states from the +postpatched program. Each of these states have constraints associated with +them that were collected as the program stepped through symbolic execution. + +Suppose that we take some state A from the prepatched run, and some state +B from the postpatched run. We say that A and B are *compatible* if the +constraints associated with the A and B are jointly satisfiable. In +pseudocode syntax, this roughly means that the following is True:: + + is_sat(A.constraints & B.constraints) + +Recall that the input to our functions are symbolic variables, so the +set of constraints is in terms of these symbolic variables. We can think +of the constraints as creating a predicate that exactly determines the +subset of the input that leads to a specific state. Taking the conjunction +of the constraints is therefore equivalent to creating a predicate +that restricts the set of input values to the intersection of the input +set for state A and state B. If this predicate is satisfiable, then +this intersection of sets is nonempty, which means that there is at +least one concrete input that will cause the program to end in state A +in the prepatched program and state B in the postpatched program. + +Therefore the naive approach is to compare all pairs of terminal states +from the prepatched and postpatched and check for satisfiability. cozy +makes an optimization by using memoization, so in practice compatibility +checks over most programs should be fast. cozy is also capable of generating +concrete examples, which is useful for generating test cases and +walking through program execution. + +=================== +Example Walkthrough +=================== + +Let's open a Python REPL and import the required libraries:: + + import cozy + from cozy.project import Project + from cozy.directive import Assume, Assert + import claripy + +Let's begin by creating cozy projects for the two programs given +previously. A Project is a cozy class that encapsulates a single +program:: + + proj_prepatched = Project("null_deref") + proj_postpatched = Project("null_deref_patched") + +To execute the my_fun function, angr needs to know the function signature +of the functions. This information is typically not retained in the binary, +so we need to determine that with some other method. In this case we have +the source code, so we can add the function signature quite easily:: + + proj_prepatched.add_prototype("my_fun", "void f(int *a)") + proj_postpatched.add_prototype("my_fun", "void f(int *a)") + +We now need to create sessions from each project. A session is created +from a specific project, and represents a single run of symbolic +execution. Here we pass "my_fun" to the +:py:meth:`~cozy.project.Project.session` method, which indicates that +we are going to be running the "my_fun" function:: + + sess_prepatched = proj_prepatched.session("my_fun") + sess_postpatched = proj_postpatched.session("my_fun") + +Since we will only be comparing the my_fun function, we need to create +the symbolic value to pass to the functions:: + + arg0 = claripy.BVS("num_arg", 64) + +The symbolic value arg0 has 64 bits because it represents a pointer +on a 64-bit architecture. + +Alternatively we could have used the :py:func:`cozy.primitives.sym_ptr` helper +function to create the claripy symbolic variable:: + + import archinfo + arg0 = cozy.primitives.sym_ptr(archinfo.ArchAMD64, "num_arg") + +We will now constrain arg0 to be either NULL or be equal to a valid memory +address in our two sessions. Currently angr has limited support for symbolic +memory addressing, so we will malloc space for our integers then constrain +arg0 accordingly:: + + addr_prepatched = sess_prepatched.malloc(4) # integers are 4 bytes on the target arch + sess_prepatched.add_constraints((arg0 == 0x0) | (arg0 == addr_prepatched)) + addr_postpatched = sess_postpatched.malloc(4) + sess_postpatched.add_constraints((arg0 == 0x0) | (arg0 == addr_postpatched)) + +So before any execution we have constrained arg0 to be either NULL +(0x0) or a concrete 64-bit address returned by +:py:meth:`~cozy.project.Session.malloc`. + +================================ +Directives - Assumes and Asserts +================================ + +cozy provides support for *directives*, which are attached to specific +program instructions. Two basic directives that you should know about +are :py:class:`cozy.directive.Assume` and :py:class:`cozy.directive.Assert`. +Assume and assert function by pausing execution once a specific instruction +is reached and adding constraints to the SMT solver. Assumes are used for +adding preconditions, and are often set to be triggered at the start of +functions. Asserts are triggered if there exists an input that will cause +the assert to evaluate to false. Note that directives do not change the +code being executed: they work more or less in the same way as debug +breakpoints. + +To demonstrate that a null dereference can occur in the prepatched binary +and not in the postpatched binary, let's add asserts to specific addresses. +Running the binaries through a tool like Ghidra reveals that the NULL +dereference occurs at an offset of 0x10 from the start of my_fun in the +prepatched binary. At this point the address being dereferenced is stored +in the RAX register. Let's create a directive that encodes these observations:: + + mem_write_okay_prepatched = Assert.from_fun_offset( + project=proj_prepatched, + fun_name="my_fun", + offset=0x10, + condition_fun=lambda state: state.regs.rax != 0x0, + info_str="Dereferencing null pointer" + ) + +When execution reaches my_fun+0x10, the evaluation will be halted and +cozy will pass the angr.SimState to the condition_fun and will check to see +if it is possible to find an input value that will trigger the condition. +Let's add the directive to the prepatch session:: + + sess_prepatched.add_directives(mem_write_okay_prepatched) + +Let's invoke the prepatched my_fun with arg0 as the symbolic input via the +:py:meth:`~cozy.project.Session.run` method:: + + run_result = sess_prepatched.run([arg0]) + print(run_result) + +Which prints the following result that informs us that an assertion was triggered:: + + RunResult(1 deadended, 0 errored, 1 asserts_failed, 0 assume_warnings, 0 postconditions_failed, 0 spinning) + +To view a report on what went wrong with the assertion, let's create +a report using the :py:meth:`~cozy.project.RunResult.report_asserts_failed` +method:: + + print(run_result.report([arg0])) + +Which prints off the human-readable report:: + + Errored Report: + No errored states + + Asserts Failed Report: + Assert for address 0x401179 was triggered: + Dereferencing null pointer + Here are 1 concrete input(s) for this particular assertion: + 1. + [] + + Postconditions Failed Report: + No postcondition failure triggered + + Spinning (Looping) States Report: + No spinning states were reported + +As part of the report, cozy reports that the concretized input that leads to +this assertion being triggered occurs when the input argument is 0. + +Now let's make another assert for the postpatched session and verify +that no NULL dereference occurs in the postpatch:: + + mem_write_okay_postpatched = Assert.from_fun_offset( + project=proj_postpatched, + fun_name="my_fun", + offset=0x17, + condition_fun=lambda state: state.regs.rax != 0x0, + info_str="Dereferencing null pointer" + ) + sess_postpatched.add_directives(mem_write_okay_postpatched) + run_result = sess_postpatched.run() + print(run_result) + +In the console we see that no assertions were triggered:: + + RunResult(1 deadended, 0 errored, 0 asserts_failed, 0 assume_warnings, 0 postconditions_failed) + +====================== +Making the Comparisons +====================== + +To compare two program executions, we need two :py:class:`cozy.project.RunResult` objects. +Let's create fresh sessions and re-run without any directives attached. This time we will make use of +:py:func:`primitive.sym_ptr_constraints` to generate the constraints instead of creating them manually:: + + sess_prepatched = proj_prepatched.session("my_fun") + sess_postpatched = proj_postpatched.session("my_fun") + addr_prepatched = sess_prepatched.malloc(cozy.constants.INT_SIZE) + sess_prepatched.add_constraints(cozy.primitives.sym_ptr_constraints(arg0, addr_prepatched, can_be_null=True)) + addr_postpatched = sess_postpatched.malloc(cozy.constants.INT_SIZE) + sess_postpatched.add_constraints(cozy.primitives.sym_ptr_constraints(arg0, addr_postpatched, can_be_null=True)) + +Now let's run both of our new sessions:: + + prepatched_result = sess_prepatched.run([arg0]) + postpatched_result = sess_postpatched.run([arg0]) + +We can inspect the results object to see how many states we are dealing with:: + + print(prepatched_result) + print(postpatched_result) + +This prints the following messages:: + + RunResult(1 deadended, 0 errored, 0 asserts_failed, 0 assume_warnings, 0 postconditions_failed, 0 spinning) + RunResult(2 deadended, 0 errored, 0 asserts_failed, 0 assume_warnings, 0 postconditions_failed, 0 spinning) + +We can now make a comparison between these two terminated results. Constructing a Comparison object is used to do +the comparison computation:: + + comparison_results = cozy.analysis.Comparison(prepatched_result, postpatched_result, simplify=True) + +To view a human readable report, we can now call the :py:meth:`cozy.analysis.Comparison.report` method, which +will convert the :py:class:`~cozy.analysis.Comparison` to a human readable summary:: + + print(comparison_results.report([arg0])) + +We now see the human readable report + +.. code-block:: text + :linenos: + + STATE PAIR (0, DEADENDED_STATE), (0, DEADENDED_STATE) are different + Memory difference detected for 0,0: + {'range(0x0, 0x4)': (, )} + Instruction pointers for these memory writes: + {'range(0x0, 0x4)': (frozenset({}), frozenset())} + Register difference detected for 0,0: + {'eflags': (, ), 'flags': (, ), 'rflags': (, )} + Here are 1 concrete input(s) for this particular state pair: + 1. + Input arguments: [] + Concrete mem diff: {'range(0x0, 0x4)': (, )} + Concrete reg diff: {'eflags': (, ), 'flags': (, ), 'rflags': (, )} + + STATE PAIR (0, DEADENDED_STATE), (1, DEADENDED_STATE) are different + The memory was equal for this state pair + Register difference detected for 0,1: + {'eflags': (, ), 'flags': (, ), 'rflags': (, )} + Here are 1 concrete input(s) for this particular state pair: + 1. + Input arguments: [] + Concrete reg diff: {'eflags': (, ), 'flags': (, ), 'rflags': (, )} + + There are no prepatched orphans + There are no postpatched orphans + +We can see that cozy found a diff between the 0th deadended +(terminated) state in the prepatched program (we will refer to this +state as s0) and the 0th deadended state in the postpatched program +(we will refer to this state as s0'). Together these two states form a +state pair, which is displayed on line 1 of the report. As we will see +from the following lines of the report, s0 represents the sole final +symbolic state for the prepatched function (there is only one path +through this function), and s0' represents the final state for the +"false" branch of the postpatched function (i.e., the path that is +triggered by a NULL argument). + +Line 3 displays the memory addresses that are different. Contents of +memory for written ranges are mapped to a tuple containing the +symbolic bytes at those addresses as a (prepatched, postpatched) +tuple. In this case, memory at addresses 0x0 to 0x4 is 0x2a000000 in +s0 (because the prepatched function writes 0x2a = 42 to the NULL +address), and 0x0 in s0' (because the NULL check prevents the write +from occurring). + +Line 5 tells the instruction pointer the program was at when it wrote +to those specific memory address ranges. Here we see that the +prepatched program was at the instruction 0x401179 when it wrote to +address 0x0, and the postpatched program never wrote to that address +(hence the empty frozenset). + +Line 7 gives the symbolic register difference between the states. As we can see, the flags registers +are different due to the presence of a branch in the postpatched program. As with the memory, each register +maps to a (prepatched, postpatched) tuple which gives the symbolic contents of the registers. + +Lines 8-12 gives concretized input that will cause the prepatched program to end in state s0 and +the postpatched program in state s0'. The input argument is concretized to 0x0 (aka NULL). Additionally since +the memory contents and register contents may be symbolic, we provide a concretized version of those as well. + +Lines 14-21 tells us that there is another diff for the state pair +(0,1). The second state in this pair represents the "true" branch +through the postpatched function. In this case we observe that the +only difference is in the flags registers, and that there are no +observable differences in memory. The concrete input argument for this +pair is when the input is non-NULL. + +The next lines describe any orphaned states - typically there will be none. An orphaned state is a state in which +there are no compatible pair states. + +================ +Further Examples +================ + +Further examples on how to use cozy for some simple programs can be found at https://github.com/draperlaboratory/cozy/tree/main/examples diff --git a/_sources/hooks.rst.txt b/_sources/hooks.rst.txt new file mode 100644 index 0000000..41bacf6 --- /dev/null +++ b/_sources/hooks.rst.txt @@ -0,0 +1,27 @@ +Dealing with Hooks +================== + +Some of the default C library hooks provided by angr will not function properly with +comparitive symbolic execution, including joint concolic execution. The issue stems +from two different factors: + +1. Hooks may provide an incomplete implementation of the C library hooks, or the complete +implementation may be disabled by default. For example, the ``strtok_r`` function's +more complete implementation may be disabled by default, and should be enabled by setting +:py:attr:`angr.SimState.libc.simple_strtok` to False. Likewise the ``strstr`` libc function +has a configuration option :py:attr:`angr.SimState.libc.max_symbolic_strstr` which is by +default set to a very conservative value of 1. + +2. The default angr hooks create fresh symbolic variables, and constrain these symbolic +values by adding to the state's constraints. This is problematic since in comparitive +symbolic execution we assume that both programs are fed the same symbolic variables. +Fortunately it is possible to eliminate the fresh symbolic variables in most cases. To see +an example of how to do this, see our provided replacement hook for ``strlen`` at +:py:class:`cozy.hooks.strlen.strlen`. + +In general, the best strategy for dealing with hooks is to be aware of their limitations, +understand the configuration options found in :py:attr:`angr.SimState.libc`, and replace +the default hooks when needed. + +To replace a hook for a specific project, you may use the +:py:meth:`cozy.project.Project.hook_symbol` method. \ No newline at end of file diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 0000000..c12b2b4 --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,26 @@ +.. cozy documentation master file, created by + sphinx-quickstart on Tue Oct 10 11:14:55 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to cozy's documentation! +================================= + +.. toctree:: + :maxdepth: 4 + :caption: Contents: + + gettingstarted + launchingavisualization + concolic + hooks + sideeffects + + + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/_sources/launchingavisualization.rst.txt b/_sources/launchingavisualization.rst.txt new file mode 100644 index 0000000..703594d --- /dev/null +++ b/_sources/launchingavisualization.rst.txt @@ -0,0 +1,91 @@ +Launching a Visualization +================================= + +In this example we will cover adding user defined types to the angr project as well as +visualizing our results with cozy. We will be using the AMP Hackathon Target 5 binaries, +which can be found in the examples folder in the cozy repository. There is no source +code available for these binaries, so some manual inspection of the assembly in your +preferred reverse engineering tool may be necessary. + +The micropatch bug in the Target 5 demo occurs during conversion of the temperature +value. Within the function we want replace the following logic:: + + int32_t rover_process(RoverMessage_t* msg){ + // ... + //convert Kelvin to Farhenheit. + temp = ( (temp - 273) * 1.8 ) + 32; + // ... + } + +With this logic:: + + int32_t rover_process(RoverMessage_t* msg){ + // ... + //convert Celsius to Farhenheit. + temp = ( temp * 1.8 ) + 32; + // ... + } + +To achieve this patch, we replaced the constant 273 in the original binary with 0. + +Let's get started by making two cozy projects, one for each binary:: + + proj_prepatched = cozy.project.Project('examples/amp_target5_hackathon/gs_data_processor') + proj_postpatched = cozy.project.Project('examples/amp_target5_hackathon/gs_data_processor_draper_patched') + +========================== +Defining Custom Types +========================== + +Our next task will be to define the structs used by this function. The primary inputs +to this function is the temperature field and the cmd field. Let's register these datatypes +with cozy:: + + cozy.types.register_type('struct RoverData_t { int temp; unsigned int cmd; }', proj_prepatched.arch) + rover_message_struct = cozy.types.register_type('struct RoverMessage_t { unsigned char header[8]; struct RoverData_t packetData; }', proj_prepatched.arch) + +We are now ready to add the type signature of the method we wish to analyze to the cozy project:: + + proj_prepatched.add_prototype("rover_process", "int rover_process(struct RoverMessage_t *msg)") + proj_postpatched.add_prototype("rover_process", "int rover_process(struct RoverMessage_t *msg)") + +========================== +Comparing and Visualizing +========================== + +Now let's create two symbolic variables to represent the ``temp`` and ``cmd`` fields in the ``RoverData_t`` struct:: + + temp = claripy.BVS("temp", 32) + cmd = claripy.BVS("cmd", 32) + +We now define a run function, which will run a prepatched or postpatched session:: + + def run(sess: cozy.project.Session): + arg0 = sess.malloc(rover_message_struct.size) + sess.mem[arg0].struct.RoverMessage_t.packetData.temp = temp.reversed + sess.mem[arg0].struct.RoverMessage_t.packetData.cmd = cmd.reversed + + return sess.run([arg0]) + +In this case we are mutating the memory by changing the memory of the angr state before +cozy runs. In this case we use angr's API to mutate the temp and cmd fields. Since the +incoming network packet uses network order endianness, we store ``temp.reversed`` and +``cmd.reversed`` to swap the endianness. + +Let's use our new run function to run the prepatched and postpatched session:: + + prepatched_results = run(proj_prepatched.session("rover_process")) + postpatched_results = run(proj_postpatched.session("rover_process")) + +Now we make the comparison between the two RunResult objects:: + + comparison = cozy.analysis.Comparison(prepatched_results, postpatched_results) + +After which we can launch the visualization in our web browser. This should automatically +open a browser window which visualizes our results:: + + cozy.execution_graph.visualize_comparison(proj_prepatched, proj_postpatched, + prepatched_results, postpatched_results, + comparison, + args={"temp": temp, "cmd": cmd}, + num_examples=2, open_browser=True) \ No newline at end of file diff --git a/_sources/sideeffects.rst.txt b/_sources/sideeffects.rst.txt new file mode 100644 index 0000000..b878f23 --- /dev/null +++ b/_sources/sideeffects.rst.txt @@ -0,0 +1,51 @@ +Modeling I/O Side Effects +========================= + +Many programs of interest that we wish to simulate produce side effects, which we would like to be available for comparison in our analysis. +To enable this use case, cozy has a subsystem for producing IO side effects. Common examples of IO side effects we have found in example programs +include writing to stdout/stderr, writing to the network, or writing over a serial connection. + +Modeling IO side effects is typically straightforward, and can be accomplished by hooking side effect producing functions and instead redirecting +the side effect payload to a list attached to the current state. When a child state is forked from its parent, it obtains a copy of side effects +from its parent. cozy keeps track of IO side effects over different channels (ie, a channel for stdout, network, etc.) and attempts to +intelligently align side effects in the visualization interface. + +Note that by default, angr automatically concretizes data written to stdout/stderr. cozy side effects keeps the data symbolic and avoids the concretization. +In this way cozy's side effects interface is superior to the angr default. + +========================== +Performing a Side Effect +========================== + +The primary function to take a look at is :py:func:`cozy.side_effect.perform`. The first argument is the :py:class:`angr.SimState` that the side effect +will attach to. This argument can be obtained by hooking a side effect function, whose :py:meth:`angr.SimProcedure.run` method takes in a +:py:class:`angr.SimState` object. Alternatively you can set a breakpoint using :py:class:`cozy.directive.Breakpoint` and obtain the :py:class:`angr.SimState` +object in the breakpoint's `breakpoint_fun` callback. + +Here is an example of the use of :py:func:`cozy.side_effect.perform` in a custom :py:class:`angr.SimProcedure` hook:: + + # Here we are hooking a function called process_command, + # so we need to make a class that inherits from SimProcedure + class process_command(angr.SimProcedure): + def run(self, cmd_str): + strlen = angr.SIM_PROCEDURES["libc"]["strlen"] + max_len = self.state.solver.max(self.inline_call(strlen, cmd_str).ret_expr) + # Here we construct the side effect payload. Here it is a bunch of symbolic data. + cmd = [self.state.memory.load(cmd_str + i, 1) for i in range(max_len)] + def concrete_post_processor(concrete_cmd): + return [chr(r.concrete_value) for r in concrete_cmd] + cozy.side_effect.perform(self.state, "process_command", cmd, concrete_post_processor=concrete_post_processor) + +The second argument is the side effect channel. Different types of side effects should be performed over different channels. For example, +you may have a channel for networked output and a channel for stdout. + +The third argument is the side effect body. The body must be a mixture of string-keyed Python dictionaries, Python lists, Python tuples, +claripy concrete values, and claripy symbolic values. This should represent the payload of the side effect. + +The fourth argument is an optional post processing function to apply to concretized versions of the side effect's body if post processing is required. +In this example we use the Python `chr` function to convert the integer to Python characters, which will be shown in the visualization +user interface. + +The fifth argument is an optional label used to aid alignment in the user interface. For example, if you have multiple sites that produce +side effects on the same channel, you will want to label the different sites with different labels. This aids the alignment algorithm to intelligently +compare the produced side effects. One possible label is the code address location that the side effect is produced at. \ No newline at end of file diff --git a/_static/alabaster.css b/_static/alabaster.css new file mode 100644 index 0000000..e3174bf --- /dev/null +++ b/_static/alabaster.css @@ -0,0 +1,708 @@ +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: Georgia, serif; + font-size: 17px; + background-color: #fff; + color: #000; + margin: 0; + padding: 0; +} + + +div.document { + width: 940px; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 220px; +} + +div.sphinxsidebar { + width: 220px; + font-size: 14px; + line-height: 1.5; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #fff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +div.body > .section { + text-align: left; +} + +div.footer { + width: 940px; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +p.caption { + font-family: inherit; + font-size: inherit; +} + + +div.relations { + display: none; +} + + +div.sphinxsidebar { + max-height: 100%; + overflow-y: auto; +} + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0; + margin: -10px 0 0 0px; + text-align: center; +} + +div.sphinxsidebarwrapper h1.logo { + margin-top: -10px; + text-align: center; + margin-bottom: 5px; + text-align: left; +} + +div.sphinxsidebarwrapper h1.logo-name { + margin-top: 0px; +} + +div.sphinxsidebarwrapper p.blurb { + margin-top: 0; + font-style: normal; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: Georgia, serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar ul li.toctree-l1 > a { + font-size: 120%; +} + +div.sphinxsidebar ul li.toctree-l2 > a { + font-size: 110%; +} + +div.sphinxsidebar input { + border: 1px solid #CCC; + font-family: Georgia, serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox input[type="text"] { + width: 160px; +} + +div.sphinxsidebar .search > div { + display: table-cell; +} + +div.sphinxsidebar hr { + border: none; + height: 1px; + color: #AAA; + background: #AAA; + + text-align: left; + margin-left: 0; + width: 50%; +} + +div.sphinxsidebar .badge { + border-bottom: none; +} + +div.sphinxsidebar .badge:hover { + border-bottom: none; +} + +/* To address an issue with donation coming after search */ +div.sphinxsidebar h3.donation { + margin-top: 10px; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: Georgia, serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #DDD; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #EAEAEA; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + margin: 20px 0px; + padding: 10px 30px; + background-color: #EEE; + border: 1px solid #CCC; +} + +div.admonition tt.xref, div.admonition code.xref, div.admonition a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fafafa; +} + +div.admonition p.admonition-title { + font-family: Georgia, serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: #fff; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.warning { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.danger { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.error { + background-color: #FCC; + border: 1px solid #FAA; + -moz-box-shadow: 2px 2px 4px #D52C2C; + -webkit-box-shadow: 2px 2px 4px #D52C2C; + box-shadow: 2px 2px 4px #D52C2C; +} + +div.caution { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.attention { + background-color: #FCC; + border: 1px solid #FAA; +} + +div.important { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.note { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.tip { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.hint { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.seealso { + background-color: #EEE; + border: 1px solid #CCC; +} + +div.topic { + background-color: #EEE; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt, code { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +.hll { + background-color: #FFC; + margin: 0 -12px; + padding: 0 12px; + display: block; +} + +img.screenshot { +} + +tt.descname, tt.descclassname, code.descname, code.descclassname { + font-size: 0.95em; +} + +tt.descname, code.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #EEE; + -webkit-box-shadow: 2px 2px 4px #EEE; + box-shadow: 2px 2px 4px #EEE; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #EEE; + background: #FDFDFD; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.field-list p { + margin-bottom: 0.8em; +} + +/* Cloned from + * https://github.com/sphinx-doc/sphinx/commit/ef60dbfce09286b20b7385333d63a60321784e68 + */ +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +table.footnote td.label { + width: .1px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin-left: 0; + margin-right: 0; + margin-top: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + /* Matches the 30px from the narrow-screen "li > ul" selector below */ + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #EEE; + padding: 7px 30px; + margin: 15px 0px; + line-height: 1.3em; +} + +div.viewcode-block:target { + background: #ffd; +} + +dl pre, blockquote pre, li pre { + margin-left: 0; + padding-left: 30px; +} + +tt, code { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, code.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid #fff; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +/* Don't put an underline on images */ +a.image-reference, a.image-reference:hover { + border-bottom: none; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt, a:hover code { + background: #EEE; +} + + +@media screen and (max-width: 870px) { + + div.sphinxsidebar { + display: none; + } + + div.document { + width: 100%; + + } + + div.documentwrapper { + margin-left: 0; + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + } + + div.bodywrapper { + margin-top: 0; + margin-right: 0; + margin-bottom: 0; + margin-left: 0; + } + + ul { + margin-left: 0; + } + + li > ul { + /* Matches the 30px from the "ul, ol" selector above */ + margin-left: 30px; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .bodywrapper { + margin: 0; + } + + .footer { + width: auto; + } + + .github { + display: none; + } + + + +} + + + +@media screen and (max-width: 875px) { + + body { + margin: 0; + padding: 20px 30px; + } + + div.documentwrapper { + float: none; + background: #fff; + } + + div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: #FFF; + } + + div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, + div.sphinxsidebar h3 a { + color: #fff; + } + + div.sphinxsidebar a { + color: #AAA; + } + + div.sphinxsidebar p.logo { + display: none; + } + + div.document { + width: 100%; + margin: 0; + } + + div.footer { + display: none; + } + + div.bodywrapper { + margin: 0; + } + + div.body { + min-height: 0; + padding: 0; + } + + .rtd_doc_footer { + display: none; + } + + .document { + width: auto; + } + + .footer { + width: auto; + } + + .footer { + width: auto; + } + + .github { + display: none; + } +} + + +/* misc. */ + +.revsys-inline { + display: none!important; +} + +/* Hide ugly table cell borders in ..bibliography:: directive output */ +table.docutils.citation, table.docutils.citation td, table.docutils.citation th { + border: none; + /* Below needed in some edge cases; if not applied, bottom shadows appear */ + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + + +/* relbar */ + +.related { + line-height: 30px; + width: 100%; + font-size: 0.9rem; +} + +.related.top { + border-bottom: 1px solid #EEE; + margin-bottom: 20px; +} + +.related.bottom { + border-top: 1px solid #EEE; +} + +.related ul { + padding: 0; + margin: 0; + list-style: none; +} + +.related li { + display: inline; +} + +nav#rellinks { + float: right; +} + +nav#rellinks li+li:before { + content: "|"; +} + +nav#breadcrumbs li+li:before { + content: "\00BB"; +} + +/* Hide certain items when printing */ +@media print { + div.related { + display: none; + } +} \ No newline at end of file diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 0000000..e5179b7 --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,925 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 230px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: inherit; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a:visited { + color: #551A8B; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +nav.contents, +aside.topic, +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +nav.contents, +aside.topic, +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +nav.contents > :last-child, +aside.topic > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +nav.contents::after, +aside.topic::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +aside.footnote > span, +div.citation > span { + float: left; +} +aside.footnote > span:last-of-type, +div.citation > span:last-of-type { + padding-right: 0.5em; +} +aside.footnote > p { + margin-left: 2em; +} +div.citation > p { + margin-left: 4em; +} +aside.footnote > p:last-of-type, +div.citation > p:last-of-type { + margin-bottom: 0em; +} +aside.footnote > p:last-of-type:after, +div.citation > p:last-of-type:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +.sig dd { + margin-top: 0px; + margin-bottom: 0px; +} + +.sig dl { + margin-top: 0px; + margin-bottom: 0px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +.translated { + background-color: rgba(207, 255, 207, 0.2) +} + +.untranslated { + background-color: rgba(255, 207, 207, 0.2) +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/custom.css b/_static/custom.css new file mode 100644 index 0000000..2a924f1 --- /dev/null +++ b/_static/custom.css @@ -0,0 +1 @@ +/* This file intentionally left blank. */ diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 0000000..4d67807 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,156 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Base JavaScript utilities for all Sphinx HTML documentation. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([ + "TEXTAREA", + "INPUT", + "SELECT", + "BUTTON", +]); + +const _ready = (callback) => { + if (document.readyState !== "loading") { + callback(); + } else { + document.addEventListener("DOMContentLoaded", callback); + } +}; + +/** + * Small JavaScript module for the documentation. + */ +const Documentation = { + init: () => { + Documentation.initDomainIndexTable(); + Documentation.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS: {}, + PLURAL_EXPR: (n) => (n === 1 ? 0 : 1), + LOCALE: "unknown", + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext: (string) => { + const translated = Documentation.TRANSLATIONS[string]; + switch (typeof translated) { + case "undefined": + return string; // no translation + case "string": + return translated; // translation exists + default: + return translated[0]; // (singular, plural) translation tuple exists + } + }, + + ngettext: (singular, plural, n) => { + const translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated !== "undefined") + return translated[Documentation.PLURAL_EXPR(n)]; + return n === 1 ? singular : plural; + }, + + addTranslations: (catalog) => { + Object.assign(Documentation.TRANSLATIONS, catalog.messages); + Documentation.PLURAL_EXPR = new Function( + "n", + `return (${catalog.plural_expr})` + ); + Documentation.LOCALE = catalog.locale; + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar: () => { + document.querySelectorAll("input[name=q]")[0]?.focus(); + }, + + /** + * Initialise the domain index toggle buttons + */ + initDomainIndexTable: () => { + const toggler = (el) => { + const idNumber = el.id.substr(7); + const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`); + if (el.src.substr(-9) === "minus.png") { + el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`; + toggledRows.forEach((el) => (el.style.display = "none")); + } else { + el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`; + toggledRows.forEach((el) => (el.style.display = "")); + } + }; + + const togglerElements = document.querySelectorAll("img.toggler"); + togglerElements.forEach((el) => + el.addEventListener("click", (event) => toggler(event.currentTarget)) + ); + togglerElements.forEach((el) => (el.style.display = "")); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler); + }, + + initOnKeyListeners: () => { + // only install a listener if it is really needed + if ( + !DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS + ) + return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.altKey || event.ctrlKey || event.metaKey) return; + + if (!event.shiftKey) { + switch (event.key) { + case "ArrowLeft": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const prevLink = document.querySelector('link[rel="prev"]'); + if (prevLink && prevLink.href) { + window.location.href = prevLink.href; + event.preventDefault(); + } + break; + case "ArrowRight": + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break; + + const nextLink = document.querySelector('link[rel="next"]'); + if (nextLink && nextLink.href) { + window.location.href = nextLink.href; + event.preventDefault(); + } + break; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case "/": + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break; + Documentation.focusSearchBar(); + event.preventDefault(); + } + }); + }, +}; + +// quick alias for translations +const _ = Documentation.gettext; + +_ready(Documentation.init); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 0000000..6aedc8a --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,13 @@ +const DOCUMENTATION_OPTIONS = { + VERSION: '1.1', + LANGUAGE: 'en', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: false, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 0000000..a858a41 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/graphviz.css b/_static/graphviz.css new file mode 100644 index 0000000..027576e --- /dev/null +++ b/_static/graphviz.css @@ -0,0 +1,19 @@ +/* + * graphviz.css + * ~~~~~~~~~~~~ + * + * Sphinx stylesheet -- graphviz extension. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +img.graphviz { + border: 0; + max-width: 100%; +} + +object.graphviz { + max-width: 100%; +} diff --git a/_static/language_data.js b/_static/language_data.js new file mode 100644 index 0000000..367b8ed --- /dev/null +++ b/_static/language_data.js @@ -0,0 +1,199 @@ +/* + * language_data.js + * ~~~~~~~~~~~~~~~~ + * + * This script contains the language-specific data used by searchtools.js, + * namely the list of stopwords, stemmer, scorer and splitter. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"]; + + +/* Non-minified version is copied as a separate JS file, if available */ + +/** + * Porter Stemmer + */ +var Stemmer = function() { + + var step2list = { + ational: 'ate', + tional: 'tion', + enci: 'ence', + anci: 'ance', + izer: 'ize', + bli: 'ble', + alli: 'al', + entli: 'ent', + eli: 'e', + ousli: 'ous', + ization: 'ize', + ation: 'ate', + ator: 'ate', + alism: 'al', + iveness: 'ive', + fulness: 'ful', + ousness: 'ous', + aliti: 'al', + iviti: 'ive', + biliti: 'ble', + logi: 'log' + }; + + var step3list = { + icate: 'ic', + ative: '', + alize: 'al', + iciti: 'ic', + ical: 'ic', + ful: '', + ness: '' + }; + + var c = "[^aeiou]"; // consonant + var v = "[aeiouy]"; // vowel + var C = c + "[^aeiouy]*"; // consonant sequence + var V = v + "[aeiou]*"; // vowel sequence + + var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0 + var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1 + var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1 + var s_v = "^(" + C + ")?" + v; // vowel in stem + + this.stemWord = function (w) { + var stem; + var suffix; + var firstch; + var origword = w; + + if (w.length < 3) + return w; + + var re; + var re2; + var re3; + var re4; + + firstch = w.substr(0,1); + if (firstch == "y") + w = firstch.toUpperCase() + w.substr(1); + + // Step 1a + re = /^(.+?)(ss|i)es$/; + re2 = /^(.+?)([^s])s$/; + + if (re.test(w)) + w = w.replace(re,"$1$2"); + else if (re2.test(w)) + w = w.replace(re2,"$1$2"); + + // Step 1b + re = /^(.+?)eed$/; + re2 = /^(.+?)(ed|ing)$/; + if (re.test(w)) { + var fp = re.exec(w); + re = new RegExp(mgr0); + if (re.test(fp[1])) { + re = /.$/; + w = w.replace(re,""); + } + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1]; + re2 = new RegExp(s_v); + if (re2.test(stem)) { + w = stem; + re2 = /(at|bl|iz)$/; + re3 = new RegExp("([^aeiouylsz])\\1$"); + re4 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re2.test(w)) + w = w + "e"; + else if (re3.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + else if (re4.test(w)) + w = w + "e"; + } + } + + // Step 1c + re = /^(.+?)y$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(s_v); + if (re.test(stem)) + w = stem + "i"; + } + + // Step 2 + re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step2list[suffix]; + } + + // Step 3 + re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + suffix = fp[2]; + re = new RegExp(mgr0); + if (re.test(stem)) + w = stem + step3list[suffix]; + } + + // Step 4 + re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/; + re2 = /^(.+?)(s|t)(ion)$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + if (re.test(stem)) + w = stem; + } + else if (re2.test(w)) { + var fp = re2.exec(w); + stem = fp[1] + fp[2]; + re2 = new RegExp(mgr1); + if (re2.test(stem)) + w = stem; + } + + // Step 5 + re = /^(.+?)e$/; + if (re.test(w)) { + var fp = re.exec(w); + stem = fp[1]; + re = new RegExp(mgr1); + re2 = new RegExp(meq1); + re3 = new RegExp("^" + C + v + "[^aeiouwxy]$"); + if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) + w = stem; + } + re = /ll$/; + re2 = new RegExp(mgr1); + if (re.test(w) && re2.test(w)) { + re = /.$/; + w = w.replace(re,""); + } + + // and turn initial Y back to y + if (firstch == "y") + w = firstch.toLowerCase() + w.substr(1); + return w; + } +} + diff --git a/_static/minus.png b/_static/minus.png new file mode 100644 index 0000000..d96755f Binary files /dev/null and b/_static/minus.png differ diff --git a/_static/plus.png b/_static/plus.png new file mode 100644 index 0000000..7107cec Binary files /dev/null and b/_static/plus.png differ diff --git a/_static/pygments.css b/_static/pygments.css new file mode 100644 index 0000000..04a4174 --- /dev/null +++ b/_static/pygments.css @@ -0,0 +1,84 @@ +pre { line-height: 125%; } +td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } +td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } +.highlight .hll { background-color: #ffffcc } +.highlight { background: #f8f8f8; } +.highlight .c { color: #8f5902; font-style: italic } /* Comment */ +.highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ +.highlight .g { color: #000000 } /* Generic */ +.highlight .k { color: #004461; font-weight: bold } /* Keyword */ +.highlight .l { color: #000000 } /* Literal */ +.highlight .n { color: #000000 } /* Name */ +.highlight .o { color: #582800 } /* Operator */ +.highlight .x { color: #000000 } /* Other */ +.highlight .p { color: #000000; font-weight: bold } /* Punctuation */ +.highlight .ch { color: #8f5902; font-style: italic } /* Comment.Hashbang */ +.highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #8f5902 } /* Comment.Preproc */ +.highlight .cpf { color: #8f5902; font-style: italic } /* Comment.PreprocFile */ +.highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ +.highlight .gd { color: #a40000 } /* Generic.Deleted */ +.highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ +.highlight .ges { color: #000000 } /* Generic.EmphStrong */ +.highlight .gr { color: #ef2929 } /* Generic.Error */ +.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #00A000 } /* Generic.Inserted */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #745334 } /* Generic.Prompt */ +.highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ +.highlight .kc { color: #004461; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #004461; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #004461; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #004461; font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { color: #004461; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #004461; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #000000 } /* Literal.Date */ +.highlight .m { color: #990000 } /* Literal.Number */ +.highlight .s { color: #4e9a06 } /* Literal.String */ +.highlight .na { color: #c4a000 } /* Name.Attribute */ +.highlight .nb { color: #004461 } /* Name.Builtin */ +.highlight .nc { color: #000000 } /* Name.Class */ +.highlight .no { color: #000000 } /* Name.Constant */ +.highlight .nd { color: #888888 } /* Name.Decorator */ +.highlight .ni { color: #ce5c00 } /* Name.Entity */ +.highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #000000 } /* Name.Function */ +.highlight .nl { color: #f57900 } /* Name.Label */ +.highlight .nn { color: #000000 } /* Name.Namespace */ +.highlight .nx { color: #000000 } /* Name.Other */ +.highlight .py { color: #000000 } /* Name.Property */ +.highlight .nt { color: #004461; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #000000 } /* Name.Variable */ +.highlight .ow { color: #004461; font-weight: bold } /* Operator.Word */ +.highlight .pm { color: #000000; font-weight: bold } /* Punctuation.Marker */ +.highlight .w { color: #f8f8f8 } /* Text.Whitespace */ +.highlight .mb { color: #990000 } /* Literal.Number.Bin */ +.highlight .mf { color: #990000 } /* Literal.Number.Float */ +.highlight .mh { color: #990000 } /* Literal.Number.Hex */ +.highlight .mi { color: #990000 } /* Literal.Number.Integer */ +.highlight .mo { color: #990000 } /* Literal.Number.Oct */ +.highlight .sa { color: #4e9a06 } /* Literal.String.Affix */ +.highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ +.highlight .sc { color: #4e9a06 } /* Literal.String.Char */ +.highlight .dl { color: #4e9a06 } /* Literal.String.Delimiter */ +.highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ +.highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ +.highlight .se { color: #4e9a06 } /* Literal.String.Escape */ +.highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ +.highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ +.highlight .sx { color: #4e9a06 } /* Literal.String.Other */ +.highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ +.highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ +.highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ +.highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ +.highlight .fm { color: #000000 } /* Name.Function.Magic */ +.highlight .vc { color: #000000 } /* Name.Variable.Class */ +.highlight .vg { color: #000000 } /* Name.Variable.Global */ +.highlight .vi { color: #000000 } /* Name.Variable.Instance */ +.highlight .vm { color: #000000 } /* Name.Variable.Magic */ +.highlight .il { color: #990000 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/_static/searchtools.js b/_static/searchtools.js new file mode 100644 index 0000000..92da3f8 --- /dev/null +++ b/_static/searchtools.js @@ -0,0 +1,619 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2024 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ +"use strict"; + +/** + * Simple result scoring code. + */ +if (typeof Scorer === "undefined") { + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [docname, title, anchor, descr, score, filename] + // and returns the new score. + /* + score: result => { + const [docname, title, anchor, descr, score, filename] = result + return score + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: { + 0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5, // used to be unimportantResults + }, + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2, + }; +} + +const _removeChildren = (element) => { + while (element && element.lastChild) element.removeChild(element.lastChild); +}; + +/** + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping + */ +const _escapeRegExp = (string) => + string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string + +const _displayItem = (item, searchTerms, highlightTerms) => { + const docBuilder = DOCUMENTATION_OPTIONS.BUILDER; + const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX; + const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX; + const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY; + const contentRoot = document.documentElement.dataset.content_root; + + const [docName, title, anchor, descr, score, _filename] = item; + + let listItem = document.createElement("li"); + let requestUrl; + let linkUrl; + if (docBuilder === "dirhtml") { + // dirhtml builder + let dirname = docName + "/"; + if (dirname.match(/\/index\/$/)) + dirname = dirname.substring(0, dirname.length - 6); + else if (dirname === "index/") dirname = ""; + requestUrl = contentRoot + dirname; + linkUrl = requestUrl; + } else { + // normal html builders + requestUrl = contentRoot + docName + docFileSuffix; + linkUrl = docName + docLinkSuffix; + } + let linkEl = listItem.appendChild(document.createElement("a")); + linkEl.href = linkUrl + anchor; + linkEl.dataset.score = score; + linkEl.innerHTML = title; + if (descr) { + listItem.appendChild(document.createElement("span")).innerHTML = + " (" + descr + ")"; + // highlight search terms in the description + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + } + else if (showSearchSummary) + fetch(requestUrl) + .then((responseData) => responseData.text()) + .then((data) => { + if (data) + listItem.appendChild( + Search.makeSearchSummary(data, searchTerms, anchor) + ); + // highlight search terms in the summary + if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js + highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted")); + }); + Search.output.appendChild(listItem); +}; +const _finishSearch = (resultCount) => { + Search.stopPulse(); + Search.title.innerText = _("Search Results"); + if (!resultCount) + Search.status.innerText = Documentation.gettext( + "Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories." + ); + else + Search.status.innerText = _( + "Search finished, found ${resultCount} page(s) matching the search query." + ).replace('${resultCount}', resultCount); +}; +const _displayNextItem = ( + results, + resultCount, + searchTerms, + highlightTerms, +) => { + // results left, load the summary and display it + // this is intended to be dynamic (don't sub resultsCount) + if (results.length) { + _displayItem(results.pop(), searchTerms, highlightTerms); + setTimeout( + () => _displayNextItem(results, resultCount, searchTerms, highlightTerms), + 5 + ); + } + // search finished, update title and status message + else _finishSearch(resultCount); +}; +// Helper function used by query() to order search results. +// Each input is an array of [docname, title, anchor, descr, score, filename]. +// Order the results by score (in opposite order of appearance, since the +// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically. +const _orderResultsByScoreThenName = (a, b) => { + const leftScore = a[4]; + const rightScore = b[4]; + if (leftScore === rightScore) { + // same score: sort alphabetically + const leftTitle = a[1].toLowerCase(); + const rightTitle = b[1].toLowerCase(); + if (leftTitle === rightTitle) return 0; + return leftTitle > rightTitle ? -1 : 1; // inverted is intentional + } + return leftScore > rightScore ? 1 : -1; +}; + +/** + * Default splitQuery function. Can be overridden in ``sphinx.search`` with a + * custom function per language. + * + * The regular expression works by splitting the string on consecutive characters + * that are not Unicode letters, numbers, underscores, or emoji characters. + * This is the same as ``\W+`` in Python, preserving the surrogate pair area. + */ +if (typeof splitQuery === "undefined") { + var splitQuery = (query) => query + .split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu) + .filter(term => term) // remove remaining empty strings +} + +/** + * Search Module + */ +const Search = { + _index: null, + _queued_query: null, + _pulse_status: -1, + + htmlToText: (htmlString, anchor) => { + const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html'); + for (const removalQuery of [".headerlinks", "script", "style"]) { + htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() }); + } + if (anchor) { + const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`); + if (anchorContent) return anchorContent.textContent; + + console.warn( + `Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.` + ); + } + + // if anchor not specified or not found, fall back to main content + const docContent = htmlElement.querySelector('[role="main"]'); + if (docContent) return docContent.textContent; + + console.warn( + "Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template." + ); + return ""; + }, + + init: () => { + const query = new URLSearchParams(window.location.search).get("q"); + document + .querySelectorAll('input[name="q"]') + .forEach((el) => (el.value = query)); + if (query) Search.performSearch(query); + }, + + loadIndex: (url) => + (document.body.appendChild(document.createElement("script")).src = url), + + setIndex: (index) => { + Search._index = index; + if (Search._queued_query !== null) { + const query = Search._queued_query; + Search._queued_query = null; + Search.query(query); + } + }, + + hasIndex: () => Search._index !== null, + + deferQuery: (query) => (Search._queued_query = query), + + stopPulse: () => (Search._pulse_status = -1), + + startPulse: () => { + if (Search._pulse_status >= 0) return; + + const pulse = () => { + Search._pulse_status = (Search._pulse_status + 1) % 4; + Search.dots.innerText = ".".repeat(Search._pulse_status); + if (Search._pulse_status >= 0) window.setTimeout(pulse, 500); + }; + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch: (query) => { + // create the required interface elements + const searchText = document.createElement("h2"); + searchText.textContent = _("Searching"); + const searchSummary = document.createElement("p"); + searchSummary.classList.add("search-summary"); + searchSummary.innerText = ""; + const searchList = document.createElement("ul"); + searchList.classList.add("search"); + + const out = document.getElementById("search-results"); + Search.title = out.appendChild(searchText); + Search.dots = Search.title.appendChild(document.createElement("span")); + Search.status = out.appendChild(searchSummary); + Search.output = out.appendChild(searchList); + + const searchProgress = document.getElementById("search-progress"); + // Some themes don't use the search progress node + if (searchProgress) { + searchProgress.innerText = _("Preparing search..."); + } + Search.startPulse(); + + // index already loaded, the browser was quick! + if (Search.hasIndex()) Search.query(query); + else Search.deferQuery(query); + }, + + _parseQuery: (query) => { + // stem the search terms and add them to the correct list + const stemmer = new Stemmer(); + const searchTerms = new Set(); + const excludedTerms = new Set(); + const highlightTerms = new Set(); + const objectTerms = new Set(splitQuery(query.toLowerCase().trim())); + splitQuery(query.trim()).forEach((queryTerm) => { + const queryTermLower = queryTerm.toLowerCase(); + + // maybe skip this "word" + // stopwords array is from language_data.js + if ( + stopwords.indexOf(queryTermLower) !== -1 || + queryTerm.match(/^\d+$/) + ) + return; + + // stem the word + let word = stemmer.stemWord(queryTermLower); + // select the correct list + if (word[0] === "-") excludedTerms.add(word.substr(1)); + else { + searchTerms.add(word); + highlightTerms.add(queryTermLower); + } + }); + + if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js + localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" ")) + } + + // console.debug("SEARCH: searching for:"); + // console.info("required: ", [...searchTerms]); + // console.info("excluded: ", [...excludedTerms]); + + return [query, searchTerms, excludedTerms, highlightTerms, objectTerms]; + }, + + /** + * execute search (requires search index to be loaded) + */ + _performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + const allTitles = Search._index.alltitles; + const indexEntries = Search._index.indexentries; + + // Collect multiple result groups to be sorted separately and then ordered. + // Each is an array of [docname, title, anchor, descr, score, filename]. + const normalResults = []; + const nonMainIndexResults = []; + + _removeChildren(document.getElementById("search-progress")); + + const queryLower = query.toLowerCase().trim(); + for (const [title, foundTitles] of Object.entries(allTitles)) { + if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) { + for (const [file, id] of foundTitles) { + let score = Math.round(100 * queryLower.length / title.length) + normalResults.push([ + docNames[file], + titles[file] !== title ? `${titles[file]} > ${title}` : title, + id !== null ? "#" + id : "", + null, + score, + filenames[file], + ]); + } + } + } + + // search for explicit entries in index directives + for (const [entry, foundEntries] of Object.entries(indexEntries)) { + if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) { + for (const [file, id, isMain] of foundEntries) { + const score = Math.round(100 * queryLower.length / entry.length); + const result = [ + docNames[file], + titles[file], + id ? "#" + id : "", + null, + score, + filenames[file], + ]; + if (isMain) { + normalResults.push(result); + } else { + nonMainIndexResults.push(result); + } + } + } + } + + // lookup as object + objectTerms.forEach((term) => + normalResults.push(...Search.performObjectSearch(term, objectTerms)) + ); + + // lookup as search terms in fulltext + normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms)); + + // let the scorer override scores with a custom scoring function + if (Scorer.score) { + normalResults.forEach((item) => (item[4] = Scorer.score(item))); + nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item))); + } + + // Sort each group of results by score and then alphabetically by name. + normalResults.sort(_orderResultsByScoreThenName); + nonMainIndexResults.sort(_orderResultsByScoreThenName); + + // Combine the result groups in (reverse) order. + // Non-main index entries are typically arbitrary cross-references, + // so display them after other results. + let results = [...nonMainIndexResults, ...normalResults]; + + // remove duplicate search results + // note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept + let seen = new Set(); + results = results.reverse().reduce((acc, result) => { + let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(','); + if (!seen.has(resultStr)) { + acc.push(result); + seen.add(resultStr); + } + return acc; + }, []); + + return results.reverse(); + }, + + query: (query) => { + const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query); + const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms); + + // for debugging + //Search.lastresults = results.slice(); // a copy + // console.info("search results:", Search.lastresults); + + // print the results + _displayNextItem(results, results.length, searchTerms, highlightTerms); + }, + + /** + * search for object names + */ + performObjectSearch: (object, objectTerms) => { + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const objects = Search._index.objects; + const objNames = Search._index.objnames; + const titles = Search._index.titles; + + const results = []; + + const objectSearchCallback = (prefix, match) => { + const name = match[4] + const fullname = (prefix ? prefix + "." : "") + name; + const fullnameLower = fullname.toLowerCase(); + if (fullnameLower.indexOf(object) < 0) return; + + let score = 0; + const parts = fullnameLower.split("."); + + // check for different match types: exact matches of full name or + // "last name" (i.e. last dotted part) + if (fullnameLower === object || parts.slice(-1)[0] === object) + score += Scorer.objNameMatch; + else if (parts.slice(-1)[0].indexOf(object) > -1) + score += Scorer.objPartialMatch; // matches in last name + + const objName = objNames[match[1]][2]; + const title = titles[match[0]]; + + // If more than one term searched for, we require other words to be + // found in the name/title/description + const otherTerms = new Set(objectTerms); + otherTerms.delete(object); + if (otherTerms.size > 0) { + const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase(); + if ( + [...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0) + ) + return; + } + + let anchor = match[3]; + if (anchor === "") anchor = fullname; + else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname; + + const descr = objName + _(", in ") + title; + + // add custom score for some objects according to scorer + if (Scorer.objPrio.hasOwnProperty(match[2])) + score += Scorer.objPrio[match[2]]; + else score += Scorer.objPrioDefault; + + results.push([ + docNames[match[0]], + fullname, + "#" + anchor, + descr, + score, + filenames[match[0]], + ]); + }; + Object.keys(objects).forEach((prefix) => + objects[prefix].forEach((array) => + objectSearchCallback(prefix, array) + ) + ); + return results; + }, + + /** + * search for full-text terms in the index + */ + performTermsSearch: (searchTerms, excludedTerms) => { + // prepare search + const terms = Search._index.terms; + const titleTerms = Search._index.titleterms; + const filenames = Search._index.filenames; + const docNames = Search._index.docnames; + const titles = Search._index.titles; + + const scoreMap = new Map(); + const fileMap = new Map(); + + // perform the search on the required terms + searchTerms.forEach((word) => { + const files = []; + const arr = [ + { files: terms[word], score: Scorer.term }, + { files: titleTerms[word], score: Scorer.title }, + ]; + // add support for partial matches + if (word.length > 2) { + const escapedWord = _escapeRegExp(word); + if (!terms.hasOwnProperty(word)) { + Object.keys(terms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: terms[term], score: Scorer.partialTerm }); + }); + } + if (!titleTerms.hasOwnProperty(word)) { + Object.keys(titleTerms).forEach((term) => { + if (term.match(escapedWord)) + arr.push({ files: titleTerms[term], score: Scorer.partialTitle }); + }); + } + } + + // no match but word was a required one + if (arr.every((record) => record.files === undefined)) return; + + // found search word in contents + arr.forEach((record) => { + if (record.files === undefined) return; + + let recordFiles = record.files; + if (recordFiles.length === undefined) recordFiles = [recordFiles]; + files.push(...recordFiles); + + // set score for the word in each file + recordFiles.forEach((file) => { + if (!scoreMap.has(file)) scoreMap.set(file, {}); + scoreMap.get(file)[word] = record.score; + }); + }); + + // create the mapping + files.forEach((file) => { + if (!fileMap.has(file)) fileMap.set(file, [word]); + else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word); + }); + }); + + // now check if the files don't contain excluded terms + const results = []; + for (const [file, wordList] of fileMap) { + // check if all requirements are matched + + // as search terms with length < 3 are discarded + const filteredTermCount = [...searchTerms].filter( + (term) => term.length > 2 + ).length; + if ( + wordList.length !== searchTerms.size && + wordList.length !== filteredTermCount + ) + continue; + + // ensure that none of the excluded terms is in the search result + if ( + [...excludedTerms].some( + (term) => + terms[term] === file || + titleTerms[term] === file || + (terms[term] || []).includes(file) || + (titleTerms[term] || []).includes(file) + ) + ) + break; + + // select one (max) score for the file. + const score = Math.max(...wordList.map((w) => scoreMap.get(file)[w])); + // add result to the result list + results.push([ + docNames[file], + titles[file], + "", + null, + score, + filenames[file], + ]); + } + return results; + }, + + /** + * helper function to return a node containing the + * search summary for a given text. keywords is a list + * of stemmed words. + */ + makeSearchSummary: (htmlText, keywords, anchor) => { + const text = Search.htmlToText(htmlText, anchor); + if (text === "") return null; + + const textLower = text.toLowerCase(); + const actualStartPosition = [...keywords] + .map((k) => textLower.indexOf(k.toLowerCase())) + .filter((i) => i > -1) + .slice(-1)[0]; + const startWithContext = Math.max(actualStartPosition - 120, 0); + + const top = startWithContext === 0 ? "" : "..."; + const tail = startWithContext + 240 < text.length ? "..." : ""; + + let summary = document.createElement("p"); + summary.classList.add("context"); + summary.textContent = top + text.substr(startWithContext, 240).trim() + tail; + + return summary; + }, +}; + +_ready(Search.init); diff --git a/_static/sphinx_highlight.js b/_static/sphinx_highlight.js new file mode 100644 index 0000000..8a96c69 --- /dev/null +++ b/_static/sphinx_highlight.js @@ -0,0 +1,154 @@ +/* Highlighting utilities for Sphinx HTML documentation. */ +"use strict"; + +const SPHINX_HIGHLIGHT_ENABLED = true + +/** + * highlight a given string on a node by wrapping it in + * span elements with the given class name. + */ +const _highlight = (node, addItems, text, className) => { + if (node.nodeType === Node.TEXT_NODE) { + const val = node.nodeValue; + const parent = node.parentNode; + const pos = val.toLowerCase().indexOf(text); + if ( + pos >= 0 && + !parent.classList.contains(className) && + !parent.classList.contains("nohighlight") + ) { + let span; + + const closestNode = parent.closest("body, svg, foreignObject"); + const isInSVG = closestNode && closestNode.matches("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.classList.add(className); + } + + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + const rest = document.createTextNode(val.substr(pos + text.length)); + parent.insertBefore( + span, + parent.insertBefore( + rest, + node.nextSibling + ) + ); + node.nodeValue = val.substr(0, pos); + /* There may be more occurrences of search term in this node. So call this + * function recursively on the remaining fragment. + */ + _highlight(rest, addItems, text, className); + + if (isInSVG) { + const rect = document.createElementNS( + "http://www.w3.org/2000/svg", + "rect" + ); + const bbox = parent.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute("class", className); + addItems.push({ parent: parent, target: rect }); + } + } + } else if (node.matches && !node.matches("button, select, textarea")) { + node.childNodes.forEach((el) => _highlight(el, addItems, text, className)); + } +}; +const _highlightText = (thisNode, text, className) => { + let addItems = []; + _highlight(thisNode, addItems, text, className); + addItems.forEach((obj) => + obj.parent.insertAdjacentElement("beforebegin", obj.target) + ); +}; + +/** + * Small JavaScript module for the documentation. + */ +const SphinxHighlight = { + + /** + * highlight the search words provided in localstorage in the text + */ + highlightSearchWords: () => { + if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight + + // get and clear terms from localstorage + const url = new URL(window.location); + const highlight = + localStorage.getItem("sphinx_highlight_terms") + || url.searchParams.get("highlight") + || ""; + localStorage.removeItem("sphinx_highlight_terms") + url.searchParams.delete("highlight"); + window.history.replaceState({}, "", url); + + // get individual terms from highlight string + const terms = highlight.toLowerCase().split(/\s+/).filter(x => x); + if (terms.length === 0) return; // nothing to do + + // There should never be more than one element matching "div.body" + const divBody = document.querySelectorAll("div.body"); + const body = divBody.length ? divBody[0] : document.querySelector("body"); + window.setTimeout(() => { + terms.forEach((term) => _highlightText(body, term, "highlighted")); + }, 10); + + const searchBox = document.getElementById("searchbox"); + if (searchBox === null) return; + searchBox.appendChild( + document + .createRange() + .createContextualFragment( + '" + ) + ); + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords: () => { + document + .querySelectorAll("#searchbox .highlight-link") + .forEach((el) => el.remove()); + document + .querySelectorAll("span.highlighted") + .forEach((el) => el.classList.remove("highlighted")); + localStorage.removeItem("sphinx_highlight_terms") + }, + + initEscapeListener: () => { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return; + + document.addEventListener("keydown", (event) => { + // bail for input elements + if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return; + // bail with special keys + if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return; + if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) { + SphinxHighlight.hideSearchWords(); + event.preventDefault(); + } + }); + }, +}; + +_ready(() => { + /* Do not call highlightSearchWords() when we are on the search page. + * It will highlight words from the *previous* search query. + */ + if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords(); + SphinxHighlight.initEscapeListener(); +}); diff --git a/autoapi/cozy/__main__/index.html b/autoapi/cozy/__main__/index.html new file mode 100644 index 0000000..522561b --- /dev/null +++ b/autoapi/cozy/__main__/index.html @@ -0,0 +1,459 @@ + + + + + + + + cozy.__main__ — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.__main__

+
+

Classes

+ + + + + + + + + + + + +

Results

Stage

Generic enumeration.

Wizard

+
+
+

Module Contents

+
+
+class cozy.__main__.Results
+
+
+to_string()
+
+ +
+ +
+
+class cozy.__main__.Stage
+

Bases: enum.Enum

+

Generic enumeration.

+

Derive from this class to define new enumerations.

+
+
+confirm_start = 1
+
+ +
+
+request_script_name = 2
+
+ +
+
+confirm_script_clobber = 3
+
+ +
+
+request_prepatched = 4
+
+ +
+
+request_postpatched = 5
+
+ +
+
+request_function_name = 6
+
+ +
+
+request_signature = 7
+
+ +
+
+request_concolic = 8
+
+ +
+
+request_concolic_complete = 9
+
+ +
+
+request_hooks = 10
+
+ +
+
+request_textual_report = 11
+
+ +
+
+request_visualization = 12
+
+ +
+
+request_dump = 13
+
+ +
+
+request_dump_name = 14
+
+ +
+
+complete = 15
+
+ +
+ +
+
+class cozy.__main__.Wizard
+

Bases: textual.app.App

+
+
+CSS = Multiline-String
+
Show Value
"""
+    Screen {
+        padding:1
+    }
+    """
+
+
+
+ +
+
+compose() textual.app.ComposeResult
+
+ +
+
+async ask_yes_no(text)
+
+ +
+
+async ask_string(text, placeholder='')
+
+ +
+
+async ask_file(text)
+
+ +
+
+async set_stage(stage)
+
+ +
+
+async set_confirm_start()
+
+ +
+
+async handle_confirm_start(message)
+
+ +
+
+async set_confirm_script_clobber()
+
+ +
+
+async handle_confirm_script_clobber(message)
+
+ +
+
+async set_request_script_name()
+
+ +
+
+async handle_request_script_name(message)
+
+ +
+
+async set_request_prepatched()
+
+ +
+
+async handle_request_prepatched(message)
+
+ +
+
+async set_request_postpatched()
+
+ +
+
+async handle_request_postpatched(message)
+
+ +
+
+async set_request_function_name()
+
+ +
+
+async handle_request_function_name(message)
+
+ +
+
+async set_request_signature()
+
+ +
+
+async handle_request_signature(message)
+
+ +
+
+async set_request_hooks()
+
+ +
+
+async handle_request_hooks(message)
+
+ +
+
+async set_request_concolic()
+
+ +
+
+async handle_request_concolic(message)
+
+ +
+
+async set_request_concolic_complete()
+
+ +
+
+async handle_request_concolic_complete(message)
+
+ +
+
+async set_request_textual_report()
+
+ +
+
+async handle_request_textual_report(message)
+
+ +
+
+async set_request_dump()
+
+ +
+
+async handle_request_dump(message)
+
+ +
+
+async set_request_dump_name()
+
+ +
+
+async handle_request_dump_name(message)
+
+ +
+
+async set_request_visualization()
+
+ +
+
+async handle_request_visualization(message)
+
+ +
+
+async complete()
+
+ +
+
+async on_input_submitted(message: textual.widgets.Input.Submitted) None
+
+ +
+
+async on_option_list_option_selected(message: textual.widgets.OptionList.OptionSelected) None
+
+ +
+
+async on_directory_tree_file_selected(message: textual.widgets.DirectoryTree.FileSelected) None
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/analysis/index.html b/autoapi/cozy/analysis/index.html new file mode 100644 index 0000000..0b70918 --- /dev/null +++ b/autoapi/cozy/analysis/index.html @@ -0,0 +1,493 @@ + + + + + + + + cozy.analysis — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.analysis

+
+

Classes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +

FieldDiff

EqFieldDiff

For a field to be equal, all subcomponents of the body must be equal. In this case, left_body and right_body

NotEqLeaf

A not equal leaf is a field that cannot be further unpacked/traversed.

NotEqFieldDiff

For a field to be not equal, there must be at least one subcomponent of the body that was not equal. In this case,

DiffResult

StateDiff

StateDiff encapsulates the memoized state used by the difference method. This class is used internally by Comparison and is typically not for external use.

CompatiblePair

Stores information about comparing two compatible states.

Comparison

This class stores all compatible pairs and orphaned states. An orphan state is one in which there is no compatible state in the other execution tree. In most scenarios there will be no orphaned states.

+
+
+

Functions

+ + + + + + + + + + + + + + + + + + + + + +

_invalid_stack_addrs(→ range)

_invalid_stack_overlap(invalid_stack_left, ...)

_stack_addrs(→ range)

nice_name(→ str | None)

This function attempts to create a human understandable name for an address, or returns None if it can't figure

compare_side_effect(→ FieldDiff)

hexify(val0)

Recursively transforms all integers in a Python datastructure (that is mappable with functools_ext.fmap) to hex strings.

+
+
+

Module Contents

+
+
+cozy.analysis._invalid_stack_addrs(st: angr.SimState) range
+
+ +
+
+cozy.analysis._invalid_stack_overlap(invalid_stack_left: range, invalid_stack_right: range, stack_change: int)
+
+ +
+
+cozy.analysis._stack_addrs(st: angr.SimState) range
+
+ +
+
+cozy.analysis.nice_name(state: angr.SimState, malloced_names: portion.IntervalDict[tuple[str, portion.Interval]], addr: int) str | None
+

This function attempts to create a human understandable name for an address, or returns None if it can’t figure +one out.

+
+ +
+
+class cozy.analysis.FieldDiff
+
+ +
+
+class cozy.analysis.EqFieldDiff(left_body, right_body)
+

Bases: FieldDiff

+

For a field to be equal, all subcomponents of the body must be equal. In this case, left_body and right_body +should not hold any further FieldDiffs within themselves. Rather left_body and right_body should be the entire +fields for which differencing was checked (and it was determined that all subfields are equal).

+
+ +
+
+class cozy.analysis.NotEqLeaf(left_leaf, right_leaf)
+

Bases: FieldDiff

+

A not equal leaf is a field that cannot be further unpacked/traversed.

+
+ +
+
+class cozy.analysis.NotEqFieldDiff(body_diff)
+

Bases: FieldDiff

+

For a field to be not equal, there must be at least one subcomponent of the body that was not equal. In this case, +body_diff will hold further FieldDiffs within itself. Equal subfields of the bodies will be +represented by EqFieldDiff, whereas unequal subfields will be represented by further nested NotEqFieldDiff.

+
+ +
+
+cozy.analysis.compare_side_effect(joint_solver, left_se, right_se) FieldDiff
+
+ +
+
+class cozy.analysis.DiffResult(mem_diff: dict[range, tuple[claripy.ast.bits, claripy.ast.bits]], reg_diff: dict[str, tuple[claripy.ast.bits, claripy.ast.bits]], side_effect_diff: dict[str, list[tuple[cozy.side_effect.PerformedSideEffect | None, cozy.side_effect.PerformedSideEffect | None, FieldDiff]]])
+
+ +
+
+class cozy.analysis.StateDiff
+

StateDiff encapsulates the memoized state used by the difference method. This class is used internally by Comparison and is typically not for external use.

+
+
+difference(sl: angr.SimState, sr: angr.SimState, ignore_addrs: collections.abc.Iterable[range] | None = None, compute_mem_diff=True, compute_reg_diff=True, compute_side_effect_diff=True, use_unsat_core=True, simplify=False) DiffResult | None
+

Compares two states to find differences in memory. This function will return None if the two states have non-intersecting inputs. Otherwise, it will return a dict of addresses and a dict of registers which are different between the two. This function is based off of angr.analyses.congruency_check.CongruencyCheck().compare_states, but has been customized for our purposes. Note that this function may use memoization to enhance performance.

+
+
Parameters:
+
    +
  • sl (SimState) – The first state to compare

  • +
  • sr (SimState) – The second state to compare

  • +
  • ignore_addrs (collections.abc.Iterable[range] | None) – Memory addresses to ignore when doing the memory diffing. This representation is more efficient than a set of integers since the ranges involved can be quite large.

  • +
  • compute_mem_diff (bool) – If this flag is True, then we will diff the memory. If this is false, then the first element of the return tuple will be None.

  • +
  • compute_reg_diff (bool) – If this flag is True, then we will diff the registers. If this is false, then the second element of the return tuple will be None.

  • +
  • use_unsat_core (bool) – If this flag is True, then we will use unsat core optimization to speed up comparison of pairs of states. This option may cause errors in Z3, so disable if this occurs.

  • +
+
+
Returns:
+

None if the two states are not compatible, otherwise returns an object containing the memory, register differences, and side effect differences.

+
+
Return type:
+

DiffResult | None

+
+
+
+ +
+ +
+
+cozy.analysis.hexify(val0)
+

Recursively transforms all integers in a Python datastructure (that is mappable with functools_ext.fmap) to hex strings.

+
+
Parameters:
+

val0 – The datastructure to traverse.

+
+
Returns:
+

A deep copy of the datastructure, with all integers converted to hex strings.

+
+
+
+ +
+
+class cozy.analysis.CompatiblePair(state_left: cozy.terminal_state.TerminalState, state_right: cozy.terminal_state.TerminalState, mem_diff: dict[range, tuple[claripy.ast.Base, claripy.ast.Base]], reg_diff: dict[str, tuple[claripy.ast.Base, claripy.ast.Base]], side_effect_diff: dict[str, list[tuple[cozy.side_effect.PerformedSideEffect | None, cozy.side_effect.PerformedSideEffect | None, FieldDiff]]], mem_diff_ip: dict[int, tuple[frozenset[claripy.ast.Base]], frozenset[claripy.ast.Base]], compare_std_out: bool, compare_std_err: bool)
+

Stores information about comparing two compatible states.

+
+
Variables:
+
    +
  • state_left (TerminalState) – Information pertaining specifically to the pre-patched state being compared.

  • +
  • state_right (TerminalState) – Information pertaining specifically to the post-patched state being compared.

  • +
  • mem_diff (dict[range, tuple[claripy.ast.Base, claripy.ast.Base]]) – Maps memory addresses to pairs of claripy ASTs, where the left element of the tuple is the data in memory for state_left, and the right element of the tuple is what was found in memory for state_right. Only memory locations that are different are saved in this dict.

  • +
  • reg_diff (dict[str, tuple[claripy.ast.Base, claripy.ast.Base]]) – Similar to mem_diff, except that the dict is keyed by register names. Note that some registers may be subparts of another. For example in x64, EAX is a subregister of RAX.

  • +
  • side_effect_diff (dict[str, list[tuple[PerformedSideEffect | None, PerformedSideEffect | None, FieldDiff]]]) – Maps side effect channels to a list of 3 element tuples, where the first element is the performed side effect from the left binary, the second element is the performed side effect from the right binary, and the third element is the diff between the body of the side effects.

  • +
  • mem_diff_ip (dict[int, tuple[frozenset[claripy.ast.Base]], frozenset[claripy.ast.Base]]) – Maps memory addresses to a set of instruction pointers that the program was at when it wrote that byte in memory. In most cases the frozensets will have a single element, but this may not be the case in the scenario where a symbolic value determined the write address.

  • +
  • compare_std_out (bool) – If True then we should consider stdout when checking if the two input states are equal.

  • +
  • compare_std_err (bool) – If True then we should consider stderr when checking if the two input states are equal.

  • +
+
+
+
+
+equal_side_effects() bool
+
+ +
+
+equal() bool
+

Determines if the two compatible states are observationally equal. That is, they contain the same memory contents, registers, stdout, and stderr after execution.

+
+
Returns:
+

True if the two compatible states are observationally equal, and False otherwise.

+
+
Return type:
+

bool

+
+
+
+ +
+
+concrete_examples(args: any, num_examples=3) list[cozy.concrete.CompatiblePairInput]
+

Concretizes the arguments used to put the program in these states by jointly using the constraints attached to the compatible states.

+
+
Parameters:
+
    +
  • args (any) – The input arguments to concretize. This argument may be a Python datastructure, the concretizer will make a deep copy with claripy symbolic variables replaced with concrete values.

  • +
  • num_examples (int) – The maximum number of concrete examples to generate for this particular pair.

  • +
+
+
Returns:
+

A list of concrete inputs that satisfy both constraints attached to the states.

+
+
Return type:
+

list[CompatiblePairInput]

+
+
+
+ +
+ +
+
+class cozy.analysis.Comparison(pre_patched: cozy.session.RunResult, post_patched: cozy.session.RunResult, ignore_addrs: list[range] | None = None, ignore_invalid_stack=True, compare_memory=True, compare_registers=True, compare_side_effects=True, compare_std_out=False, compare_std_err=False, use_unsat_core=True, simplify=False)
+

This class stores all compatible pairs and orphaned states. An orphan state is one in which there is no compatible state in the other execution tree. In most scenarios there will be no orphaned states.

+
+
Variables:
+
    +
  • pairs (dict[tuple[SimState, SimState], CompatiblePair]) – pairs stores a dictionary that maps a pair of (pre_patch_state, post_patch_state) compatible states to their comparison information

  • +
  • orphans_left (set[TerminalState]) – Pre-patched states for which there are 0 corresponding compatible states in the post-patch

  • +
  • orphans_right (set[TerminalState]) – Post-patched states for which there are 0 corresponding compatible states in the pre-patch

  • +
+
+
+

Compares a bundle of pre-patched states with a bundle of post-patched states.

+
+
Parameters:
+
    +
  • pre_patched (project.RunResult) – The pre-patched state bundle

  • +
  • post_patched (project.RunResult) – The post-patched state bundle

  • +
  • ignore_addrs (list[range] | None) – A list of addresses ranges to ignore when comparing memory.

  • +
  • ignore_invalid_stack (bool) – If this flag is True, then memory differences in locations previously occupied by the stack are ignored.

  • +
  • compare_memory (bool) – If True, then the analysis will compare locations in the program memory.

  • +
  • compare_registers (bool) – If True, then the analysis will compare registers used by the program.

  • +
  • compare_side_effects (bool) – If True, then the analysis will compare side effects outputted by the program.

  • +
  • compare_std_out (bool) – If True, then the analysis will save stdout written by the program in the results. Note that angr currently concretizes values written to stdout, so these values will be binary strings.

  • +
  • compare_std_err (bool) – If True, then the analysis will save stderr written by the program in the results.

  • +
  • use_unsat_core (bool) – If this flag is True, then we will use unsat core optimization to speed up comparison of pairs of states. This option may cause errors in Z3, so disable if this occurs.

  • +
  • simplify (bool) – If this flag is True, then symbolic memory and register differences will be simplified as much as possible. This flag is typically only necessary if you want to do some deep inspection of symbolic contents. simplify can speed things down a lot, and symbolic expressions are usually very complex to the point where they are not easily understandable. This is why in most scenarios the flag should be left as False.

  • +
+
+
+
+
+get_pair(state_left: angr.SimState, state_right: angr.SimState) CompatiblePair
+

Retrieves a CompatiblePair given two compatible input states.

+
+
Parameters:
+
    +
  • state_left (SimState) – The pre-patched state

  • +
  • state_right (SimState) – The post-patched state

  • +
+
+
Returns:
+

The CompatiblePair object corresponding to this compatible state pair.

+
+
Return type:
+

CompatiblePair

+
+
+
+ +
+
+is_compatible(state_left: angr.SimState, state_right: angr.SimState) bool
+

Returns True when the two input states are compatible based on the pairs stored in this object, and False otherwise.

+
+
Parameters:
+
    +
  • state_left (SimState) – The pre-patched state

  • +
  • state_right (SimState) – The post-patched state

  • +
+
+
Returns:
+

True if the input states are compatible, and False otherwise

+
+
Return type:
+

bool

+
+
+
+ +
+
+__iter__() collections.abc.Iterator[CompatiblePair]
+

Iterates over compatible pairs stored in the comparison.

+
+
Returns:
+

An iterator over compatible pairs.

+
+
Return type:
+

Iterator[CompatiblePair]

+
+
+
+ +
+
+verify(verification_assertion: Callable[[CompatiblePair], claripy.ast.Base | bool]) list[CompatiblePair]
+

Determines what compatible state pairs are valid with respect to a verification assertion. Note that the comparison results are verified with respect to the verification_assertion if the returned list is empty (has length 0).

+
+
Parameters:
+

verification_assertion (Callable[[CompatiblePair], claripy.ast.Base | bool]) – A function which takes in a compatible pair and returns a claripy expression which must be satisfiable for all inputs while under the joint constraints of the state pair. Alternatively the function can return a bool. If the return value is False, this will be considered a verification failure. If the return value is True, this will be considered a verification success.

+
+
Returns:
+

A list of all compatible pairs for which there was a concrete input that caused the verification assertion to fail.

+
+
Return type:
+

list[CompatiblePair]

+
+
+
+ +
+
+report(args: any, concrete_post_processor: Callable[[any], any] | None = None, num_examples: int = 3) str
+

Generates a human-readable report of the result object, saved as a string. This string is suitable for printing.

+
+
Parameters:
+
    +
  • args (any) – The symbolic/concolic arguments used during exeuction, here these args are concretized so that we can give examples of concrete input.

  • +
  • concrete_post_processor (Callable[[any], any] | None) – This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two’s complement, or slicing off parts of the argument based on another part of the input arguments.

  • +
  • num_examples (int) – The number of concrete examples to show the user.

  • +
+
+
Returns:
+

A human-readable summary of the comparison.

+
+
Return type:
+

str

+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/claripy_ext/index.html b/autoapi/cozy/claripy_ext/index.html new file mode 100644 index 0000000..46f36b9 --- /dev/null +++ b/autoapi/cozy/claripy_ext/index.html @@ -0,0 +1,193 @@ + + + + + + + + cozy.claripy_ext — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.claripy_ext

+
+

Functions

+ + + + + + + + + + + + +

simplify_kb(→ claripy.ast.bits)

Simplifies a claripy AST expression, given some knowledge base (kb) of information

get_symbol_name(sym)

model([n])

+
+
+

Module Contents

+
+
+cozy.claripy_ext.simplify_kb(expr: claripy.ast.bits, kb: claripy.ast.Bool) claripy.ast.bits
+

Simplifies a claripy AST expression, given some knowledge base (kb) of information

+
+
Parameters:
+
    +
  • expr (claripy.ast.bits) – The expression to simplify

  • +
  • kb (claripy.ast.Bool) – The knowledge base which is used to simplify the expr. This is typically a series of equalities conjoined together.

  • +
+
+
Returns:
+

A simplified version of the input expression, or the original expression if no simplification occurred.

+
+
Return type:
+

claripy.ast.bits

+
+
+
+ +
+
+cozy.claripy_ext.get_symbol_name(sym)
+
+ +
+
+cozy.claripy_ext.model(constraints, extra_symbols: set[claripy.BVS | claripy.FPS] | frozenset[claripy.BVS | claripy.FPS] = frozenset(), n=1, **kwargs) list[dict[claripy.BVS | claripy.FPS, claripy.BVV | claripy.FPV]]
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/concolic/exploration/index.html b/autoapi/cozy/concolic/exploration/index.html new file mode 100644 index 0000000..e69d122 --- /dev/null +++ b/autoapi/cozy/concolic/exploration/index.html @@ -0,0 +1,320 @@ + + + + + + + + cozy.concolic.exploration — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.concolic.exploration

+
+

Classes

+ + + + + + + + + + + + +

ConcolicSim

This class implements concolic execution without using an external emulator

_ExploreMode

Generic enumeration.

JointConcolicSim

Jointly runs two SimulationManager objects by concretizing symbols, then running the left and right simulations

+
+
+

Module Contents

+
+
+class cozy.concolic.exploration.ConcolicSim(concrete_init: dict[claripy.BVS, claripy.BVV] | set[claripy.BVS] | frozenset[claripy.BVS], deferred_stash='deferred', check_only_recent_constraints=True)
+

Bases: angr.ExplorationTechnique

+

This class implements concolic execution without using an external emulator +like QEMU or Unicorn. This class functions by storing a concrete assignment +for each symbolic variable. When the state branches, the assignment is +substituted into both children’s constraints. If the substitution results +in the constraints evaluating to false, that child is placed in the deferred +stash. By querying the simulation manager’s active stash length, we can +tell when the concrete execution ends. When concrete execution ends, we +can either choose a new concrete substitution ourselves and set it with +the ConcolicDeferred.set_concrete() method. Alternatively we +can take one of the deferred states and generate and autogenerate a new +concrete substitution by finding a satisfying assignment for that state’s +constraints.

+

ConcolicSim constructor

+
+
Parameters:
+
    +
  • concrete_init (dict[claripy.BVS, claripy.BVV] | set[claripy.BVS] | frozenset[claripy.BVS]) – Used to initialize the concrete value. If this value is a substitution dictionary, then that dictionary is used as our concrete input. If this value is a set or frozenset, then a substituting dictionary is autogenerated, subject to the initial state’s constraints.

  • +
  • deferred_stash (string) – The name of the deferred stash

  • +
  • check_only_recent_constraints (bool) – If this value is true, then whenever child states are created, only the new constraints are checked with respect to the concrete substitution. Here we assume that the parent of the child already satisfied the concrete input on a previous iteration, so it’s safe to only check with respect to the new constraints.

  • +
+
+
+
+
+setup(simgr)
+
+ +
+
+is_satisfied(constraints: list[claripy.ast.bool]) bool
+

Substitutes the current concrete input into the constraints, and returns True if the constraints are True after +the substitution is made.

+
+
Parameters:
+

constraints (list[claripy.ast.bool]) – The constraints in which the concrete solution will be substituted.

+
+
Return type:
+

bool

+
+
Returns:
+

If the constraints are True after substitution, then True is returned. Otherwise returns False.

+
+
+
+ +
+
+_set_replacement_dict(concrete)
+
+ +
+
+set_concrete(simgr, concrete: dict[claripy.BVS | claripy.FPS, claripy.BVV | claripy.FPV])
+

Sets the concrete input via a substitution dictionary. All the symbols used by the program should have concrete +values provided for them. The active and deferred stash will be mutated to ensure that only states which +are satisfied by the concrete substitution are active.

+
+
Parameters:
+

concrete (dict[claripy.BVS, claripy.BVV]) – A dictionary mapping each symbol to its concrete value.

+
+
+
+ +
+
+_generate_concrete(simgr: angr.SimulationManager, from_stash: list[angr.SimState], symbols: set[claripy.BVS] | frozenset[claripy.BVS], candidate_heuristic: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None)
+
+ +
+
+generate_concrete(simgr: angr.SimulationManager, symbols: set[claripy.BVS] | frozenset[claripy.BVS], candidate_heuristic: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None)
+

Autogenerates a new concrete input by choosing a deferred state and finding a satisfying assignment +with respect to that state’s constraints. The symbols provided will be used to internally generate +a substitution dictionary. The candidate_heuristic is used to choose a state from the current stash +of deferred states. If no heuristic is provided, the last state in the deferred stash will be chosen next. +If there are any active states in the simulation manager, a ValueError will be thrown.

+
+
Parameters:
+
    +
  • simgr (angr.SimulationManager) – The simulation manager

  • +
  • symbols (set[claripy.BVS] | frozenset[claripy.BVS]) – The symbols that we will generate a substitution for.

  • +
  • candidate_heuristic (Callable[[list[angr.SimState]], angr.SimState] | None) – The heuristic that should be used to choose the deferred state that should be explored further. Note that this function should mutate its input list (ie, remove the desired state), and return that desired state.

  • +
+
+
+
+ +
+
+filter(simgr, state, **kwargs)
+
+ +
+ +
+
+class cozy.concolic.exploration._ExploreMode
+

Bases: enum.Enum

+

Generic enumeration.

+

Derive from this class to define new enumerations.

+
+
+EXPLORE_LEFT = 0
+
+ +
+
+EXPLORE_RIGHT = 1
+
+ +
+ +
+
+class cozy.concolic.exploration.JointConcolicSim(simgr_left: angr.SimulationManager, simgr_right: angr.SimulationManager, symbols: set[claripy.BVS] | frozenset[claripy.BVS], left_explorer: ConcolicSim, right_explorer: ConcolicSim, candidate_heuristic_left: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None, candidate_heuristic_right: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None)
+

Jointly runs two SimulationManager objects by concretizing symbols, then running the left and right simulations +with the same concrete input. This joint simulator alternates between the left and right simulations +when generating new concrete inputs.

+
+
Parameters:
+
    +
  • simgr_left (SimulationManager) – The first simulation manager to run in concolic execution

  • +
  • simgr_right (SimulationManager) – The second simulation manager to run in concolic execution.

  • +
  • left_explorer (ConcolicSim) – The first exploration method to use for concolic execution. Note that this exploration technique will be attached to the left simulation manager.

  • +
  • right_explorer (ConcolicSim) – The second exploration method to use for concolic execution. Note that this exploration technique will be attached to the right simulation manager.

  • +
  • candidate_heuristic_left (Callable[[list[angr.SimState]], angr.SimState] | None) – The heuristic that should be used to choose the deferred state that should be explored further. Note that this function should mutate its input list (ie, remove the desired state), and return that desired state. Note that some pre-made candidate heuristic techniques can be found in the cozy.concolic.heuristics module.

  • +
  • candidate_heuristic_right (Callable[[list[angr.SimState]], angr.SimState] | None) – The heuristic that should be used to choose the deferred state that should be explored further. Note that this function should mutate its input list (ie, remove the desired state), and return that desired state. Note that some pre-made candidate heuristic techniques can be found in the cozy.concolic.heuristics module.

  • +
+
+
+
+
+_swap_explore_mode()
+
+ +
+
+_generate_concrete(from_stash_left, from_stash_right)
+
+ +
+
+explore(explore_fun_left: collections.abc.Callable[[angr.SimulationManager], None] | None = None, explore_fun_right: collections.abc.Callable[[angr.SimulationManager], None] | None = None, termination_fun_left: collections.abc.Callable[[angr.SimulationManager], bool] | None = None, termination_fun_right: collections.abc.Callable[[angr.SimulationManager], bool] | None = None, loop_bound_left: int | None = None, loop_bound_right: int | None = None) None
+

Explores the simulations given in the left and right simulation manager.

+
+
Parameters:
+
    +
  • explore_fun_left (Callable[[SimulationManager], None] | None) – If this parameter is not None, then instead of SimulationManager.explore() being called to do the exploration, we call explore_fun_left instead.

  • +
  • explore_fun_right (Callable[[SimulationManager], None] | None) – If this parameter is not None, then instead of SimulationManager.explore() being called to do the exploration, we call explore_fun_right instead.

  • +
  • termination_fun_left (Callable[[SimulationManager], bool] | None) – Every time we finish exploring one concrete input, this function is called to determine if the exploration should terminate. If both termination functions return True, then exploration is halted and this function returns. If this parameter is None, then the left simulation manager will terminate only when no further exploration is possible (ie, execution is complete). Pre-made termination functions can be found in the cozy.concolic.heuristics module.

  • +
  • termination_fun_right (Callable[[SimulationManager], bool] | None) – Every time we finish exploring one concrete input, this function is called to determine if the exploration should terminate. If both termination functions return True, then exploration is halted and this function returns. If this parameter is None, then the right simulation manager will terminate only when no further exploration is possible (ie, execution is complete). Pre-made termination functions can be found in the cozy.concolic.heuristics module.

  • +
  • loop_bound_left (int | None) – Sets an upper bound on loop iteration count for the left session. Useful for programs with non-terminating loops.

  • +
  • loop_bound_right (int | None) – Sets an upper bound on loop iteration count for the right session. Useful for programs with non-terminating loops.

  • +
+
+
Returns:
+

None

+
+
Return type:
+

None

+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/concolic/heuristics/index.html b/autoapi/cozy/concolic/heuristics/index.html new file mode 100644 index 0000000..a0ad379 --- /dev/null +++ b/autoapi/cozy/concolic/heuristics/index.html @@ -0,0 +1,267 @@ + + + + + + + + cozy.concolic.heuristics — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.concolic.heuristics

+
+

Classes

+ + + + + + + + + + + + + + + + + + +

ArbitraryCandidate

For use as the candidate heuristic in cozy.exploration.ConcolicSim.generate_concrete()

BBTransitionCandidate

For use as the candidate heuristic in cozy.exploration.ConcolicSim.generate_concrete()

CompleteTermination

This termination heuristic tells the concolic execution to explore until all states are deadended.

CoverageTermination

This termination heuristic tells the concolic execution to explore until a certain fraction of a

CyclomaticComplexityTermination

This termination heuristic tells the concolic execution to explore until a certain number of terminated

+
+
+

Module Contents

+
+
+class cozy.concolic.heuristics.ArbitraryCandidate
+

For use as the candidate heuristic in cozy.exploration.ConcolicSim.generate_concrete() +This heuristic will choose the next exploration candidate by popping the last element off the candidate’s list.

+
+
+__call__(candidate_states: list[angr.SimState])
+
+ +
+ +
+
+class cozy.concolic.heuristics.BBTransitionCandidate(lookback: int = 2)
+

For use as the candidate heuristic in cozy.exploration.ConcolicSim.generate_concrete() +This heuristic will select a candidate whose basic block history has been seen least frequently in the past. This +class keeps an internal record of candidates it chose in the past to compute this metric.

+
+
Parameters:
+

lookback (int) – The number of basic blocks we should look back to when computing a candidate’s transition history. This should be a small integer, somewhere in the range 1 to 6. This number should in general only be increased if the total number of states we search goes up. The candidate state with the most unique transition history will be chosen by this heuristic.

+
+
+
+
+__call__(candidate_states: list[angr.SimState])
+
+ +
+ +
+
+class cozy.concolic.heuristics.CompleteTermination
+

This termination heuristic tells the concolic execution to explore until all states are deadended.

+
+
+__call__(simgr)
+
+ +
+ +
+
+class cozy.concolic.heuristics.CoverageTermination(fun: angr.knowledge_plugins.Function, coverage_fraction: float = 0.9)
+

This termination heuristic tells the concolic execution to explore until a certain fraction of a +function’s basic blocks have been visited at least once.

+
+
Parameters:
+
    +
  • fun (Function) – The function that we are seeking a specific coverage over.

  • +
  • coverage_fraction (float) – A number in the range [0, 1] that determines what fraction of basic blocks need to be visited before termination is reached.

  • +
+
+
+
+
+static from_session(sess: cozy.project.Session, coverage_fraction: float = 0.9) CoverageTermination
+

Constructs a CoverageTermination object from an unrun session.

+
+
Parameters:
+
    +
  • sess (Session) – The session which is set to call some specific function, but has not yet been run.

  • +
  • coverage_fraction (float) – A number in the range [0, 1] that determines what fraction of basic blocks need to be visited before termination is reached.

  • +
+
+
+
+ +
+
+__call__(simgr)
+
+ +
+ +
+
+class cozy.concolic.heuristics.CyclomaticComplexityTermination(fun: angr.knowledge_plugins.Function, fun_manager: angr.knowledge_plugins.FunctionManager, add_callees=True, multiplier: int | float = 1)
+

This termination heuristic tells the concolic execution to explore until a certain number of terminated +states are reached. If add_callees is False, then this value is equal to the cyclomatic complexity of the function. +Otherwise, it is equal to the cyclomatic complexity of the function plus the cyclomatic complexity of all callees +of the function (recursively).

+
+
Parameters:
+
    +
  • add_callees (bool) – If this parameter is True, the cyclomatic complexity of all functions deeper in the call graph will be summed to determine the maximum number of states to explore. If False, the upper bound will be the cyclomatic complexity of the session.

  • +
  • multiplier (int | float) – The computed cyclomatic complexity sum will be multiplied by this value to determine the number of states to explore

  • +
+
+
+
+
+static from_session(sess: cozy.project.Session, add_callees=True, multiplier: int | float = 1) CyclomaticComplexityTermination
+

Constructs an object from a session. The session must be started from a specific function.

+
+
Parameters:
+
    +
  • add_callees (bool) – If this parameter is True, the cyclomatic complexity of all functions deeper in the call graph will be summed to determine the maximum number of states to explore. If False, the upper bound will be the cyclomatic complexity of the session.

  • +
  • multiplier (int | float) – The computed cyclomatic complexity sum will be multiplied by this value to determine the number of states to explore

  • +
+
+
+
+ +
+
+__call__(simgr)
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/concolic/index.html b/autoapi/cozy/concolic/index.html new file mode 100644 index 0000000..8c49b95 --- /dev/null +++ b/autoapi/cozy/concolic/index.html @@ -0,0 +1,139 @@ + + + + + + + + cozy.concolic — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.concolic

+
+

Submodules

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/concolic/session/index.html b/autoapi/cozy/concolic/session/index.html new file mode 100644 index 0000000..1a8ef5c --- /dev/null +++ b/autoapi/cozy/concolic/session/index.html @@ -0,0 +1,180 @@ + + + + + + + + cozy.concolic.session — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.concolic.session

+
+

Classes

+ + + + + + +

JointConcolicSession

This class is used for jointly running two sessions using concolic execution. The sessions need to be run

+
+
+

Module Contents

+
+
+class cozy.concolic.session.JointConcolicSession(sess_left: cozy.session.Session, sess_right: cozy.session.Session, candidate_heuristic_left: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None, candidate_heuristic_right: collections.abc.Callable[[list[angr.SimState]], angr.SimState] | None = None, termination_heuristic_left: collections.abc.Callable[[angr.sim_manager.SimulationManager], bool] | None = None, termination_heuristic_right: collections.abc.Callable[[angr.sim_manager.SimulationManager], bool] | None = None)
+

This class is used for jointly running two sessions using concolic execution. The sessions need to be run +jointly because the concrete values generated during concolic execution need to be fed to both programs.

+
+
+run(args_left: list[claripy.ast.bits], args_right: list[claripy.ast.bits], symbols: set[claripy.BVS] | frozenset[claripy.BVS], cache_intermediate_info: bool = True, ret_addr_left: int | None = None, ret_addr_right: int | None = None, loop_bound_left: int | None = None, loop_bound_right: int | None = None) tuple[cozy.session.RunResult, cozy.session.RunResult]
+

Jointly run two sessions.

+
+
Parameters:
+
    +
  • args_left (list[claripy.ast.bits]) – The arguments to pass to the left session.

  • +
  • args_right (list[claripy.ast.bits]) – The arguments to pass to the right session.

  • +
  • symbols (set[claripy.BVS] | frozenset[claripy.BVS]) – All symbolic values used in the two sessions. These symbols may be passed as arguments, or may have been pre-stored in the memory of the session before this method was called. This set is required during the concretization step where we need to generate concrete values for all symbolic values in the program.

  • +
  • cache_intermediate_info (bool) – If this flag is True, then information about intermediate states will be

  • +
+
+
+

cached. This is required for dumping the execution graph which is used in visualization. +:param int | None ret_addr_left: What address to return to if calling as a function +:param int | None ret_addr_right: What address to return to if calling as a function +:param int | None loop_bound_left: Sets an upper bound on loop iteration count for the left session. Useful for programs with non-terminating loops. +:param int | None loop_bound_right: Sets an upper bound on loop iteration count for the right session. Useful for programs with non-terminating loops.

+
+
Returns:
+

The result of running the two sessions, where the first element of the return tuple being the left session’s result, and the second element being the right session’s result.

+
+
Return type:
+

tuple[RunResult, RunResult]

+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/concrete/index.html b/autoapi/cozy/concrete/index.html new file mode 100644 index 0000000..b61fa69 --- /dev/null +++ b/autoapi/cozy/concrete/index.html @@ -0,0 +1,206 @@ + + + + + + + + cozy.concrete — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.concrete

+
+

Classes

+ + + + + + + + + +

CompatiblePairInput

Stores information about the concretization of a compatible state pair.

TerminalStateInput

Stores information about the concretization of a TerminalState.

+
+
+

Functions

+ + + + + + +

_concretize(solver, state_bundle[, n])

+
+
+

Module Contents

+
+
+cozy.concrete._concretize(solver, state_bundle, n=1)
+
+ +
+
+class cozy.concrete.CompatiblePairInput(args, mem_diff: dict[range, tuple[int, int]], reg_diff: dict[str, tuple[int, int]], left_side_effects: dict[str, list[cozy.side_effect.ConcretePerformedSideEffect]], right_side_effects: dict[str, list[cozy.side_effect.ConcretePerformedSideEffect]])
+

Stores information about the concretization of a compatible state pair.

+
+
Variables:
+
    +
  • args (any) – The same Python datastructures as the arguments passed to concrete_examples, except that all claripy symbolic variables are replaced with concrete values.

  • +
  • mem_diff (dict[range, tuple[int, int]]) – Concretized version of memory difference. Each key is a memory address range, and each value is a concretized version of the data stored at that location for the prepatched, postpatched runs.

  • +
  • reg_diff (dict[str, tuple[int, int]]) – Concretized version of register difference. Each key is a register name, and each value is a concretized version of the data stored at that register for the prepatched, postpatched runs.

  • +
  • left_side_effects (dict[str, list[ConcretePerformedSideEffect]]) – Concretized versions of side effects made by the prepatched state.

  • +
  • right_side_effects (dict[str, list[ConcretePerformedSideEffect]]) – Concretized versions of side_effects made by the postpatched state.

  • +
+
+
+
+ +
+
+class cozy.concrete.TerminalStateInput(args, side_effects: dict[str, list[cozy.side_effect.ConcretePerformedSideEffect]])
+

Stores information about the concretization of a TerminalState.

+
+
Variables:
+
    +
  • args (any) – The same Python datastructures as the arguments passed to concrete_examples, except that all claripy symbolic variables are replaced with concrete values.

  • +
  • side_effects (dict[str, list[PerformedSideEffect]]) – Concretized side effects outputted by the singleton state.

  • +
+
+
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/constants/index.html b/autoapi/cozy/constants/index.html new file mode 100644 index 0000000..725e654 --- /dev/null +++ b/autoapi/cozy/constants/index.html @@ -0,0 +1,170 @@ + + + + + + + + cozy.constants — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.constants

+
+

Attributes

+ + + + + + + + + +

NULL_PTR

INT_SIZE

+
+
+

Module Contents

+
+
+cozy.constants.NULL_PTR = 0
+
+ +
+
+cozy.constants.INT_SIZE = 4
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/directive/index.html b/autoapi/cozy/directive/index.html new file mode 100644 index 0000000..dcd316d --- /dev/null +++ b/autoapi/cozy/directive/index.html @@ -0,0 +1,491 @@ + + + + + + + + cozy.directive — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.directive

+
+

Classes

+ + + + + + + + + + + + + + + + + + + + + + + + + + + +

Directive

Abstract base class for all directives.

AssertType

An enum to determine the type of assertion.

Assert

An assert directive sets a breakpoint at a certain address.

Assume

An assume directive sets a breakpoint at a certain address. An assume simply adds an extra constraint to the state's accumulated constraints before resuming execution. An assume is useful for adding a precondition.

VirtualPrint

This directive is used to log some piece of information about the program in a list attached to the state. When execution reaches the desired address, the log function will be called, and the result will be saved inside the state's globals dictionary.

ErrorDirective

If the program execution reaches the desired address, the state will be considered to be in an errored state and will be moved to the errored cache. This state will have no further execution.

Breakpoint

This directive is used to halt execution at some particular address, and pass the current state to the provided

Postcondition

A Postcondition is a special type of assertion that is executed on terminal states for which execution has been

+
+
+

Module Contents

+
+
+class cozy.directive.Directive
+

Abstract base class for all directives.

+
+ +
+
+class cozy.directive.AssertType
+

Bases: enum.Enum

+

An enum to determine the type of assertion.

+
+
+ASSERT_MUST = 0
+

This type of assert will be triggered if the assertion condition can be falsified. This assertion type replicates +the behaviour of assertions as used in a typical testing environment. More precisely, this assertion uses +universal quantification. The assertion fails if the following condition does not hold: forall x . P(x), where +x is the program input, and P is the assertion condition.

+
+ +
+
+ASSERT_CAN = 1
+

This type of assert will be triggered if the assertion condition cannot be satisfied, under the constraints of +the local state. This assertion type is a dual to ASSERT_MUST, and an exact analogue does not exist from +typical testing environments. More precisely, this assertion uses existential quantification. The assertion +fails if the following condition does not hold: exists x . P(x), where x is the program input, P is the +assertion condition, and C is the state’s constraints.

+
+ +
+
+ASSERT_CAN_GLOBAL = 2
+

This is type of assert is like ASSERT_CAN, but is computed under a global setting. If on any path the local +assertion exists x . P(x) holds, then all cases where the assertion failed will be scrubbed from the output. +This is much the same E from computation tree logic, which is also a global property. Note that this assertion +type should only be used in cases where the exploration is complete - ie all states can be explored.

+
+ +
+ +
+
+class cozy.directive.Assert(addr: int, condition_fun: collections.abc.Callable[[angr.SimState], claripy.ast.bool], info_str: str | None = None, assert_type: AssertType = AssertType.ASSERT_MUST)
+

Bases: Directive

+

An assert directive sets a breakpoint at a certain address.

+
+
Variables:
+
    +
  • addr (int) – The program address this assert is attached to.

  • +
  • condition_fun (Callable[[SimState], claripy.ast.bool]) – When the program reaches the desired address, the SimState will be passed to this function, and an assertion condition should be returned. This is then used internally by the SAT solver, along with the state’s accumulated constraints.

  • +
  • info_str (str | None) – Human readable label for this assertion, printed to the user if the assert is triggered.

  • +
  • assert_type (AssertType) – The type of assert.

  • +
+
+
+

Constructor for an Assert object.

+
+
Parameters:
+
    +
  • addr (int) – The address at which the assert will be triggered.

  • +
  • condition_fun (Callable[[SimState], claripy.ast.bool]) – When the program reaches the desired address, the SimState will be passed to this function, and an assertion condition should be returned. This is then used internally by the SAT solver, along with the state’s accumulated constraints.

  • +
  • info_str (str | None) – Human readable label for this assertion, printed to the user if the assert is triggered.

  • +
  • assert_type (AssertType) – The type of assert to construct.

  • +
+
+
+
+
+static from_fun_offset(project, fun_name: str, offset: int, condition_fun: collections.abc.Callable[[angr.SimState], claripy.ast.bool], info_str: str | None = None, assert_type: AssertType = AssertType.ASSERT_MUST)
+

Factory for an Assert object set at a certain offset from a function start.

+
+
Parameters:
+
    +
  • project (cozy.project.Project) – The project which this assert is attached to. The project is used to compute the address of the assert.

  • +
  • str (fun_name) – The name of the function in which this assert will be located.

  • +
  • int (offset) – The offset into the function in which this assert will be located.

  • +
  • condition_fun (Callable[[SimState], claripy.ast.bool]) – When the program reaches the desired address, the SimState will be passed to this function, and an assertion condition should be returned. This is then used internally by the SAT solver, along with the state’s accumulated constraints.

  • +
  • info_str (str | None) – Human readable label for this assertion, printed to the user if the assert is triggered.

  • +
  • assert_type (AssertType) – The type of assert to construct.

  • +
+
+
Return type:
+

Assert

+
+
+
+ +
+ +
+
+class cozy.directive.Assume(addr: int, condition_fun: collections.abc.Callable[[angr.SimState], claripy.ast.bool], info_str: str | None = None)
+

Bases: Directive

+

An assume directive sets a breakpoint at a certain address. An assume simply adds an extra constraint to the state’s accumulated constraints before resuming execution. An assume is useful for adding a precondition.

+
+
Variables:
+
    +
  • addr (int) – The program address this assume is attached to.

  • +
  • condition_fun (Callable[[SimState], claripy.ast.bool]) – When the program reaches the desired address, the SimState will be passed to this function, and a condition should be returned. This condition is then attached to the state’s set of constraints.

  • +
  • info_str (str | None) – Human readable label for this assume.

  • +
+
+
+

Constructor for an Assume object.

+
+
Parameters:
+
    +
  • addr (int) – The address at which the assume will be triggered.

  • +
  • condition_fun (Callable[[SimState], claripy.ast.bool]) – When the program reaches the desired address, the SimState will be passed to this function, and an assumption should be returned. This assumption is attached to the state’s constraints for future execution.

  • +
  • info_str (str | None) – Human readable label for this assume.

  • +
+
+
+
+
+static from_fun_offset(project, fun_name: str, offset: int, condition_fun: collections.abc.Callable[[angr.SimState], claripy.ast.bool], info_str: str | None = None)
+

Factory for an Assume object set at a certain offset from a function start.

+
+
Parameters:
+
    +
  • project (cozy.project.Project) – The project this assume is attached to.

  • +
  • str (fun_name) – The name of the function in which this assume will be located.

  • +
  • int (offset) – The offset into the function in which this assume will be located.

  • +
  • condition_fun (Callable[[SimState], claripy.ast.bool]) – When the program reaches the desired address, the SimState will be passed to this function, and an assumption should be returned. This assumption is attached to the state’s constraints for future execution.

  • +
  • info_str (str | None) – Human readable label for this assume.

  • +
+
+
Returns:
+

A new Assume object at the desired function offset.

+
+
Return type:
+

Assume

+
+
+
+ +
+ +
+
+class cozy.directive.VirtualPrint(addr: int, log_fun: collections.abc.Callable[[angr.SimState], claripy.ast.Base], concrete_post_processor: collections.abc.Callable[[claripy.ast.Base], any] | None = None, info_str: str = 'Unknown Virtual Print: ', label=None)
+

Bases: Directive

+

This directive is used to log some piece of information about the program in a list attached to the state. When execution reaches the desired address, the log function will be called, and the result will be saved inside the state’s globals dictionary.

+
+
Variables:
+
    +
  • addr (int) – The program address this virutal print is attached to.

  • +
  • log_fun (Callable[[SimState], claripy.ast.Base]) – This function takes in the current state and returns a claripy AST which should be logged. This value may be symbolic.

  • +
  • info_str (str) – Human readable label for this virtual print.

  • +
  • label (str) – Internal label used for side effect alignment. Side effects (including virtual prints) are diffed if they have the same label.

  • +
  • concrete_post_processor (Callable[[claripy.ast.Base], any] | None) – concrete_post_processor takes as input a concretized version of the output from log_fun and returns a result which is printed to the screen. For example, a log fun may return state.regs.eax to log the value of eax. But if eax represents a 32 bit signed value, we want to pretty print to negative number. This is where concrete_post_processor is useful. In this example concrete_post_processor would take a concrete bit vector representing a possible value of EAX and return a Python integer (which can be negative). This is what is shown to the user.

  • +
+
+
+

Constructor for a VirtualPrint object.

+
+
Parameters:
+
    +
  • addr (int) – The program address this virutal print is attached to.

  • +
  • log_fun (Callable[[SimState], claripy.ast.Base]) – This function takes in the current state and returns a claripy AST which should be logged. This value may be symbolic.

  • +
  • concrete_post_processor (Callable[[claripy.ast.Base], any] | None) – concrete_post_processor takes as input a concretized version of the output from log_fun and returns a result which is printed to the screen. For example, a log fun may return state.regs.eax to log the value of eax. But if eax represents a 32 bit signed value, we want to pretty print to negative number. This is where concrete_post_processor is useful. In this example concrete_post_processor would take a concrete bit vector representing a possible value of EAX and return a Python integer (which can be negative). This is what is shown to the user.

  • +
  • info_str (str) – Human readable label for this virtual print.

  • +
  • label (str) – Internal label used for side effect alignment. Side effects (including virtual prints) are diffed if they have the same label.

  • +
+
+
+
+
+effect_concrete_post_processor(concrete_value)
+
+ +
+
+static from_fun_offset(project, fun_name: str, offset: int, log_fun: collections.abc.Callable[[angr.SimState], claripy.ast.Base], concrete_post_processor: collections.abc.Callable[[claripy.ast.Base], any] | None = None, info_str: str | None = None, label=None)
+

Factory for VirtualPrint object set at a certain offset from a function start.

+
+
Parameters:
+
    +
  • project (cozy.project.Project) – The project which this virtual print is attached to. The project is used to compute the address of the virtual print.

  • +
  • fun_name (str) – The name of the function in which this virtual print will be located.

  • +
  • offset (int) – The offset into the function in which this virtual print will be located.

  • +
  • log_fun (Callable[[SimState], claripy.ast.Base]) – This function takes in the current state and returns a claripy AST which should be logged. The return value may be symbolic.

  • +
  • concrete_post_processor (Callable[[claripy.ast.Base], any] | None) – concrete_post_processor takes as input a concretized version of the output from log_fun and returns a result which is printed to the screen. For example, a log fun may return state.regs.eax to log the value of eax. But if eax represents a 32 bit signed value, we want to pretty print to negative number. This is where concrete_post_processor is useful. In this example concrete_post_processor would take a concrete bit vector representing a possible value of EAX and return a Python integer (which can be negative). This is what is shown to the user.

  • +
  • info_str (str) – Human readable label for this virtual print.

  • +
  • label (str) – Internal label used for side effect alignment. Side effects (including virtual prints) are diffed if they have the same label.

  • +
+
+
Returns:
+

A new VirtualPrint object at the desired function offset.

+
+
Return type:
+

VirtualPrint

+
+
+
+ +
+ +
+
+class cozy.directive.ErrorDirective(addr: int, info_str: str | None = None)
+

Bases: Directive

+

If the program execution reaches the desired address, the state will be considered to be in an errored state and will be moved to the errored cache. This state will have no further execution.

+
+
Variables:
+
    +
  • addr (int) – The program address this error directive is attached to.

  • +
  • str – Human readable information for this error directive.

  • +
+
+
+

Constructor for an ErrorDirective object.

+
+
Parameters:
+
    +
  • addr (int) – The program address this error directive is attached to.

  • +
  • info_str (str) – Human readable information for this error directive.

  • +
+
+
+
+
+static from_fun_offset(project, fun_name: str, offset: int, info_str: str | None = None)
+

Factory for ErrorDirective object set at a certain offset from a function start.

+
+
Parameters:
+
    +
  • project (cozy.project.Project) – The project this error directive should be attached to.

  • +
  • fun_name (str) – The name of the function in which this error directive will be located.

  • +
  • offset (int) – The offset into the function in which this error directive will be located.

  • +
  • info_str (str | None) – Human readable information for this error directive.

  • +
+
+
Returns:
+

A new ErrorDirective object at the desired function offset.

+
+
Return type:
+

ErrorDirective

+
+
+
+ +
+ +
+
+class cozy.directive.Breakpoint(addr: int, breakpoint_fun: collections.abc.Callable[[angr.SimState], None])
+

Bases: Directive

+

This directive is used to halt execution at some particular address, and pass the current state to the provided +breakpoint function, which can then either modify the state or do some other side effect (like executing a Python +print() call).

+
+
Variables:
+
    +
  • addr (int) – The program address this breakpoint is attached to.

  • +
  • breakpoint_fun (Callable[[SimState], None]) – This function takes in the state reached by the program at the attachment point.

  • +
+
+
+

Constructor for a VirtualPrint object.

+
+
Parameters:
+
    +
  • addr (int) – The program address this breakpoint is attached to.

  • +
  • breakpoint_fun (Callable[[SimState], None]) – This function takes in the state reached by the program at the attachment point.

  • +
+
+
+
+
+static from_fun_offset(project, fun_name: str, offset: int, breakpoint_fun: collections.abc.Callable[[angr.SimState], None])
+

Factory for VirtualPrint object set at a certain offset from a function start.

+
+
Parameters:
+
    +
  • project (cozy.project.Project) – The project which this virtual print is attached to. The project is used to compute the address of the virtual print.

  • +
  • fun_name (str) – The name of the function in which this virtual print will be located.

  • +
  • offset (int) – The offset into the function in which this virtual print will be located.

  • +
  • breakpoint_fun (Callable[[SimState], None]) – This function takes in the state reached by the program at the attachment point.

  • +
+
+
Returns:
+

A new Breakpoint object at the desired function offset.

+
+
Return type:
+

Breakpoint

+
+
+
+ +
+ +
+
+class cozy.directive.Postcondition(condition_fun: collections.abc.Callable[[angr.SimState], claripy.ast.bool], info_str: str | None = None, assert_type=AssertType.ASSERT_MUST)
+

Bases: Directive

+

A Postcondition is a special type of assertion that is executed on terminal states for which execution has been +completed. This is identical to attaching an ASSERT_MUST assertion to all return points. This type of property +is useful for verifying that a property holds in all terminal states. Note that if you are looking to add a +precondition, you can add your proposition to the session before the run via +cozy.Session.add_constraints().

+
+
Parameters:
+
    +
  • condition_fun (Callable[[SimState], claripy.ast.bool]) – When the program reaches a terminal state, the SimState will be passed to this function, and an assertion condition should be returned. This is then used internally by the SAT solver, along with the state’s accumulated constraints.

  • +
  • info_str (str | None) – Human readable label for this postcondition assertion, printed to the user if the assert is triggered.

  • +
  • assert_type (AssertType) – The type of assert to construct.

  • +
+
+
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/execution_graph/index.html b/autoapi/cozy/execution_graph/index.html new file mode 100644 index 0000000..bd5713d --- /dev/null +++ b/autoapi/cozy/execution_graph/index.html @@ -0,0 +1,421 @@ + + + + + + + + cozy.execution_graph — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.execution_graph

+
+

Classes

+ + + + + + +

ExecutionGraph

This class is used to store a networkx.DiGraph, decorated with SimStates, representing the full history of a symbolic program execution.

+
+
+

Functions

+ + + + + + + + + + + + + + + + + + +

_serialize_diff(diff[, nice_name_a, nice_name_b])

_serialized_field_diff(diff)

dump_comparison(→ None)

Generates and saves JSON data for Cozy-Viz.

visualize_comparison(proj_a, proj_b, rslt_a, rslt_b, ...)

Generates and visualizes JSON data for Cozy-Viz.

_generate_comparison(→ tuple[networkx.DiGraph, ...)

Generates JSON data for Cozy-Viz.

+
+
+

Module Contents

+
+
+cozy.execution_graph._serialize_diff(diff, nice_name_a: collections.abc.Callable[[int], str | None] | None = None, nice_name_b: collections.abc.Callable[[int], str | None] | None = None)
+
+ +
+
+cozy.execution_graph._serialized_field_diff(diff: any)
+
+ +
+
+cozy.execution_graph.dump_comparison(proj_a: cozy.project.Project, proj_b: cozy.project.Project, rslt_a: cozy.session.RunResult, rslt_b: cozy.session.RunResult, comparison_results: cozy.analysis.Comparison, file_name_a: str = 'prepatch', file_name_b: str = 'postpatch', output_file: str = 'cozy-result.json', concrete_post_processor: collections.abc.Callable[[any], any] | None = None, include_vex: bool = False, include_simprocs: bool = False, flag_syscalls: bool = False, include_actions: bool = False, include_debug: bool = False, include_side_effects: bool = True, args: any = [], num_examples: int = 0) None
+

Generates and saves JSON data for Cozy-Viz.

+

Generates JSON data for Cozy-Viz from the results of two symbolic +executions, and saves the result to two files, one for pre and one for post.

+
+
Parameters:
+
    +
  • proj_a (Project) – The project associated with the first execuction.

  • +
  • proj_b (Project) – The project associated with the second execuction.

  • +
  • rslt_a (RunResult) – The result of the first execution.

  • +
  • rslt_b (RunResult) – The result of the second execution.

  • +
  • comparison_results (analysis.Comparison) – The comparison we wish to dump.

  • +
  • file_name_a (str, optional) – A name for the prepatch binary, displayed in visualization. +Default “prepatch”.

  • +
  • file_name_b (str, optional) – A name for the postpatch binary, displayed in visualization. +Default “postpatch”

  • +
  • output_file (str, optional) – A name for generated JSON file. Default “cozy-result.json”.

  • +
  • concrete_post_processor (Callable [[any],any] | None, optional) – This function is used to +post-process concretized versions of args before they are added to the +return string. Some examples of this function include converting an integer +to a negative number due to use of two’s complement, or slicing off parts of +the argument based on another part of the input arguments. Default None.

  • +
  • include_vex (bool, optional) – whether to, for each SimState, generate the +corresponding VEX IR and include the result in the JSON. Default False.

  • +
  • include_simprocs (bool, optional) – whether to, for each SimState, flag any +SimProcedure locations occurring in the corrsponding basic block. Default False.

  • +
  • include_simprocs – whether to include a listing of +SimProcedures called in each basic block. Default False.

  • +
  • include_actions (bool, optional) – whether to include logging of +read/write operations on memory and registers. Default False.

  • +
  • include_debug (bool, optional) – whether to include debugging information +recovered from DWARF metadata. Default False.

  • +
  • include_side_effects (bool, optional) – whether to include cozy side effects, +like virtual prints, if present. Default True.

  • +
  • args (any, optional) – The input arguments to concretize. This argument +may be a Python datastructure, the concretizer will make a deep copy with +claripy symbolic variables replaced with concrete values. See +cozy.analysis.CompatiblePair. Default = [].

  • +
  • num_examples (int, optional) – The number of concrete examples to +generate and incorporate into the JSON, for each dead-end state. Default 0.

  • +
+
+
+
+ +
+
+cozy.execution_graph.visualize_comparison(proj_a: cozy.project.Project, proj_b: cozy.project.Project, rslt_a: cozy.session.RunResult, rslt_b: cozy.session.RunResult, comparison_results: cozy.analysis.Comparison, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, include_vex: bool = False, include_simprocs: bool = False, flag_syscalls: bool = False, include_actions: bool = False, include_debug: bool = False, include_side_effects: bool = True, args: any = [], num_examples: int = 0, open_browser=False, port=8080)
+

Generates and visualizes JSON data for Cozy-Viz.

+

Generates JSON data suitable for visual comparison using Cozy-Viz from the results of two symbolic executions, and launches a server to view the data.

+
+
Parameters:
+
    +
  • proj_a (Project) – The project associated with the first execuction.

  • +
  • proj_b (Project) – The project associated with the second execuction.

  • +
  • rslt_a (RunResult) – The result of the first execution.

  • +
  • rslt_b (RunResult) – The result of the second execution.

  • +
  • comparison_results (analysis.Comparison) – The comparison we wish to dump.

  • +
  • concrete_post_processor (Callable [[any],any] | None, optional) – This function is used to +post-process concretized versions of args before they are added to the +return string. Some examples of this function include converting an integer +to a negative number due to use of two’s complement, or slicing off parts of +the argument based on another part of the input arguments. Default None.

  • +
  • include_vex (bool, optional) – whether to, for each SimState, generate the +corresponding VEX IR and include the result in the JSON. Default False.

  • +
  • include_simprocs (bool, optional) – whether to include a listing of +SimProcedures called in each basic block. Default False.

  • +
  • include_actions (bool, optional) – whether to include logging of +read/write operations on memory and registers. Default False.

  • +
  • include_debug (bool, optional) – whether to include debugging information +recovered from DWARF metadata. Default False.

  • +
  • include_side_effects (bool, optional) – whether to include cozy side effects, +like virtual prints, if present. Default True.

  • +
  • args (any, optional) – The input arguments to concretize. This argument +may be a Python datastructure, the concretizer will make a deep copy with +claripy symbolic variables replaced with concrete values. See +cozy.analysis.CompatiblePair. Default [].

  • +
  • num_examples (int, optional) – The number of concrete examples to +generate and incorporate into the JSON, for each dead-end state. Default 0.

  • +
  • open_browser (bool, optional) – Automatically open cozy-viz with the +comparison data loaded. Default False.

  • +
  • port (int, optional) – The port to serve cozy-viz on. Default 8080.

  • +
+
+
+
+ +
+
+cozy.execution_graph._generate_comparison(proj_a: cozy.project.Project, proj_b: cozy.project.Project, rslt_a: cozy.session.RunResult, rslt_b: cozy.session.RunResult, comparison_results: cozy.analysis.Comparison, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, include_vex: bool = False, include_simprocs: bool = False, flag_syscalls: bool = False, include_actions: bool = False, include_debug: bool = False, include_side_effects: bool = True, args: any = [], num_examples: int = 0) tuple[networkx.DiGraph, networkx.DiGraph]
+

Generates JSON data for Cozy-Viz.

+

Generates JSON data suitable for visual comparison using Cozy-Viz from the results of two symbolic executions.

+
+
Parameters:
+
    +
  • proj_a (Project) – The project associated with the first execuction.

  • +
  • proj_b (Project) – The project associated with the second execuction.

  • +
  • rslt_a (RunResult) – The result of the first execution.

  • +
  • rslt_b (RunResult) – The result of the second execution.

  • +
  • concrete_post_processor (Callable [[any],any] | None, optional) – This function is used to +post-process concretized versions of args before they are added to the +return string. Some examples of this function include converting an integer +to a negative number due to use of two’s complement, or slicing off parts of +the argument based on another part of the input arguments. Default None.

  • +
  • include_vex (bool, optional) – whether to, for each SimState, generate the +corresponding VEX IR and include the result in the JSON. Default False.

  • +
  • include_simprocs (bool, optional) – whether to include a listing of +SimProcedures called in each basic block. Default False.

  • +
  • include_actions (bool, optional) – whether to include logging of +read/write operations on memory and registers. Default False.

  • +
  • include_debug (bool, optional) – whether to include debugging information +recovered from DWARF metadata. Default False.

  • +
  • include_side_effects (bool, optional) – whether to include cozy side effects, +like virtual prints, if present. Default True.

  • +
  • args (any, optional) – The input arguments to concretize. This argument +may be a Python datastructure, the concretizer will make a deep copy with +claripy symbolic variables replaced with concrete values. See +cozy.analysis.CompatiblePair. Default = [].

  • +
  • num_examples (int, optional) – The number of concrete examples to +generate and incorporate into the JSON, for each dead-end state. Default 0.

  • +
+
+
Return (networkx.DiGraph, networkx.DiGraph):
+

A pair of directed graphs +representing the two symbolic executions.

+
+
+
+ +
+
+class cozy.execution_graph.ExecutionGraph(proj: cozy.project.Project, result: cozy.session.RunResult)
+

This class is used to store a networkx.DiGraph, decorated with SimStates, representing the full history of a symbolic program execution. +It constructs an ExecutionGraph, from a project and the results of an +executed project session.

+
+
Variables:
+
    +
  • proj (Project) – the project associated with the execution.

  • +
  • result (RunResult) – the result of the execution.

  • +
+
+
+
+
+_get_bbl_asm(b: angr.block.Block)
+

An internal method which renders the assembly corresponding to a given basic block as a formatted string

+
+
Parameters:
+

b (Block) – The block to render.

+
+
Return str:
+

The rendered string.

+
+
+
+ +
+
+_list_simprocs(b: angr.block.Block)
+

An internal method which lists SimProcedure calls occuring in a block

+
+
Parameters:
+

b (Block) – the block to scan

+
+
+
+ +
+
+_has_syscall(b: angr.block.Block)
+

An internal method which checks whether the jumpkind of a Block is +a syscall.

+
+
Parameters:
+

b (Block) – the relevant Block

+
+
Return bool:
+

Whether the jumpkind is a syscall

+
+
+
+ +
+
+_list_actions(child: angr.SimState | angr.state_plugins.SimStateHistory, parent: angr.SimState)
+
+ +
+
+reconstruct_bbl_addr_graph()
+

Convert the SimState-decorated graph into a graph decorated with +integers, carrying symbolic program execution data in the attributes +stdout, stderr, contents (this holds a basic block), +constraints, actions (optionally) and state.

+
+
Return networkx.DiGraph:
+

The resulting graph.

+
+
+
+ +
+
+reconstruct_bbl_pp_graph()
+

Convert the SimState-decorated graph into a graph decorated with +integers, carrying symbolic program execution data in the attributes +stdout, stderr, contents, constraints ,`vex` and state. The +difference from reconstruct_bbl_addr_graph() is that the data is +now pretty-printed and suitable for serialization.

+
+
Return networkx.DiGraph:
+

The resulting graph.

+
+
+
+ +
+
+dump_bbp_pp_cytoscape(name: str)
+

Dump the graph as cytoscapejs readable JSON.

+
+
Parameters:
+

name (str) – The filename for the generated json.

+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/functools_ext/index.html b/autoapi/cozy/functools_ext/index.html new file mode 100644 index 0000000..4892209 --- /dev/null +++ b/autoapi/cozy/functools_ext/index.html @@ -0,0 +1,292 @@ + + + + + + + + cozy.functools_ext — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.functools_ext

+
+

Attributes

+ + + + + + + + + + + + + + + + + + +

T

U

V

B

C

+
+
+

Functions

+ + + + + + + + + + + + + + + +

preorder_mapfold(→ tuple[any, T])

Simultaneously maps and folds over a nested Python datastructure in preorder traversal order. The datastructure may consist of arbitrarily nested lists, tuples, dictionaries and sets. Note that for dictionaries, both keys and values will be traversed.

preorder_fold(→ U)

Folds over a Python datastructure in preorder traversal. The datastructure may consist of arbitrarily nested lists, tuples, dictionaries and sets. Note that for dictionaries, both keys and values will be traversed.

fmap(→ any)

Maps a Python datastructure. The datastructure may consist of arbitrarily nested lists, tuples, dictionaries and sets.

compose(→ collections.abc.Callable[[Ellipsis], C])

Composes two functions, f and g, to create a new function h(*a, **kw) = f(g(*a, **kw))

+
+
+

Module Contents

+
+
+cozy.functools_ext.T
+
+ +
+
+cozy.functools_ext.U
+
+ +
+
+cozy.functools_ext.V
+
+ +
+
+cozy.functools_ext.B
+
+ +
+
+cozy.functools_ext.C
+
+ +
+
+cozy.functools_ext.preorder_mapfold(val0: any, f: collections.abc.Callable[[any, T], tuple[any, T]], accum0: T, sort=True) tuple[any, T]
+

Simultaneously maps and folds over a nested Python datastructure in preorder traversal order. The datastructure may consist of arbitrarily nested lists, tuples, dictionaries and sets. Note that for dictionaries, both keys and values will be traversed.

+
+
Parameters:
+
    +
  • val0 (any) – The datastructure to traverse.

  • +
  • f (Callable[[any, T], tuple[any, T]]) – This function takes as input a value inside the datastructure, the accumulated value and should return a mapped value and newly accumulated value.

  • +
  • accum0 (T) – Initial accumulation parameter.

  • +
+
+
Returns:
+

The mapped datastructure and final accumulated value.

+
+
Return type:
+

tuple[any, T]

+
+
+
+ +
+
+cozy.functools_ext.preorder_fold(val0: any, f: collections.abc.Callable[[any, U], U], accum0: U) U
+

Folds over a Python datastructure in preorder traversal. The datastructure may consist of arbitrarily nested lists, tuples, dictionaries and sets. Note that for dictionaries, both keys and values will be traversed.

+
+
Parameters:
+
    +
  • val0 (any) – The datastructure to traverse.

  • +
  • f (Callable[[any, U], U]) – This function takes as input the value inside the datastructure, the accumulated value and should return a new accumulated value.

  • +
  • accum0 (U) – Initial accumulation parameter.

  • +
+
+
Returns:
+

The final accumulated value.

+
+
Return type:
+

U

+
+
+
+ +
+
+cozy.functools_ext.fmap(val0: any, f: collections.abc.Callable[[any], any]) any
+

Maps a Python datastructure. The datastructure may consist of arbitrarily nested lists, tuples, dictionaries and sets.

+
+
Parameters:
+

val0 (any) – The datastructure to map. Note that for dictionaries, both keys and values will be mapped.

+
+
Returns:
+

The mapped datastructure.

+
+
Return type:
+

any

+
+
+
+ +
+
+cozy.functools_ext.compose(f: collections.abc.Callable[[B], C], g: collections.abc.Callable[[Ellipsis], B]) collections.abc.Callable[[Ellipsis], C]
+

Composes two functions, f and g, to create a new function h(*a, **kw) = f(g(*a, **kw))

+
+
Parameters:
+
    +
  • f (Callable[[B], C]) – The first function to compose.

  • +
  • g (Callable[[...], B]) – The second function to compose.

  • +
+
+
Returns:
+

A newly composed function which takes in an arbitrary number of arguments and keyword arguments, and returns a C.

+
+
Return type:
+

Callable[[…], C]

+
+
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/hooks/index.html b/autoapi/cozy/hooks/index.html new file mode 100644 index 0000000..c645526 --- /dev/null +++ b/autoapi/cozy/hooks/index.html @@ -0,0 +1,139 @@ + + + + + + + + cozy.hooks — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.hooks

+
+

Submodules

+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/hooks/strlen/index.html b/autoapi/cozy/hooks/strlen/index.html new file mode 100644 index 0000000..58d149f --- /dev/null +++ b/autoapi/cozy/hooks/strlen/index.html @@ -0,0 +1,175 @@ + + + + + + + + cozy.hooks.strlen — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.hooks.strlen

+
+

Attributes

+ + + + + + +

l

+
+
+

Classes

+ + + + + + +

strlen

+
+
+

Module Contents

+
+
+cozy.hooks.strlen.l
+
+ +
+
+class cozy.hooks.strlen.strlen
+

Bases: angr.SimProcedure

+
+
+max_null_index = None
+
+ +
+
+run(s, wchar=False, maxlen=None)
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/hooks/strncmp/index.html b/autoapi/cozy/hooks/strncmp/index.html new file mode 100644 index 0000000..98d162e --- /dev/null +++ b/autoapi/cozy/hooks/strncmp/index.html @@ -0,0 +1,170 @@ + + + + + + + + cozy.hooks.strncmp — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.hooks.strncmp

+
+

Attributes

+ + + + + + +

l

+
+
+

Classes

+ + + + + + +

strncmp

+
+
+

Module Contents

+
+
+cozy.hooks.strncmp.l
+
+ +
+
+class cozy.hooks.strncmp.strncmp
+

Bases: angr.SimProcedure

+
+
+run(a_addr, b_addr, limit, a_len=None, b_len=None, wchar=False, ignore_case=False)
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/hooks/strtok_r/index.html b/autoapi/cozy/hooks/strtok_r/index.html new file mode 100644 index 0000000..cedc88a --- /dev/null +++ b/autoapi/cozy/hooks/strtok_r/index.html @@ -0,0 +1,170 @@ + + + + + + + + cozy.hooks.strtok_r — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.hooks.strtok_r

+
+

Attributes

+ + + + + + +

l

+
+
+

Classes

+ + + + + + +

strtok_r

+
+
+

Module Contents

+
+
+cozy.hooks.strtok_r.l
+
+ +
+
+class cozy.hooks.strtok_r.strtok_r
+

Bases: angr.SimProcedure

+
+
+run(str_ptr, delim_ptr, save_ptr, str_strlen=None, delim_strlen=None)
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/index.html b/autoapi/cozy/index.html new file mode 100644 index 0000000..efc2738 --- /dev/null +++ b/autoapi/cozy/index.html @@ -0,0 +1,179 @@ + + + + + + + + cozy — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/log/index.html b/autoapi/cozy/log/index.html new file mode 100644 index 0000000..9047d1a --- /dev/null +++ b/autoapi/cozy/log/index.html @@ -0,0 +1,232 @@ + + + + + + + + cozy.log — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.log

+
+

Attributes

+ + + + + + +

logger

+
+
+

Functions

+ + + + + + + + + + + + + + + + + + + + + + + + +

disable()

Disable cozy logging

set_level(level)

Set the level of logging

info(*args, **kwargs)

Log at the info level

warning(*args, **kwargs)

Log at the warning level

error(*args, **kwargs)

Log at the error level

debug(*args, **kwargs)

Log at the debug level

critical(*args, **kwargs)

Log at the critical level

+
+
+

Module Contents

+
+
+cozy.log.logger
+
+ +
+
+cozy.log.disable()
+

Disable cozy logging

+
+ +
+
+cozy.log.set_level(level)
+

Set the level of logging

+
+ +
+
+cozy.log.info(*args, **kwargs)
+

Log at the info level

+
+ +
+
+cozy.log.warning(*args, **kwargs)
+

Log at the warning level

+
+ +
+
+cozy.log.error(*args, **kwargs)
+

Log at the error level

+
+ +
+
+cozy.log.debug(*args, **kwargs)
+

Log at the debug level

+
+ +
+
+cozy.log.critical(*args, **kwargs)
+

Log at the critical level

+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/primitives/index.html b/autoapi/cozy/primitives/index.html new file mode 100644 index 0000000..68ad2ac --- /dev/null +++ b/autoapi/cozy/primitives/index.html @@ -0,0 +1,262 @@ + + + + + + + + cozy.primitives — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.primitives

+
+

Attributes

+ + + + + + +

_malloc_name_ctr

+
+
+

Functions

+ + + + + + + + + + + + + + + +

sym_ptr(→ claripy.BVS)

Generates a fresh symbolic pointer for the input architecture.

sym_ptr_constraints(→ claripy.ast.bool)

Generates claripy expressions for constraining the input symbolic pointer to a specific concrete value.

from_twos_comp(→ int)

Converts an integer from two's complement form back to an integer, assuming the value is stored in num_bits space.

to_twos_comp(→ int)

Converts an integer value to two's complement representation, assuming the value is stored in num_bits space.

+
+
+

Module Contents

+
+
+cozy.primitives._malloc_name_ctr = 0
+
+ +
+
+cozy.primitives.sym_ptr(arch: Type[archinfo.Arch], name: str | None = None) claripy.BVS
+

Generates a fresh symbolic pointer for the input architecture.

+
+
Parameters:
+
    +
  • arch (Type[Arch]) – The architecture the pointer is for. For example in x64, the paramater passed should be archinfo.ArchAMD64, and a 64 bit symbolic pointer will be returned.

  • +
  • name (str | None) – A human readable name for the symbolic pointer. If None is passed an autogenerated symbolic_ptr_i name is used.

  • +
+
+
Returns:
+

A fresh symbolic bitvector whose size is appropriate for the input architecture.

+
+
Return type:
+

claripy.BVS

+
+
+
+ +
+
+cozy.primitives.sym_ptr_constraints(symbolic_ptr: claripy.ast.bits, concrete_addr: int, can_be_null: bool = True) claripy.ast.bool
+

Generates claripy expressions for constraining the input symbolic pointer to a specific concrete value.

+
+
Parameters:
+
    +
  • symbolic_ptr (claripy.ast.bits) – The symbolic pointer to constrain.

  • +
  • concrete_addr (int) – The concrete address which the symbolic pointer should be equal to.

  • +
  • can_be_null (bool) – If this value is True, the returned constraints will contain a disjunction which allows the symbolic pointer to be NULL.

  • +
+
+
Returns:
+

A claripy proposition constraining the symbolic pointer.

+
+
Return type:
+

claripy.ast.bool

+
+
+
+ +
+
+cozy.primitives.from_twos_comp(val: int, num_bits: int) int
+

Converts an integer from two’s complement form back to an integer, assuming the value is stored in num_bits space.

+
+
Parameters:
+
    +
  • val (int) – The two’s complement integer to convert. This number must be non-negative.

  • +
  • num_bits (int) – The number of bits used to store the number.

  • +
+
+
Returns:
+

A signed Python representation of the integer.

+
+
Return type:
+

int

+
+
+
+ +
+
+cozy.primitives.to_twos_comp(val: int, num_bits: int) int
+

Converts an integer value to two’s complement representation, assuming the value is stored in num_bits space.

+
+
Parameters:
+
    +
  • val (int) – The integer value to convert.

  • +
  • num_bits (int) – The number of bits used to store the integer.

  • +
+
+
Returns:
+

A two’s complement representation of the value. This value is non-negative.

+
+
Return type:
+

int

+
+
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/project/index.html b/autoapi/cozy/project/index.html new file mode 100644 index 0000000..0377e32 --- /dev/null +++ b/autoapi/cozy/project/index.html @@ -0,0 +1,307 @@ + + + + + + + + cozy.project — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.project

+
+

Classes

+ + + + + + +

Project

Represents a project for a single executable

+
+
+

Module Contents

+
+
+class cozy.project.Project(binary_path: str, fun_prototypes: dict[str | int, str] | None = None, load_debug_info: bool = False, **kwargs)
+

Represents a project for a single executable

+
+
Variables:
+
    +
  • angr_proj (angr.Project) – The angr project created for this cozy project.

  • +
  • fun_prototypes (dict[str | int, str]) – Maps function names or function addresses to their type signatures.

  • +
+
+
+

Constructor for a project.

+
+
Parameters:
+
    +
  • binary_path (str) – The path to the binary to analyze.

  • +
  • fun_prototypes (dict[str | int, str] | None) – Initial dictionary that maps function names or addresses to their type signatures. If None is passed, fun_prototypes is initialized to the empty dictionary.

  • +
  • kwargs – Extra arguments to pass to angr.Project

  • +
+
+
+
+
+object_ranges(obj_filter: collections.abc.Callable[[cle.Backend], bool] | None = None) list[range]
+

Returns the ranges of the objects stored in the executable (for example: ELF objects). If obj_filter is specified, only objects that pass the filter make it into the return list.

+
+
Parameters:
+

obj_filter (Callable[[Backend], bool] | None) – Used to filter certain objects from the output list.

+
+
Returns:
+

A list of memory ranges.

+
+
Return type:
+

list[range]

+
+
+
+ +
+
+find_symbol_addr(sym_name: str) int
+

Finds the rebased addressed of a symbol. Functions are the most common symbol type.

+
+
Parameters:
+

sym_name (str) – The symbol to lookup.

+
+
Returns:
+

The rebased symbol address

+
+
Return type:
+

int

+
+
+
+ +
+
+add_prototype(fun: str | int, fun_prototype: str) None
+

Adds a function prototype to this project.

+
+
Parameters:
+
    +
  • fun (str | int) – The function’s name or address.

  • +
  • fun_prototype (str) – The function’s type signature.

  • +
+
+
Returns:
+

None

+
+
Return type:
+

None

+
+
+
+ +
+
+session(start_fun: str | int | None = None) cozy.session.Session
+

Returns a new session derived from this project.

+
+
Parameters:
+

start_fun (str | int | None) – The name or address of the function which this session will start with. If None is specified, then the program will start at the entry point (main function).

+
+
Returns:
+

The fresh session.

+
+
Return type:
+

Session

+
+
+
+ +
+
+property cfg
+
+Returns the control flow graph for this project. This property will cache the cfg in a pickle file
+
+to speed up future runs. This means if you change the underlying program you will need to delete the
+
+.cfg.pickle file located in the same directory as your executable.
+
+ +
+
+property arch
+
+Returns the underlying angr project architecture
+
+ +
+
+hook_symbol(symbol_name: str, simproc_class: type[angr.SimProcedure], kwargs=None, replace: bool | None = None) int
+

Hooks a symbol in the angr project. If the symbol is one from libc, this method will also replace +what is stored in angr.SIM_PROCEDURES["libc"][symbol_name].

+
+
Parameters:
+
    +
  • symbol_name (str) – The name of the symbol to hook.

  • +
  • simproc_class (type[SimProcedure]) – The class to use to hook the symbol. Note that this is not an instance of SimProcedure, but is instead a reference to the class itself.

  • +
  • kwargs – These are the keyword arguments that will be passed to the procedure’s run method eventually.

  • +
  • replace (bool | None) – Control the behavior on finding that the address is already hooked. If true, silently replace the hook. If false, warn and do not replace the hook. If none (default), warn and replace the hook.

  • +
+
+
Return type:
+

int

+
+
Returns:
+

The address of the new symbol.

+
+
+
+ +
+
+hook_syscall(syscall_name: str, simproc_class: type[angr.SimProcedure])
+

Hooks a syscall in the angr project.

+
+
Parameters:
+
    +
  • syscall_name (str) – The name of the syscall to hook.

  • +
  • simproc_class (type[SimProcedure]) – The class to use to hook the symbol. Note that this is not an instance of SimProcedure, but is instead a reference to the class itself.

  • +
+
+
+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/server/index.html b/autoapi/cozy/server/index.html new file mode 100644 index 0000000..04ad749 --- /dev/null +++ b/autoapi/cozy/server/index.html @@ -0,0 +1,212 @@ + + + + + + + + cozy.server — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.server

+
+

Classes

+ + + + + + +

VizHandler

Simple HTTP request handler with GET and HEAD commands.

+
+
+

Functions

+ + + + + + + + + +

get_vizroot()

start_viz_server([pre, post, open_browser, port])

Serves Cozy-Viz on localhost:8080.

+
+
+

Module Contents

+
+
+cozy.server.get_vizroot()
+
+ +
+
+class cozy.server.VizHandler(prepatch, postpatch, *args, **kwargs)
+

Bases: http.server.SimpleHTTPRequestHandler

+

Simple HTTP request handler with GET and HEAD commands.

+

This serves files from the current directory and any of its +subdirectories. The MIME type for files is determined by +calling the .guess_type() method.

+

The GET and HEAD requests are identical except that the HEAD +request omits the actual contents of the file.

+
+
+do_GET()
+

Serve a GET request.

+
+ +
+ +
+
+cozy.server.start_viz_server(pre={}, post={}, open_browser=False, port=8080)
+

Serves Cozy-Viz on localhost:8080.

+

Useful for visualization of information generated using +cozy.execution_graph.compare_and_dump().

+

To include comparison data, use the pre and post arguments, and add +a query string to the URL, like so: localhost:8080?pre=/pre&post=/post.

+
+
Parameters:
+
    +
  • pre (dict, optional) – served as JSON at /pre on the server. Default {}.

  • +
  • post (dict, optional) – served as JSON at /post on the server. Default {}.

  • +
  • port (int, optional) – An alternative port to serve on. Default 8080.

  • +
+
+
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/session/index.html b/autoapi/cozy/session/index.html new file mode 100644 index 0000000..6465072 --- /dev/null +++ b/autoapi/cozy/session/index.html @@ -0,0 +1,566 @@ + + + + + + + + cozy.session — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.session

+
+

Attributes

+ + + + + + + + + +

_mem_write_ctr

_malloc_name_ctr

+
+
+

Classes

+ + + + + + + + + + + + + + + + + + +

RunResult

This class is used for storing the results of running a session.

_SessionExploration

_SessionDirectiveExploration

_SessionBasicExploration

Session

A session is a particular run of a project, consisting of attached directives (asserts/assumes). You can malloc memory for storage prior to running the session. Once you are ready to run the session, use the run method.

+
+
+

Functions

+ + + + + + + + + + + + +

_on_mem_write(state)

_on_simprocedure(state)

_save_states(states)

+
+
+

Module Contents

+
+
+class cozy.session.RunResult(deadended: list[cozy.terminal_state.DeadendedState], errored: list[cozy.terminal_state.ErrorState], asserts_failed: list[cozy.terminal_state.AssertFailedState], assume_warnings: list[tuple[cozy.directive.Assume, angr.SimState]], postconditions_failed: list[cozy.terminal_state.PostconditionFailedState], spinning: list[cozy.terminal_state.SpinningState])
+

This class is used for storing the results of running a session.

+
+
Variables:
+
    +
  • deadended (list[DeadendedState]) – States that reached normal termination.

  • +
  • errored (list[ErrorState]) – States that reached an error state. This may be triggered for example by program errors such as division by 0, or by reaching a cozy.directive.ErrorDirective.

  • +
  • asserts_failed (list[AssertFailed]) – States where an assertion was able to be falsified.

  • +
  • assume_warnings (list[tuple[Assume, SimState]]) – An assume warning occurs when a Assume is reached, and the added assumption contradicts the constraints for that state. This means that due to the assumption, the new constraints are not satisfiable.

  • +
  • postconditions_failed (list[PostconditionFailedState]) – States where the function returned, and the assertion as part of the postcondition could be falsified.

  • +
  • spinning (list[SpinningState]) – States that were stashed due to a loop bound being breached.

  • +
+
+
+
+
+property assertion_triggered: bool
+

Returns True if there were any assertions triggered during this run.

+
+
Returns:
+

True if there were assertions triggered.

+
+
Return type:
+

bool

+
+
+
+ +
+
+property postcondition_triggered: bool
+

Returns True if there were any postcondition assertions triggered during this run.

+
+
Returns:
+

True if there were postcondition assertions triggered.

+
+
Return type:
+

bool

+
+
+
+ +
+
+__str__()
+

Return str(self).

+
+ +
+
+report(args: any, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, num_examples: int = 3) str
+

Creates a composite human readable report with information about errored states, asserts failed, postconditions failed, and spinning states.

+
+
Parameters:
+
    +
  • args (any) – The arguments to concretize

  • +
  • concrete_post_processor (Callable[[any], any] | None) – This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two’s complement, or slicing off parts of the argument based on another part of the input arguments.

  • +
  • num_examples (int) – The maximum number of concrete examples to show the user.

  • +
+
+
Returns:
+

The report as a string

+
+
Return type:
+

str

+
+
+
+ +
+
+report_errored(args: any, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, num_examples: int = 3) str
+

Creates a human readable report about a list of errored states.

+
+
Parameters:
+
    +
  • args (any) – The arguments to concretize

  • +
  • concrete_post_processor (Callable[[any], any] | None) – This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two’s complement, or slicing off parts of the argument based on another part of the input arguments.

  • +
  • num_examples (int) – The maximum number of concrete examples to show the user for each errored state.

  • +
+
+
Returns:
+

The report as a string

+
+
Return type:
+

str

+
+
+
+ +
+
+report_spinning(args: any, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, num_examples: int = 3) str
+

Creates a human readable report about a list of failed postcondition assertions.

+
+
Parameters:
+
    +
  • args (any) – The arguments to concretize

  • +
  • concrete_post_processor (Callable[[any], any] | None) – This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two’s complement, or slicing off parts of the argument based on another part of the input arguments.

  • +
  • num_examples (int) – The maximum number of concrete examples to show the user for each assertion failed state.

  • +
+
+
Returns:
+

The report as a string

+
+
Return type:
+

str

+
+
+
+ +
+
+report_postconditions_failed(args: any, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, num_examples: int = 3) str
+

Creates a human readable report about the failed postcondition assertions.

+
+
Parameters:
+
    +
  • args (any) – The arguments to concretize

  • +
  • concrete_post_processor (Callable[[any], any] | None) – This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two’s complement, or slicing off parts of the argument based on another part of the input arguments.

  • +
  • num_examples (int) – The maximum number of concrete examples to show the user for each assertion failed state.

  • +
+
+
Returns:
+

The report as a string

+
+
Return type:
+

str

+
+
+
+ +
+
+report_asserts_failed(args: any, concrete_post_processor: collections.abc.Callable[[any], any] | None = None, num_examples: int = 3) str
+

Creates a human readable report about any failed assertions.

+
+
Parameters:
+
    +
  • args (any) – The arguments to concretize

  • +
  • concrete_post_processor (Callable[[any], any] | None) – This function is used to post-process concretized versions of args before they are added to the return string. Some examples of this function include converting an integer to a negative number due to use of two’s complement, or slicing off parts of the argument based on another part of the input arguments.

  • +
  • num_examples (int) – The maximum number of concrete examples to show the user for each assertion failed state.

  • +
+
+
Returns:
+

The report as a string

+
+
Return type:
+

str

+
+
+
+ +
+ +
+
+cozy.session._mem_write_ctr = 0
+
+ +
+
+cozy.session._on_mem_write(state)
+
+ +
+
+cozy.session._malloc_name_ctr = 0
+
+ +
+
+cozy.session._on_simprocedure(state)
+
+ +
+
+cozy.session._save_states(states)
+
+ +
+
+class cozy.session._SessionExploration(session: Session, cache_intermediate_info: bool = True)
+
+
+abstract explore(simgr)
+
+ +
+ +
+
+class cozy.session._SessionDirectiveExploration(session: Session, cache_intermediate_info: bool = True)
+

Bases: _SessionExploration

+
+
+check_postconditions(simgr)
+
+ +
+
+explore(simgr)
+
+ +
+ +
+
+class cozy.session._SessionBasicExploration(session: Session, cache_intermediate_info: bool = True)
+

Bases: _SessionExploration

+
+
+explore(simgr)
+
+ +
+ +
+
+class cozy.session.Session(proj, start_fun: str | int | None = None)
+

A session is a particular run of a project, consisting of attached directives (asserts/assumes). You can malloc memory for storage prior to running the session. Once you are ready to run the session, use the run method. +:ivar angr.SimState state: The initial state tied to this particular session. You can access this member to modify properties of the state before a run. +:ivar cozy.project.Project proj: The Project tied to this session. +:ivar str | int | None start_fun: The starting function tied to this session. If start_fun is None, then the session starts in an entry state. +:ivar list[Directive] directives: The directives added to this session. +:ivar bool has_run: True if the cozy.project.Session.run() method has been called, otherwise False.

+

Constructs a session derived from a project. The cozy.project.Project.session() is the preferred method for creating a session, not this constructor.

+
+
+store_fs(filename: str, simfile: angr.SimFile) None
+

Stores a file in a virtual filesystem available during execution. This method simply forwards the arguments to state.fs.insert.

+
+
Parameters:
+
    +
  • filename (str) – The filename of the new file.

  • +
  • simfile (angr.SimFile) – The file to make available to the simulated program.

  • +
+
+
Returns:
+

None

+
+
Return type:
+

None

+
+
+
+ +
+
+malloc(num_bytes: int, name=None) int
+

Mallocs a fixed amount of memory using the angr heap simulation plugin. Useful for setting things up in memory before the run() method is called.

+
+
Parameters:
+

num_bytes (int) – The number of bytes to allocate.

+
+
Returns:
+

A pointer to the allocated memory block.

+
+
Return type:
+

int

+
+
+
+ +
+
+store(addr: int, data: claripy.ast.bits, **kwargs)
+

Stores data at some address. This method simply forwards the arguments to state.memory.store.

+
+
Parameters:
+
    +
  • addr (int) – Address to store the data at.

  • +
  • data (claripy.ast.bits) – The data to store in memory.

  • +
  • kwargs – Additional keyword arguments to pass to state.memory.store

  • +
+
+
+
+ +
+
+property memory
+
+ +
+
+property mem
+
+Access memory using a dict-like interface. This property simply forwards to state.mem
+
+ +
+
+add_directives(*directives: cozy.directive.Directive) None
+

Adds multiple directives to the session.

+
+
Parameters:
+

directives (Directive) – The directives to add.

+
+
Returns:
+

None

+
+
Return type:
+

None

+
+
+
+ +
+
+add_constraints(*constraints: claripy.ast.bool) None
+

Adds multiple constraints to the session’s state.

+
+
Parameters:
+

constraints (claripy.ast.bool) – The constraints to add

+
+
Returns:
+

None

+
+
Return type:
+

None

+
+
+
+ +
+
+property start_fun_addr
+
+ +
+
+_call(args: list[claripy.ast.bits], cache_intermediate_info: bool = True, ret_addr: int | None = None) angr.sim_manager.SimulationManager
+
+ +
+
+_session_exploration(cache_intermediate_info: bool = True) _SessionExploration
+
+ +
+
+_run_result(simgr: angr.sim_manager.SimulationManager, sess_exploration: _SessionExploration) RunResult
+
+ +
+
+run(args: list[claripy.ast.bits], cache_intermediate_info: bool = True, ret_addr: int | None = None, loop_bound: int | None = None) RunResult
+

Runs a session to completion, either starting from the start_fun used to create the session, or from the program start. Note that currently a session may be run only once. If run is called multiple times, a RuntimeError will be thrown.

+
+
Parameters:
+
    +
  • args (list[claripy.ast.bits]) – The arguments to pass to the function. angr will utilize the function’s type signature to figure out the calling convention to use with the arguments.

  • +
  • cache_intermediate_info (bool) – If this flag is True, then information about intermediate states will be

  • +
+
+
+

cached. This is required for dumping the execution graph which is used in visualization. +:param int | None ret_addr: What address to return to if calling as a function +:param int | None loop_bound: Sets an upper bound on loop iteration count. Useful for programs with non-terminating loops. +:return: The result of running this session. +:rtype: RunResult

+
+ +
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/side_effect/index.html b/autoapi/cozy/side_effect/index.html new file mode 100644 index 0000000..02d6cc5 --- /dev/null +++ b/autoapi/cozy/side_effect/index.html @@ -0,0 +1,290 @@ + + + + + + + + cozy.side_effect — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.side_effect

+
+

Classes

+ + + + + + + + + +

PerformedSideEffect

This class encapsulates the idea of a side effect whose body may consist of mixed symbolic and concrete values.

ConcretePerformedSideEffect

This class encapsulates the idea of a side effect whose body previously consisted of mixed symbolic and concrete

+
+
+

Functions

+ + + + + + + + + + + + + + + + + + +

perform(state, channel, body[, ...])

Attaches a side effect to the passed state.

get_effects(→ dict[str, list[PerformedSideEffect]])

Gets the side effects attached to a specific state

get_channel(→ list[PerformedSideEffect])

Gets the side effects from the given channel attached to a specific state. An empty list is returned for channels

levenshtein_alignment(lst_a, lst_b[, key])

test_levenshtein_alignment()

+
+
+

Module Contents

+
+
+class cozy.side_effect.PerformedSideEffect(state_history: angr.state_plugins.SimStateHistory, body, concrete_post_processor=None, label=None)
+

This class encapsulates the idea of a side effect whose body may consist of mixed symbolic and concrete values.

+
+
Parameters:
+
    +
  • state_history (SimStateHistory) – The point in execution at which the side effect was performed.

  • +
  • body – The body must be a mixture of string-keyed Python dictionaries, Python lists, Python tuples, and claripy concrete and symbolic values.

  • +
  • concrete_post_processor – The optional post processing function to apply to concretized versions of the side effect’s body if post processing is required.

  • +
  • label – The label to apply to the side effect, used to align instances of side effects when making comparisons. For example, if you have two call sites to a network send function, you would want different labels for the two different call locations.

  • +
+
+
+
+ +
+
+class cozy.side_effect.ConcretePerformedSideEffect(base_effect: PerformedSideEffect, state_history: angr.state_plugins.SimStateHistory, body, concrete_post_processor=None, label=None)
+

This class encapsulates the idea of a side effect whose body previously consisted of mixed symbolic and concrete +values, but now consists of only concrete values (ie, BVV and FPV). At the point of the construction, this concrete +value has not yet been passed through the user provided concrete_post_processor, whose job is to take the concrete value +and transform the BVV values into ordinary Python values. The purpose of concrete_post_processor for instance could be +to transform a two’s complement BVV that is negative into a negative Python integer. This will make the display +more readable to the user. Hence, the concrete_post_processor can be viewed as a post-processing function.

+
+
Parameters:
+
    +
  • base_effect (PerformedSideEffect) – The non-symbolic side effect that was concretized.

  • +
  • state_history (SimStateHistory) – The point in execution at which the side effect was performed.

  • +
  • body – The body must be a mixture of string-keyed Python dictionaries, Python lists, Python tuples, and claripy concrete values.

  • +
  • concrete_post_processor – The optional post processing function to apply to concretized versions of the side effect’s body if post processing is required.

  • +
  • label – The label to apply to the side effect, used to align instances of side effects when making comparisons. For example, if you have two call sites to a network send function, you would want different labels for the two different call locations.

  • +
+
+
+
+
+property mapped_body
+
+ +
+ +
+
+cozy.side_effect.perform(state: angr.SimState, channel: str, body, concrete_post_processor=None, label=None)
+

Attaches a side effect to the passed state.

+
+
Parameters:
+
    +
  • state (SimState) – The state in which the side effect should be performed and attached to.

  • +
  • channel (str) – The name of the channel in which the side effect should be performed. Different side effects should be sent down different channels. For example, the virtual print side effect channel is different from the networking side effect channel.

  • +
  • body – The body must be a mixture of string-keyed Python dictionaries, Python lists, Python tuples, claripy concrete values, and claripy symbolic values. This should represent the payload of the side effect.

  • +
  • concrete_post_processor – The optional post processing function to apply to concretized versions of the side effect’s body if post processing is required.

  • +
  • label – The label to apply to the side effect, used to align instances of side effects when making comparisons. For example, if you have two call sites to a network send function, you would want different labels for the two different call locations.

  • +
+
+
+
+ +
+
+cozy.side_effect.get_effects(state: angr.SimState) dict[str, list[PerformedSideEffect]]
+

Gets the side effects attached to a specific state

+
+
Parameters:
+

state (SimState) – The state from which we should retrieve the side effects.

+
+
Return type:
+

dict[str, list[PerformedSideEffect]]

+
+
Returns:
+

All side effects attached to this state. Each entry in the dictionary is a different channel.

+
+
+
+ +
+
+cozy.side_effect.get_channel(state: angr.SimState, channel: str) list[PerformedSideEffect]
+

Gets the side effects from the given channel attached to a specific state. An empty list is returned for channels +in which the channel has not yet been used.

+
+
Parameters:
+
    +
  • state (SimState) – The state from which we should retrieve the side effects channel.

  • +
  • channel (str) – The name of the channel

  • +
+
+
Return type:
+

list[PerformedSideEffect]

+
+
Returns:
+

A list of side effects for the requested channel.

+
+
+
+ +
+
+cozy.side_effect.levenshtein_alignment(lst_a, lst_b, key=None)
+
+ +
+
+cozy.side_effect.test_levenshtein_alignment()
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/stubs/index.html b/autoapi/cozy/stubs/index.html new file mode 100644 index 0000000..969e3ea --- /dev/null +++ b/autoapi/cozy/stubs/index.html @@ -0,0 +1,270 @@ + + + + + + + + cozy.stubs — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.stubs

+
+

Attributes

+ + + + + + +

unstripped_binary_path

+
+
+

Classes

+ + + + + + +

Stubber

A Stubber outputs Python source code that represents stubs for the callees of a given binary function.

+
+
+

Module Contents

+
+
+class cozy.stubs.Stubber(binary_path: str)
+

A Stubber outputs Python source code that represents stubs for the callees of a given binary function.

+

If foo is the function to be analyzed, and foo calls a two-argument function bar, then the following stub +will be among those generated for foo:

+

+
+
+
+
class bar(angr.SimProcedure):
+
def run(self, arg0, arg1):

pass

+
+
+
+
+

The stub can then be filled out and used during symbolic execution.

+
+
Parameters:
+

binary_path (str) – Path for the binary under analysis.

+
+
Variables:
+
    +
  • cfg (angr.analyses.cfg.cfg_fast.CFGFast) – CFG for the binary.

  • +
  • cg (networkx.classes.multidigraph.MultiDiGraph) – Call graph for the binary.

  • +
+
+
+
+
+extract_func(func_name: str) angr.knowledge_plugins.functions.function.Function
+

Returns the function with the given name from the CFG.

+
+
Parameters:
+

func_name (str) – Name of the function to extract.

+
+
Returns:
+

Function with the given name.

+
+
Return type:
+

angr.knowledge_plugins.functions.function.Function

+
+
+
+ +
+
+get_callees(func_name: str) list[angr.knowledge_plugins.functions.function.Function]
+

Returns the list of functions called by function func_name.

+
+
Parameters:
+

func_name (str) – Name of the caller function.

+
+
Returns:
+

The list of functions called by func_name.

+
+
Return type:
+

list[angr.knowledge_plugins.functions.function.Function]

+
+
+
+ +
+
+make_stub(func: angr.knowledge_plugins.functions.function.Function) str
+

Returns an empty Python class definition (in string form) named after func that inherits from angr.SimProcedure.

+
+
Parameters:
+

func (angr.knowledge_plugins.functions.function.Function) – Function to be stubbed.

+
+
Returns:
+

Empty Python class definition representing a symbolic execution stub for function func.

+
+
Return type:
+

str

+
+
+
+ +
+
+make_callee_stubs(func_name: str) list[str]
+

Returns a list of stubs for the callees of function func_name.

+
+
Parameters:
+

func_name (str) – Name of the caller function.

+
+
Returns:
+

Stubs for the callees of function func_name.

+
+
Return type:
+

list[str]

+
+
+
+ +
+ +
+
+cozy.stubs.unstripped_binary_path = '../test_programs/GridIDPS/build/amp_challenge_arm.ino_unstripped.elf'
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/terminal_state/index.html b/autoapi/cozy/terminal_state/index.html new file mode 100644 index 0000000..2ca7fd1 --- /dev/null +++ b/autoapi/cozy/terminal_state/index.html @@ -0,0 +1,382 @@ + + + + + + + + cozy.terminal_state — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.terminal_state

+
+

Classes

+ + + + + + + + + + + + + + + + + + + + + +

TerminalState

Stores information pertaining specifically to a single SimState.

DeadendedState

This class is used to indicate that execution terminated normally in the contained state.

SpinningState

This class is used to indicate that the contained state was killed by the LocalLoopSeer, indicating that an upper

AssertFailedState

This class is used to indicate that execution failed due to an Assert being satisfiable.

PostconditionFailedState

This class is used to indicate that execution failed due to an Assert being satisfiable.

ErrorState

This class is used to indicate a state that resulted in an error (either my an execution error or ErrorDirective).

+
+
+

Module Contents

+
+
+class cozy.terminal_state.TerminalState(state: angr.SimState, state_id: int, state_type_str: str)
+

Stores information pertaining specifically to a single SimState.

+
+
Variables:
+
    +
  • state (SimState) – The state we are storing information about.

  • +
  • state_id (int) – The index of this particular state in the corresponding list in RunResult. Note that errored states have separate state_ids from deadended states. Therefore a particular input state here is uniquely identified by the pair (state_id, state_tag), not just state_id by itself.

  • +
  • state_type_str (str) – A string representation of the state’s type

  • +
+
+
+
+
+property std_out: bytes
+

The data that has been written to stdout when the program is in this state.

+
+
Getter:
+

The data written to stdout

+
+
Type:
+

bytes

+
+
+
+ +
+
+property std_err: bytes
+

The data that has been written to stderr when the program is in this state.

+
+
Getter:
+

The data written to stderr

+
+
Type:
+

bytes

+
+
+
+ +
+
+property side_effects: dict[str, list[cozy.side_effect.PerformedSideEffect]]
+
+ +
+
+property virtual_prints: list[cozy.side_effect.PerformedSideEffect]
+

Returns the output of the virtual prints that occurred while reaching this state.

+
+
Getter:
+

A list of VirtualPrint directives, along with the values they produced.

+
+
Type:
+

list[tuple[VirtualPrint, claripy.ast.Base]]

+
+
+
+ +
+
+property mem_writes: portion.IntervalDict
+

The memory writes that occurred while reaching this state.

+
+
Getter:
+

An interval dictionary, with the keys being ranges and the values being tuple[int, frozenset[int]]. The first element of the tuple is a unique placeholder, the second element of the tuple are the possible instruction pointer values that wrote to this memory.

+
+
Type:
+

P.IntervalDict

+
+
+
+ +
+
+property malloced_names: portion.IntervalDict
+
+ +
+
+concrete_examples(args: any, num_examples=3) list[cozy.concrete.TerminalStateInput]
+

Concretizes the arguments used to put the program in this singleton state.

+
+
Parameters:
+
    +
  • args (any) – The input arguments to concretize. This argument may be a Python datastructure, the concretizer will make a deep copy with claripy symbolic variables replaced with concrete values.

  • +
  • num_examples (int) – The maximum number of concrete examples to generate for this singleton state.

  • +
+
+
Returns:
+

A list of concrete inputs that satisfies the constraints attached to the state.

+
+
Return type:
+

list[TerminalStateInput]

+
+
+
+ +
+ +
+
+class cozy.terminal_state.DeadendedState(state: angr.SimState, state_id: int)
+

Bases: TerminalState

+

This class is used to indicate that execution terminated normally in the contained state.

+

Constructor for DeadendedState

+
+
Variables:
+
    +
  • state (SimState) – The state that terminated normally.

  • +
  • state_id (int) – The identifer of the state, determined by its position in the list cozy.project.RunResult.deadended

  • +
+
+
+
+ +
+
+class cozy.terminal_state.SpinningState(state: angr.SimState, state_id: int)
+

Bases: TerminalState

+

This class is used to indicate that the contained state was killed by the LocalLoopSeer, indicating that an upper +bound on number of loop iterations was reached.

+

Constructor for SpinningState

+
+
Variables:
+
    +
  • state (SimState) – The state that was spinning

  • +
  • state_id (int) – The identifer of the state, determined by its position in the list cozy.project.RunResult.spinning

  • +
+
+
+
+ +
+
+class cozy.terminal_state.AssertFailedState(assertion: cozy.directive.Assert, cond: claripy.ast.bool, failure_state: angr.SimState, state_id: int)
+

Bases: TerminalState

+

This class is used to indicate that execution failed due to an Assert being satisfiable.

+
+
Variables:
+
    +
  • assertion (Assert) – The assertion that was triggered.

  • +
  • cond (claripy.ast.bool) – The condition that caused the assertion to trigger

  • +
+
+
+

Constructor for AssertFailedState

+
+
Parameters:
+
    +
  • assertion (Assert) – The assertion that was triggered.

  • +
  • claripy.ast.bool – The condition which if falsified will trigger the assertion.

  • +
  • failure_state (SimState) – The state that was created to test the assertion.

  • +
  • state_id (int) – The identifier of the state, determined by its position in the list cozy.project.RunResult.asserts_failed

  • +
+
+
+
+ +
+
+class cozy.terminal_state.PostconditionFailedState(postcondition: cozy.directive.Postcondition, cond: claripy.ast.bool, failure_state: angr.SimState, state_id: int)
+

Bases: TerminalState

+

This class is used to indicate that execution failed due to an Assert being satisfiable.

+
+
Variables:
+
    +
  • assertion (Assert) – The assertion that was triggered.

  • +
  • cond (claripy.ast.bool) – The condition that caused the assertion to trigger

  • +
+
+
+

Constructor for AssertFailedState

+
+
Parameters:
+
    +
  • assertion (Postcondition) – The postcondition that was triggered.

  • +
  • claripy.ast.bool – The condition which if falsified will trigger the postcondition assertion.

  • +
  • failure_state (SimState) – The state that was created to test the postcondition assertion.

  • +
  • state_id (int) – The identifier of the state, determined by its position in the list cozy.project.RunResult.postconditions_failed

  • +
+
+
+
+ +
+
+class cozy.terminal_state.ErrorState(error_record: angr.sim_manager.ErrorRecord, state_id: int)
+

Bases: TerminalState

+

This class is used to indicate a state that resulted in an error (either my an execution error or ErrorDirective).

+
+
Variables:
+
    +
  • error (SimError) – The error that was thrown.

  • +
  • traceback – The traceback attached to the error.

  • +
+
+
+

Constructor for ErrorState

+
+
Parameters:
+
    +
  • error_record (ErrorRecord) – The error thrown for this state.

  • +
  • state_id (int) – The identifier of the state, determined by it’s position in the list cozy.project.RunResult.errored

  • +
+
+
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/cozy/types/index.html b/autoapi/cozy/types/index.html new file mode 100644 index 0000000..e0c4249 --- /dev/null +++ b/autoapi/cozy/types/index.html @@ -0,0 +1,198 @@ + + + + + + + + cozy.types — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

cozy.types

+
+

Functions

+ + + + + + + + + +

register_type(→ angr.sim_type.SimType)

Parses a C-style type definition given in the input string, assuming the given architecture. Registers this type with angr.

register_types(→ angr.sim_type.SimType)

Parses a series of type definition given in the input string. Registers this type with angr.

+
+
+

Module Contents

+
+
+cozy.types.register_type(type_definition: str, arch: archinfo.Arch) angr.sim_type.SimType
+

Parses a C-style type definition given in the input string, assuming the given architecture. Registers this type with angr.

+
+
Parameters:
+
    +
  • type_definition (str) – The type definition, given in C-style format.

  • +
  • arch (Arch) – The architecture this type should be used with.

  • +
+
+
Returns:
+

The parsed typed.

+
+
Return type:
+

SimType

+
+
+
+ +
+
+cozy.types.register_types(type_definition: str) angr.sim_type.SimType
+

Parses a series of type definition given in the input string. Registers this type with angr.

+
+
Parameters:
+
    +
  • type_definition (str) – The type definition, given in C-style format.

  • +
  • arch (Arch) – The architecture this type should be used with.

  • +
+
+
Returns:
+

The parsed typed.

+
+
Return type:
+

SimType

+
+
+
+ +
+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/autoapi/index.html b/autoapi/index.html new file mode 100644 index 0000000..9090ee6 --- /dev/null +++ b/autoapi/index.html @@ -0,0 +1,160 @@ + + + + + + + + API Reference — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + + + + + \ No newline at end of file diff --git a/concolic.html b/concolic.html new file mode 100644 index 0000000..da82cb3 --- /dev/null +++ b/concolic.html @@ -0,0 +1,191 @@ + + + + + + + + Using Concolic Execution — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ +
+

Using Concolic Execution

+

Concolic execution is a state exploration strategy that uses concrete values to +guide symbolic execution. cozy performs concolic execution slightly differently +than what you might be used to with angr, including angr’s Unicorn engine. In +our implementation of concolic execution, concrete values for each symbol are +chosen, and symbolic execution proceeds as normal. When a branch is created in +the symbolic execution, the concrete values are substituted into the constraints +of both children, and the children that evaluate to false are placed in a deferred +stash. Once execution reaches a terminal state, a deferred child is selected using +some heuristic, and its constraints are used to generate a new concrete input. +This is equivalent to negating a portion of the path constraint that you might +typically see in literature on concolic execution. Our core implementation of +concolic execution can be used outside of the cozy workflow as a standalone +exploration technique. The relevant classes if you wish to do this can be found +in the cozy.concolic.exploration module.

+

Since cozy is a comparative framework, we implement an additional strategy +called joint concolic execution. In joint concolic execution, we alternate +between the two programs when generating concrete values. After a concrete value +is implemented, we run both programs on the same concrete values, which automatically +leads to compatible state pairs being generated.

+

Note that like typical symbolic execution, concolic execution can be complete +if so desired. The execution is complete if there are no deferred states in either +program. However the primary benefit of concolic execution is that we can explore +promising paths at the expense of an incomplete analysis. The promising paths in cozy +are determined by heuristics. There are two different heuristics required for +concolic execution:

+
    +
  1. A termination heuristic, which determines when to halt concolic execution.

  2. +
  3. A candidate heuristic, which determines which deferred state to explore next.

  4. +
+

Some pre-made heuristics can be found in cozy.concolic.heuristics. Let’s +walk through an example of using a joint concolic session to explore how to use +cozy.concolic.session.JointConcolicSession.

+

Let’s assume that we already have a prepatched and postpatched cozy project set up:

+
sess_prepatched = proj_prepatched.session('process_sensor_data')
+add_directives(sess_prepatched)
+initialize_state(sess_orig)
+
+sess_postpatched = proj_postpatched.session('process_sensor_data')
+add_directives(sess_postpatched)
+initialize_state(sess_postpatched)
+
+
+

We are now ready to create and run a joint concolic session. We must remember to pass +a set of symbols used in the program to the +run() method, as we need to assign +concrete values to every symbolic value. We also construct the candidate and termination +heuristics:

+
joint_sess = JointConcolicSession(sess_prepatched, sess_postpatched,
+                                  candidate_heuristic_left=BBTransitionCandidate(),
+                                  candidate_heuristic_right=BBTransitionCandidate(),
+                                  termination_heuristic_left=CyclomaticComplexityTermination.from_session(sess_prepatched),
+                                  termination_heuristic_right=CyclomaticComplexityTermination.from_session(sess_postpatched))
+(prepatched_results, postpatched_results) = joint_sess.run([], [], symbols)
+
+
+

Here we are setting heuristics so that we do not explore every state. Instead, our candidate +heuristic will pick states with the most unique basic block edge transitions in their history, +and the exploration will be terminated once the number of terminal states exceeds the +cyclomatic complexity of the session’s function. The return result from the +run() method gives two +RunResult objects, which can be directly be used by +cozy.analysis.Comparison:

+
comparison_results = analysis.Comparison(prepatched_results, postpatched_results)
+
+
+

We can of course visualize the results in the browser:

+
# Here args is not the function arguments, but rather the contents of the memory
+# mutated by initialize_state
+execution_graph.visualize_comparison(proj_prepatched, proj_postpatched,
+                                     prepatched_results, postpatched_results,
+                                     comparison_results,
+                                     concrete_arg_mapper=concrete_mapper, args=args,
+                                     num_examples=2, open_browser=True)
+
+
+

The full implementation used in this guide can be found at +https://github.com/draperlaboratory/cozy/blob/main/examples/cmp_weather_v5_concolic.py

+
+ + +
+ +
+
+ +
+
+ + + + + + + \ No newline at end of file diff --git a/cozy-viz/components/actionDifference.js b/cozy-viz/components/actionDifference.js new file mode 100644 index 0000000..ec37f6a --- /dev/null +++ b/cozy-viz/components/actionDifference.js @@ -0,0 +1,130 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import { getEdgesFromEnds } from '../util/segmentation.js' +import { hunkFormat } from './hunk.js' +import LineDiffView from './lineDiffView.js' +import SearchInput from './searchInput.js' + +export default class ActionDifference extends Component { + getActions(focus) { + const segment = getEdgesFromEnds(focus.top, focus.bot).reverse() + let contents = "" + let msg = "" + const lines = [] + const ids = [] + const msgs = [] + + for (const edge of segment) { + const id = edge.id() + for (const line of edge.data('actions')) { + contents += line + '\n' + lines.push(line) + msgs.push(msg) + ids.push(id) + } + } + + if (focus.bot.data('actions')) { + for (const line of focus.bot.data('actions')) { + contents += line + '\n' + lines.push(line) + msgs.push(msg) + ids.push("bottomNode") + } + } + + return { contents, lines, ids, msgs } + } + + onInput(e) { + this.setState({ filterExpr: e.target.value }) + } + + diffWords(leftLine, rightLine) { + + const lwords = leftLine.split(/\s+/) + const rwords = rightLine.split(/\s+/) + + const laddr = lwords.shift() + const raddr = rwords.shift() + + const comparison = lwords.map((lw, idx) => rwords[idx] === lw) + + leftLine = lwords + .map((w, idx) => comparison[idx] ? `${w} ` : hunkFormat(w, "hunkRemoved")) + rightLine = rwords + .map((w, idx) => comparison[idx] ? `${w} ` : hunkFormat(w, "hunkAdded")) + + leftLine.unshift(`${laddr} `) + rightLine.unshift(`${raddr} `) + + return [leftLine, rightLine] + } + + highlightNodes(idLeft, idRight) { + const cyLeft = this.props.leftFocus.cy() + const cyRight = this.props.rightFocus.cy() + if (idLeft == 'bottomNode') { + const botId = this.props.leftFocus.bot.id() + cyLeft.highlight(cyLeft.nodes(`#${botId}, [mergedIds*='#${botId}#']`)) + } else { + const leftEdges = cyLeft.edges(`#${idLeft}, [mergedIds*='#${idLeft}#']`) + cyLeft.highlight(leftEdges.sources()) + } + if (idRight == 'bottomNode') { + const botId = this.props.rightFocus.bot.id() + cyRight.highlight(cyRight.nodes(`#${botId}, [mergedIds*='#${botId}#']`)) + } else { + const rightEdges = cyRight.edges(`#${idRight}, [mergedIds*='#${idRight}#']`) + cyRight.highlight(rightEdges.sources()) + } + } + + dimAll() { + this.props.leftFocus.cy().dim() + this.props.rightFocus.cy().dim() + } + + compare(l, r) { + // TODO: more fleshed out comparator, case split on action type + const [, , laction, ...lterms] = l.split(/\s+/); + const [, , raction, ...rterms] = r.split(/\s+/); + if (laction === raction) { + switch (laction) { + case "reg/write:": return lterms[0] == rterms[0] && lterms.length === rterms.length + case "reg/read:": return lterms[0] == rterms[0] && lterms.length === rterms.length + default: return lterms.length === rterms.length + } + } + return false + } + + format(s) { + let [, ...results] = s.slice(1, -1).split(/\s+/); + results = results.map(s => { + switch (s) { + case "---->>": return "→" + case "<<----": return "←" + default: return s + } + }) + return results.join(' ') + } + + render(props, state) { + return html`
+ <${SearchInput} onInput=${e => this.onInput(e)} value=${this.filterExpr}/> + <${LineDiffView} + filterExpr=${state.filterExpr} + leftLines=${props.leftFocus ? this.getActions(props.leftFocus) : null} + rightLines=${props.rightFocus ? this.getActions(props.rightFocus) : null} + comparator=${(l, r) => this.compare(l, r)} + diffWords=${(l, r) => this.diffWords(l, r)} + highlight=${(idLeft, idRight) => this.highlightNodes(idLeft, idRight)} + format=${s => this.format(s)} + dim=${() => this.dimAll()} + /> +
+ ` + } +} diff --git a/cozy-viz/components/app.js b/cozy-viz/components/app.js new file mode 100644 index 0000000..801947a --- /dev/null +++ b/cozy-viz/components/app.js @@ -0,0 +1,442 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' + +import { Component, createRef } from 'https://unpkg.com/preact@latest?module' +import cytoscape from "https://cdn.jsdelivr.net/npm/cytoscape@3.26.0/+esm" +import cytoscapeCola from 'https://cdn.jsdelivr.net/npm/cytoscape-cola@2.5.1/+esm' +import Tooltip from './tooltip.js'; +import DiffPanel from './diffPanel.js'; +import MenuBar from './menuBar.js'; +import { focusMixin } from '../util/focusMixin.js'; +import { checkedMixin } from '../util/checkedMixin.js'; +import { Segment } from '../util/segmentation.js'; +import { segmentationMixin } from '../util/segmentationMixin.js'; +import * as GraphStyle from '../util/graphStyle.js'; +import { tidyMixin } from '../util/graph-tidy.js'; +import { Status, View } from '../data/cozy-data.js' +import { breadthFirst } from '../data/layouts.js' + +cytoscape.use(cytoscapeCola) + +export default class App extends Component { + + constructor() { + super(); + this.state = { + status: Status.unloaded, // awaiting graph data + layout: breadthFirst, // we start with the breadthfirst layout + view: View.plain, //we start with all nodes visible, not a CFG + prelabel: "prepatch", + postlabel: "postpatch", + } + this.cy1 = createRef() + this.cy2 = createRef() + this.cy1.other = this.cy2 + this.cy2.other = this.cy1 + this.tooltip = createRef() + + this.handleDragleave = this.handleDragleave.bind(this) + this.handleDragover = this.handleDragover.bind(this) + this.clearTooltip = this.clearTooltip.bind(this) + this.resetLayout = this.resetLayout.bind(this) + this.getJSON = this.getJSON.bind(this) + + this.viewMenu = createRef() + this.pruneMenu = createRef() + + window.app = this + } + + // Produces an object encapsulating data and methods needed by a cozy report + // window. + getReportInterface() { + return { + prelabel: this.state.prelabel, + postlabel: this.state.postlabel, + pruningStatus: this.pruneMenu.current.state, + leftPanelRef: this.cy1, + refreshPrune: () => { + //we might need to refresh the pruning of the tree, if we've + //checked/unchecked a new report item and the tree is pruning checked + //branches + if (this.pruneMenu.current.state.pruningChecked) { + this.pruneMenu.current.setPrune({}) + } + }, + focusLeafById: (id) => { + const leaf = this.cy1.cy.nodes(`#${id}`) + const selfCy = this.cy1.cy + const otherCy = this.cy2.cy + const selfRoot = selfCy.nodes().roots()[0] + const selfSegment = new Segment(selfRoot, leaf) + const compatibilities = leaf.data().compatibilities + selfCy.blur().focus(leaf) + otherCy + .blur() + .focus(otherCy.nodes().filter(node => +node.data().id in compatibilities)) + this.setState({ leftFocus: selfSegment, rightFocus: null }) + }, + } + } + + componentDidMount() { + const urlParams = new URLSearchParams(window.location.search); + const isServedPre = urlParams.get('pre') + const isServedPost = urlParams.get('post') + if (isServedPre) { + fetch(isServedPre) + .then(rslt => rslt.json()) + .then(raw => { + const obj = JSON.parse(raw) + if (!obj.elements) throw new Error("Malformed post-patch JSON") + this.mountToCytoscape(obj, this.cy1) + }) + .catch(e => console.error(e)) + } + if (isServedPost) { + fetch(isServedPost) + .then(rslt => rslt.json()) + .then(raw => { + const obj = JSON.parse(raw) + if (!obj.elements) throw new Error("Malformed post-patch JSON") + this.mountToCytoscape(obj, this.cy2) + }) + .catch(e => console.error(e)) + } + } + + handleClick(ev) { + + //bail out if graphs are not available + if (this.state.status == Status.unloaded) { + alert("Please load both graphs before attempting comparison.") + return + } + if (this.state.view == View.plain) this.handlePlainClick(ev) + if (this.state.view == View.cfg) this.handleCFGClick(ev) + } + + handleCFGClick(ev) { + if (!ev.originalEvent.shiftKey) return + const addr = ev.target.data('address') + this.resetLayout(breadthFirst, View.plain) + const similar = ev.target.cy().nodes(`[address=${addr}]`) + ev.target.cy().highlight(similar) + } + + handlePlainClick(ev) { + + const isLeft = ev.target.cy() == this.cy1.cy + const self = ev.cy + const other = ev.cy.ref.other.cy + // we're selecting just a segment if the shift key is held + const segmentSelect = ev.originalEvent.shiftKey + // we're refining if we click on an existing locus, there's more than + // one such, and we're not switching from a shift to regular click + const refining = + self.loci?.includes(ev.target) && + self.loci.length > 1 && + segmentSelect == this.lastSegmentSelect + + this.lastSegmentSelect = segmentSelect + + // segments are linear sequences of nodes given by a top and bottom + let selfSegment + + if (segmentSelect) { + // if we're selecting a segment, choose the corresponding segment + selfSegment = Segment.fromRange(self.getRangeOf(ev.target)) + self.blur().focusRange(self.getRangeOf(ev.target)) + } else { + // otherwise, bail out if we're not on a leaf + if (ev.target.outgoers().length !== 0) return + // and choose the full branch, if we are on a leaf + const selfRoot = ev.cy.nodes().roots()[0] + selfSegment = new Segment(selfRoot, ev.target) + self.blur().focus(ev.target) + } + + // unconditionally focus the clicked segment + if (isLeft) this.setState({ leftFocus: selfSegment }) + else this.setState({ rightFocus: selfSegment }) + + // if we're not refining, we need to update the focus on the other side + if (!refining) { + let otherSegment + if (segmentSelect) { + const compats = self.getLeavesCompatibleWith(ev.target, other) + // if we're selecting a segment, get the compatible range and focus it + const otherRange = other.getCompatibilityRangeOf(self.getMinimalCeiling(compats), self) + other.blur().focusRange(otherRange) + if (other.loci.length == 1) { + //if there's only one compatibility, introduce a segment + otherSegment = Segment.fromRange(otherRange) + } + } else { + // if we're selecting a full branch, get compatible roots and focus those + const compatibilities = ev.target.data().compatibilities + other + .blur() + .focus(other.nodes().filter(node => +node.data().id in compatibilities)) + if (other.loci.length == 1) { + // if there's only one compatibility, start a diff + const otherRoot = other.nodes().roots()[0] + otherSegment = new Segment(otherRoot, other.loci) + } + } + + if (otherSegment) { + // if we picked out a corresponding segment, focus it. + if (isLeft) this.setState({ rightFocus: otherSegment }) + else this.setState({ leftFocus: otherSegment }) + } else { + //otherwise clear the focus + if (isLeft) this.setState({ rightFocus: null, leftFocus: selfSegment }) + else this.setState({ leftFocus: null, rightFocus: selfSegment }) + } + } + } + + getJSON() { + return JSON.stringify({ + pre: { + data: JSON.parse(this.cy1.orig), + name: this.state.prelabel + }, + post: { + data: JSON.parse(this.cy2.orig), + name: this.state.postlabel + } + }) + } + + + setStatus(status) { this.setState({ status }) } + + regenerateFocus() { + const connectedLeft = this.cy1.cy.loci?.filter(node => node.inside()) + const connectedRight = this.cy2.cy.loci?.filter(node => node.inside()) + + if (connectedLeft?.length == 0 || connectedRight?.length == 0) { + // we just throw away the focus if all the loci on either side have been filtered out + this.cy1.cy.blur() + this.cy2.cy.blur() + this.setState({ leftFocus: null, rightFocus: null }) + } else { + this.setState({ + leftFocus: this.state.leftFocus ? new Segment(this.cy1.cy.root, this.cy1.cy.loci) : null, + rightFocus: this.state.rightFocus ? new Segment(this.cy2.cy.root, this.cy2.cy.loci) : null, + }) + } + // we sometimes need to regenerate focus, + // so that the assembly diff is regenerated, + // so that its lines are properly mapped on to the merged nodes. + } + + async handleDrop(ev) { + ev.stopPropagation() + ev.preventDefault() + ev.currentTarget.classList.remove("dragHover") + const file = ev.dataTransfer.files[0] + const raw = await file.text().then(JSON.parse) + this.setState({ + prelabel: raw.pre.name, + postlabel: raw.post.name, + }) + this.mountToCytoscape(raw.pre.data, this.cy1) + this.mountToCytoscape(raw.post.data, this.cy2) + } + + handleDragover(ev) { + ev.stopPropagation() + ev.preventDefault() + ev.currentTarget.classList.add("dragHover") + } + + handleDragleave(ev) { + ev.stopPropagation() + ev.preventDefault() + ev.currentTarget.classList.remove("dragHover") + } + + mountToCytoscape(raw, ref) { + if (ref.cy) ref.cy.destroy() + const cy = cytoscape({ + style: GraphStyle.style, + elements: raw.elements + }) + + // mount to DOM + cy.mount(ref.current) + + // monkeypatch in additional methods + Object.assign(cy, focusMixin); + Object.assign(cy, tidyMixin); + Object.assign(cy, segmentationMixin); + Object.assign(cy, checkedMixin); + cy.debugData = cy.nodes().roots()[0].data("debug") + + // set layout + ref.currentLayout = cy.layout(this.state.layout).run() + + cy.on('add', ev => { + if (ev.target.group() === 'nodes') { + this.initializeNode(ev.target) + } + }) + + // clear focus on click without target + cy.on('click', ev => { + if (this.state.view == View.cfg) return + if (!ev.target.group) { + this.batch(() => { + this.cy1.cy?.blur() + this.cy2.cy?.blur() + this.setState({ leftFocus: null, rightFocus: null }) + this.tooltip.current.clearTooltip() + }) + } + }) + + cy.on('zoom pan', () => { + this.tooltip.current.clearTooltip() + }) + + // stow graph data in reference + ref.cy = cy + ref.orig = JSON.stringify(cy.json()) + + // stow reference data in graph + cy.ref = ref + + cy.nodes().map(node => this.initializeNode(node)) + + this.setState({ + status: !this.cy1.cy || !this.cy2.cy + ? Status.unloaded + : Status.idle + }) + } + + initializeNode(node) { + + // turn off manual graph dragging + node.ungrabify() + + // add methods for actively querying display features + + // mouseover handling + node.on('mouseout', ev => { + ev.cy.container().style.cursor = "default" + }) + + node.on('mouseover', ev => { + + + if (ev.target.outgoers().length == 0) { + ev.cy.container().style.cursor = "pointer" + } + + this.tooltip.current.attachTo(ev.target) + }) + + node.on('click', ev => this.handleClick(ev)) + } + + startRender(method) { + this.setState({ status: Status.rendering }, method) + } + + batch(cb) { + this.cy1.cy?.startBatch() + this.cy2.cy?.startBatch() + cb() + this.cy1.cy?.endBatch() + this.cy2.cy?.endBatch() + } + + resetLayout(layout, view) { + this.setState(oldState => { + this.cy1.currentLayout.stop() + this.cy2.currentLayout.stop() + layout = layout ?? oldState.layout + if (view != oldState.view) { + if (view == View.cfg) { + // we're going from View.plain to View.cfg + this.cy1.cy.mergeByAddress() + this.cy2.cy.mergeByAddress() + } else if (view == View.plain) { + //we're going from View.cfg to View.plain + this.cy1.cy.removeCFGData() + this.cy2.cy.removeCFGData() + // we need to restore the tidiness level and the pruning to the + // reconstucted graph + this.viewMenu.current.retidy() + this.pruneMenu.current.doPrune() + } else { + //no view given, we're just recomputing the view, + view = oldState.view + } + } + this.cy1.currentLayout = this.cy1.cy.layout(layout).run() + this.cy2.currentLayout = this.cy2.cy.layout(layout).run() + + return { view, layout } + }) + } + + refreshLayout() { + this.cy1.currentLayout = this.cy1.cy.layout(this.state.layout).run() + this.cy2.currentLayout = this.cy2.cy.layout(this.state.layout).run() + } + + clearTooltip() { + this.tooltip.current.clearTooltip() + } + + render(_props, state) { + // TODO I could get rid of a lot of lambdas here if I properly bound "this" + // in some of these methods + return html` + <${Tooltip} ref=${this.tooltip}/> + <${MenuBar} + cyLeft=${this.cy1} + cyRight=${this.cy2} + view=${state.view} + layout=${state.layout} + getReportInterface=${() => this.getReportInterface()} + regenerateFocus=${() => this.regenerateFocus()} + resetLayout=${this.resetLayout} + refreshLayout=${() => this.refreshLayout()} + tidiness=${state.tidiness} + status=${state.status} + batch=${cb => this.batch(cb)} + viewMenu=${this.viewMenu} + pruneMenu=${this.pruneMenu} + getJSON=${this.getJSON} + /> +
this.startRender(() => this.handleDrop(ev))} + > + ${state.prelabel} + ${state.postlabel} +
+
+
+
+
+ <${DiffPanel} + rightFocus=${state.rightFocus} + leftFocus=${state.leftFocus} + onMouseEnter=${() => this.tooltip.current.clearTooltip()} + /> + ${state.status == Status.rendering && html`rendering...`} + ` + } +} diff --git a/cozy-viz/components/assemblyDifference.js b/cozy-viz/components/assemblyDifference.js new file mode 100644 index 0000000..91cd622 --- /dev/null +++ b/cozy-viz/components/assemblyDifference.js @@ -0,0 +1,97 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import { hunkFormat } from './hunk.js' +import SearchInput from './searchInput.js' +import LineDiffView from './lineDiffView.js' +import { getNodesFromEnds } from '../util/segmentation.js' + +export default class AssemblyDifference extends Component { + + getAssembly(focus) { + const segment = getNodesFromEnds(focus.top, focus.bot).reverse() + let contents = "" + let msg = "" + const lines = [] + const ids = [] + const msgs = [] + const debug = focus.cy().debugData + + for (const node of segment) { + const id = node.id() + for (const line of node.data().contents.split('\n')) { + if (debug) { + const addr = parseInt(line.match(/^[0-9a-f]*/), 16) + if (debug[addr]) msg = "" // start fresh list of debug locations + for (const loc of debug[addr] || []) msg += loc + '\n' + } + contents += line + '\n' + lines.push(line) + msgs.push(msg) + ids.push(id) + } + } + + return { contents, lines, ids, msgs } + } + + onInput(e) { + this.setState({ filterExpr: e.target.value }) + } + + highlightNodes(idLeft, idRight) { + const cyLeft = this.props.leftFocus.cy() + const cyRight = this.props.rightFocus.cy() + cyLeft.highlight(cyLeft.nodes(`#${idLeft}, [mergedIds*='#${idLeft}#']`)) + cyRight.highlight(cyRight.nodes(`#${idRight}, [mergedIds*='#${idRight}#']`)) + } + + dimAll() { + this.props.leftFocus.cy().dim() + this.props.rightFocus.cy().dim() + } + + compare(l, r) { + // TODO --- count lines with identical mnemonics and numbers of operands as + // the same, and do a word-level diff. + const [, lmnemonic, ...loperands] = l.split(/\s+/); + const [, rmnemonic, ...roperands] = r.split(/\s+/); + return lmnemonic == rmnemonic && loperands.length == roperands.length + } + + diffWords(leftLine, rightLine) { + + const lwords = leftLine.split(/\s+/) + const rwords = rightLine.split(/\s+/) + + const laddr = lwords.shift() + const raddr = rwords.shift() + + const comparison = lwords.map((lw, idx) => rwords[idx] === lw) + + leftLine = lwords + .map((w, idx) => comparison[idx] ? `${w} ` : hunkFormat(w, "hunkRemoved")) + rightLine = rwords + .map((w, idx) => comparison[idx] ? `${w} ` : hunkFormat(w, "hunkAdded")) + + leftLine.unshift(`${laddr} `) + rightLine.unshift(`${raddr} `) + + return [leftLine, rightLine] + } + + render(props, state) { + return html`
+ <${SearchInput} value=${state.filterExpr} onInput=${e => this.onInput(e)}/> + <${LineDiffView} + filterExpr=${state.filterExpr} + leftLines=${props.leftFocus ? this.getAssembly(props.leftFocus) : null} + rightLines=${props.rightFocus ? this.getAssembly(props.rightFocus) : null} + comparator=${(l, r) => this.compare(l, r)} + diffWords=${(l, r) => this.diffWords(l, r)} + highlight=${(idLeft, idRight) => this.highlightNodes(idLeft, idRight)} + dim=${() => this.dimAll()} + /> +
` + } +} + diff --git a/cozy-viz/components/concretionSelector.js b/cozy-viz/components/concretionSelector.js new file mode 100644 index 0000000..aa84107 --- /dev/null +++ b/cozy-viz/components/concretionSelector.js @@ -0,0 +1,25 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' + +export default class ConcretionSelector extends Component { + render(props) { + + if (props.concretionCount === 0) return null + + const buttons = [html`` + ] + + for (let i = 0; i < props.concretionCount; i++) { + buttons.push(html`` + ) + } + + return html`
${buttons}
` + } +} diff --git a/cozy-viz/components/concretions.js b/cozy-viz/components/concretions.js new file mode 100644 index 0000000..c2a5668 --- /dev/null +++ b/cozy-viz/components/concretions.js @@ -0,0 +1,79 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' + +export default class Concretions extends Component { + + constructor() { + super() + this.state = { + view: "shared" + } + } + + render(props, state) { + const rightId = props.rightFocus.bot.id() + const leftId = props.leftFocus.bot.id() + const examples = [] + const sharedConcretions = props.leftFocus.bot.data().compatibilities[rightId].conc_args + const leftOnlyConcretions = Object.entries(props.leftFocus.bot.data().compatibilities).flatMap( + ([key, compat]) => key == rightId ? [] : compat.conc_args + ) + const rightOnlyConcretions = Object.entries(props.rightFocus.bot.data().compatibilities).flatMap( + ([key, compat]) => key == leftId ? [] : compat.conc_args + ) + + const concretions = + state.view == "shared" ? sharedConcretions + : state.view == "left" ? leftOnlyConcretions + : state.view == "right" ? rightOnlyConcretions + : null + + for (const concretion of concretions) { + examples.push(html` +
${JSON.stringify(concretion, undefined, 2)}
+ `) + } + + const sharedMsg = sharedConcretions.length == 0 + ? "No concretions available" + : html`Viewing ${sharedConcretions.length} concrete input examples shared by both branches` + + const leftMsg = leftOnlyConcretions.length == 0 + ? rightOnlyConcretions.length == 0 + ? "There are no inputs that go down the left but not the right branch. The two branches correspond to exactly the same inputs." + : "There are no inputs that go down the left but not the right branch. The left branch refines the right." + : html`Viewing ${leftOnlyConcretions.length} concrete input examples that go down the left but not the right branch` + + const rightMsg = rightOnlyConcretions.length == 0 + ? leftOnlyConcretions.length == 0 + ? "There are no inputs that go down the right but not the left branch. The two branches correspond to exactly the same inputs." + : "There are no inputs that go down the right but not the left branch. The right branch refines the left." + : html`Viewing ${rightOnlyConcretions.length} concrete input examples that go down the right but not the left branch` + + return html` +
+ + + +
+
+ ${state.view == "shared" ? sharedMsg + : state.view == "left" ? leftMsg + : state.view == "right" ? rightMsg + : null + } +
+
+ ${examples} +
` + } +} diff --git a/cozy-viz/components/diffPanel.js b/cozy-viz/components/diffPanel.js new file mode 100644 index 0000000..b472f53 --- /dev/null +++ b/cozy-viz/components/diffPanel.js @@ -0,0 +1,112 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component, createRef } from 'https://unpkg.com/preact@latest?module' +import Concretions from './concretions.js' +import ActionDifference from './actionDifference.js' +import AssemblyDifference from './assemblyDifference.js' +import RegisterDifference from './registerDifference.js' +import MemoryDifference from './memoryDifference.js' +import SideEffectDifference from './sideEffectDifference.js' + +export default class DiffPanel extends Component { + constructor() { + super(); + this.state = { + mode: null, + } + + this.diffPanel = createRef() + this.dragHandle = createRef() + } + + toggleMode(mode) { + if (this.state.mode == mode) { + this.setState({ mode: null }) + } else { + this.setState({ mode }) + } + } + + startResize(e) { + this.diffPanel.current.onpointermove = e => { + this.diffPanel.current.style.maxHeight = `${Math.max(50, window.innerHeight - e.clientY)}px` + } + this.dragHandle.current.setPointerCapture(e.pointerId) + this.dragHandle.current.classList.add("grabbed") + this.diffPanel.current.classList.add("resizing") + } + + stopResize(e) { + this.diffPanel.current.onpointermove = null + this.dragHandle.current.releasePointerCapture(e.pointerId) + this.dragHandle.current.classList.remove("grabbed") + this.diffPanel.current.classList.remove("resizing") + } + + render(props, state) { + const assemblyAvailable = props.leftFocus || props.rightFocus + const registersAvailable = props.leftFocus && props.rightFocus && + props.leftFocus.bot.data().compatibilities?.[props.rightFocus.bot.id()]?.regdiff + const memoryAvailable = props.leftFocus && props.rightFocus && + props.leftFocus.bot.data().compatibilities?.[props.rightFocus.bot.id()]?.memdiff + const concretionAvailable = props.leftFocus && props.rightFocus && + props.leftFocus.bot.data().compatibilities?.[props.rightFocus.bot.id()]?.conc_args + const actionsAvailable = + props.rightFocus?.top.outgoers("edge")[0]?.data('actions')?.length > 0 || + props.leftFocus?.top.outgoers("edge")[0]?.data('actions')?.length > 0 + const sideEffectsAvailable = props.leftFocus && props.rightFocus && + props.leftFocus.bot.data().compatibilities?.[props.rightFocus.bot.id()]?.conc_sediff + return html`
+
this.startResize(e)} + onPointerUp=${e => this.stopResize(e)} + ref=${this.dragHandle} + /> +
+ + + + + + +
+ ${state.mode == "assembly" && assemblyAvailable && html` + <${AssemblyDifference} rightFocus=${props.rightFocus} leftFocus=${props.leftFocus}/>` + } + ${state.mode == "registers" && registersAvailable && html` + <${RegisterDifference} rightFocus=${props.rightFocus} leftFocus=${props.leftFocus}/>` + } + ${state.mode == "memory" && memoryAvailable && html` + <${MemoryDifference} rightFocus=${props.rightFocus} leftFocus=${props.leftFocus}/>` + } + ${state.mode == "concretions" && concretionAvailable && html` + <${Concretions} rightFocus=${props.rightFocus} leftFocus=${props.leftFocus}/>` + } + ${state.mode == "actions" && actionsAvailable && html` + <${ActionDifference} rightFocus=${props.rightFocus} leftFocus=${props.leftFocus}/>` + } + ${state.mode == "side-effects" && sideEffectsAvailable && html` + <${SideEffectDifference} rightFocus=${props.rightFocus} leftFocus=${props.leftFocus}/>` + } +
` + } +} diff --git a/cozy-viz/components/hunk.js b/cozy-viz/components/hunk.js new file mode 100644 index 0000000..132c4c3 --- /dev/null +++ b/cozy-viz/components/hunk.js @@ -0,0 +1,32 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' + +export function hunkFormat(hunk, className) { + const terminator = hunk.slice(-1) + if (terminator === '>' || terminator == ',') { + const newHunk = hunk.slice(0, hunk.length - 1) + return html`${newHunk}${terminator} ` + } else { + return html`${hunk} ` + } +} + + +export function Hunk({ dim, highlight, hunkCtx, curLeft, curRight, leftContent, leftClass, rightContent, rightClass }) { + const hunk = html`
+
${leftContent}
+
${rightContent}
+
` + + hunk.contentListing = { left: leftContent, right: rightContent } + + return hunk +} diff --git a/cozy-viz/components/layoutMenu.js b/cozy-viz/components/layoutMenu.js new file mode 100644 index 0000000..42694f2 --- /dev/null +++ b/cozy-viz/components/layoutMenu.js @@ -0,0 +1,41 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import { View } from '../data/cozy-data.js' +import { breadthFirst, cola, cose } from '../data/layouts.js' +import Menu from './menu.js' + +export default class LayoutMenu extends Component { + + render(props) { + return html` + <${Menu} + enabled=${props.enabled} + open=${props.open} + title="Layout" + setOpen=${o => props.setOpen(o)}> + <${Menu.Option} + onClick=${() => props.resetLayout(breadthFirst, View.plain)} + selected=${props.layout.name == "breadthfirst" && props.view == View.plain}> + Tree + + <${Menu.Option} + onClick=${() => props.resetLayout(breadthFirst, View.cfg)} + selected=${props.layout.name == "breadthfirst" && props.view == View.cfg}> + CFG - Tree layout + + <${Menu.Option} onClick=${() => props.resetLayout()} + onClick=${() => props.resetLayout(cose, View.cfg)} + selected=${props.layout.name == "cose" && props.view == View.cfg}> + CFG - Cose layout + + <${Menu.Option} + onClick=${() => props.resetLayout(cola, View.cfg)} + selected=${props.layout.name == "cola" && props.view == View.cfg}> + CFG - Cola layout + + <${Menu.Option} onClick=${() => props.resetLayout()}> + Refresh + + ` + } +} diff --git a/cozy-viz/components/lineDiffView.js b/cozy-viz/components/lineDiffView.js new file mode 100644 index 0000000..1ab032b --- /dev/null +++ b/cozy-viz/components/lineDiffView.js @@ -0,0 +1,157 @@ +import * as Diff from 'https://cdn.jsdelivr.net/npm/diff@5.1.0/+esm' +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import { Hunk } from './hunk.js' + +export default class LineDiffView extends Component { + getContents() { + if (!this.props.leftLines && !this.props.rightLines) return null + if (!this.props.rightLines) { + const { + lines: leftLines, + ids: leftIds, + msgs: leftMsgs, + } = this.props.leftLines + const hunkCtx = { leftIds, rightIds: [""], leftMsgs, rightMsgs: [""] } + return leftLines + .map((line, idx) => Hunk({ + hunkCtx, + curLeft: idx, + curRight: 0, + leftContent: this.props.format?.(line) || line, + rightContent: " ", + })) + } + if (!this.props.leftLines) { + const { + lines: rightLines, + ids: rightIds, + msgs: rightMsgs, + } = this.props.rightLines + const hunkCtx = { rightIds, leftIds: [""], rightMsgs, leftMsgs: [""] } + return rightLines + .map((line, idx) => Hunk({ + hunkCtx, + curLeft: 0, + curRight: idx, + leftContent: " ", + rightContent: this.props.format?.(line) || line, + })) + } + return this.diffLines() + } + + diffLines() { + // simple memoization + if (this.prevLeftLines == this.props.leftLines && + this.prevRightLines == this.props.rightLines) { + return this.prevDiff + } + + this.prevLeftFocus = this.props.leftFocus + this.prevRightFocus = this.props.rightFocus + + const { + contents: leftContents, + lines: leftLines, + ids: leftIds, + msgs: leftMsgs, + } = this.props.leftLines + + const { + contents: rightContents, + lines: rightLines, + ids: rightIds, + msgs: rightMsgs, + } = this.props.rightLines + + const hunkCtx = { leftIds, leftMsgs, rightIds, rightMsgs } + const diffs = Diff.diffLines(leftContents, rightContents, { + comparator: this.props.comparator + }) + let rendered = [] + let curLeft = 0 + let curRight = 0 + let mkHunk = ({ curLeft, curRight, leftContent, rightContent, leftClass, rightClass }) => Hunk({ + highlight: this.props.highlight + ? () => this.props.highlight(leftIds[curLeft], rightIds[curRight]) + : () => { }, + dim: this.props.dim + ? () => this.props.dim() + : () => { }, + hunkCtx, + curLeft, + curRight, + leftContent, + rightContent, + leftClass, + rightClass, + }) + + for (const diff of diffs) { + if (diff?.added) { + for (const line of diff.value.split('\n')) { + if (line == "") continue + const hunk = mkHunk({ + curLeft, + curRight, + leftContent: " ", + rightContent: this.props.format?.(line) || line, + rightClass: "hunkAdded", + }) + curRight++ + rendered.push(hunk) + } + } else if (diff?.removed) { + for (const line of diff.value.split('\n')) { + if (line == "") continue + const hunk = mkHunk({ + curLeft, + curRight, + leftContent: this.props.format?.(line) || line, + rightContent: " ", + leftClass: "hunkRemoved", + }) + curLeft++ + rendered.push(hunk) + } + } else { + for (let i = 0; i < diff.count; i++) { + let rightContent = this.props.format?.(rightLines[curRight]) || rightLines[curRight] + let leftContent = this.props.format?.(leftLines[curLeft]) || leftLines[curLeft]; + [leftContent, rightContent] = this.props.diffWords?.(leftContent, rightContent) || [leftContent, rightContent] + const hunk = mkHunk({ + curLeft, + curRight, + leftContent, + rightContent, + }) + curRight++ + curLeft++ + rendered.push(hunk) + } + } + } + + this.prevDiff = rendered + + return rendered + } + + render(props) { + const hunks = this.getContents().filter(({ contentListing }) => { + if (!props.filterExpr) return true + let lineFilter + try { + lineFilter = new RegExp(props.filterExpr) + } catch (e) { + lineFilter = /^/ + } + + return lineFilter.test(contentListing.left) || + lineFilter.test(contentListing.right) + }) + + return html`
${hunks}
` + } +} diff --git a/cozy-viz/components/memoryDifference.js b/cozy-viz/components/memoryDifference.js new file mode 100644 index 0000000..a5d8a8f --- /dev/null +++ b/cozy-viz/components/memoryDifference.js @@ -0,0 +1,38 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import ConcretionSelector from './concretionSelector.js' + +export default class MemoryDifference extends Component { + constructor() { + super(); + this.state = { view: "symbolic" } + } + + render(props, state) { + const rightId = props.rightFocus.bot.id() + const addresses = [] + const conc_adiffs = props.leftFocus.bot.data().compatibilities[rightId].conc_memdiff ?? [] + const adiffs = state.view === "symbolic" + ? props.leftFocus.bot.data().compatibilities[rightId].memdiff + : conc_adiffs[state.view] + for (const addr in adiffs) { + const addrparts = addr + .split('\n') + .map(part => [part, html`
`]) + .flat() + addresses.push(html` + ${adiffs[addr][0]} + ${addrparts} + ${adiffs[addr][1]}`) + } + return html`
+ <${ConcretionSelector} + view=${state.view} + setView=${view => this.setState({ view })} + concretionCount=${conc_adiffs.length}/> +
${addresses.length > 0 + ? addresses + : html`no memory differences detected ✓` + }
` + } +} diff --git a/cozy-viz/components/menu.js b/cozy-viz/components/menu.js new file mode 100644 index 0000000..442dda2 --- /dev/null +++ b/cozy-viz/components/menu.js @@ -0,0 +1,73 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component, createRef } from 'https://unpkg.com/preact@latest?module' +import { computePosition } from 'https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.5.1/+esm'; + +// this should be mounted and unmounted rather than toggled; adding and removing +// the event-listener for closing the menu should be part of the mount/unmount +// lifecycle +export default class Menu extends Component { + constructor() { + super() + this.button = createRef() + this.options = createRef() + } + + static Option = class extends Component { + render(props) { + return html`
+ ${props.children} +
` + } + } + + componentDidUpdate() { + if (this.props.open == this.props.title) { + computePosition(this.button.current, this.options.current, { + placement: "bottom-start" + }).then(({ x, y }) => { + this.options.current.style.left = `${x}px` + this.options.current.style.top = `${y}px` + }) + } + } + + toggleOpen() { + if (!this.props.enabled) return + if (this.props.open != this.props.title) { + this.props.setOpen(this.props.title) + } else { + this.props.setOpen(null) + } + } + + render(props) { + const optionStyle = { + position: "absolute", + display: "block", + backgroundColor: "#e1e1e1" + } + const menuStyle = { + color: props.enabled ? "black" : "#ccc", + backgroundColor: props.open === props.title + ? "#e1e1e1" + : "white" + } + return html` + + ${props.open == props.title && html` +
+ ${props.children} +
` + }` + } +} + diff --git a/cozy-viz/components/menuBar.js b/cozy-viz/components/menuBar.js new file mode 100644 index 0000000..27535ee --- /dev/null +++ b/cozy-viz/components/menuBar.js @@ -0,0 +1,135 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { render } from 'https://unpkg.com/preact@latest?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import { Status } from '../data/cozy-data.js' +import Report from './report.js' +import Menu from './menu.js' +import SearchMenu from './searchMenu.js' +import PruneMenu from './pruneMenu.js' +import ViewMenu from './viewMenu.js' +import LayoutMenu from './layoutMenu.js' +import { View } from '../data/cozy-data.js' + +export default class MenuBar extends Component { + constructor() { + super() + this.state = { + open: null, + searchStdoutRegex: ".*", + } + } + + componentDidMount() { + this.globalClickListener = ev => this.handleGlobalClick(ev) + this.closeListener = () => this.setOpen(null) + window.addEventListener("blur", this.closeListener) + window.addEventListener("mousedown", this.globalClickListener) + } + + componentWillUnmount() { + window.removeEventListener("blur", this.closeListener) + window.removeEventListener("mousedown", this.globalClickListener) + } + + setOpen(open) { + this.setState({ open }) + } + + handleGlobalClick() { + if (this.state.open) { + this.setState({ open: null }) + } + } + + handleLocalClick(ev) { + if (this.state.open) { + ev.stopPropagation() + } + } + + resetLayout(layout, view) { + this.props.resetLayout(layout, view) + this.setOpen(null) + } + + saveFile(data) { + const filename = prompt("please provide a filename") + + var blob = new Blob([data], { type: 'text/json' }), + a = document.createElement('a') + + a.download = filename + a.href = window.URL.createObjectURL(blob) + a.dataset.downloadurl = ['text/json', a.download, a.href].join(':') + a.dispatchEvent(new MouseEvent("click")) + } + + openReport() { + this.reportWindow = open() + if (!this.reportWindow) { + alert("couldn't open report - double check that cozy has permission to open new windows in your popup-blocker") + } + render(html`<${Report} + data=${this.props.getReportInterface()} + window=${this.reportWindow}/>`, + this.reportWindow.document.body) + } + + render(props, state) { + const enabled = props.status === Status.idle + return html`` + } +} diff --git a/cozy-viz/components/pruneMenu.js b/cozy-viz/components/pruneMenu.js new file mode 100644 index 0000000..d384e45 --- /dev/null +++ b/cozy-viz/components/pruneMenu.js @@ -0,0 +1,175 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import { removeBranch } from '../util/graph-tidy.js'; +import Menu from './menu.js' + +function noMemoryDiffs(leaf, other) { + const comparison = leaf.data().compatibilities[other.id()] + if (Object.keys(comparison.memdiff).length) return false + else return noErrors(leaf, other) +} + +function noRegisterDiffs(leaf, other) { + const comparison = leaf.data().compatibilities[other.id()] + if (Object.keys(comparison.regdiff).length) return false + else return noErrors(leaf, other) +} + +function noErrors(leaf, other) { + if (leaf.data().error || other.data().error) return false + else return true +} + +function noStdDiffs(leaf, other) { + if (leaf.data().stdout != other.data().stdout || + leaf.data().stderr != other.data().stderr) return false + else return noErrors(leaf, other) +} + +// this test preserves any pair where both sides aren't checked. So it removes +// checked notes, and removes nodes all of whose partners are checked. +function reviewed(leaf, other) { + if (!leaf.data("checked") && !other.data("checked")) return false + else return true +} + +function equivConstraints(leaf, other) { + const leftOnlyConcretions = Object.entries(leaf.data().compatibilities).flatMap( + ([key, compat]) => key == leaf.id() ? [] : compat.conc_args + ) + const rightOnlyConcretions = Object.entries(other.data().compatibilities).flatMap( + ([key, compat]) => key == other.id() ? [] : compat.conc_args + ) + return (rightOnlyConcretions.length + leftOnlyConcretions.length == 0) +} + +const matchRegex = (regexStrs) => (leaf, other) => { + const regexes = [] + try { + for (const regexStr of regexStrs.split('||')) { + regexes.push(new RegExp(regexStr)) + } + } catch (e) { + if (matchRegex.debounce) return + matchRegex.debounce = true + alert("Unreadable Regular Expression") + setTimeout(() => matchRegex.debounce = false, 500) + } + for (const regex of regexes) { + if ( + leaf.data().stdout.match(regex) && + other.data().stdout.match(regex) + ) { + return noErrors(leaf, other) + } + } + return false +} + +export default class PruneMenu extends Component { + constructor() { + super() + this.state = { + pruningMemory: false, + pruningStdout: false, + pruningRegisters: false, + pruningCorrect: false, + pruningDoRegex: false, + pruningChecked: false, + pruningEquivConstraints: false, + pruningRegex: ".*", + awaitingPrune: null, + } + this.prune.bind(this) + } + + // prune all branches whose compatibilities all fail some test (e.g. all have + // the same memory contents as the given branch) + prune(test) { + const leaves1 = this.props.cyLeft.cy.nodes().leaves() + const leaves2 = this.props.cyRight.cy.nodes().leaves() + for (const leaf of [...leaves1, ...leaves2]) { + let flag = true + let other = leaf.cy() == this.props.cyLeft.cy ? this.props.cyRight.cy : this.props.cyLeft.cy + for (const key in leaf.data().compatibilities) { + const otherleaf = other.nodes(`#${key}`) + if (otherleaf.length == 0) continue + flag &&= test(leaf, otherleaf) + } + if (flag) removeBranch(leaf) + } + this.props.cyLeft.cy.refocus() + this.props.cyRight.cy.refocus() + } + + setPrune(update) { + this.setState(update, () => { + // we retidy before we set a pruning level to get a clean slate, in case + // we're actually removing some pruning + this.props.viewMenu.current.retidy() + this.props.refreshLayout() + this.doPrune() + }) + } + + doPrune() { + // true means "prune" + let test = () => false + + // So the more tests are added disjunctively, the more branches will be pruned + const extendTest = (f, g) => (l, r) => f(l, r) || g(l, r) + + if (this.state.pruningMemory) test = extendTest(noMemoryDiffs, test) + if (this.state.pruningStdout) test = extendTest(noStdDiffs, test) + if (this.state.pruningEquivConstraints) test = extendTest(equivConstraints, test) + if (this.state.pruningRegisters) test = extendTest(noRegisterDiffs, test) + if (this.state.pruningCorrect) test = extendTest(noErrors, test) + if (this.state.pruningChecked) test = extendTest(reviewed, test) + if (this.state.pruningDoRegex) test = extendTest(matchRegex(this.state.pruningRegex), test) + + this.prune(test) + } + + debounceRegex(e) { + this.setState({ pruningRegex: e.target.value }) + if (this.state.pruningDoRegex) { + this.setState({ awaitingPrune: true }) + clearTimeout(this.regexDebounceTimeout) + this.regexDebounceTimeout = setTimeout(() => this.setPrune({ awaitingPrune: null }), 500) + } + } + + render(props, state) { + return html`<${Menu} + enabled=${props.enabled} + open=${props.open} + title="Prune" + setOpen=${o => props.setOpen(o)}> + <${Menu.Option} onClick=${() => this.setPrune({ pruningMemory: !state.pruningMemory })}> + Identical Memory + + <${Menu.Option} onClick=${() => this.setPrune({ pruningRegisters: !state.pruningRegisters })}> + Identical Register Contents + + <${Menu.Option} onClick=${() => this.setPrune({ pruningStdout: !state.pruningStdout })}> + Identical Stdout/Stderr + + <${Menu.Option} onClick=${() => this.setPrune({ pruningCorrect: !state.pruningCorrect })}> + Error-free + + <${Menu.Option} onClick=${() => this.setPrune({ pruningEquivConstraints: !state.pruningEquivConstraints })}> + Equivalent Constraints + + <${Menu.Option} onClick=${() => this.setPrune({ pruningChecked: !state.pruningChecked })}> + Reviewed + + <${Menu.Option} onClick=${() => this.setPrune({ pruningDoRegex: !state.pruningDoRegex })}> + Both Stdout Matching e.stopPropagation()} + onInput=${e => this.debounceRegex(e)} + value=${state.pruningRegex}/> + + ` + } +} diff --git a/cozy-viz/components/registerDifference.js b/cozy-viz/components/registerDifference.js new file mode 100644 index 0000000..058af5f --- /dev/null +++ b/cozy-viz/components/registerDifference.js @@ -0,0 +1,36 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import ConcretionSelector from './concretionSelector.js' + +export default class RegisterDifference extends Component { + + constructor() { + super(); + this.state = { view: "symbolic" } + } + + render(props, state) { + const rightId = props.rightFocus.bot.id() + const registers = [] + const conc_regdiffs = props.leftFocus.bot.data().compatibilities[rightId].conc_regdiff ?? [] + const rdiffs = state.view === "symbolic" + ? props.leftFocus.bot.data().compatibilities[rightId].regdiff + : conc_regdiffs[state.view] + for (const reg in rdiffs) { + registers.push(html` + ${rdiffs[reg][0]} + ${reg} + ${rdiffs[reg][1]}`) + } + return html`
+ <${ConcretionSelector} + view=${state.view} + setView=${view => this.setState({ view })} + concretionCount=${conc_regdiffs.length}/> +
${registers.length > 0 + ? registers + : html`no register differences detected ✓` + }
` + } +} + diff --git a/cozy-viz/components/report.js b/cozy-viz/components/report.js new file mode 100644 index 0000000..4b4c2b7 --- /dev/null +++ b/cozy-viz/components/report.js @@ -0,0 +1,218 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import Colors from "../data/colors.js" + +class NodeBadge extends Component { + + badgeStyle(color) { + return { + background: color, + color: "white", + fontWeight: "bold", + padding: "5px 10px 3px 10px", + //less padding at the bottom because there's already naturally some space there for descenders. + borderRadius: "25px", + printColorAdjust: "exact" + } + } + + render(props) { + const node = props.node + if (node.data("error")) { + return html`Error:` + } else if (node.data("assertion_info")) { + return html`Assertion:` + } else if (node.data("postcondition_info")) { + return html`Postcondition:` + } else if (node.data("spinning")) { + return html`Error:` + } + } +} + +class BranchData extends Component { + + getData() { + return this.props.node.data("error") || + this.props.node.data("assertion_info") || + this.props.node.data("postcondition_info") || + (this.props.node.data("spinning") && "Loop bounds exceeded") || + null + } + + render(props) { + const data = this.getData() + if (!data) return + + return html`
+ <${NodeBadge} node=${props.node}/> ${data} +
` + } +} + +class ReportField extends Component { + onMouseEnter() { + this.props.panel.cy.dim() + this.props.panel.cy.highlight(this.props.leaf) + } + + onMouseLeave() { + this.props.panel.cy.dim() + } + + onCheck(e) { + const cy = this.props.panel.cy + // pasing the ID rather than the node is necessary, since the graph may have + // been regenerated, and the leaf attached to the report may no longer be + // attached to the cytoscape graph + if (e.target.checked) { + this.props.setStatus("complete") + cy.addCheckMark(this.props.leaf.id()) + } else { + this.props.setStatus(undefined) + cy.removeCheckMark(this.props.leaf.id()) + } + this.props.refreshPrune() + } + + onClick() { + this.props.focus() + } + + render(props) { + const num = props.index + 1 + return html`
+

this.onMouseEnter()} + onMouseLeave=${() => this.onMouseLeave()} + onClick=${() => this.onClick()} + > + Path ${num} +

+ <${BranchData} node=${props.leaf}/> +
+
+ +
+
+ + this.onCheck(e)} name="reviewed-check"> +
+
+
` + } +} + +class ReportStatus extends Component { + render(props) { + return html`
+

Review Coverage: ${Math.floor(props.value * 100 / props.max)}%

+ +
` + } +} + +class ReportContents extends Component { + differenceBullets() { + const bullets = [] + const pruningStatus = this.props.pruningStatus + if (pruningStatus.pruningMemory) { + bullets.push(html`
  • differ with respect to final memory contents
  • `) + } + if (pruningStatus.pruningStdout) { + bullets.push(html`
  • differ with respect to stdout behavior
  • `) + } + if (pruningStatus.pruningRegisters) { + bullets.push(html`
  • differ with respect to final register contents
  • `) + } + if (pruningStatus.pruningEquivConstraints) { + bullets.push(html`
  • differ with respect to final constraints
  • `) + } + if (pruningStatus.pruningCorrect) { + bullets.push(html`
  • have an error, or correspond to an erroring branch
  • `) + } + if (pruningStatus.pruningDoRegex) { + bullets.push(html`
  • don't match the regex ${pruningStatus.pruningRegex}, + or correspond to a branch that doesn't match this regex
  • `) + } + return bullets + } + + render(props) { + const bullets = this.differenceBullets() + return html` +

    Summary:

    +

    Comparing + ${props.prelabel} + and + ${props.postlabel}. +

    + ${bullets.length > 0 && + html`

    Limiting attention to branches that:

      ${bullets}

    ` + }` + } +} + +export default class Report extends Component { + + constructor(props) { + super() + this.state = { + branchStatuses: {}, + } + const panel = props.data.leftPanelRef + this.leaves = [...panel.cy.nodes().leaves()] + } + + componentDidMount() { + const reportStyle = this.props.window.document.createElement("link") + reportStyle.setAttribute("rel", "stylesheet") + const loc = window.location + reportStyle.setAttribute("href", `${loc.origin}${loc.pathname}/report.css`) + this.props.window.document.head.appendChild(reportStyle) + } + + getReportFields() { + const panel = this.props.data.leftPanelRef + return this.leaves.map((leaf, idx) => { + return html`<${ReportField} + setStatus=${(status) => this.setBranchStatus(idx, status)} + leaf=${leaf} + focus=${() => this.props.data.focusLeafById(leaf.id())} + panel=${panel} + refreshPrune=${this.props.data.refreshPrune} + index=${idx}/>` + }) + } + + setBranchStatus(idx, status) { + this.setState(oldState => ({ branchStatuses: { ...oldState.branchStatuses, [idx]: status } })) + } + + getProgress() { + return Object + .values(this.state.branchStatuses) + .filter((status) => status == "complete") + .length + } + + render(props) { + const fields = this.getReportFields() + const progress = this.getProgress() + return html`
    +
    +

    Cozy Report

    +
    + <${ReportContents} + prelabel=${props.data.prelabel} + postlabel=${props.data.postlabel} + pruningStatus=${props.data.pruningStatus} + /> + <${ReportStatus} value=${progress} max=${fields.length} /> +
    + ${fields} +
    +
    ` + } +} + diff --git a/cozy-viz/components/searchInput.js b/cozy-viz/components/searchInput.js new file mode 100644 index 0000000..c71d4ae --- /dev/null +++ b/cozy-viz/components/searchInput.js @@ -0,0 +1,8 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' + +export default function SearchInput({ value, onInput }) { + return html`
    + + +
    ` +} diff --git a/cozy-viz/components/searchMenu.js b/cozy-viz/components/searchMenu.js new file mode 100644 index 0000000..bb4cf89 --- /dev/null +++ b/cozy-viz/components/searchMenu.js @@ -0,0 +1,65 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import Menu from './menu.js' + +export default class SearchMenu extends Component { + constructor() { + super() + this.state = { + searchStdoutRegex: "", + } + } + + updateSearch(e) { + if (e.target.value == '') this.clearSearch() + else { + this.setState({ searchStdoutRegex: e.target.value }, () => { + const cyLeft = this.props.cyLeft.cy + const cyRight = this.props.cyRight.cy + cyLeft.dim() + cyRight.dim() + let regex + try { + regex = new RegExp(this.state.searchStdoutRegex) + } catch (e) { + return + } + const ltargets = cyLeft.nodes() + .filter(node => node.data().stdout.match(regex)) + const rtargets = cyRight.nodes() + .filter(node => node.data().stdout.match(regex)) + cyLeft.highlight(ltargets) + cyRight.highlight(rtargets) + }) + } + } + + clearSearch() { + this.setState({ searchStdoutRegex: '' }, () => { + const cyLeft = this.props.cyLeft.cy + const cyRight = this.props.cyRight.cy + cyLeft.dim() + cyRight.dim() + }) + this.props.setOpen(null) + } + + render(props, state) { + return html`<${Menu} + enabled=${props.enabled} + open=${props.open} + title="Search" + setOpen=${o => props.setOpen(o)}> + <${Menu.Option} onClick=${() => props.setOpen(null)}> + Stdout e.stopPropagation()} + onInput=${e => this.updateSearch(e)} + value=${state.searchStdoutRegex}/> + + <${Menu.Option} onClick=${() => this.clearSearch(null)}> + Clear Search + + ` + } +} diff --git a/cozy-viz/components/sideEffectDifference.js b/cozy-viz/components/sideEffectDifference.js new file mode 100644 index 0000000..7ddb3bc --- /dev/null +++ b/cozy-viz/components/sideEffectDifference.js @@ -0,0 +1,137 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' +import ConcretionSelector from './concretionSelector.js' +import LineDiffView from './lineDiffView.js' +import * as Diff from 'https://cdn.jsdelivr.net/npm/diff@5.1.0/+esm' + +export default class SideEffectDifference extends Component { + constructor() { + super(); + this.state = { view: 0 } + } + + diffableSideEffects(effects, presence) { + + let contents = "" + let msg = "" + let effectIdx = 0 + const lines = [] + const ids = [] + const msgs = [] + + for (const isPresent of presence) { + if (isPresent) { + contents += effects[effectIdx].body + '\n' + lines.push(effects[effectIdx].body) + ids.push(effects[effectIdx].id) + effectIdx++ + } else { + contents += '\n' + lines.push("") + ids.push(null) + } + msgs.push(msg) + } + + return { contents, lines, ids, msgs } + } + + handleSymbolicDiff(symbolicDiff) { + const rslt = {} + for (const channel in symbolicDiff) { + const lines = {} + lines.left = symbolicDiff[channel].map(([x,]) => ({ body: x })) + lines.right = symbolicDiff[channel].map(([, x]) => ({ body: x })) + rslt[channel] = lines + } + return rslt + } + + highlightNodes(idLeft, idRight) { + const cyLeft = this.props.leftFocus.cy() + const cyRight = this.props.rightFocus.cy() + cyLeft.highlight(cyLeft.nodes(`#${idLeft}, [mergedIds*='#${idLeft}#']`)) + cyRight.highlight(cyRight.nodes(`#${idRight}, [mergedIds*='#${idRight}#']`)) + } + + dimAll() { + this.props.leftFocus.cy().dim() + this.props.rightFocus.cy().dim() + } + + diffWords(leftLine, rightLine) { + + const diffs = Diff.diffWords(leftLine, rightLine) + const newLeft = [] + const newRight = [] + + for (const diff of diffs) { + if (diff?.added) { + newRight.push(html`${diff.value}`) + } + else if (diff?.removed) { + newLeft.push(html`${diff.value}`) + } else { + newRight.push(html`${diff.value}`) + newLeft.push(html`${diff.value}`) + } + } + + return [newLeft, newRight] + } + + render(props, state) { + + const rightId = props.rightFocus.bot.id() + const concretions = props.leftFocus.bot.data().compatibilities[rightId].conc_sediff ?? [] + const symbolicDiff = props.leftFocus.bot.data().compatibilities[rightId].sediff ?? {} + const chandivs = [] + const replacer = (_, s) => s == "leafNeq" ? "These constraints are not equivalent" + : s == "fieldEq" ? "The remaining constrants shown here are equivalent" + : s + if (state.view == "symbolic") { + for (const channel in symbolicDiff) { + const chandiv = html`
    +

    ${channel}

    + ${symbolicDiff[channel].map(([, , x]) => html`
    ${JSON.stringify(x, replacer, 2)}
    `)} +
    ` + chandivs.push(chandiv) + } + } else { + + // Note, line-diffing is handled on the python side, because of the + // complexity of diffing non-concrete side effects. Hence, we have + // a trivial comparator here. + const sediffs = concretions[state.view] + for (const channel in sediffs) { + if (!(channel in symbolicDiff)) continue + const chandiv = html`
    +

    ${channel}

    + <${LineDiffView} + leftLines=${this.diffableSideEffects( + sediffs[channel].left, + symbolicDiff[channel].map(([x,]) => x) + )} + rightLines=${this.diffableSideEffects( + sediffs[channel].right, + symbolicDiff[channel].map(([, y]) => y) + )} + diffWords=${(l, r) => this.diffWords(l, r)} + comparator=${() => true} + highlight=${(idLeft, idRight) => this.highlightNodes(idLeft, idRight)} + dim=${() => this.dimAll()} + /> +
    ` + chandivs.push(chandiv) + } + } + + return html`
    + <${ConcretionSelector} + view=${state.view} + setView=${view => this.setState({ view })} + concretionCount=${concretions.length}/> + ${chandivs} +
    ` + } +} diff --git a/cozy-viz/components/tooltip.js b/cozy-viz/components/tooltip.js new file mode 100644 index 0000000..c4f077b --- /dev/null +++ b/cozy-viz/components/tooltip.js @@ -0,0 +1,223 @@ +import { computePosition, flip } from 'https://cdn.jsdelivr.net/npm/@floating-ui/dom@1.5.1/+esm'; +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Component } from 'https://unpkg.com/preact@latest?module' + +export default class Tooltip extends Component { + constructor() { + super(); + this.state = { + style : { + visibility:"hidden", + left: 0, + top: 0, + zIndex: 0, + }, + mode : null + } + } + + attachTo(cyNode) { + this.setState({node : cyNode}, () => this.refreshPosition()); + } + + refreshPosition() { + const {x, y} = this.state.node + .cy().container().getBoundingClientRect() + + const tooltip = this.base; + + const theNode = this.state.node + + const virtualElt = { + getBoundingClientRect() { + const bbox = theNode.renderedBoundingBox(); + return { + x: bbox.x1 + x, + y: bbox.y1 + y, + top: bbox.y1 + y, + left: bbox.x1 + x, + bottom: bbox.y2 + y, + right: bbox.x2 + x, + height: bbox.h, + width: bbox.w + } + } + }; + + computePosition(virtualElt, tooltip, { + placement: "right-end", + middleware: [flip()] + }).then(({x,y, placement}) => + this.positionAt(x,y, placement) + ) + } + + positionAt(x,y, placement) { + this.setState({ + style : { + flexDirection: placement.slice(-3) == "end" + ? "column-reverse" + : "column" + , + visibility:"visible", + left: `${x}px`, + top: `${y}px`, + zIndex: 5, + }}) + } + + getViewData() { + switch (this.state.mode) { + case "constraints" : { + return html`
    ${
    +            this.state.node?.data().constraints?.map?.(
    +              c => html`
    ${c}
    ` + )}
    ` + } + case "assembly" : { + return html`
    ${this.state.node.data().contents}
    ` + } + case "vex" : { + return html`
    ${this.state.node.data().vex}
    ` + } + case "errors" : { + if (this.state.node.data().error) { + return html`
    ${this.state.node.data().error}
    ` + } else if (this.state.node.data().spinning) { + return html`
    Spinning: Loop bounds exceeded
    ` + } else { + return null + } + } + case "stdout" : { + if (this.state.node.data().stdout) { + return html`
    ${this.state.node.data().stdout}
    ` + } else { + return null + } + } + case "stderr" : { + if (this.state.node.data().stderr) { + return html`
    ${this.state.node.data().stderr}
    ` + } else { + return null + } + } + case "simprocs" : { + return html`
    ${
    +          this.state.node?.data().simprocs?.map?.(
    +            simproc => html`
    ${simproc}
    ` + )}
    ` + } + case "assertion" : { + if (this.state.node?.data().assertion_info) { + return html` +
    ${this.state.node?.data().assertion_info}
    +
    +          Condition: ${this.state.node?.data().failed_cond}
    + Address: ${this.state.node?.data().assertion_addr} +
    + ` + } else { + return null + } + } + case "postcondition" : { + if (this.state.node?.data().postcondition_info) { + return html` +
    ${this.state.node?.data().postcondition_info}
    +
    +          Condition: ${this.state.node?.data().failed_cond}
    +
    + ` + } else { + return null + } + } + default: return null; + } + } + + setView(mode) { + this.setState({mode}, () => this.refreshPosition()) + } + + clearTooltip() { + this.setState({style : { + visibility:"hidden", + left: 0, + top: 0, + zIndex: 0, + }}) + } + + render(_props,state) { + return html`
    +
    + + ${this.state.node?.data().constraints && html` + ` + } + ${this.state.node?.data().vex && html` + ` + } + ${(this.state.node?.data().error || + this.state.node?.data().spinning) && html` + ` + } + ${this.state.node?.data().stdout && html` + ` + } + ${this.state.node?.data().stderr && html` + ` + } + ${this.state.node?.data().simprocs?.length > 0 && html` + ` + } + ${this.state.node?.data().assertion_info && html` + ` + } + ${this.state.node?.data().postcondition_info && html` + ` + } +
    +
    ${this.getViewData()}
    +
    ` + } +} diff --git a/cozy-viz/components/viewMenu.js b/cozy-viz/components/viewMenu.js new file mode 100644 index 0000000..c1c8e6a --- /dev/null +++ b/cozy-viz/components/viewMenu.js @@ -0,0 +1,145 @@ +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { Tidiness } from '../data/cozy-data.js' +import { Component } from 'https://unpkg.com/preact@latest?module' +import * as GraphStyle from '../util/graphStyle.js'; +import Menu from './menu.js' +import Colors from '../data/colors.js' + +function MenuBadge(props) { + return html` + + ` +} + +export default class ViewMenu extends Component { + constructor() { + super() + this.state = { + showingSyscalls: true, // we start with syscalls visible + showingSimprocs: true, // we start with SimProcedure calls visible + showingErrors: true, // we start with errors visible + showingAsserts: true, // we start with asserts visible + showingPostconditions: true, // we start with postconditions visible + tidiness: Tidiness.untidy, // we're not yet tidying anything + } + this.toggleErrors = this.toggleErrors.bind(this) + this.togglePostconditions = this.togglePostconditions.bind(this) + this.toggleView = this.toggleView.bind(this) + this.toggleSyscalls = this.toggleSyscalls.bind(this) + this.toggleSimprocs = this.toggleSimprocs.bind(this) + this.toggleAsserts = this.toggleAsserts.bind(this) + } + + componentDidUpdate(_prevProps, prevState) { + if (prevState.tidiness !== this.state.tidiness) { + // when we actually change tidiness, we need to clean up the layout + // afterwards and reapply any pruning + this.props.pruneMenu.current.doPrune() + this.props.refreshLayout() + } + } + + retidy() { + this.setTidiness(this.state.tidiness) + } + + setTidiness(tidiness) { + this.props.batch(() => { + this.props.cyLeft.cy.json({ elements: JSON.parse(this.props.cyLeft.orig).elements }) + this.props.cyRight.cy.json({ elements: JSON.parse(this.props.cyRight.orig).elements }) + // refocus all foci, and reset viewport + this.props.cyLeft.cy.nodes().map(node => node.ungrabify()) + this.props.cyRight.cy.nodes().map(node => node.ungrabify()) + // restore any checked nodes + this.props.cyLeft.cy.restoreCheckMarks() + + switch (tidiness) { + case Tidiness.untidy: break; + case Tidiness.tidy: this.tidy({}); break; + case Tidiness.veryTidy: this.tidy({ mergeConstraints: true }); break; + } + this.setState({ tidiness }, this.props.regenerateFocus) + }) + } + + tidy(opts) { + // merge similar nodes + this.props.cyLeft.cy.tidy(opts) + this.props.cyRight.cy.tidy(opts) + // remove all foci, and reset viewport + this.props.cyLeft.cy.refocus().fit() + this.props.cyRight.cy.refocus().fit() + } + + toggleView(type) { + this.setState(oldState => { + GraphStyle.settings[type] = !oldState[type]; + this.props.cyLeft.cy.style().update() + this.props.cyRight.cy.style().update() + return { + [type]: !oldState[type] + } + }) + } + + toggleSyscalls() { this.toggleView("showingSyscalls") } + + toggleSimprocs() { this.toggleView("showingSimprocs") } + + toggleErrors() { this.toggleView("showingErrors") } + + toggleAsserts() { this.toggleView("showingAsserts") } + + togglePostconditions() { this.toggleView("showingPostconditions") } + + render(props, state) { + return html`<${Menu} + enabled=${props.enabled} + open=${props.open} + title="View" + setOpen=${o => props.setOpen(o)}> + <${Menu.Option} + onClick=${() => state.tidiness !== Tidiness.untidy && this.setTidiness(Tidiness.untidy)} + selected=${state.tidiness == Tidiness.untidy}> + Show All Blocks + + <${Menu.Option} + onClick=${() => state.tidiness !== Tidiness.tidy && this.setTidiness(Tidiness.tidy)} + selected=${state.tidiness == Tidiness.tidy}> + Merge Unless Constaints Change + + <${Menu.Option} + onClick=${() => state.tidiness !== Tidiness.veryTidy && this.setTidiness(Tidiness.veryTidy)} + selected=${state.tidiness == Tidiness.veryTidy}> + Merge Unless Branching Occurs + +
    + <${Menu.Option} + onClick=${this.toggleSyscalls} + selected=${state.showingSyscalls}> + <${MenuBadge} color=${Colors.focusedSyscallNode}/> Show Syscalls + + <${Menu.Option} + onClick=${this.toggleSimprocs} + selected=${state.showingSimprocs}> + <${MenuBadge} color=${Colors.focusedSimprocNode}/> Show SimProcedure calls + + <${Menu.Option} + onClick=${this.toggleErrors} + selected=${state.showingErrors}> + <${MenuBadge} color=${Colors.focusedErrorNode}/> Show Errors + + <${Menu.Option} + onClick=${this.toggleAsserts} + selected=${state.showingAsserts}> + <${MenuBadge} color=${Colors.focusedAssertNode}/> Show Asserts + + <${Menu.Option} + onClick=${this.togglePostconditions} + selected=${state.showingPostconditions}> + <${MenuBadge} color=${Colors.focusedPostconditionNode}/> Show Postcondition failures + + ` + } +} diff --git a/cozy-viz/cozy-viz.js b/cozy-viz/cozy-viz.js new file mode 100644 index 0000000..96cb302 --- /dev/null +++ b/cozy-viz/cozy-viz.js @@ -0,0 +1,5 @@ +import App from './components/app.js' +import { html } from 'https://unpkg.com/htm/preact/index.module.js?module' +import { render } from 'https://unpkg.com/preact@latest?module' + +render(html`<${App}/>`, document.body); diff --git a/cozy-viz/data/colors.js b/cozy-viz/data/colors.js new file mode 100644 index 0000000..56342cb --- /dev/null +++ b/cozy-viz/data/colors.js @@ -0,0 +1,26 @@ +const Colors = { + defaultNode: '#ccc', + focusedNode: '#666', + + defaultBorder: '#aaa', + + defaultEdge: '#aaa', + focusedEdge: '#666', + + syscallNode: '#add8e6', + focusedSyscallNode: "#00ade6", + + simprocNode: "#ade6b6", + focusedSimprocNode: "#4eb302", + + errorNode: "#facdcd", + focusedErrorNode: "#d00", + + assertNode: "#edcdfa", + focusedAssertNode: "#a600de", + + postconditionNode: '#f7be6d', + focusedPostconditionNode: "#f79000", +} + +export default Colors diff --git a/cozy-viz/data/cozy-data.js b/cozy-viz/data/cozy-data.js new file mode 100644 index 0000000..5f6541b --- /dev/null +++ b/cozy-viz/data/cozy-data.js @@ -0,0 +1,16 @@ +export const Status = Object.freeze({ + unloaded: Symbol("unloaded"), + idle: Symbol("idle"), + rendering: Symbol("rendering") +}) + +export const Tidiness = Object.freeze({ + untidy: Symbol("untidy"), + tidy: Symbol("tidy"), + veryTidy: Symbol("very-tidy") +}) + +export const View = Object.freeze({ + plain: Symbol("plain"), + cfg: Symbol("cfg") +}) diff --git a/cozy-viz/data/layouts.js b/cozy-viz/data/layouts.js new file mode 100644 index 0000000..f4bf51b --- /dev/null +++ b/cozy-viz/data/layouts.js @@ -0,0 +1,16 @@ +export const breadthFirst = { + name: 'breadthfirst', + directed: true, + spacingFactor: 2 +} + +export const cola = { + name: 'cola', +} + +export const cose = { + name: 'cose', + nodeRepulsion: function () { return 10000}, + idealEdgeLength: function(){ return 64 }, + edgeElasticity: function( ){ return 128; }, +} diff --git a/cozy-viz/index.css b/cozy-viz/index.css new file mode 100644 index 0000000..ed5fd1c --- /dev/null +++ b/cozy-viz/index.css @@ -0,0 +1,296 @@ +body { + padding: 0px; + margin: 0px; + max-width: 100vw; + max-height: 100vh; +} + +button { + cursor: pointer; + border: none; + background: none; + font-weight: bold; +} + +button:disabled { + cursor: not-allowed; +} + +#main-view { + display: flex; +} + +/* Menu Bar */ + +#menubar { + height: 2em; + width: 100vw; + border-bottom: solid black 1px; + display: flex; + align-items: center; +} + +#menubar button { + padding: .5em; +} + +#menubar .options-wrapper { + z-index: 5; + font-size: 10pt; + font-family: monospace; +} + +#menubar .options-wrapper .option { + padding: .25em .5em .25em .5em; +} + +#menubar .options-wrapper .option:hover { + color: white; + background: black; +} + +#menubar .options-wrapper .option[data-selected="true"] { + font-weight: bold; +} + +#menubar .options-wrapper .option[data-disabled="true"] { + color: #999999; +} + +#menubar input[data-awaiting="true"] { + color: #999999; +} + +/* Graph Panels */ + +#labelLeft, +#labelRight { + font-family: monospace; + margin: 10px; + font-size: 1.5em; + position: absolute; + z-index: 1; +} + +#labelRight { + right: 0px; +} + +#cy1 { + border-right: 1px dashed; +} + +#cy1, +#cy2 { + height: calc(100vh - 2em - 1px); + width: 50vw; +} + +.dragHover { + background: #c1c1c1 +} + +#tooltip { + display: flex; + flex-direction: column-reverse; + width: max-content; + position: absolute; + top: 0; + left: 0; + background: #222; + color: white; + font-weight: bold; + padding: 5px; + border-radius: 4px; + font-size: 90%; +} + +#tooltip button { + display: inline-block; + margin: .5em; + padding: .5em; + background-color: #555; + border-radius: 4px; + border: none; + color: white; + font-weight: bold; +} + +#tooltip button[data-highlighted="true"] { + background-color: white; + color: black; +} + +#tooltip button:disabled { + color: #999; +} + +#tooltip-data { + max-height: 50vh; + max-width: 400px; + overflow: scroll; +} + +#tooltip-data::-webkit-scrollbar { + display: none; +} + +/* Diff panel */ + +#action-diff, +#assembly-diff, +.side-effect-channel { + padding: 5px 1em 0vw 1em; + width: calc(100vw - 2em); +} + +.side-effect-channel h3 { + font-family: monospace; +} + +/* When a full line is added in a single hunk, we highlight to the end + * Maybe this should not be restricted to just the side effect channel? + */ +.side-effect-channel .hunkAdded:first-child:last-child, +.side-effect-channel .hunkRemoved:first-child:last-child { + display:block; +} + +.search-input { + border: 1px solid #ccc; + display: inline-block; + padding: 5px 5px 5px 0px; +} + +.search-input>input { + border: none; +} + +.search-input>svg { + height: 1.2em; + vertical-align: middle; +} + +#line-diff-data-view { + z-index: 2; +} + +#line-diff-data-view>div { + display: grid; + grid-template-columns: 1fr 1fr; + grid-column-gap: 5px; +} + +#line-diff-data-view>div>div { + max-width: 49vw; + overflow-x: hidden; + text-overflow: ellipsis; +} + +#diff-panel { + background: white; + position: absolute; + max-height: 50vh; + bottom: 0px; + overflow-y: scroll; + border-top: 1px solid black; + width: 100vw; + padding-bottom: .5em; +} + +#diff-panel.resizing { + overflow: hidden; +} + +#diff-drag-handle { + top: 0px; + position: sticky; + height: 5px; + cursor: grab; +} + +#diff-drag-handle.grabbed { + background: black; + cursor: grabbing; +} + +#diff-panel button { + margin: .5em; + padding: .5em; + background-color: #555; + border-radius: 4px; + border: none; + color: white; + font-weight: bold; +} + +#diff-panel .subordinate-buttons button { + padding: .2em; + background-color: white; + color: black +} + +#diff-panel .subordinate-buttons button[data-selected="true"] { + text-decoration: underline; + text-decoration-thickness: 2px; +} + +#diff-panel button:disabled { + color: #999; +} + +#grid-diff-data { + display: grid; + grid-template-columns: 1fr max-content 1fr; + grid-gap: 1em; + padding-bottom: 1em; +} + +#grid-diff-data .no-difference { + grid-column: 2/3; + padding: 1em; +} + +.grid-diff-label { + border: 1px solid black; + text-align: center; + font-weight: bold; + padding: 0 1em 0 1em; +} + +.grid-diff-left { + text-align: right; +} + +#concretion-header { + font-family: monospace; + margin-left: 1em; +} + +#concretion-data { + display: flex; + justify-content: center; +} + +#concretion-data>pre { + width: 500px; + padding: 1em; + margin: 1em; + border: 1px solid #999; +} + +.hunkAdded { + background-color: #c9ffc7 +} + +.hunkRemoved { + background-color: #facdcd +} + +#status-indicator { + position: absolute; + bottom: 0px; + right: 0px; + margin: .5em; + font-size: .75em; +} diff --git a/cozy-viz/index.html b/cozy-viz/index.html new file mode 100644 index 0000000..a5494b4 --- /dev/null +++ b/cozy-viz/index.html @@ -0,0 +1,22 @@ + + + + + + + + + + +
    +
    +
    + prepatch +
    +
    + postpatch +
    +
    +
    + + diff --git a/cozy-viz/util/checkedMixin.js b/cozy-viz/util/checkedMixin.js new file mode 100644 index 0000000..92873f9 --- /dev/null +++ b/cozy-viz/util/checkedMixin.js @@ -0,0 +1,25 @@ +export const checkedMixin = { + + checkedIds: new Set(), + + setCheckMarks(nodes) { + this.nodes().removeData("checked") + this.checkedIds = new Set([...nodes.map(node => node.id())]) + nodes.data("checked", true) + }, + + addCheckMark(id) { + this.checkedIds.add(id) + this.nodes(`#${id}`).data("checked", true) + }, + + removeCheckMark(id) { + this.checkedIds.delete(id) + this.nodes(`#${id}`).data("checked", false) //needed to force the cytoscape view to update + this.nodes(`#${id}`).removeData("checked") + }, + + restoreCheckMarks() { + this.setCheckMarks(this.filter(node => this.checkedIds.has(node.id()))) + } +} diff --git a/cozy-viz/util/constraints.js b/cozy-viz/util/constraints.js new file mode 100644 index 0000000..449c133 --- /dev/null +++ b/cozy-viz/util/constraints.js @@ -0,0 +1,7 @@ +export function constraintsEq(c1, c2) { + if (c1.length != c2.length) return false; + for (let i = 0; i < c1.length; i++) { + if (c1[i] != c2[i]) return false; + } + return true; +} diff --git a/cozy-viz/util/focusMixin.js b/cozy-viz/util/focusMixin.js new file mode 100644 index 0000000..249dbd6 --- /dev/null +++ b/cozy-viz/util/focusMixin.js @@ -0,0 +1,89 @@ +export const focusMixin = { + focus(loci) { + if (!loci) return; + + this.loci = loci; + + this.root = this.nodes().roots()[0] + + for (const locus of loci) { + if (locus.removed()) continue + if (loci.length > 1) { + locus.addClass('availablePath'); + } else { + locus.addClass('pathHighlight'); + } + + locus + .predecessors() + .addClass('pathHighlight'); + } + + return this + }, + + // focus a range of nodes, and set its lower tips to be the loci + focusRange(nodes) { + + this.elements().removeClass("pathHighlight") + this.elements().removeClass("availablePath") + + this.loci = nodes.filter( + ele => ele.outgoers("node").intersection(nodes).length == 0) + + this.root = nodes.filter( + ele => ele.incomers("node").intersection(nodes).length == 0) + + + nodes.addClass('pathHighlight') + + if (this.loci.length > 1) { + for (const locus of this.loci) { + locus.removeClass('pathHighlight'); + locus.addClass('availablePath'); + } + } else { + this.loci.addClass('pathHighlight'); + } + }, + + refocus() { + this.elements() + .removeClass('pathHighlight') + .removeClass('availablePath'); + + // a segment is focused, refocus it. + if (this.root?.incomers?.().length > 0 || this.loci?.outgoers?.().length > 0) { + // regenerate the range based on the locus, and focus that + this.focusRange(this.getRangeOf(this.loci)) + } else { + // either there's no focus or a full branch is focused + this.focus(this.loci) + } + + return this + }, + + highlight(nodes) { + nodes.addClass('temporaryFocus') + }, + + dim() { + this.elements().removeClass('temporaryFocus') + }, + + blur() { + this.loci = null + this.root = null + + this.elements() + .removeClass('pathHighlight') + .removeClass('availablePath'); + + this.elements() + .removeData('traversals') + + return this + } +} + diff --git a/cozy-viz/util/graph-tidy.js b/cozy-viz/util/graph-tidy.js new file mode 100644 index 0000000..dc205da --- /dev/null +++ b/cozy-viz/util/graph-tidy.js @@ -0,0 +1,215 @@ +import { constraintsEq } from './constraints.js' + + +//remove a branch by consuming its root and parents until you reach a parent +//that has more than one child +export function removeBranch(node) { + let target + while (node.outgoers('node').length == 0 && + node.incomers('node').length > 0) { + + target = node + node = node.incomers('node')[0] + target.remove() + } + if (target && + node.outgoers('node').length == 0 && + node.incomers('node').length == 0) { + node.remove() + } + +} + +export const tidyMixin = { + // array of graph elements merged out of existence + mergedNodes: [], + mergedEdges: [], + + // We try to tidy up a given graph by merging non-branching series of nodes + // into single nodes + tidy(opts) { + + const root = this.nodes().roots() + + this.tidyChildren(root, opts) + + }, + + tidyChildren(node, { mergeConstraints }) { + + let candidates = [node]; + let next = []; + + while (candidates.length > 0) { + for (const candidate of candidates) { + const out = candidate.outgoers('node') + const constraints1 = out[0]?.data().constraints + const constraints2 = candidate.data().constraints + // We merge nodes with their children if they have exactly one child and either + // A. the child has the same constraints, or + // B. we've enabled constraint merging + if (out.length == 1 && (mergeConstraints || constraintsEq(constraints1, constraints2))) { + // we accumulate the assembly, into the child + out[0].data().contents = candidate.data().contents + '\n' + out[0].data().contents + /// we accumulate vex, if there is any, into the child + if (candidate.data().vex) { + out[0].data().vex = candidate.data().vex + '\n' + out[0].data().vex + } + /// we accumulate has_syscall, if defined, into the child + if (candidate.data("has_syscall")) { + out[0].data().has_syscall |= candidate.data().has_syscall + } + /// we accumulate simprocs, if defined, into the child + if (candidate.data("simprocs")) { + out[0].data().simprocs.unshift(...candidate.data().simprocs) + } + if (candidate.data("actions")) { + if (out[0].outgoers('edge').length) for (const edge of out[0].outgoers('edge')) { + // we accoumulate actions into the child by putting them into its outgoing edges + edge.data('actions', candidate.outgoers('edge')[0].data('actions').concat(edge.data('actions'))) + } else { + // unless it has no outgoing edges, in which case we accumulate them into the node itelf + out[0].data('actions', candidate.outgoers('edge')[0].data('actions')) + } + } + // introduce edges linking the child to its grandparent + for (const parent of candidate.incomers('node')) { + const edgeData = { + id: `${parent.id()}-${out[0].id()}`, + source: parent.id(), + target: out[0].id(), + // we copy the relevant actions (those associated with the grandparent) into the new edge + actions: candidate.incomers('edge').data('actions') + } + node.cy().add({ group: 'edges', data: edgeData }) + } + // if the candidate is the root of a focused segment, the child becomes + // the new root + if (this.root?.id() == candidate.id()) { + this.root = out[0] + } + + // we remove the merge-candidate node + candidate.remove() + next.push(out[0]) + } else { + for (const baby of out) { + next.push(baby) + } + } + } + candidates = next + next = [] + } + }, + + + //merge blocks that share an address + mergeByAddress() { + const constructed = {} + this.mergedNodes = [] + this.mergedEdges = [] + for (const node of this.nodes()) { + this.tidyStdOut(node) + } + for (const node of this.nodes()) { + const addr = node.data().address + if (addr in constructed) { + // node is already represented + this.mergedNodes.push(node) + // so we mark it as merged + const priorStdout = constructed[addr].data('newStdout') + if (priorStdout.length > 0) { + constructed[addr].data('stdout', priorStdout + '\n--\n' + node.data('newStdout')) + } + constructed[addr].data('mergedIds', `${constructed[addr].data('mergedIds')}${node.id()}#`) + // and merge its stdout, and id with the representation + } else { + // node isn't represented + this.removePlainData(node) + node.data('stdout', node.data('newStdout')) + node.data('mergedIds', `#${node.id()}#`) + constructed[addr] = node + // so we touch it up a bit and add it to the CFG + } + if (node.hasClass('pathHighlight')) constructed[addr].data('traversed', true) + if (node.incomers().length == 0) constructed[addr].data('initial', true) + if (node.outgoers().length == 0) constructed[addr].data('terminal', true) + } + const startingEdges = [...this.edges()] + for (const edge of startingEdges) { + const sourceRepr = constructed[edge.source().data("address")] + const targetRepr = constructed[edge.target().data("address")] + if (edge.source() == sourceRepr && edge.target() == targetRepr) { + if (edge.hasClass("pathHighlight")) { + edge.data("traversals", (edge.data("traversals") || 0) + 1) + } + edge.data('mergedIds', `#${edge.id()}#`) + } else { + if (sourceRepr.edgesTo(targetRepr).length > 0) { + if (edge.hasClass("pathHighlight")) { + const traversals = sourceRepr.edgesTo(targetRepr)[0].data("traversals") + sourceRepr.edgesTo(targetRepr)[0] + .data("traversals", (traversals || 0) + 1) + .data("mergedIds", `${sourceRepr.edgesTo(targetRepr)[0].data('mergedIds')}${edge.id()}#`) + } + } else { + this.add({ + group: 'edges', + data: { + source: sourceRepr.id(), + target: targetRepr.id(), + mergedIds: `#${edge.id()}#`, + traversals: edge.hasClass("pathHighlight") ? 1 : 0 + } + }) + } + this.mergedEdges.push(edge) + } + } + for (const element of [...this.mergedNodes, ...this.mergedEdges]) { + element.remove() + } + // this kinda mangles the styles, so we refresh them + this.style().update() + }, + + // remove data that doesn't make sense in the CFG context + removePlainData(node) { + node.removeData('constraints') + node.removeData('stdout') + node.removeData('stderr') + }, + + // take a node from a tree, and derive what is *new* at that node + tidyStdOut(node) { + if (node.incomers('node').length == 1) { + const incomerStdout = node.incomers('node')[0].data('stdout') + node.data('newStdout', node.data('stdout').slice(incomerStdout.length, Infinity)) + } else { + node.data('newStdout', node.data('stdout')) + } + }, + + // tidy extraneous data added to existing elements by merging. Constructed + // nodes are removed automatically. + removeCFGData() { + let element + while (element = this.mergedNodes.pop()) { + element.restore() + } + while (element = this.mergedEdges.pop()) { + element.restore() + } + for (const node of this.nodes()) { + node.removeData("traversed") + node.removeData("initial") + node.removeData("terminal") + node.removeData("mergedIds") + } + for (const edge of this.edges()) { + edge.removeData("traversals") + edge.removeData("mergedIds") + } + } +} diff --git a/cozy-viz/util/graphStyle.js b/cozy-viz/util/graphStyle.js new file mode 100644 index 0000000..e5b2841 --- /dev/null +++ b/cozy-viz/util/graphStyle.js @@ -0,0 +1,184 @@ +import Colors from "../data/colors.js" + +export const settings = { + showingSimprocs: true, + showingSyscalls: true, + showingErrors: true, + showingAsserts: true, + showingPostconditions: true, +} + +//Cytoscape doesn't have specificity - last matching selector wins. +// +//There's a bit more logic here depending on that rule than would be ideal, but +//it seems like it's best for performance to bake everything into the +//stylesheet rather than trying to change the styling rules on the fly +export const style = [ + { + selector: "node", + style: { + 'shape': 'round-rectangle', + 'background-color': Colors.defaultNode, + 'border-color': Colors.defaultBorder, + }, + }, + { + selector: "[[outdegree = 0]], node[?terminal]", + style: { 'border-width': '5px' }, + }, + { + selector: "node[?initial]", + style: { 'border-width': '5px' }, + }, + { + selector: 'edge', + style: { + 'width': 3, + 'line-color': Colors.defaultEdge, + 'target-arrow-color': Colors.defaultEdge, + 'target-arrow-shape': 'triangle', + 'arrow-scale': 1.5, + 'source-distance-from-node':'5px', + 'target-distance-from-node':'5px', + 'curve-style': 'bezier' + } + }, + { + selector: 'edge.pathHighlight, edge[traversals > 0]', + style: { + 'width': 3, + 'line-color': Colors.focusedEdge, + 'target-arrow-color': Colors.focusedEdge, + 'z-compound-depth' : 'top', + } + }, + { + selector: 'node.pathHighlight, node[?traversed]', + style: { + 'background-color': Colors.focusedNode, + 'z-compound-depth' : 'top', + } + }, + { + selector: 'node[?has_syscall]', + style: { 'background-color': () => settings.showingSyscalls + ? Colors.syscallNode + : Colors.defaultNode + } + }, + { + selector: 'node.pathHighlight[?has_syscall]', + style: { 'background-color': () => settings.showingSyscalls + ? Colors.syscallNode + : Colors.focusedNode + } + }, + { + selector: 'node[simprocs.length > 0]', + style: { 'background-color': () => settings.showingSimprocs + ? Colors.simprocNode + : Colors.defaultNode + } + }, + { + selector: 'node.pathHighlight[simprocs.length > 0]', + style: { 'background-color': () => settings.showingSimprocs + ? Colors.simprocNode + : Colors.focusedNode + } + }, + { selector: "node.temporaryFocus", + style: { + 'underlay-color': '#708090', + 'underlay-opacity': 0.5, + } + }, + { + selector: 'node[?assertion_info]', + style: { 'background-color': () => settings.showingAsserts + ? Colors.assertNode + : Colors.defaultNode + } + }, + { + selector: 'node.pathHighlight[?assertion_info]', + style: { + 'border-width':'0px', + 'background-color': () => settings.showingAsserts + ? Colors.focusedAssertNode + : Colors.focusedNode + } + }, + { + selector: 'node[?postcondition_info]', + style: { 'background-color': () => settings.showingPostconditions + ? Colors.postconditionNode + : Colors.defaultNode + } + }, + { + selector: 'node.pathHighlight[?postcondition_info]', + style: { + 'border-width':'0px', + 'background-color': () => settings.showingPostconditions + ? Colors.focusedPostconditionNode + : Colors.focusedNode + } + }, + { + selector: 'node[?error]', + style: { + 'border-width':'0px', + 'background-color': () => settings.showingErrors + ? Colors.errorNode + : Colors.defaultNode + } + }, + { + selector: 'node[?spinning]', + style: { + 'shape' : 'vee', + 'width' : 50, + 'height' : 50, + 'background-color': () => settings.showingErrors + ? Colors.errorNode + : Colors.defaultNode + } + }, + { + selector: 'node.pathHighlight[?error],node.pathHighlight[?spinning]', + style: { + 'border-width':'0px', + 'background-color': () => settings.showingErrors + ? Colors.focusedErrorNode + : Colors.focusedNode + } + }, + { + selector: 'node.availablePath', + style: { + 'border-width':'12px', + 'width' : 25, + 'height' : 25, + 'border-color': Colors.focusedNode, + 'underlay-padding':"15px" + } + }, + { + //we don't display checks in CFG mode, since we don't really have + //meaningful branches there. + selector: 'node[?checked][^mergedIds]', + style: { + 'label':'×', + 'font-size':'36px', + 'text-halign':'center', + 'text-valign':'center' + } + }, + { + selector: 'node.pathHighlight[?checked]', + style: { + "color":"white" + } + } +] diff --git a/cozy-viz/util/segmentation.js b/cozy-viz/util/segmentation.js new file mode 100644 index 0000000..d2254f6 --- /dev/null +++ b/cozy-viz/util/segmentation.js @@ -0,0 +1,43 @@ +import cytoscape from "https://cdn.jsdelivr.net/npm/cytoscape@3.26.0/+esm" + +// given the top and bottom nodes, return a list of nodes in a segment +export function getNodesFromEnds(top, bottom) { + const interval = [ bottom ] + while (interval[ interval.length - 1 ].id() !== top.id()) { + interval.push(interval[interval.length - 1].incomers("node")[0]) + } + return interval +} + +// given the top and bottom nodes, return a list of edges in a segment +export function getEdgesFromEnds(top, bottom) { + const nodes = getNodesFromEnds(top, bottom) + nodes.pop() + return nodes.map(node => node.incomers("edge")[0]) +} + +// Given a top and bottom node, construct a new segment (cloning the original +// range), including edges +export class Segment { + constructor(top, bot) { + this.cy = cytoscape({ + elements: + bot.predecessors() + .intersection(top.successors()) + .union(top) + .union(bot) + .jsons() + }) + + + this.top = this.cy.nodes().roots()[0] + this.bot = this.cy.nodes().leaves()[0] + this.cy = () => bot.cy() + } + + static fromRange(range) { + const bot = range.filter(ele => ele.outgoers("node").intersection(range).length == 0)[0] + const top = range.filter(ele => ele.incomers("node").intersection(range).length == 0)[0] + return new Segment(top, bot) + } +} diff --git a/cozy-viz/util/segmentationMixin.js b/cozy-viz/util/segmentationMixin.js new file mode 100644 index 0000000..2b76a5b --- /dev/null +++ b/cozy-viz/util/segmentationMixin.js @@ -0,0 +1,106 @@ +import {constraintsEq} from './constraints.js' + +export const segmentationMixin = { + + // XXX: It would be possible to memoize on constraints here, but that adds + // some complexity when the granularity of the graph changes + // + // The connector is an extra function that we can use to potentially link + // segments with different constraints under some conditions + getRangeOf(node, connector) { + const constraints = node.data().constraints + + // first we ascend to the highest node with the given constraints + let target = node + while (target.incomers('node').length == 1 && + constraintsEq(constraints, target.incomers('node')[0].data().constraints)) { + target = target.incomers('node')[0] + } + + // then we descend, assembling the relevant nodes + let generations = [[target]] + while (true) { + const lastGen = generations[generations.length - 1] + const nextGen = lastGen.flatMap(n => + n.outgoers('node') + .filter(outgoer => { + if (constraintsEq(outgoer.data().constraints, n.data().constraints)) return true + if (connector?.(outgoer, n)) return true + }) + .toArray() + ) + if (nextGen.length > 0) generations.push(nextGen) + else break + } + + generations = generations.flat() + + return this.collection(generations) + }, + + segmentToRange(segment) { + return segment.bot.predecessors('node') + .intersection(segment.top.successors('node')) + .union(segment.top) + .union(segment.bot) + }, + + rangeToSegment(range) { + return { + bot: range.filter(ele => ele.outgoers("node").intersection(range).length == 0)[0], + top: range.filter(ele => ele.incomers("node").intersection(range).length == 0)[0], + } + }, + + // this shows a generalized segment, in which we ignore additional + // constraints that don't narrow the pool of compatible nodes on the opposite + // side. + getCompatibilityRangeOf(node, cy) { + const connector = (n,o) => { + const ncompat = this.getLeavesCompatibleWith(n,cy) + const ocompat = this.getLeavesCompatibleWith(o,cy) + return ncompat.size == ocompat.size + } + return this.getRangeOf(node, connector) + }, + + // gets all leaves in cy compatible with a given node + // + // XXX : this should probably be disabled when pruning is in progress, it + // doesn't necessarily make sense once nodes have been removed. + getLeavesCompatibleWith(node, cy) { + const leaves = node.successors().add(node).leaves() + const ids = leaves.flatMap(leaf => Object.keys(leaf.data().compatibilities)) + .map(s => `#${s}`) + const compats = new Set() + for (const id of ids) { + compats.add(cy.$(id)[0]) + } + return compats + }, + + // in a preorder, find the greatest/lowest element p such that each element of leaves + // is > p; i.e the strongest set of constraints implied by the constraints on + // each member of leaves + getMinimalCeiling(leaves) { + let depth = 1; + const [canonicalLeaf] = leaves + const canonicalPreds = canonicalLeaf.predecessors('node') + // If there's only one leaf, it's the strongest thing that is implied by each member of leaves + if (leaves.size === 1) return canonicalLeaf + // Otherwise, we walk down the predecessors from the root of the tree until + // we hit a place where the predecessors of one of our leaves separates + // from our canonical list of predecessors or run out of predecessors + // (which happens if the fork is right above a leaf) and then back up one. + while (true) { + for (const leaf of leaves) { + // only look up once per loop. Could be much further optimized. + const preds = leaf.predecessors('node') + if (depth > preds.length || preds[preds.length - depth] !== canonicalPreds[canonicalPreds.length - depth]) { + return canonicalPreds[canonicalPreds.length - (depth - 1)] + } + } + depth += 1 + } + }, +} diff --git a/genindex.html b/genindex.html new file mode 100644 index 0000000..10092a8 --- /dev/null +++ b/genindex.html @@ -0,0 +1,1076 @@ + + + + + + + Index — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Index

    + +
    + _ + | A + | B + | C + | D + | E + | F + | G + | H + | I + | J + | L + | M + | N + | O + | P + | R + | S + | T + | U + | V + | W + +
    +

    _

    + + + +
    + +

    A

    + + + +
    + +

    B

    + + + +
    + +

    C

    + + + +
    + +

    D

    + + + +
    + +

    E

    + + + +
    + +

    F

    + + + +
    + +

    G

    + + + +
    + +

    H

    + + + +
    + +

    I

    + + + +
    + +

    J

    + + + +
    + +

    L

    + + + +
    + +

    M

    + + +
    + +

    N

    + + + +
    + +

    O

    + + + +
    + +

    P

    + + + +
    + +

    R

    + + + +
    + +

    S

    + + + +
    + +

    T

    + + + +
    + +

    U

    + + + +
    + +

    V

    + + + +
    + +

    W

    + + + +
    + + + +
    + +
    +
    + +
    +
    + + + + + + + \ No newline at end of file diff --git a/gettingstarted.html b/gettingstarted.html new file mode 100644 index 0000000..4f0c3bd --- /dev/null +++ b/gettingstarted.html @@ -0,0 +1,451 @@ + + + + + + + + Getting Started — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + +
    +

    Getting Started

    +

    On this page we will cover the architecture of cozy and how you can use +it to compare two binary programs. cozy is based on the angr symbolic +execution framework, so we support the same architectures as angr. We +will be following the null_deref example, which can be found in the +examples and test_programs folder in the cozy repository. The null_deref +source code is a very simple C program which writes the integer 42 to +some location in memory:

    +

    Prepatched null_deref (first program being compared):

    +
    #include <stdio.h>
    +
    +void my_fun(int *num) {
    +    *num = 42;
    +}
    +
    +int main(int argc, char *argv[]) {
    +    int my_num;
    +    my_fun(&my_num);
    +    printf("my_num: %d\n", my_num);
    +    return 0;
    +}
    +
    +
    +

    Postpatched null_deref_patched (second program being compared):

    +
    #include <stdio.h>
    +
    +void my_fun(int *num) {
    +    if (num != NULL) {
    +        *num = 42;
    +    }
    +}
    +
    +int main(int argc, char *argv[]) {
    +    int my_num;
    +    my_fun(&my_num);
    +    printf("my_num: %d\n", my_num);
    +    return 0;
    +}
    +
    +
    +

    Let’s assume that we are compiling for x64 architecture. In this case we +are interested in comparing my_fun between the two programs. Much like angr, +cozy can be used interactively through a Python REPL or through a Python script.

    +
    +

    How cozy makes comparisons

    +

    To make comparisons between two programs with different function +implementations, cozy uses symbolic execution. Both programs are fed +the same symbolic input, and cozy runs symbolic execution until all states +terminate. At the end of execution, we have a list of deadended (terminated) +states from the prepatched program, and a list of deadended states from the +postpatched program. Each of these states have constraints associated with +them that were collected as the program stepped through symbolic execution.

    +

    Suppose that we take some state A from the prepatched run, and some state +B from the postpatched run. We say that A and B are compatible if the +constraints associated with the A and B are jointly satisfiable. In +pseudocode syntax, this roughly means that the following is True:

    +
    is_sat(A.constraints & B.constraints)
    +
    +
    +

    Recall that the input to our functions are symbolic variables, so the +set of constraints is in terms of these symbolic variables. We can think +of the constraints as creating a predicate that exactly determines the +subset of the input that leads to a specific state. Taking the conjunction +of the constraints is therefore equivalent to creating a predicate +that restricts the set of input values to the intersection of the input +set for state A and state B. If this predicate is satisfiable, then +this intersection of sets is nonempty, which means that there is at +least one concrete input that will cause the program to end in state A +in the prepatched program and state B in the postpatched program.

    +

    Therefore the naive approach is to compare all pairs of terminal states +from the prepatched and postpatched and check for satisfiability. cozy +makes an optimization by using memoization, so in practice compatibility +checks over most programs should be fast. cozy is also capable of generating +concrete examples, which is useful for generating test cases and +walking through program execution.

    +
    +
    +

    Example Walkthrough

    +

    Let’s open a Python REPL and import the required libraries:

    +
    import cozy
    +from cozy.project import Project
    +from cozy.directive import Assume, Assert
    +import claripy
    +
    +
    +

    Let’s begin by creating cozy projects for the two programs given +previously. A Project is a cozy class that encapsulates a single +program:

    +
    proj_prepatched = Project("null_deref")
    +proj_postpatched = Project("null_deref_patched")
    +
    +
    +

    To execute the my_fun function, angr needs to know the function signature +of the functions. This information is typically not retained in the binary, +so we need to determine that with some other method. In this case we have +the source code, so we can add the function signature quite easily:

    +
    proj_prepatched.add_prototype("my_fun", "void f(int *a)")
    +proj_postpatched.add_prototype("my_fun", "void f(int *a)")
    +
    +
    +

    We now need to create sessions from each project. A session is created +from a specific project, and represents a single run of symbolic +execution. Here we pass “my_fun” to the +session() method, which indicates that +we are going to be running the “my_fun” function:

    +
    sess_prepatched = proj_prepatched.session("my_fun")
    +sess_postpatched = proj_postpatched.session("my_fun")
    +
    +
    +

    Since we will only be comparing the my_fun function, we need to create +the symbolic value to pass to the functions:

    +
    arg0 = claripy.BVS("num_arg", 64)
    +
    +
    +

    The symbolic value arg0 has 64 bits because it represents a pointer +on a 64-bit architecture.

    +

    Alternatively we could have used the cozy.primitives.sym_ptr() helper +function to create the claripy symbolic variable:

    +
    import archinfo
    +arg0 = cozy.primitives.sym_ptr(archinfo.ArchAMD64, "num_arg")
    +
    +
    +

    We will now constrain arg0 to be either NULL or be equal to a valid memory +address in our two sessions. Currently angr has limited support for symbolic +memory addressing, so we will malloc space for our integers then constrain +arg0 accordingly:

    +
    addr_prepatched = sess_prepatched.malloc(4) # integers are 4 bytes on the target arch
    +sess_prepatched.add_constraints((arg0 == 0x0) | (arg0 == addr_prepatched))
    +addr_postpatched = sess_postpatched.malloc(4)
    +sess_postpatched.add_constraints((arg0 == 0x0) | (arg0 == addr_postpatched))
    +
    +
    +

    So before any execution we have constrained arg0 to be either NULL +(0x0) or a concrete 64-bit address returned by +malloc().

    +
    +
    +

    Directives - Assumes and Asserts

    +

    cozy provides support for directives, which are attached to specific +program instructions. Two basic directives that you should know about +are cozy.directive.Assume and cozy.directive.Assert. +Assume and assert function by pausing execution once a specific instruction +is reached and adding constraints to the SMT solver. Assumes are used for +adding preconditions, and are often set to be triggered at the start of +functions. Asserts are triggered if there exists an input that will cause +the assert to evaluate to false. Note that directives do not change the +code being executed: they work more or less in the same way as debug +breakpoints.

    +

    To demonstrate that a null dereference can occur in the prepatched binary +and not in the postpatched binary, let’s add asserts to specific addresses. +Running the binaries through a tool like Ghidra reveals that the NULL +dereference occurs at an offset of 0x10 from the start of my_fun in the +prepatched binary. At this point the address being dereferenced is stored +in the RAX register. Let’s create a directive that encodes these observations:

    +
    mem_write_okay_prepatched = Assert.from_fun_offset(
    +        project=proj_prepatched,
    +        fun_name="my_fun",
    +        offset=0x10,
    +        condition_fun=lambda state: state.regs.rax != 0x0,
    +        info_str="Dereferencing null pointer"
    +    )
    +
    +
    +

    When execution reaches my_fun+0x10, the evaluation will be halted and +cozy will pass the angr.SimState to the condition_fun and will check to see +if it is possible to find an input value that will trigger the condition. +Let’s add the directive to the prepatch session:

    +
    sess_prepatched.add_directives(mem_write_okay_prepatched)
    +
    +
    +

    Let’s invoke the prepatched my_fun with arg0 as the symbolic input via the +run() method:

    +
    run_result = sess_prepatched.run([arg0])
    +print(run_result)
    +
    +
    +

    Which prints the following result that informs us that an assertion was triggered:

    +
    RunResult(1 deadended, 0 errored, 1 asserts_failed, 0 assume_warnings, 0 postconditions_failed, 0 spinning)
    +
    +
    +

    To view a report on what went wrong with the assertion, let’s create +a report using the report_asserts_failed() +method:

    +
    print(run_result.report([arg0]))
    +
    +
    +

    Which prints off the human-readable report:

    +
    Errored Report:
    +No errored states
    +
    +Asserts Failed Report:
    +Assert for address 0x401179 was triggered: <Bool int_arg_0_64 != 0x0>
    +Dereferencing null pointer
    +Here are 1 concrete input(s) for this particular assertion:
    +1.
    +    [<BV64 0x0>]
    +
    +Postconditions Failed Report:
    +No postcondition failure triggered
    +
    +Spinning (Looping) States Report:
    +No spinning states were reported
    +
    +
    +

    As part of the report, cozy reports that the concretized input that leads to +this assertion being triggered occurs when the input argument is 0.

    +

    Now let’s make another assert for the postpatched session and verify +that no NULL dereference occurs in the postpatch:

    +
    mem_write_okay_postpatched = Assert.from_fun_offset(
    +        project=proj_postpatched,
    +        fun_name="my_fun",
    +        offset=0x17,
    +        condition_fun=lambda state: state.regs.rax != 0x0,
    +        info_str="Dereferencing null pointer"
    +    )
    +sess_postpatched.add_directives(mem_write_okay_postpatched)
    +run_result = sess_postpatched.run()
    +print(run_result)
    +
    +
    +

    In the console we see that no assertions were triggered:

    +
    RunResult(1 deadended, 0 errored, 0 asserts_failed, 0 assume_warnings, 0 postconditions_failed)
    +
    +
    +
    +
    +

    Making the Comparisons

    +

    To compare two program executions, we need two cozy.project.RunResult objects. +Let’s create fresh sessions and re-run without any directives attached. This time we will make use of +primitive.sym_ptr_constraints() to generate the constraints instead of creating them manually:

    +
    sess_prepatched = proj_prepatched.session("my_fun")
    +sess_postpatched = proj_postpatched.session("my_fun")
    +addr_prepatched = sess_prepatched.malloc(cozy.constants.INT_SIZE)
    +sess_prepatched.add_constraints(cozy.primitives.sym_ptr_constraints(arg0, addr_prepatched, can_be_null=True))
    +addr_postpatched = sess_postpatched.malloc(cozy.constants.INT_SIZE)
    +sess_postpatched.add_constraints(cozy.primitives.sym_ptr_constraints(arg0, addr_postpatched, can_be_null=True))
    +
    +
    +

    Now let’s run both of our new sessions:

    +
    prepatched_result = sess_prepatched.run([arg0])
    +postpatched_result = sess_postpatched.run([arg0])
    +
    +
    +

    We can inspect the results object to see how many states we are dealing with:

    +
    print(prepatched_result)
    +print(postpatched_result)
    +
    +
    +

    This prints the following messages:

    +
    RunResult(1 deadended, 0 errored, 0 asserts_failed, 0 assume_warnings, 0 postconditions_failed, 0 spinning)
    +RunResult(2 deadended, 0 errored, 0 asserts_failed, 0 assume_warnings, 0 postconditions_failed, 0 spinning)
    +
    +
    +

    We can now make a comparison between these two terminated results. Constructing a Comparison object is used to do +the comparison computation:

    +
    comparison_results = cozy.analysis.Comparison(prepatched_result, postpatched_result, simplify=True)
    +
    +
    +

    To view a human readable report, we can now call the cozy.analysis.Comparison.report() method, which +will convert the Comparison to a human readable summary:

    +
    print(comparison_results.report([arg0]))
    +
    +
    +

    We now see the human readable report

    +
     1STATE PAIR (0, DEADENDED_STATE), (0, DEADENDED_STATE) are different
    + 2Memory difference detected for 0,0:
    + 3{'range(0x0, 0x4)': (<BV32 0x2a000000>, <BV32 0x0>)}
    + 4Instruction pointers for these memory writes:
    + 5{'range(0x0, 0x4)': (frozenset({<BV64 0x401179>}), frozenset())}
    + 6Register difference detected for 0,0:
    + 7{'eflags': (<BV64 0x0>, <BV64 0x44>), 'flags': (<BV64 0x0>, <BV64 0x44>), 'rflags': (<BV64 0x0>, <BV64 0x44>)}
    + 8Here are 1 concrete input(s) for this particular state pair:
    + 91.
    +10    Input arguments: [<BV64 0x0>]
    +11    Concrete mem diff: {'range(0x0, 0x4)': (<BV32 0x2a000000>, <BV32 0x0>)}
    +12    Concrete reg diff: {'eflags': (<BV64 0x0>, <BV64 0x44>), 'flags': (<BV64 0x0>, <BV64 0x44>), 'rflags': (<BV64 0x0>, <BV64 0x44>)}
    +13
    +14STATE PAIR (0, DEADENDED_STATE), (1, DEADENDED_STATE) are different
    +15The memory was equal for this state pair
    +16Register difference detected for 0,1:
    +17{'eflags': (<BV64 0x0>, <BV64 0x4>), 'flags': (<BV64 0x0>, <BV64 0x4>), 'rflags': (<BV64 0x0>, <BV64 0x4>)}
    +18Here are 1 concrete input(s) for this particular state pair:
    +191.
    +20    Input arguments: [<BV64 0xc0000000>]
    +21    Concrete reg diff: {'eflags': (<BV64 0x0>, <BV64 0x4>), 'flags': (<BV64 0x0>, <BV64 0x4>), 'rflags': (<BV64 0x0>, <BV64 0x4>)}
    +22
    +23There are no prepatched orphans
    +24There are no postpatched orphans
    +
    +
    +

    We can see that cozy found a diff between the 0th deadended +(terminated) state in the prepatched program (we will refer to this +state as s0) and the 0th deadended state in the postpatched program +(we will refer to this state as s0’). Together these two states form a +state pair, which is displayed on line 1 of the report. As we will see +from the following lines of the report, s0 represents the sole final +symbolic state for the prepatched function (there is only one path +through this function), and s0’ represents the final state for the +“false” branch of the postpatched function (i.e., the path that is +triggered by a NULL argument).

    +

    Line 3 displays the memory addresses that are different. Contents of +memory for written ranges are mapped to a tuple containing the +symbolic bytes at those addresses as a (prepatched, postpatched) +tuple. In this case, memory at addresses 0x0 to 0x4 is 0x2a000000 in +s0 (because the prepatched function writes 0x2a = 42 to the NULL +address), and 0x0 in s0’ (because the NULL check prevents the write +from occurring).

    +

    Line 5 tells the instruction pointer the program was at when it wrote +to those specific memory address ranges. Here we see that the +prepatched program was at the instruction 0x401179 when it wrote to +address 0x0, and the postpatched program never wrote to that address +(hence the empty frozenset).

    +

    Line 7 gives the symbolic register difference between the states. As we can see, the flags registers +are different due to the presence of a branch in the postpatched program. As with the memory, each register +maps to a (prepatched, postpatched) tuple which gives the symbolic contents of the registers.

    +

    Lines 8-12 gives concretized input that will cause the prepatched program to end in state s0 and +the postpatched program in state s0’. The input argument is concretized to 0x0 (aka NULL). Additionally since +the memory contents and register contents may be symbolic, we provide a concretized version of those as well.

    +

    Lines 14-21 tells us that there is another diff for the state pair +(0,1). The second state in this pair represents the “true” branch +through the postpatched function. In this case we observe that the +only difference is in the flags registers, and that there are no +observable differences in memory. The concrete input argument for this +pair is when the input is non-NULL.

    +

    The next lines describe any orphaned states - typically there will be none. An orphaned state is a state in which +there are no compatible pair states.

    +
    +
    +

    Further Examples

    +

    Further examples on how to use cozy for some simple programs can be found at https://github.com/draperlaboratory/cozy/tree/main/examples

    +
    +
    + + +
    + +
    +
    + +
    +
    + + + + + + + \ No newline at end of file diff --git a/hooks.html b/hooks.html new file mode 100644 index 0000000..bff7b56 --- /dev/null +++ b/hooks.html @@ -0,0 +1,134 @@ + + + + + + + + Dealing with Hooks — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + +
    +

    Dealing with Hooks

    +

    Some of the default C library hooks provided by angr will not function properly with +comparitive symbolic execution, including joint concolic execution. The issue stems +from two different factors:

    +

    1. Hooks may provide an incomplete implementation of the C library hooks, or the complete +implementation may be disabled by default. For example, the strtok_r function’s +more complete implementation may be disabled by default, and should be enabled by setting +angr.SimState.libc.simple_strtok to False. Likewise the strstr libc function +has a configuration option angr.SimState.libc.max_symbolic_strstr which is by +default set to a very conservative value of 1.

    +

    2. The default angr hooks create fresh symbolic variables, and constrain these symbolic +values by adding to the state’s constraints. This is problematic since in comparitive +symbolic execution we assume that both programs are fed the same symbolic variables. +Fortunately it is possible to eliminate the fresh symbolic variables in most cases. To see +an example of how to do this, see our provided replacement hook for strlen at +cozy.hooks.strlen.strlen.

    +

    In general, the best strategy for dealing with hooks is to be aware of their limitations, +understand the configuration options found in angr.SimState.libc, and replace +the default hooks when needed.

    +

    To replace a hook for a specific project, you may use the +cozy.project.Project.hook_symbol() method.

    +
    + + +
    + +
    +
    + +
    +
    + + + + + + + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..732fe18 --- /dev/null +++ b/index.html @@ -0,0 +1,175 @@ + + + + + + + + Welcome to cozy’s documentation! — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/launchingavisualization.html b/launchingavisualization.html new file mode 100644 index 0000000..219d7ef --- /dev/null +++ b/launchingavisualization.html @@ -0,0 +1,202 @@ + + + + + + + + Launching a Visualization — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + +
    +

    Launching a Visualization

    +

    In this example we will cover adding user defined types to the angr project as well as +visualizing our results with cozy. We will be using the AMP Hackathon Target 5 binaries, +which can be found in the examples folder in the cozy repository. There is no source +code available for these binaries, so some manual inspection of the assembly in your +preferred reverse engineering tool may be necessary.

    +

    The micropatch bug in the Target 5 demo occurs during conversion of the temperature +value. Within the function we want replace the following logic:

    +
    int32_t rover_process(RoverMessage_t* msg){
    +    // ...
    +    //convert Kelvin to Farhenheit.
    +    temp = ( (temp - 273) * 1.8 ) + 32;
    +    // ...
    +}
    +
    +
    +

    With this logic:

    +
    int32_t rover_process(RoverMessage_t* msg){
    +    // ...
    +    //convert Celsius to Farhenheit.
    +    temp = ( temp * 1.8 ) + 32;
    +    // ...
    +}
    +
    +
    +

    To achieve this patch, we replaced the constant 273 in the original binary with 0.

    +

    Let’s get started by making two cozy projects, one for each binary:

    +
    proj_prepatched = cozy.project.Project('examples/amp_target5_hackathon/gs_data_processor')
    +proj_postpatched = cozy.project.Project('examples/amp_target5_hackathon/gs_data_processor_draper_patched')
    +
    +
    +
    +

    Defining Custom Types

    +

    Our next task will be to define the structs used by this function. The primary inputs +to this function is the temperature field and the cmd field. Let’s register these datatypes +with cozy:

    +
    cozy.types.register_type('struct RoverData_t { int temp; unsigned int cmd; }', proj_prepatched.arch)
    +rover_message_struct = cozy.types.register_type('struct RoverMessage_t { unsigned char header[8]; struct RoverData_t packetData; }', proj_prepatched.arch)
    +
    +
    +

    We are now ready to add the type signature of the method we wish to analyze to the cozy project:

    +
    proj_prepatched.add_prototype("rover_process", "int rover_process(struct RoverMessage_t *msg)")
    +proj_postpatched.add_prototype("rover_process", "int rover_process(struct RoverMessage_t *msg)")
    +
    +
    +
    +
    +

    Comparing and Visualizing

    +

    Now let’s create two symbolic variables to represent the temp and cmd fields in the RoverData_t struct:

    +
    temp = claripy.BVS("temp", 32)
    +cmd = claripy.BVS("cmd", 32)
    +
    +
    +

    We now define a run function, which will run a prepatched or postpatched session:

    +
    def run(sess: cozy.project.Session):
    +    arg0 = sess.malloc(rover_message_struct.size)
    +    sess.mem[arg0].struct.RoverMessage_t.packetData.temp = temp.reversed
    +    sess.mem[arg0].struct.RoverMessage_t.packetData.cmd = cmd.reversed
    +
    +    return sess.run([arg0])
    +
    +
    +

    In this case we are mutating the memory by changing the memory of the angr state before +cozy runs. In this case we use angr’s API to mutate the temp and cmd fields. Since the +incoming network packet uses network order endianness, we store temp.reversed and +cmd.reversed to swap the endianness.

    +

    Let’s use our new run function to run the prepatched and postpatched session:

    +
    prepatched_results = run(proj_prepatched.session("rover_process"))
    +postpatched_results = run(proj_postpatched.session("rover_process"))
    +
    +
    +

    Now we make the comparison between the two RunResult objects:

    +
    comparison = cozy.analysis.Comparison(prepatched_results, postpatched_results)
    +
    +
    +

    After which we can launch the visualization in our web browser. This should automatically +open a browser window which visualizes our results:

    +
    cozy.execution_graph.visualize_comparison(proj_prepatched, proj_postpatched,
    +                                          prepatched_results, postpatched_results,
    +                                          comparison,
    +                                          args={"temp": temp, "cmd": cmd},
    +                                          num_examples=2, open_browser=True)
    +
    +
    +
    +
    + + +
    + +
    +
    + +
    +
    + + + + + + + \ No newline at end of file diff --git a/objects.inv b/objects.inv new file mode 100644 index 0000000..de57c75 Binary files /dev/null and b/objects.inv differ diff --git a/py-modindex.html b/py-modindex.html new file mode 100644 index 0000000..09f9c17 --- /dev/null +++ b/py-modindex.html @@ -0,0 +1,249 @@ + + + + + + + Python Module Index — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + + +

    Python Module Index

    + +
    + c +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
     
    + c
    + cozy +
        + cozy.__main__ +
        + cozy.analysis +
        + cozy.claripy_ext +
        + cozy.concolic +
        + cozy.concolic.exploration +
        + cozy.concolic.heuristics +
        + cozy.concolic.session +
        + cozy.concrete +
        + cozy.constants +
        + cozy.directive +
        + cozy.execution_graph +
        + cozy.functools_ext +
        + cozy.hooks +
        + cozy.hooks.strlen +
        + cozy.hooks.strncmp +
        + cozy.hooks.strtok_r +
        + cozy.log +
        + cozy.primitives +
        + cozy.project +
        + cozy.server +
        + cozy.session +
        + cozy.side_effect +
        + cozy.stubs +
        + cozy.terminal_state +
        + cozy.types +
    + + +
    + +
    +
    + +
    +
    + + + + + + + \ No newline at end of file diff --git a/search.html b/search.html new file mode 100644 index 0000000..69dd1d5 --- /dev/null +++ b/search.html @@ -0,0 +1,126 @@ + + + + + + + Search — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + +

    Search

    + + + + +

    + Searching for multiple words only shows matches that contain + all words. +

    + + +
    + + + +
    + + +
    + + +
    + +
    +
    + +
    +
    + + + + + + + \ No newline at end of file diff --git a/searchindex.js b/searchindex.js new file mode 100644 index 0000000..d569139 --- /dev/null +++ b/searchindex.js @@ -0,0 +1 @@ +Search.setIndex({"alltitles": {"API Reference": [[26, "api-reference"]], "Attributes": [[8, "attributes"], [11, "attributes"], [13, "attributes"], [14, "attributes"], [15, "attributes"], [17, "attributes"], [18, "attributes"], [21, "attributes"], [23, "attributes"]], "Classes": [[0, "classes"], [1, "classes"], [3, "classes"], [4, "classes"], [6, "classes"], [7, "classes"], [9, "classes"], [10, "classes"], [13, "classes"], [14, "classes"], [15, "classes"], [19, "classes"], [20, "classes"], [21, "classes"], [22, "classes"], [23, "classes"], [24, "classes"]], "Comparing and Visualizing": [[31, "comparing-and-visualizing"]], "Contents:": [[30, null]], "Dealing with Hooks": [[29, "dealing-with-hooks"]], "Defining Custom Types": [[31, "defining-custom-types"]], "Directives - Assumes and Asserts": [[28, "directives-assumes-and-asserts"]], "Example Walkthrough": [[28, "example-walkthrough"]], "Functions": [[1, "functions"], [2, "functions"], [7, "functions"], [10, "functions"], [11, "functions"], [17, "functions"], [18, "functions"], [20, "functions"], [21, "functions"], [22, "functions"], [25, "functions"]], "Further Examples": [[28, "further-examples"]], "Getting Started": [[28, "getting-started"]], "How cozy makes comparisons": [[28, "how-cozy-makes-comparisons"]], "Indices and tables": [[30, "indices-and-tables"]], "Launching a Visualization": [[31, "launching-a-visualization"]], "Making the Comparisons": [[28, "making-the-comparisons"]], "Modeling I/O Side Effects": [[32, "modeling-i-o-side-effects"]], "Module Contents": [[0, "module-contents"], [1, "module-contents"], [2, "module-contents"], [3, "module-contents"], [4, "module-contents"], [6, "module-contents"], [7, "module-contents"], [8, "module-contents"], [9, "module-contents"], [10, "module-contents"], [11, "module-contents"], [13, "module-contents"], [14, "module-contents"], [15, "module-contents"], [17, "module-contents"], [18, "module-contents"], [19, "module-contents"], [20, "module-contents"], [21, "module-contents"], [22, "module-contents"], [23, "module-contents"], [24, "module-contents"], [25, "module-contents"]], "Performing a Side Effect": [[32, "performing-a-side-effect"]], "Submodules": [[5, "submodules"], [12, "submodules"], [16, "submodules"]], "Subpackages": [[16, "subpackages"]], "Using Concolic Execution": [[27, "using-concolic-execution"]], "Welcome to cozy\u2019s documentation!": [[30, "welcome-to-cozy-s-documentation"]], "cozy": [[16, "module-cozy"]], "cozy.__main__": [[0, "module-cozy.__main__"]], "cozy.analysis": [[1, "module-cozy.analysis"]], "cozy.claripy_ext": [[2, "module-cozy.claripy_ext"]], "cozy.concolic": [[5, "module-cozy.concolic"]], "cozy.concolic.exploration": [[3, "module-cozy.concolic.exploration"]], "cozy.concolic.heuristics": [[4, "module-cozy.concolic.heuristics"]], "cozy.concolic.session": [[6, "module-cozy.concolic.session"]], "cozy.concrete": [[7, "module-cozy.concrete"]], "cozy.constants": [[8, "module-cozy.constants"]], "cozy.directive": [[9, "module-cozy.directive"]], "cozy.execution_graph": [[10, "module-cozy.execution_graph"]], "cozy.functools_ext": [[11, "module-cozy.functools_ext"]], "cozy.hooks": [[12, "module-cozy.hooks"]], "cozy.hooks.strlen": [[13, "module-cozy.hooks.strlen"]], "cozy.hooks.strncmp": [[14, "module-cozy.hooks.strncmp"]], "cozy.hooks.strtok_r": [[15, "module-cozy.hooks.strtok_r"]], "cozy.log": [[17, "module-cozy.log"]], "cozy.primitives": [[18, "module-cozy.primitives"]], "cozy.project": [[19, "module-cozy.project"]], "cozy.server": [[20, "module-cozy.server"]], "cozy.session": [[21, "module-cozy.session"]], "cozy.side_effect": [[22, "module-cozy.side_effect"]], "cozy.stubs": [[23, "module-cozy.stubs"]], "cozy.terminal_state": [[24, "module-cozy.terminal_state"]], "cozy.types": [[25, "module-cozy.types"]]}, "docnames": ["autoapi/cozy/__main__/index", "autoapi/cozy/analysis/index", "autoapi/cozy/claripy_ext/index", "autoapi/cozy/concolic/exploration/index", "autoapi/cozy/concolic/heuristics/index", "autoapi/cozy/concolic/index", "autoapi/cozy/concolic/session/index", "autoapi/cozy/concrete/index", "autoapi/cozy/constants/index", "autoapi/cozy/directive/index", "autoapi/cozy/execution_graph/index", "autoapi/cozy/functools_ext/index", "autoapi/cozy/hooks/index", "autoapi/cozy/hooks/strlen/index", "autoapi/cozy/hooks/strncmp/index", "autoapi/cozy/hooks/strtok_r/index", "autoapi/cozy/index", "autoapi/cozy/log/index", "autoapi/cozy/primitives/index", "autoapi/cozy/project/index", "autoapi/cozy/server/index", "autoapi/cozy/session/index", "autoapi/cozy/side_effect/index", "autoapi/cozy/stubs/index", "autoapi/cozy/terminal_state/index", "autoapi/cozy/types/index", "autoapi/index", "concolic", "gettingstarted", "hooks", "index", "launchingavisualization", "sideeffects"], "envversion": {"sphinx": 61, "sphinx.domains.c": 3, "sphinx.domains.changeset": 1, "sphinx.domains.citation": 1, "sphinx.domains.cpp": 9, "sphinx.domains.index": 1, "sphinx.domains.javascript": 3, "sphinx.domains.math": 2, "sphinx.domains.python": 4, "sphinx.domains.rst": 2, "sphinx.domains.std": 2}, "filenames": ["autoapi/cozy/__main__/index.rst", "autoapi/cozy/analysis/index.rst", "autoapi/cozy/claripy_ext/index.rst", "autoapi/cozy/concolic/exploration/index.rst", "autoapi/cozy/concolic/heuristics/index.rst", "autoapi/cozy/concolic/index.rst", "autoapi/cozy/concolic/session/index.rst", "autoapi/cozy/concrete/index.rst", "autoapi/cozy/constants/index.rst", "autoapi/cozy/directive/index.rst", "autoapi/cozy/execution_graph/index.rst", "autoapi/cozy/functools_ext/index.rst", "autoapi/cozy/hooks/index.rst", "autoapi/cozy/hooks/strlen/index.rst", "autoapi/cozy/hooks/strncmp/index.rst", "autoapi/cozy/hooks/strtok_r/index.rst", "autoapi/cozy/index.rst", "autoapi/cozy/log/index.rst", "autoapi/cozy/primitives/index.rst", "autoapi/cozy/project/index.rst", "autoapi/cozy/server/index.rst", "autoapi/cozy/session/index.rst", "autoapi/cozy/side_effect/index.rst", "autoapi/cozy/stubs/index.rst", "autoapi/cozy/terminal_state/index.rst", "autoapi/cozy/types/index.rst", "autoapi/index.rst", "concolic.rst", "gettingstarted.rst", "hooks.rst", "index.rst", "launchingavisualization.rst", "sideeffects.rst"], "indexentries": {"__call__() (cozy.concolic.heuristics.arbitrarycandidate method)": [[4, "cozy.concolic.heuristics.ArbitraryCandidate.__call__", false]], "__call__() (cozy.concolic.heuristics.bbtransitioncandidate method)": [[4, "cozy.concolic.heuristics.BBTransitionCandidate.__call__", false]], "__call__() (cozy.concolic.heuristics.completetermination method)": [[4, "cozy.concolic.heuristics.CompleteTermination.__call__", false]], "__call__() (cozy.concolic.heuristics.coveragetermination method)": [[4, "cozy.concolic.heuristics.CoverageTermination.__call__", false]], "__call__() (cozy.concolic.heuristics.cyclomaticcomplexitytermination method)": [[4, "cozy.concolic.heuristics.CyclomaticComplexityTermination.__call__", false]], "__iter__() (cozy.analysis.comparison method)": [[1, "cozy.analysis.Comparison.__iter__", false]], "__str__() (cozy.session.runresult method)": [[21, "cozy.session.RunResult.__str__", false]], "_call() (cozy.session.session method)": [[21, "cozy.session.Session._call", false]], "_concretize() (in module cozy.concrete)": [[7, "cozy.concrete._concretize", false]], "_exploremode (class in cozy.concolic.exploration)": [[3, "cozy.concolic.exploration._ExploreMode", false]], "_generate_comparison() (in module cozy.execution_graph)": [[10, "cozy.execution_graph._generate_comparison", false]], "_generate_concrete() (cozy.concolic.exploration.concolicsim method)": [[3, "cozy.concolic.exploration.ConcolicSim._generate_concrete", false]], "_generate_concrete() (cozy.concolic.exploration.jointconcolicsim method)": [[3, "cozy.concolic.exploration.JointConcolicSim._generate_concrete", false]], "_get_bbl_asm() (cozy.execution_graph.executiongraph method)": [[10, "cozy.execution_graph.ExecutionGraph._get_bbl_asm", false]], "_has_syscall() (cozy.execution_graph.executiongraph method)": [[10, "cozy.execution_graph.ExecutionGraph._has_syscall", false]], "_invalid_stack_addrs() (in module cozy.analysis)": [[1, "cozy.analysis._invalid_stack_addrs", false]], "_invalid_stack_overlap() (in module cozy.analysis)": [[1, "cozy.analysis._invalid_stack_overlap", false]], "_list_actions() (cozy.execution_graph.executiongraph method)": [[10, "cozy.execution_graph.ExecutionGraph._list_actions", false]], "_list_simprocs() (cozy.execution_graph.executiongraph method)": [[10, "cozy.execution_graph.ExecutionGraph._list_simprocs", false]], "_malloc_name_ctr (in module cozy.primitives)": [[18, "cozy.primitives._malloc_name_ctr", false]], "_malloc_name_ctr (in module cozy.session)": [[21, "cozy.session._malloc_name_ctr", false]], "_mem_write_ctr (in module cozy.session)": [[21, "cozy.session._mem_write_ctr", false]], "_on_mem_write() (in module cozy.session)": [[21, "cozy.session._on_mem_write", false]], "_on_simprocedure() (in module cozy.session)": [[21, "cozy.session._on_simprocedure", false]], "_run_result() (cozy.session.session method)": [[21, "cozy.session.Session._run_result", false]], "_save_states() (in module cozy.session)": [[21, "cozy.session._save_states", false]], "_serialize_diff() (in module cozy.execution_graph)": [[10, "cozy.execution_graph._serialize_diff", false]], "_serialized_field_diff() (in module cozy.execution_graph)": [[10, "cozy.execution_graph._serialized_field_diff", false]], "_session_exploration() (cozy.session.session method)": [[21, "cozy.session.Session._session_exploration", false]], "_sessionbasicexploration (class in cozy.session)": [[21, "cozy.session._SessionBasicExploration", false]], "_sessiondirectiveexploration (class in cozy.session)": [[21, "cozy.session._SessionDirectiveExploration", false]], "_sessionexploration (class in cozy.session)": [[21, "cozy.session._SessionExploration", false]], "_set_replacement_dict() (cozy.concolic.exploration.concolicsim method)": [[3, "cozy.concolic.exploration.ConcolicSim._set_replacement_dict", false]], "_stack_addrs() (in module cozy.analysis)": [[1, "cozy.analysis._stack_addrs", false]], "_swap_explore_mode() (cozy.concolic.exploration.jointconcolicsim method)": [[3, "cozy.concolic.exploration.JointConcolicSim._swap_explore_mode", false]], "add_constraints() (cozy.session.session method)": [[21, "cozy.session.Session.add_constraints", false]], "add_directives() (cozy.session.session method)": [[21, "cozy.session.Session.add_directives", false]], "add_prototype() (cozy.project.project method)": [[19, "cozy.project.Project.add_prototype", false]], "arbitrarycandidate (class in cozy.concolic.heuristics)": [[4, "cozy.concolic.heuristics.ArbitraryCandidate", false]], "arch (cozy.project.project property)": [[19, "cozy.project.Project.arch", false]], "ask_file() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.ask_file", false]], "ask_string() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.ask_string", false]], "ask_yes_no() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.ask_yes_no", false]], "assert (class in cozy.directive)": [[9, "cozy.directive.Assert", false]], "assert_can (cozy.directive.asserttype attribute)": [[9, "cozy.directive.AssertType.ASSERT_CAN", false]], "assert_can_global (cozy.directive.asserttype attribute)": [[9, "cozy.directive.AssertType.ASSERT_CAN_GLOBAL", false]], "assert_must (cozy.directive.asserttype attribute)": [[9, "cozy.directive.AssertType.ASSERT_MUST", false]], "assertfailedstate (class in cozy.terminal_state)": [[24, "cozy.terminal_state.AssertFailedState", false]], "assertion_triggered (cozy.session.runresult property)": [[21, "cozy.session.RunResult.assertion_triggered", false]], "asserttype (class in cozy.directive)": [[9, "cozy.directive.AssertType", false]], "assume (class in cozy.directive)": [[9, "cozy.directive.Assume", false]], "b (in module cozy.functools_ext)": [[11, "cozy.functools_ext.B", false]], "bbtransitioncandidate (class in cozy.concolic.heuristics)": [[4, "cozy.concolic.heuristics.BBTransitionCandidate", false]], "breakpoint (class in cozy.directive)": [[9, "cozy.directive.Breakpoint", false]], "c (in module cozy.functools_ext)": [[11, "cozy.functools_ext.C", false]], "cfg (cozy.project.project property)": [[19, "cozy.project.Project.cfg", false]], "check_postconditions() (cozy.session._sessiondirectiveexploration method)": [[21, "cozy.session._SessionDirectiveExploration.check_postconditions", false]], "compare_side_effect() (in module cozy.analysis)": [[1, "cozy.analysis.compare_side_effect", false]], "comparison (class in cozy.analysis)": [[1, "cozy.analysis.Comparison", false]], "compatiblepair (class in cozy.analysis)": [[1, "cozy.analysis.CompatiblePair", false]], "compatiblepairinput (class in cozy.concrete)": [[7, "cozy.concrete.CompatiblePairInput", false]], "complete (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.complete", false]], "complete() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.complete", false]], "completetermination (class in cozy.concolic.heuristics)": [[4, "cozy.concolic.heuristics.CompleteTermination", false]], "compose() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.compose", false]], "compose() (in module cozy.functools_ext)": [[11, "cozy.functools_ext.compose", false]], "concolicsim (class in cozy.concolic.exploration)": [[3, "cozy.concolic.exploration.ConcolicSim", false]], "concrete_examples() (cozy.analysis.compatiblepair method)": [[1, "cozy.analysis.CompatiblePair.concrete_examples", false]], "concrete_examples() (cozy.terminal_state.terminalstate method)": [[24, "cozy.terminal_state.TerminalState.concrete_examples", false]], "concreteperformedsideeffect (class in cozy.side_effect)": [[22, "cozy.side_effect.ConcretePerformedSideEffect", false]], "confirm_script_clobber (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.confirm_script_clobber", false]], "confirm_start (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.confirm_start", false]], "coveragetermination (class in cozy.concolic.heuristics)": [[4, "cozy.concolic.heuristics.CoverageTermination", false]], "cozy": [[16, "module-cozy", false]], "cozy.__main__": [[0, "module-cozy.__main__", false]], "cozy.analysis": [[1, "module-cozy.analysis", false]], "cozy.claripy_ext": [[2, "module-cozy.claripy_ext", false]], "cozy.concolic": [[5, "module-cozy.concolic", false]], "cozy.concolic.exploration": [[3, "module-cozy.concolic.exploration", false]], "cozy.concolic.heuristics": [[4, "module-cozy.concolic.heuristics", false]], "cozy.concolic.session": [[6, "module-cozy.concolic.session", false]], "cozy.concrete": [[7, "module-cozy.concrete", false]], "cozy.constants": [[8, "module-cozy.constants", false]], "cozy.directive": [[9, "module-cozy.directive", false]], "cozy.execution_graph": [[10, "module-cozy.execution_graph", false]], "cozy.functools_ext": [[11, "module-cozy.functools_ext", false]], "cozy.hooks": [[12, "module-cozy.hooks", false]], "cozy.hooks.strlen": [[13, "module-cozy.hooks.strlen", false]], "cozy.hooks.strncmp": [[14, "module-cozy.hooks.strncmp", false]], "cozy.hooks.strtok_r": [[15, "module-cozy.hooks.strtok_r", false]], "cozy.log": [[17, "module-cozy.log", false]], "cozy.primitives": [[18, "module-cozy.primitives", false]], "cozy.project": [[19, "module-cozy.project", false]], "cozy.server": [[20, "module-cozy.server", false]], "cozy.session": [[21, "module-cozy.session", false]], "cozy.side_effect": [[22, "module-cozy.side_effect", false]], "cozy.stubs": [[23, "module-cozy.stubs", false]], "cozy.terminal_state": [[24, "module-cozy.terminal_state", false]], "cozy.types": [[25, "module-cozy.types", false]], "critical() (in module cozy.log)": [[17, "cozy.log.critical", false]], "css (cozy.__main__.wizard attribute)": [[0, "cozy.__main__.Wizard.CSS", false]], "cyclomaticcomplexitytermination (class in cozy.concolic.heuristics)": [[4, "cozy.concolic.heuristics.CyclomaticComplexityTermination", false]], "deadendedstate (class in cozy.terminal_state)": [[24, "cozy.terminal_state.DeadendedState", false]], "debug() (in module cozy.log)": [[17, "cozy.log.debug", false]], "difference() (cozy.analysis.statediff method)": [[1, "cozy.analysis.StateDiff.difference", false]], "diffresult (class in cozy.analysis)": [[1, "cozy.analysis.DiffResult", false]], "directive (class in cozy.directive)": [[9, "cozy.directive.Directive", false]], "disable() (in module cozy.log)": [[17, "cozy.log.disable", false]], "do_get() (cozy.server.vizhandler method)": [[20, "cozy.server.VizHandler.do_GET", false]], "dump_bbp_pp_cytoscape() (cozy.execution_graph.executiongraph method)": [[10, "cozy.execution_graph.ExecutionGraph.dump_bbp_pp_cytoscape", false]], "dump_comparison() (in module cozy.execution_graph)": [[10, "cozy.execution_graph.dump_comparison", false]], "effect_concrete_post_processor() (cozy.directive.virtualprint method)": [[9, "cozy.directive.VirtualPrint.effect_concrete_post_processor", false]], "eqfielddiff (class in cozy.analysis)": [[1, "cozy.analysis.EqFieldDiff", false]], "equal() (cozy.analysis.compatiblepair method)": [[1, "cozy.analysis.CompatiblePair.equal", false]], "equal_side_effects() (cozy.analysis.compatiblepair method)": [[1, "cozy.analysis.CompatiblePair.equal_side_effects", false]], "error() (in module cozy.log)": [[17, "cozy.log.error", false]], "errordirective (class in cozy.directive)": [[9, "cozy.directive.ErrorDirective", false]], "errorstate (class in cozy.terminal_state)": [[24, "cozy.terminal_state.ErrorState", false]], "executiongraph (class in cozy.execution_graph)": [[10, "cozy.execution_graph.ExecutionGraph", false]], "explore() (cozy.concolic.exploration.jointconcolicsim method)": [[3, "cozy.concolic.exploration.JointConcolicSim.explore", false]], "explore() (cozy.session._sessionbasicexploration method)": [[21, "cozy.session._SessionBasicExploration.explore", false]], "explore() (cozy.session._sessiondirectiveexploration method)": [[21, "cozy.session._SessionDirectiveExploration.explore", false]], "explore() (cozy.session._sessionexploration method)": [[21, "cozy.session._SessionExploration.explore", false]], "explore_left (cozy.concolic.exploration._exploremode attribute)": [[3, "cozy.concolic.exploration._ExploreMode.EXPLORE_LEFT", false]], "explore_right (cozy.concolic.exploration._exploremode attribute)": [[3, "cozy.concolic.exploration._ExploreMode.EXPLORE_RIGHT", false]], "extract_func() (cozy.stubs.stubber method)": [[23, "cozy.stubs.Stubber.extract_func", false]], "fielddiff (class in cozy.analysis)": [[1, "cozy.analysis.FieldDiff", false]], "filter() (cozy.concolic.exploration.concolicsim method)": [[3, "cozy.concolic.exploration.ConcolicSim.filter", false]], "find_symbol_addr() (cozy.project.project method)": [[19, "cozy.project.Project.find_symbol_addr", false]], "fmap() (in module cozy.functools_ext)": [[11, "cozy.functools_ext.fmap", false]], "from_fun_offset() (cozy.directive.assert static method)": [[9, "cozy.directive.Assert.from_fun_offset", false]], "from_fun_offset() (cozy.directive.assume static method)": [[9, "cozy.directive.Assume.from_fun_offset", false]], "from_fun_offset() (cozy.directive.breakpoint static method)": [[9, "cozy.directive.Breakpoint.from_fun_offset", false]], "from_fun_offset() (cozy.directive.errordirective static method)": [[9, "cozy.directive.ErrorDirective.from_fun_offset", false]], "from_fun_offset() (cozy.directive.virtualprint static method)": [[9, "cozy.directive.VirtualPrint.from_fun_offset", false]], "from_session() (cozy.concolic.heuristics.coveragetermination static method)": [[4, "cozy.concolic.heuristics.CoverageTermination.from_session", false]], "from_session() (cozy.concolic.heuristics.cyclomaticcomplexitytermination static method)": [[4, "cozy.concolic.heuristics.CyclomaticComplexityTermination.from_session", false]], "from_twos_comp() (in module cozy.primitives)": [[18, "cozy.primitives.from_twos_comp", false]], "generate_concrete() (cozy.concolic.exploration.concolicsim method)": [[3, "cozy.concolic.exploration.ConcolicSim.generate_concrete", false]], "get_callees() (cozy.stubs.stubber method)": [[23, "cozy.stubs.Stubber.get_callees", false]], "get_channel() (in module cozy.side_effect)": [[22, "cozy.side_effect.get_channel", false]], "get_effects() (in module cozy.side_effect)": [[22, "cozy.side_effect.get_effects", false]], "get_pair() (cozy.analysis.comparison method)": [[1, "cozy.analysis.Comparison.get_pair", false]], "get_symbol_name() (in module cozy.claripy_ext)": [[2, "cozy.claripy_ext.get_symbol_name", false]], "get_vizroot() (in module cozy.server)": [[20, "cozy.server.get_vizroot", false]], "handle_confirm_script_clobber() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_confirm_script_clobber", false]], "handle_confirm_start() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_confirm_start", false]], "handle_request_concolic() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_concolic", false]], "handle_request_concolic_complete() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_concolic_complete", false]], "handle_request_dump() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_dump", false]], "handle_request_dump_name() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_dump_name", false]], "handle_request_function_name() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_function_name", false]], "handle_request_hooks() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_hooks", false]], "handle_request_postpatched() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_postpatched", false]], "handle_request_prepatched() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_prepatched", false]], "handle_request_script_name() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_script_name", false]], "handle_request_signature() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_signature", false]], "handle_request_textual_report() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_textual_report", false]], "handle_request_visualization() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.handle_request_visualization", false]], "hexify() (in module cozy.analysis)": [[1, "cozy.analysis.hexify", false]], "hook_symbol() (cozy.project.project method)": [[19, "cozy.project.Project.hook_symbol", false]], "hook_syscall() (cozy.project.project method)": [[19, "cozy.project.Project.hook_syscall", false]], "info() (in module cozy.log)": [[17, "cozy.log.info", false]], "int_size (in module cozy.constants)": [[8, "cozy.constants.INT_SIZE", false]], "is_compatible() (cozy.analysis.comparison method)": [[1, "cozy.analysis.Comparison.is_compatible", false]], "is_satisfied() (cozy.concolic.exploration.concolicsim method)": [[3, "cozy.concolic.exploration.ConcolicSim.is_satisfied", false]], "jointconcolicsession (class in cozy.concolic.session)": [[6, "cozy.concolic.session.JointConcolicSession", false]], "jointconcolicsim (class in cozy.concolic.exploration)": [[3, "cozy.concolic.exploration.JointConcolicSim", false]], "l (in module cozy.hooks.strlen)": [[13, "cozy.hooks.strlen.l", false]], "l (in module cozy.hooks.strncmp)": [[14, "cozy.hooks.strncmp.l", false]], "l (in module cozy.hooks.strtok_r)": [[15, "cozy.hooks.strtok_r.l", false]], "levenshtein_alignment() (in module cozy.side_effect)": [[22, "cozy.side_effect.levenshtein_alignment", false]], "logger (in module cozy.log)": [[17, "cozy.log.logger", false]], "make_callee_stubs() (cozy.stubs.stubber method)": [[23, "cozy.stubs.Stubber.make_callee_stubs", false]], "make_stub() (cozy.stubs.stubber method)": [[23, "cozy.stubs.Stubber.make_stub", false]], "malloc() (cozy.session.session method)": [[21, "cozy.session.Session.malloc", false]], "malloced_names (cozy.terminal_state.terminalstate property)": [[24, "cozy.terminal_state.TerminalState.malloced_names", false]], "mapped_body (cozy.side_effect.concreteperformedsideeffect property)": [[22, "cozy.side_effect.ConcretePerformedSideEffect.mapped_body", false]], "max_null_index (cozy.hooks.strlen.strlen attribute)": [[13, "cozy.hooks.strlen.strlen.max_null_index", false]], "mem (cozy.session.session property)": [[21, "cozy.session.Session.mem", false]], "mem_writes (cozy.terminal_state.terminalstate property)": [[24, "cozy.terminal_state.TerminalState.mem_writes", false]], "memory (cozy.session.session property)": [[21, "cozy.session.Session.memory", false]], "model() (in module cozy.claripy_ext)": [[2, "cozy.claripy_ext.model", false]], "module": [[0, "module-cozy.__main__", false], [1, "module-cozy.analysis", false], [2, "module-cozy.claripy_ext", false], [3, "module-cozy.concolic.exploration", false], [4, "module-cozy.concolic.heuristics", false], [5, "module-cozy.concolic", false], [6, "module-cozy.concolic.session", false], [7, "module-cozy.concrete", false], [8, "module-cozy.constants", false], [9, "module-cozy.directive", false], [10, "module-cozy.execution_graph", false], [11, "module-cozy.functools_ext", false], [12, "module-cozy.hooks", false], [13, "module-cozy.hooks.strlen", false], [14, "module-cozy.hooks.strncmp", false], [15, "module-cozy.hooks.strtok_r", false], [16, "module-cozy", false], [17, "module-cozy.log", false], [18, "module-cozy.primitives", false], [19, "module-cozy.project", false], [20, "module-cozy.server", false], [21, "module-cozy.session", false], [22, "module-cozy.side_effect", false], [23, "module-cozy.stubs", false], [24, "module-cozy.terminal_state", false], [25, "module-cozy.types", false]], "nice_name() (in module cozy.analysis)": [[1, "cozy.analysis.nice_name", false]], "noteqfielddiff (class in cozy.analysis)": [[1, "cozy.analysis.NotEqFieldDiff", false]], "noteqleaf (class in cozy.analysis)": [[1, "cozy.analysis.NotEqLeaf", false]], "null_ptr (in module cozy.constants)": [[8, "cozy.constants.NULL_PTR", false]], "object_ranges() (cozy.project.project method)": [[19, "cozy.project.Project.object_ranges", false]], "on_directory_tree_file_selected() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.on_directory_tree_file_selected", false]], "on_input_submitted() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.on_input_submitted", false]], "on_option_list_option_selected() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.on_option_list_option_selected", false]], "perform() (in module cozy.side_effect)": [[22, "cozy.side_effect.perform", false]], "performedsideeffect (class in cozy.side_effect)": [[22, "cozy.side_effect.PerformedSideEffect", false]], "postcondition (class in cozy.directive)": [[9, "cozy.directive.Postcondition", false]], "postcondition_triggered (cozy.session.runresult property)": [[21, "cozy.session.RunResult.postcondition_triggered", false]], "postconditionfailedstate (class in cozy.terminal_state)": [[24, "cozy.terminal_state.PostconditionFailedState", false]], "preorder_fold() (in module cozy.functools_ext)": [[11, "cozy.functools_ext.preorder_fold", false]], "preorder_mapfold() (in module cozy.functools_ext)": [[11, "cozy.functools_ext.preorder_mapfold", false]], "project (class in cozy.project)": [[19, "cozy.project.Project", false]], "reconstruct_bbl_addr_graph() (cozy.execution_graph.executiongraph method)": [[10, "cozy.execution_graph.ExecutionGraph.reconstruct_bbl_addr_graph", false]], "reconstruct_bbl_pp_graph() (cozy.execution_graph.executiongraph method)": [[10, "cozy.execution_graph.ExecutionGraph.reconstruct_bbl_pp_graph", false]], "register_type() (in module cozy.types)": [[25, "cozy.types.register_type", false]], "register_types() (in module cozy.types)": [[25, "cozy.types.register_types", false]], "report() (cozy.analysis.comparison method)": [[1, "cozy.analysis.Comparison.report", false]], "report() (cozy.session.runresult method)": [[21, "cozy.session.RunResult.report", false]], "report_asserts_failed() (cozy.session.runresult method)": [[21, "cozy.session.RunResult.report_asserts_failed", false]], "report_errored() (cozy.session.runresult method)": [[21, "cozy.session.RunResult.report_errored", false]], "report_postconditions_failed() (cozy.session.runresult method)": [[21, "cozy.session.RunResult.report_postconditions_failed", false]], "report_spinning() (cozy.session.runresult method)": [[21, "cozy.session.RunResult.report_spinning", false]], "request_concolic (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_concolic", false]], "request_concolic_complete (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_concolic_complete", false]], "request_dump (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_dump", false]], "request_dump_name (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_dump_name", false]], "request_function_name (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_function_name", false]], "request_hooks (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_hooks", false]], "request_postpatched (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_postpatched", false]], "request_prepatched (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_prepatched", false]], "request_script_name (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_script_name", false]], "request_signature (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_signature", false]], "request_textual_report (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_textual_report", false]], "request_visualization (cozy.__main__.stage attribute)": [[0, "cozy.__main__.Stage.request_visualization", false]], "results (class in cozy.__main__)": [[0, "cozy.__main__.Results", false]], "run() (cozy.concolic.session.jointconcolicsession method)": [[6, "cozy.concolic.session.JointConcolicSession.run", false]], "run() (cozy.hooks.strlen.strlen method)": [[13, "cozy.hooks.strlen.strlen.run", false]], "run() (cozy.hooks.strncmp.strncmp method)": [[14, "cozy.hooks.strncmp.strncmp.run", false]], "run() (cozy.hooks.strtok_r.strtok_r method)": [[15, "cozy.hooks.strtok_r.strtok_r.run", false]], "run() (cozy.session.session method)": [[21, "cozy.session.Session.run", false]], "runresult (class in cozy.session)": [[21, "cozy.session.RunResult", false]], "session (class in cozy.session)": [[21, "cozy.session.Session", false]], "session() (cozy.project.project method)": [[19, "cozy.project.Project.session", false]], "set_concrete() (cozy.concolic.exploration.concolicsim method)": [[3, "cozy.concolic.exploration.ConcolicSim.set_concrete", false]], "set_confirm_script_clobber() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_confirm_script_clobber", false]], "set_confirm_start() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_confirm_start", false]], "set_level() (in module cozy.log)": [[17, "cozy.log.set_level", false]], "set_request_concolic() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_concolic", false]], "set_request_concolic_complete() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_concolic_complete", false]], "set_request_dump() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_dump", false]], "set_request_dump_name() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_dump_name", false]], "set_request_function_name() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_function_name", false]], "set_request_hooks() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_hooks", false]], "set_request_postpatched() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_postpatched", false]], "set_request_prepatched() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_prepatched", false]], "set_request_script_name() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_script_name", false]], "set_request_signature() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_signature", false]], "set_request_textual_report() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_textual_report", false]], "set_request_visualization() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_request_visualization", false]], "set_stage() (cozy.__main__.wizard method)": [[0, "cozy.__main__.Wizard.set_stage", false]], "setup() (cozy.concolic.exploration.concolicsim method)": [[3, "cozy.concolic.exploration.ConcolicSim.setup", false]], "side_effects (cozy.terminal_state.terminalstate property)": [[24, "cozy.terminal_state.TerminalState.side_effects", false]], "simplify_kb() (in module cozy.claripy_ext)": [[2, "cozy.claripy_ext.simplify_kb", false]], "spinningstate (class in cozy.terminal_state)": [[24, "cozy.terminal_state.SpinningState", false]], "stage (class in cozy.__main__)": [[0, "cozy.__main__.Stage", false]], "start_fun_addr (cozy.session.session property)": [[21, "cozy.session.Session.start_fun_addr", false]], "start_viz_server() (in module cozy.server)": [[20, "cozy.server.start_viz_server", false]], "statediff (class in cozy.analysis)": [[1, "cozy.analysis.StateDiff", false]], "std_err (cozy.terminal_state.terminalstate property)": [[24, "cozy.terminal_state.TerminalState.std_err", false]], "std_out (cozy.terminal_state.terminalstate property)": [[24, "cozy.terminal_state.TerminalState.std_out", false]], "store() (cozy.session.session method)": [[21, "cozy.session.Session.store", false]], "store_fs() (cozy.session.session method)": [[21, "cozy.session.Session.store_fs", false]], "strlen (class in cozy.hooks.strlen)": [[13, "cozy.hooks.strlen.strlen", false]], "strncmp (class in cozy.hooks.strncmp)": [[14, "cozy.hooks.strncmp.strncmp", false]], "strtok_r (class in cozy.hooks.strtok_r)": [[15, "cozy.hooks.strtok_r.strtok_r", false]], "stubber (class in cozy.stubs)": [[23, "cozy.stubs.Stubber", false]], "sym_ptr() (in module cozy.primitives)": [[18, "cozy.primitives.sym_ptr", false]], "sym_ptr_constraints() (in module cozy.primitives)": [[18, "cozy.primitives.sym_ptr_constraints", false]], "t (in module cozy.functools_ext)": [[11, "cozy.functools_ext.T", false]], "terminalstate (class in cozy.terminal_state)": [[24, "cozy.terminal_state.TerminalState", false]], "terminalstateinput (class in cozy.concrete)": [[7, "cozy.concrete.TerminalStateInput", false]], "test_levenshtein_alignment() (in module cozy.side_effect)": [[22, "cozy.side_effect.test_levenshtein_alignment", false]], "to_string() (cozy.__main__.results method)": [[0, "cozy.__main__.Results.to_string", false]], "to_twos_comp() (in module cozy.primitives)": [[18, "cozy.primitives.to_twos_comp", false]], "u (in module cozy.functools_ext)": [[11, "cozy.functools_ext.U", false]], "unstripped_binary_path (in module cozy.stubs)": [[23, "cozy.stubs.unstripped_binary_path", false]], "v (in module cozy.functools_ext)": [[11, "cozy.functools_ext.V", false]], "verify() (cozy.analysis.comparison method)": [[1, "cozy.analysis.Comparison.verify", false]], "virtual_prints (cozy.terminal_state.terminalstate property)": [[24, "cozy.terminal_state.TerminalState.virtual_prints", false]], "virtualprint (class in cozy.directive)": [[9, "cozy.directive.VirtualPrint", false]], "visualize_comparison() (in module cozy.execution_graph)": [[10, "cozy.execution_graph.visualize_comparison", false]], "vizhandler (class in cozy.server)": [[20, "cozy.server.VizHandler", false]], "warning() (in module cozy.log)": [[17, "cozy.log.warning", false]], "wizard (class in cozy.__main__)": [[0, "cozy.__main__.Wizard", false]]}, "objects": {"": [[16, 0, 0, "-", "cozy"]], "cozy": [[0, 0, 0, "-", "__main__"], [1, 0, 0, "-", "analysis"], [2, 0, 0, "-", "claripy_ext"], [5, 0, 0, "-", "concolic"], [7, 0, 0, "-", "concrete"], [8, 0, 0, "-", "constants"], [9, 0, 0, "-", "directive"], [10, 0, 0, "-", "execution_graph"], [11, 0, 0, "-", "functools_ext"], [12, 0, 0, "-", "hooks"], [17, 0, 0, "-", "log"], [18, 0, 0, "-", "primitives"], [19, 0, 0, "-", "project"], [20, 0, 0, "-", "server"], [21, 0, 0, "-", "session"], [22, 0, 0, "-", "side_effect"], [23, 0, 0, "-", "stubs"], [24, 0, 0, "-", "terminal_state"], [25, 0, 0, "-", "types"]], "cozy.__main__": [[0, 1, 1, "", "Results"], [0, 1, 1, "", "Stage"], [0, 1, 1, "", "Wizard"]], "cozy.__main__.Results": [[0, 2, 1, "", "to_string"]], "cozy.__main__.Stage": [[0, 3, 1, "", "complete"], [0, 3, 1, "", "confirm_script_clobber"], [0, 3, 1, "", "confirm_start"], [0, 3, 1, "", "request_concolic"], [0, 3, 1, "", "request_concolic_complete"], [0, 3, 1, "", "request_dump"], [0, 3, 1, "", "request_dump_name"], [0, 3, 1, "", "request_function_name"], [0, 3, 1, "", "request_hooks"], [0, 3, 1, "", "request_postpatched"], [0, 3, 1, "", "request_prepatched"], [0, 3, 1, "", "request_script_name"], [0, 3, 1, "", "request_signature"], [0, 3, 1, "", "request_textual_report"], [0, 3, 1, "", "request_visualization"]], "cozy.__main__.Wizard": [[0, 3, 1, "", "CSS"], [0, 2, 1, "", "ask_file"], [0, 2, 1, "", "ask_string"], [0, 2, 1, "", "ask_yes_no"], [0, 2, 1, "", "complete"], [0, 2, 1, "", "compose"], [0, 2, 1, "", "handle_confirm_script_clobber"], [0, 2, 1, "", "handle_confirm_start"], [0, 2, 1, "", "handle_request_concolic"], [0, 2, 1, "", "handle_request_concolic_complete"], [0, 2, 1, "", "handle_request_dump"], [0, 2, 1, "", "handle_request_dump_name"], [0, 2, 1, "", "handle_request_function_name"], [0, 2, 1, "", "handle_request_hooks"], [0, 2, 1, "", "handle_request_postpatched"], [0, 2, 1, "", "handle_request_prepatched"], [0, 2, 1, "", "handle_request_script_name"], [0, 2, 1, "", "handle_request_signature"], [0, 2, 1, "", "handle_request_textual_report"], [0, 2, 1, "", "handle_request_visualization"], [0, 2, 1, "", "on_directory_tree_file_selected"], [0, 2, 1, "", "on_input_submitted"], [0, 2, 1, "", "on_option_list_option_selected"], [0, 2, 1, "", "set_confirm_script_clobber"], [0, 2, 1, "", "set_confirm_start"], [0, 2, 1, "", "set_request_concolic"], [0, 2, 1, "", "set_request_concolic_complete"], [0, 2, 1, "", "set_request_dump"], [0, 2, 1, "", "set_request_dump_name"], [0, 2, 1, "", "set_request_function_name"], [0, 2, 1, "", "set_request_hooks"], [0, 2, 1, "", "set_request_postpatched"], [0, 2, 1, "", "set_request_prepatched"], [0, 2, 1, "", "set_request_script_name"], [0, 2, 1, "", "set_request_signature"], [0, 2, 1, "", "set_request_textual_report"], [0, 2, 1, "", "set_request_visualization"], [0, 2, 1, "", "set_stage"]], "cozy.analysis": [[1, 1, 1, "", "Comparison"], [1, 1, 1, "", "CompatiblePair"], [1, 1, 1, "", "DiffResult"], [1, 1, 1, "", "EqFieldDiff"], [1, 1, 1, "", "FieldDiff"], [1, 1, 1, "", "NotEqFieldDiff"], [1, 1, 1, "", "NotEqLeaf"], [1, 1, 1, "", "StateDiff"], [1, 4, 1, "", "_invalid_stack_addrs"], [1, 4, 1, "", "_invalid_stack_overlap"], [1, 4, 1, "", "_stack_addrs"], [1, 4, 1, "", "compare_side_effect"], [1, 4, 1, "", "hexify"], [1, 4, 1, "", "nice_name"]], "cozy.analysis.Comparison": [[1, 2, 1, "", "__iter__"], [1, 2, 1, "", "get_pair"], [1, 2, 1, "", "is_compatible"], [1, 2, 1, "", "report"], [1, 2, 1, "", "verify"]], "cozy.analysis.CompatiblePair": [[1, 2, 1, "", "concrete_examples"], [1, 2, 1, "", "equal"], [1, 2, 1, "", "equal_side_effects"]], "cozy.analysis.StateDiff": [[1, 2, 1, "", "difference"]], "cozy.claripy_ext": [[2, 4, 1, "", "get_symbol_name"], [2, 4, 1, "", "model"], [2, 4, 1, "", "simplify_kb"]], "cozy.concolic": [[3, 0, 0, "-", "exploration"], [4, 0, 0, "-", "heuristics"], [6, 0, 0, "-", "session"]], "cozy.concolic.exploration": [[3, 1, 1, "", "ConcolicSim"], [3, 1, 1, "", "JointConcolicSim"], [3, 1, 1, "", "_ExploreMode"]], "cozy.concolic.exploration.ConcolicSim": [[3, 2, 1, "", "_generate_concrete"], [3, 2, 1, "", "_set_replacement_dict"], [3, 2, 1, "", "filter"], [3, 2, 1, "", "generate_concrete"], [3, 2, 1, "", "is_satisfied"], [3, 2, 1, "", "set_concrete"], [3, 2, 1, "", "setup"]], "cozy.concolic.exploration.JointConcolicSim": [[3, 2, 1, "", "_generate_concrete"], [3, 2, 1, "", "_swap_explore_mode"], [3, 2, 1, "", "explore"]], "cozy.concolic.exploration._ExploreMode": [[3, 3, 1, "", "EXPLORE_LEFT"], [3, 3, 1, "", "EXPLORE_RIGHT"]], "cozy.concolic.heuristics": [[4, 1, 1, "", "ArbitraryCandidate"], [4, 1, 1, "", "BBTransitionCandidate"], [4, 1, 1, "", "CompleteTermination"], [4, 1, 1, "", "CoverageTermination"], [4, 1, 1, "", "CyclomaticComplexityTermination"]], "cozy.concolic.heuristics.ArbitraryCandidate": [[4, 2, 1, "", "__call__"]], "cozy.concolic.heuristics.BBTransitionCandidate": [[4, 2, 1, "", "__call__"]], "cozy.concolic.heuristics.CompleteTermination": [[4, 2, 1, "", "__call__"]], "cozy.concolic.heuristics.CoverageTermination": [[4, 2, 1, "", "__call__"], [4, 2, 1, "", "from_session"]], "cozy.concolic.heuristics.CyclomaticComplexityTermination": [[4, 2, 1, "", "__call__"], [4, 2, 1, "", "from_session"]], "cozy.concolic.session": [[6, 1, 1, "", "JointConcolicSession"]], "cozy.concolic.session.JointConcolicSession": [[6, 2, 1, "", "run"]], "cozy.concrete": [[7, 1, 1, "", "CompatiblePairInput"], [7, 1, 1, "", "TerminalStateInput"], [7, 4, 1, "", "_concretize"]], "cozy.constants": [[8, 5, 1, "", "INT_SIZE"], [8, 5, 1, "", "NULL_PTR"]], "cozy.directive": [[9, 1, 1, "", "Assert"], [9, 1, 1, "", "AssertType"], [9, 1, 1, "", "Assume"], [9, 1, 1, "", "Breakpoint"], [9, 1, 1, "", "Directive"], [9, 1, 1, "", "ErrorDirective"], [9, 1, 1, "", "Postcondition"], [9, 1, 1, "", "VirtualPrint"]], "cozy.directive.Assert": [[9, 2, 1, "", "from_fun_offset"]], "cozy.directive.AssertType": [[9, 3, 1, "", "ASSERT_CAN"], [9, 3, 1, "", "ASSERT_CAN_GLOBAL"], [9, 3, 1, "", "ASSERT_MUST"]], "cozy.directive.Assume": [[9, 2, 1, "", "from_fun_offset"]], "cozy.directive.Breakpoint": [[9, 2, 1, "", "from_fun_offset"]], "cozy.directive.ErrorDirective": [[9, 2, 1, "", "from_fun_offset"]], "cozy.directive.VirtualPrint": [[9, 2, 1, "", "effect_concrete_post_processor"], [9, 2, 1, "", "from_fun_offset"]], "cozy.execution_graph": [[10, 1, 1, "", "ExecutionGraph"], [10, 4, 1, "", "_generate_comparison"], [10, 4, 1, "", "_serialize_diff"], [10, 4, 1, "", "_serialized_field_diff"], [10, 4, 1, "", "dump_comparison"], [10, 4, 1, "", "visualize_comparison"]], "cozy.execution_graph.ExecutionGraph": [[10, 2, 1, "", "_get_bbl_asm"], [10, 2, 1, "", "_has_syscall"], [10, 2, 1, "", "_list_actions"], [10, 2, 1, "", "_list_simprocs"], [10, 2, 1, "", "dump_bbp_pp_cytoscape"], [10, 2, 1, "", "reconstruct_bbl_addr_graph"], [10, 2, 1, "", "reconstruct_bbl_pp_graph"]], "cozy.functools_ext": [[11, 5, 1, "", "B"], [11, 5, 1, "", "C"], [11, 5, 1, "", "T"], [11, 5, 1, "", "U"], [11, 5, 1, "", "V"], [11, 4, 1, "", "compose"], [11, 4, 1, "", "fmap"], [11, 4, 1, "", "preorder_fold"], [11, 4, 1, "", "preorder_mapfold"]], "cozy.hooks": [[13, 0, 0, "-", "strlen"], [14, 0, 0, "-", "strncmp"], [15, 0, 0, "-", "strtok_r"]], "cozy.hooks.strlen": [[13, 5, 1, "", "l"], [13, 1, 1, "", "strlen"]], "cozy.hooks.strlen.strlen": [[13, 3, 1, "", "max_null_index"], [13, 2, 1, "", "run"]], "cozy.hooks.strncmp": [[14, 5, 1, "", "l"], [14, 1, 1, "", "strncmp"]], "cozy.hooks.strncmp.strncmp": [[14, 2, 1, "", "run"]], "cozy.hooks.strtok_r": [[15, 5, 1, "", "l"], [15, 1, 1, "", "strtok_r"]], "cozy.hooks.strtok_r.strtok_r": [[15, 2, 1, "", "run"]], "cozy.log": [[17, 4, 1, "", "critical"], [17, 4, 1, "", "debug"], [17, 4, 1, "", "disable"], [17, 4, 1, "", "error"], [17, 4, 1, "", "info"], [17, 5, 1, "", "logger"], [17, 4, 1, "", "set_level"], [17, 4, 1, "", "warning"]], "cozy.primitives": [[18, 5, 1, "", "_malloc_name_ctr"], [18, 4, 1, "", "from_twos_comp"], [18, 4, 1, "", "sym_ptr"], [18, 4, 1, "", "sym_ptr_constraints"], [18, 4, 1, "", "to_twos_comp"]], "cozy.project": [[19, 1, 1, "", "Project"]], "cozy.project.Project": [[19, 2, 1, "", "add_prototype"], [19, 6, 1, "", "arch"], [19, 6, 1, "", "cfg"], [19, 2, 1, "", "find_symbol_addr"], [19, 2, 1, "", "hook_symbol"], [19, 2, 1, "", "hook_syscall"], [19, 2, 1, "", "object_ranges"], [19, 2, 1, "", "session"]], "cozy.server": [[20, 1, 1, "", "VizHandler"], [20, 4, 1, "", "get_vizroot"], [20, 4, 1, "", "start_viz_server"]], "cozy.server.VizHandler": [[20, 2, 1, "", "do_GET"]], "cozy.session": [[21, 1, 1, "", "RunResult"], [21, 1, 1, "", "Session"], [21, 1, 1, "", "_SessionBasicExploration"], [21, 1, 1, "", "_SessionDirectiveExploration"], [21, 1, 1, "", "_SessionExploration"], [21, 5, 1, "", "_malloc_name_ctr"], [21, 5, 1, "", "_mem_write_ctr"], [21, 4, 1, "", "_on_mem_write"], [21, 4, 1, "", "_on_simprocedure"], [21, 4, 1, "", "_save_states"]], "cozy.session.RunResult": [[21, 2, 1, "", "__str__"], [21, 6, 1, "", "assertion_triggered"], [21, 6, 1, "", "postcondition_triggered"], [21, 2, 1, "", "report"], [21, 2, 1, "", "report_asserts_failed"], [21, 2, 1, "", "report_errored"], [21, 2, 1, "", "report_postconditions_failed"], [21, 2, 1, "", "report_spinning"]], "cozy.session.Session": [[21, 2, 1, "", "_call"], [21, 2, 1, "", "_run_result"], [21, 2, 1, "", "_session_exploration"], [21, 2, 1, "", "add_constraints"], [21, 2, 1, "", "add_directives"], [21, 2, 1, "", "malloc"], [21, 6, 1, "", "mem"], [21, 6, 1, "", "memory"], [21, 2, 1, "", "run"], [21, 6, 1, "", "start_fun_addr"], [21, 2, 1, "", "store"], [21, 2, 1, "", "store_fs"]], "cozy.session._SessionBasicExploration": [[21, 2, 1, "", "explore"]], "cozy.session._SessionDirectiveExploration": [[21, 2, 1, "", "check_postconditions"], [21, 2, 1, "", "explore"]], "cozy.session._SessionExploration": [[21, 2, 1, "", "explore"]], "cozy.side_effect": [[22, 1, 1, "", "ConcretePerformedSideEffect"], [22, 1, 1, "", "PerformedSideEffect"], [22, 4, 1, "", "get_channel"], [22, 4, 1, "", "get_effects"], [22, 4, 1, "", "levenshtein_alignment"], [22, 4, 1, "", "perform"], [22, 4, 1, "", "test_levenshtein_alignment"]], "cozy.side_effect.ConcretePerformedSideEffect": [[22, 6, 1, "", "mapped_body"]], "cozy.stubs": [[23, 1, 1, "", "Stubber"], [23, 5, 1, "", "unstripped_binary_path"]], "cozy.stubs.Stubber": [[23, 2, 1, "", "extract_func"], [23, 2, 1, "", "get_callees"], [23, 2, 1, "", "make_callee_stubs"], [23, 2, 1, "", "make_stub"]], "cozy.terminal_state": [[24, 1, 1, "", "AssertFailedState"], [24, 1, 1, "", "DeadendedState"], [24, 1, 1, "", "ErrorState"], [24, 1, 1, "", "PostconditionFailedState"], [24, 1, 1, "", "SpinningState"], [24, 1, 1, "", "TerminalState"]], "cozy.terminal_state.TerminalState": [[24, 2, 1, "", "concrete_examples"], [24, 6, 1, "", "malloced_names"], [24, 6, 1, "", "mem_writes"], [24, 6, 1, "", "side_effects"], [24, 6, 1, "", "std_err"], [24, 6, 1, "", "std_out"], [24, 6, 1, "", "virtual_prints"]], "cozy.types": [[25, 4, 1, "", "register_type"], [25, 4, 1, "", "register_types"]]}, "objnames": {"0": ["py", "module", "Python module"], "1": ["py", "class", "Python class"], "2": ["py", "method", "Python method"], "3": ["py", "attribute", "Python attribute"], "4": ["py", "function", "Python function"], "5": ["py", "data", "Python data"], "6": ["py", "property", "Python property"]}, "objtypes": {"0": "py:module", "1": "py:class", "2": "py:method", "3": "py:attribute", "4": "py:function", "5": "py:data", "6": "py:property"}, "terms": {"": [1, 3, 4, 6, 9, 10, 13, 18, 19, 21, 22, 24, 27, 28, 29, 31, 32], "0": [1, 3, 4, 8, 9, 10, 18, 21, 28, 31], "0th": 28, "0x0": 28, "0x10": 28, "0x17": 28, "0x2a": 28, "0x2a000000": 28, "0x4": 28, "0x401179": 28, "0x44": 28, "0xc0000000": 28, "1": [0, 2, 3, 4, 7, 9, 26, 28, 29, 31, 32], "10": 0, "11": 0, "12": [0, 28], "13": 0, "14": [0, 28], "15": 0, "2": [0, 4, 9, 27, 28, 29, 31], "21": 28, "273": 31, "3": [0, 1, 21, 24, 28], "32": [9, 31], "4": [0, 8, 28], "42": 28, "5": [0, 28, 31], "6": [0, 4], "64": [18, 28], "7": [0, 28], "8": [0, 28, 31], "8080": [10, 20], "9": [0, 4], "A": [1, 2, 3, 4, 9, 10, 11, 18, 19, 21, 22, 23, 24, 27, 28], "As": 28, "At": [22, 28], "But": 9, "By": 3, "For": [1, 4, 9, 18, 22, 29, 32], "If": [1, 3, 4, 6, 9, 18, 19, 21, 23, 28], "In": [1, 9, 27, 28, 29, 31, 32], "It": 10, "No": 28, "On": 28, "One": 32, "That": 1, "The": [1, 2, 3, 4, 6, 7, 9, 10, 11, 18, 19, 20, 21, 22, 23, 24, 25, 27, 28, 29, 31, 32], "There": [27, 28, 31], "These": [6, 19], "To": [20, 28, 29, 31, 32], "With": 31, "__call__": 4, "__iter__": 1, "__main__": [16, 26, 30], "__str__": 21, "_call": 21, "_concret": 7, "_exploremod": 3, "_generate_comparison": 10, "_generate_concret": 3, "_get_bbl_asm": 10, "_has_syscal": 10, "_invalid_stack_addr": 1, "_invalid_stack_overlap": 1, "_list_act": 10, "_list_simproc": 10, "_malloc_name_ctr": [18, 21], "_mem_write_ctr": 21, "_on_mem_writ": 21, "_on_simprocedur": 21, "_run_result": 21, "_save_st": 21, "_serialize_diff": 10, "_serialized_field_diff": 10, "_session_explor": 21, "_sessionbasicexplor": 21, "_sessiondirectiveexplor": 21, "_sessionexplor": 21, "_set_replacement_dict": 3, "_stack_addr": 1, "_swap_explore_mod": 3, "a_addr": 14, "a_len": 14, "abc": [1, 3, 6, 9, 10, 11, 19, 21], "abl": 21, "about": [1, 6, 7, 9, 21, 24, 28], "abstract": [9, 21], "access": 21, "accomplish": 32, "accordingli": 28, "accum0": 11, "accumul": [9, 11], "achiev": 31, "action": 10, "activ": 3, "actual": 20, "ad": [1, 9, 10, 21, 28, 29, 31], "add": [9, 19, 20, 21, 28, 31], "add_calle": 4, "add_constraint": [9, 21, 28], "add_direct": [21, 27, 28], "add_prototyp": [19, 28, 31], "addit": [21, 27], "addition": 28, "addr": [1, 9, 21], "addr_postpatch": 28, "addr_prepatch": 28, "address": [1, 6, 7, 9, 18, 19, 21, 28, 32], "after": [1, 3, 23, 27, 31], "aid": 32, "aka": 28, "algorithm": 32, "align": [9, 22, 32], "all": [1, 3, 4, 6, 7, 9, 22, 28], "alloc": 21, "allow": 18, "along": [9, 24], "alreadi": [3, 19, 27], "also": [9, 19, 27, 28], "altern": [1, 3, 20, 27, 28, 32], "among": 23, "amount": 21, "amp": 31, "amp_challenge_arm": 23, "amp_target5_hackathon": 31, "an": [1, 3, 4, 6, 9, 10, 11, 18, 19, 20, 21, 22, 23, 24, 27, 28, 29, 32], "analogu": 9, "analys": [1, 23], "analysi": [10, 16, 23, 26, 27, 28, 30, 31, 32], "analyz": [19, 23, 31], "angr": [1, 3, 4, 6, 9, 10, 13, 14, 15, 19, 21, 22, 23, 24, 25, 27, 28, 29, 31, 32], "angr_proj": 19, "ani": [1, 3, 7, 9, 10, 11, 20, 21, 24, 28], "anoth": [1, 10, 21, 28], "api": [30, 31], "app": 0, "appli": [22, 32], "approach": 28, "appropri": 18, "ar": [1, 3, 4, 7, 9, 10, 19, 20, 21, 24, 27, 28, 29, 31, 32], "arbitrari": 11, "arbitrarili": 11, "arbitrarycandid": 4, "arch": [18, 19, 25, 28, 31], "archamd64": [18, 28], "archinfo": [18, 25, 28], "architectur": [18, 19, 25, 28], "arg": [1, 7, 10, 17, 20, 21, 24, 27, 31], "arg0": [23, 28, 31], "arg1": 23, "argc": 28, "args_left": 6, "args_right": 6, "argument": [1, 6, 7, 10, 11, 19, 20, 21, 23, 24, 27, 28, 32], "argv": 28, "ask_fil": 0, "ask_str": 0, "ask_yes_no": 0, "assembli": [10, 31], "assert": [1, 9, 21, 24, 30], "assert_can": 9, "assert_can_glob": 9, "assert_must": 9, "assert_typ": 9, "assertfail": 21, "assertfailedst": [21, 24], "assertion_trigg": 21, "asserts_fail": [21, 24, 28], "asserttyp": 9, "assign": [3, 27], "associ": [10, 28], "assum": [3, 9, 18, 21, 25, 27, 29, 30], "assume_warn": [21, 28], "assumpt": [9, 21], "ast": [1, 2, 3, 6, 9, 18, 21, 24], "async": 0, "attach": [1, 3, 9, 21, 22, 24, 28, 32], "attempt": [1, 32], "attribut": 10, "auto": 26, "autoapi": 26, "autogener": [3, 18], "automat": [10, 27, 31, 32], "avail": [21, 31, 32], "avoid": 32, "awar": 29, "b": [10, 11, 28], "b_addr": 14, "b_len": 14, "back": [4, 18], "backend": 19, "bar": 23, "base": [0, 1, 2, 3, 9, 10, 13, 14, 15, 20, 21, 24, 28], "base_effect": 22, "basic": [4, 10, 27, 28], "bbtransitioncandid": [4, 27], "becaus": [6, 28], "been": [1, 4, 6, 9, 21, 22, 24], "befor": [1, 4, 6, 9, 10, 21, 28, 31], "begin": 28, "behavior": 19, "behaviour": 9, "being": [1, 3, 6, 21, 24, 27, 28], "benefit": 27, "best": 29, "between": [1, 3, 27, 28, 31], "binari": [1, 10, 19, 23, 28, 31], "binary_path": [19, 23], "bit": [1, 2, 6, 9, 18, 21, 28], "bitvector": 18, "blob": 27, "block": [4, 10, 21, 27], "bodi": [1, 22, 32], "body_diff": 1, "bool": [1, 2, 3, 4, 6, 9, 10, 18, 19, 21, 24, 28], "both": [1, 3, 6, 11, 27, 28, 29], "bound": [3, 4, 6, 21, 24], "branch": [3, 27, 28], "breach": 21, "breakpoint": [9, 28, 32], "breakpoint_fun": [9, 32], "browser": [27, 31], "bug": 31, "build": 23, "bunch": 32, "bundl": 1, "bv": [2, 3, 6, 18, 28, 31], "bv32": 28, "bv64": 28, "bvv": [2, 3, 22], "byte": [1, 21, 24, 28], "c": [9, 11, 25, 28, 29], "cach": [6, 9, 19, 21], "cache_intermediate_info": [6, 21], "call": [3, 4, 6, 9, 10, 20, 21, 22, 23, 27, 28, 32], "callabl": [1, 3, 6, 9, 10, 11, 19, 21], "callback": 32, "calle": [4, 23], "caller": 23, "can": [1, 3, 9, 21, 22, 23, 27, 28, 31, 32], "can_be_nul": [18, 28], "candid": [3, 4, 27], "candidate_heurist": 3, "candidate_heuristic_left": [3, 6, 27], "candidate_heuristic_right": [3, 6, 27], "candidate_st": 4, "cannot": [1, 9], "capabl": 28, "carri": 10, "case": [1, 9, 28, 29, 31, 32], "caus": [1, 24, 28], "celsiu": 31, "certain": [4, 9, 19], "cfg": [19, 23], "cfg_fast": 23, "cfgfast": 23, "cg": 23, "chang": [19, 28, 31], "channel": [1, 22, 32], "char": [28, 31], "charact": 32, "check": [1, 3, 10, 28], "check_only_recent_constraint": 3, "check_postcondit": 21, "child": [3, 10, 27, 32], "children": [3, 27], "choos": [3, 4], "chose": 4, "chosen": [3, 4, 27], "chr": 32, "claripi": [1, 2, 3, 6, 7, 9, 10, 18, 21, 22, 24, 28, 31, 32], "claripy_ext": [16, 26, 30], "class": [27, 28, 32], "cle": 19, "cmd": [31, 32], "cmd_str": 32, "cmp_weather_v5_concol": 27, "code": [23, 28, 31, 32], "collect": [1, 3, 6, 9, 10, 11, 19, 21, 28], "com": [27, 28], "command": 20, "common": [19, 32], "compar": [1, 27, 28, 30, 32], "compare_and_dump": 20, "compare_memori": 1, "compare_regist": 1, "compare_side_effect": 1, "compare_st": 1, "compare_std_err": 1, "compare_std_out": 1, "comparison": [1, 10, 20, 22, 27, 30, 31, 32], "comparison_result": [10, 27, 28], "comparit": 29, "compat": [1, 7, 27, 28], "compatiblepair": [1, 10], "compatiblepairinput": [1, 7], "compil": 28, "complement": [1, 10, 18, 21, 22], "complet": [0, 3, 9, 21, 27, 29], "completetermin": 4, "complex": [1, 4, 27], "compos": [0, 11], "composeresult": 0, "composit": 21, "comput": [4, 9, 28], "compute_mem_diff": 1, "compute_reg_diff": 1, "compute_side_effect_diff": 1, "concol": [1, 16, 26, 29, 30], "concolicdef": 3, "concolicsim": [3, 4], "concret": [1, 3, 6, 9, 10, 16, 18, 21, 22, 24, 26, 27, 28, 30, 32], "concrete_addr": 18, "concrete_arg_mapp": 27, "concrete_cmd": 32, "concrete_exampl": [1, 7, 24], "concrete_init": 3, "concrete_mapp": 27, "concrete_post_processor": [1, 9, 10, 21, 22, 32], "concrete_valu": [9, 32], "concreteperformedsideeffect": [7, 22], "cond": 24, "condit": [9, 24, 28], "condition_fun": [9, 28], "configur": 29, "confirm_script_clobb": 0, "confirm_start": 0, "congruency_check": 1, "congruencycheck": 1, "conjoin": 2, "conjunct": 28, "connect": 32, "conserv": 29, "consid": [1, 9], "consist": [11, 21, 22], "consol": 28, "constant": [16, 26, 28, 30, 31], "constrain": [18, 28, 29], "constraint": [1, 2, 3, 9, 10, 18, 21, 24, 27, 28, 29], "construct": [4, 9, 10, 21, 22, 27, 28, 32], "constructor": [3, 9, 19, 21, 24], "contain": [1, 18, 24, 26, 28], "content": [27, 28], "contradict": 21, "control": 19, "convent": 21, "convers": 31, "convert": [1, 10, 18, 21, 28, 31, 32], "copi": [1, 10, 24, 32], "core": [1, 27], "correspond": [1, 10, 24], "corrspond": 10, "could": [21, 22, 28], "count": [3, 6, 21], "cours": 27, "cover": [28, 31], "coverag": 4, "coverage_fract": 4, "coveragetermin": 4, "cozi": [26, 27, 29, 31, 32], "creat": [1, 3, 11, 19, 21, 24, 26, 27, 28, 29, 31], "critic": 17, "css": 0, "current": [1, 3, 9, 20, 21, 28, 32], "custom": [1, 30, 32], "cyclomat": [4, 27], "cyclomaticcomplexitytermin": [4, 27], "cytoscapej": 10, "d": 28, "data": [1, 7, 10, 20, 21, 24, 32], "datastructur": [1, 7, 10, 11, 24], "datatyp": 31, "dead": 10, "deadend": [4, 21, 24, 28], "deadended_st": 28, "deadendedst": [21, 24], "deal": [28, 30], "debug": [10, 17, 28], "decor": 10, "deep": [1, 10, 24], "deeper": 4, "def": [23, 31, 32], "default": [10, 19, 20, 29, 32], "defer": [3, 27], "deferred_stash": 3, "defin": [0, 3, 30], "definit": [23, 25], "delet": 19, "delim_ptr": 15, "delim_strlen": 15, "demo": 31, "demonstr": 28, "derefer": 28, "dereferenc": 28, "deriv": [0, 3, 19, 21], "describ": 28, "desir": [3, 9, 27], "detect": 28, "determin": [1, 3, 4, 9, 20, 24, 27, 28], "dict": [1, 2, 3, 7, 19, 20, 21, 22, 24], "dictionari": [1, 3, 9, 11, 19, 22, 24, 32], "dif": [1, 9], "diff": [1, 10, 28], "differ": [1, 7, 10, 22, 27, 28, 29, 32], "differenc": 1, "diffresult": 1, "digraph": 10, "direct": [10, 16, 21, 24, 26, 30, 32], "directli": 27, "directori": [19, 20], "directorytre": 0, "disabl": [1, 17, 29], "disjunct": 18, "displai": [10, 22, 28], "divis": 21, "do": [1, 3, 9, 19, 27, 28, 29], "do_get": 20, "document": 26, "doe": 9, "down": [1, 22], "draperlaboratori": [27, 28], "dual": 9, "due": [1, 10, 21, 24, 28], "dump": [6, 10, 21], "dump_bbp_pp_cytoscap": 10, "dump_comparison": 10, "dure": [1, 6, 21, 23, 31], "dwarf": 10, "e": [9, 28], "each": [3, 7, 10, 21, 22, 27, 28, 31], "easili": [1, 28], "eax": [1, 9], "edg": 27, "effect": [1, 7, 9, 10, 22, 30], "effect_concrete_post_processor": 9, "effici": 1, "eflag": 28, "either": [3, 9, 21, 24, 27, 28], "element": [1, 4, 6, 24], "elf": [19, 23], "elimin": 29, "ellipsi": 11, "empti": [1, 19, 22, 23, 28], "emul": 3, "enabl": [29, 32], "encapsul": [1, 22, 28], "encod": 28, "end": [3, 10, 28], "endian": 31, "engin": [27, 31], "enhanc": 1, "ensur": 3, "entir": 1, "entri": [19, 21, 22], "enum": [0, 3, 9], "enumer": [0, 3], "environ": 9, "eqfielddiff": 1, "equal": [1, 2, 4, 18, 28], "equal_side_effect": 1, "equival": [27, 28], "error": [1, 9, 17, 21, 24, 28], "error_record": 24, "errordirect": [9, 21, 24], "errorrecord": 24, "errorst": [21, 24], "etc": 32, "evalu": [3, 27, 28], "eventu": 19, "everi": [3, 27], "exact": 9, "exactli": 28, "exampl": [1, 9, 10, 18, 19, 21, 22, 24, 27, 29, 30, 31, 32], "exce": 27, "except": [1, 7, 20], "execuct": 10, "execut": [1, 3, 4, 6, 9, 10, 19, 21, 22, 23, 24, 28, 29, 30], "execution_graph": [16, 20, 26, 27, 30, 31], "executiongraph": 10, "exeuct": 1, "exist": [9, 28], "existenti": 9, "expens": 27, "explor": [4, 5, 9, 21, 26, 27], "explorationtechniqu": 3, "explore_fun_left": 3, "explore_fun_right": 3, "explore_left": 3, "explore_right": 3, "expr": 2, "express": [1, 2, 18], "extern": [1, 3], "extra": [9, 19], "extra_symbol": 2, "extract": 23, "extract_func": 23, "f": [11, 21, 28], "factor": 29, "factori": 9, "fail": [1, 9, 21, 24, 28], "failur": [1, 28], "failure_st": 24, "fals": [1, 3, 4, 10, 13, 14, 19, 20, 21, 27, 28, 29], "falsifi": [9, 21, 24], "farhenheit": 31, "fast": 28, "fed": [6, 28, 29], "field": [1, 31], "fielddiff": 1, "fifth": 32, "figur": [1, 21], "file": [10, 19, 20, 21], "file_name_a": 10, "file_name_b": 10, "filenam": [10, 21], "fileselect": 0, "filesystem": 21, "fill": 23, "filter": [3, 19], "final": [11, 28], "find": [1, 3, 19, 28], "find_symbol_addr": 19, "finish": 3, "first": [1, 3, 6, 10, 11, 24, 28, 32], "fix": 21, "flag": [1, 6, 10, 21, 28], "flag_syscal": 10, "float": 4, "flow": 19, "fmap": [1, 11], "fold": 11, "folder": [28, 31], "follow": [9, 23, 28, 31], "foo": 23, "foral": 9, "fork": 32, "form": [18, 23, 28], "format": [10, 25], "fortun": 29, "forward": 21, "found": [1, 3, 27, 28, 29, 31, 32], "fourth": 32, "fp": [2, 3], "fpv": [2, 3, 22], "fraction": 4, "framework": [27, 28], "frequent": 4, "fresh": [18, 19, 28, 29], "from": [0, 1, 3, 4, 9, 10, 18, 19, 20, 21, 22, 23, 24, 27, 28, 29, 32], "from_fun_offset": [9, 28], "from_sess": [4, 27], "from_stash": 3, "from_stash_left": 3, "from_stash_right": 3, "from_twos_comp": 18, "frozenset": [1, 2, 3, 6, 24, 28], "full": [10, 27], "fun": [4, 9, 19], "fun_manag": 4, "fun_nam": [9, 28], "fun_prototyp": 19, "func": 23, "func_nam": 23, "function": [3, 4, 6, 9, 19, 23, 27, 28, 29, 31, 32], "functionmanag": 4, "functools_ext": [1, 16, 26, 30], "further": [1, 3, 9, 30], "futur": [9, 19], "g": 11, "gener": [0, 1, 3, 4, 6, 10, 18, 20, 23, 24, 26, 27, 28, 29], "generate_concret": [3, 4], "get": [20, 22, 30, 31], "get_calle": 23, "get_channel": 22, "get_effect": 22, "get_pair": 1, "get_symbol_nam": 2, "get_vizroot": 20, "getter": 24, "ghidra": 28, "github": [27, 28], "give": [1, 27, 28], "given": [1, 2, 3, 10, 22, 23, 25, 28], "global": 9, "go": 28, "goe": 4, "graph": [4, 6, 10, 19, 21, 23], "grididp": 23, "gs_data_processor": 31, "gs_data_processor_draper_patch": 31, "guess_typ": 20, "guid": 27, "h": [11, 28], "ha": [1, 4, 9, 21, 22, 24, 28, 29, 32], "hackathon": 31, "halt": [3, 9, 27, 28], "handle_confirm_script_clobb": 0, "handle_confirm_start": 0, "handle_request_concol": 0, "handle_request_concolic_complet": 0, "handle_request_dump": 0, "handle_request_dump_nam": 0, "handle_request_function_nam": 0, "handle_request_hook": 0, "handle_request_postpatch": 0, "handle_request_prepatch": 0, "handle_request_script_nam": 0, "handle_request_signatur": 0, "handle_request_textual_report": 0, "handle_request_visu": 0, "handler": 20, "has_run": 21, "have": [1, 3, 4, 6, 9, 22, 24, 27, 28, 32], "head": 20, "header": 31, "heap": 21, "helper": 28, "henc": [22, 28], "here": [1, 3, 24, 27, 28, 32], "heurist": [3, 5, 26, 27], "hex": 1, "hexifi": 1, "histori": [4, 10, 27], "hold": [1, 9, 10], "hook": [16, 19, 26, 30, 32], "hook_symbol": [19, 29], "hook_syscal": 19, "how": [27, 29, 30], "howev": 27, "http": [20, 27, 28], "human": [1, 9, 18, 21, 28], "i": [1, 2, 3, 4, 6, 7, 9, 10, 18, 19, 20, 21, 22, 23, 24, 27, 28, 29, 30, 31], "idea": 22, "ident": [9, 20], "identif": 24, "identifi": 24, "ie": [3, 9, 22, 32], "ignor": 1, "ignore_addr": 1, "ignore_cas": 14, "ignore_invalid_stack": 1, "implement": [3, 27, 28, 29], "import": 28, "includ": [1, 9, 10, 20, 21, 27, 28, 29, 32], "include_act": 10, "include_debug": 10, "include_side_effect": 10, "include_simproc": 10, "include_vex": 10, "incom": 31, "incomplet": [27, 29], "incorpor": 10, "increas": 4, "index": [24, 30], "indic": [24, 28], "info": 17, "info_str": [9, 28], "inform": [1, 2, 6, 7, 9, 10, 20, 21, 24, 28], "inherit": [23, 32], "initi": [3, 11, 19, 21], "initialize_st": 27, "inline_cal": 32, "ino_unstrip": 23, "input": [0, 1, 2, 3, 9, 10, 11, 18, 21, 24, 25, 27, 28, 31], "insert": 21, "insid": [9, 11], "inspect": [1, 28, 31], "instanc": [19, 22], "instead": [3, 19, 27, 28, 32], "instruct": [1, 24, 28], "int": [1, 3, 4, 6, 7, 9, 10, 18, 19, 20, 21, 24, 28, 31], "int32_t": 31, "int_arg_0_64": 28, "int_siz": [8, 28], "integ": [1, 4, 9, 10, 18, 21, 22, 28, 32], "intellig": 32, "interact": 28, "interest": [28, 32], "interfac": [21, 32], "intermedi": [6, 21], "intern": [1, 3, 4, 9, 10], "intersect": [1, 28], "interv": [1, 24], "intervaldict": [1, 24], "invalid_stack_left": 1, "invalid_stack_right": 1, "invok": 28, "involv": 1, "io": 32, "ir": 10, "is_compat": 1, "is_sat": 28, "is_satisfi": 3, "issu": 29, "iter": [1, 3, 6, 21, 24], "its": [3, 20, 24, 27, 32], "itself": [1, 19, 24], "ivar": 21, "job": 22, "joint": [1, 3, 27, 29], "joint_sess": 27, "joint_solv": 1, "jointconcolicsess": [6, 27], "jointconcolicsim": 3, "jointli": [1, 3, 6, 28], "json": [10, 20], "jumpkind": 10, "just": 24, "kb": 2, "keep": [4, 32], "kei": [1, 7, 11, 22, 24, 32], "kelvin": 31, "keyword": [11, 19, 21], "kill": 24, "know": 28, "knowledg": 2, "knowledge_plugin": [4, 23], "kw": 11, "kwarg": [2, 3, 17, 19, 20, 21], "l": [13, 14, 15], "label": [9, 22, 32], "lambda": 28, "larg": 1, "last": [3, 4], "launch": [10, 30], "lead": [27, 28], "leaf": 1, "least": [1, 4, 28], "left": [1, 3, 6], "left_bodi": 1, "left_explor": 3, "left_leaf": 1, "left_s": 1, "left_side_effect": 7, "length": [1, 3], "less": 28, "let": [27, 28, 31], "level": 17, "levenshtein_align": 22, "libc": [19, 29, 32], "librari": [28, 29], "like": [3, 9, 10, 20, 21, 27, 28, 32], "likewis": 29, "limit": [14, 28, 29], "line": 28, "list": [1, 2, 3, 4, 6, 7, 9, 10, 11, 19, 21, 22, 23, 24, 28, 32], "literatur": 27, "load": [10, 32], "load_debug_info": 19, "local": 9, "localhost": 20, "localloops": 24, "locat": [1, 7, 9, 10, 19, 22, 28, 32], "log": [9, 10, 16, 26, 30], "log_fun": 9, "logger": 17, "logic": [9, 31], "look": [4, 9, 32], "lookback": 4, "lookup": 19, "loop": [3, 6, 21, 24, 28], "loop_bound": 21, "loop_bound_left": [3, 6], "loop_bound_right": [3, 6], "lot": 1, "lst_a": 22, "lst_b": 22, "made": [3, 7, 27], "mai": [1, 6, 9, 10, 11, 21, 22, 24, 28, 29, 31, 32], "main": [19, 27, 28], "make": [1, 10, 19, 21, 22, 24, 30, 31, 32], "make_callee_stub": 23, "make_stub": 23, "malloc": [21, 28, 31], "malloced_nam": [1, 24], "manag": 3, "mani": [28, 32], "manual": [28, 31], "map": [1, 3, 11, 19, 28], "mappabl": 1, "mapped_bodi": 22, "max": 32, "max_len": 32, "max_null_index": 13, "max_symbolic_strstr": 29, "maximum": [1, 4, 21, 24], "maxlen": 13, "mean": [19, 21, 28], "mem": [21, 28, 31], "mem_diff": [1, 7], "mem_diff_ip": 1, "mem_writ": 24, "mem_write_okay_postpatch": 28, "mem_write_okay_prepatch": 28, "member": 21, "memoiz": [1, 28], "memori": [1, 6, 7, 10, 19, 21, 24, 27, 28, 31, 32], "messag": [0, 28], "metadata": 10, "method": [1, 3, 6, 10, 19, 20, 21, 27, 28, 29, 31, 32], "metric": 4, "micropatch": 31, "might": 27, "mime": 20, "mix": 22, "mixtur": [22, 32], "model": [2, 30], "modifi": [9, 21], "modul": [27, 30], "more": [1, 9, 22, 28, 29], "most": [1, 4, 19, 27, 28, 29], "move": 9, "msg": 31, "much": [1, 9, 28], "multidigraph": 23, "multilin": 0, "multipl": [21, 32], "multipli": 4, "must": [1, 4, 18, 22, 27, 32], "mutat": [3, 27, 31], "my": 24, "my_fun": 28, "my_num": 28, "n": [2, 7, 28], "naiv": 28, "name": [1, 3, 7, 9, 10, 18, 19, 21, 22, 23], "necessari": [1, 31], "need": [4, 6, 19, 27, 28, 29, 32], "neg": [1, 9, 10, 18, 21, 22], "negat": 27, "nest": [1, 11], "network": [22, 31, 32], "networkx": [10, 23], "never": 28, "new": [0, 3, 9, 11, 19, 21, 27, 28, 31], "newli": 11, "next": [3, 4, 27, 28, 31], "nice_nam": 1, "nice_name_a": 10, "nice_name_b": 10, "non": [1, 3, 6, 18, 21, 22, 28], "none": [0, 1, 3, 6, 9, 10, 13, 14, 15, 18, 19, 21, 22, 28], "nonempti": 28, "normal": [21, 24, 27], "note": [1, 3, 9, 11, 19, 21, 24, 27, 28, 32], "noteqfielddiff": 1, "noteqleaf": 1, "now": [10, 22, 27, 28, 31], "null": [18, 28], "null_deref": 28, "null_deref_patch": 28, "null_ptr": 8, "num": 28, "num_arg": 28, "num_bit": 18, "num_byt": 21, "num_exampl": [1, 10, 21, 24, 27, 31], "number": [1, 4, 9, 10, 11, 18, 21, 24, 27], "o": 30, "obj_filt": 19, "object": [1, 3, 4, 9, 19, 27, 28, 31, 32], "object_rang": 19, "observ": 28, "observation": 1, "obtain": 32, "occupi": 1, "occur": [1, 2, 10, 21, 24, 28, 31], "off": [1, 4, 10, 21, 28], "offset": [9, 28], "often": 28, "omit": 20, "on_directory_tree_file_select": 0, "on_input_submit": 0, "on_option_list_option_select": 0, "onc": [4, 21, 27, 28], "one": [1, 3, 10, 19, 28, 31], "onli": [1, 3, 4, 9, 19, 21, 22, 28], "open": [10, 28, 31], "open_brows": [10, 20, 27, 31], "oper": 10, "optim": [1, 28], "option": [1, 10, 20, 22, 29, 32], "optionlist": 0, "optionselect": 0, "order": [11, 31], "ordinari": 22, "origin": [2, 31], "orphan": [1, 28], "orphans_left": 1, "orphans_right": 1, "other": [1, 9, 28], "otherwis": [1, 3, 4, 21], "our": [1, 3, 27, 28, 29, 31, 32], "ourselv": 3, "out": [1, 21, 23], "output": [1, 7, 9, 19, 23, 24, 32], "output_fil": 10, "outsid": 27, "over": [1, 4, 11, 28, 32], "p": [9, 24], "packet": 31, "packetdata": 31, "pad": 0, "page": [26, 28, 30], "pair": [1, 7, 10, 24, 27, 28], "param": [6, 21], "paramat": 18, "paramet": [1, 2, 3, 4, 6, 9, 10, 11, 18, 19, 20, 21, 22, 23, 24, 25], "parent": [3, 10, 32], "pars": 25, "part": [1, 10, 21, 28], "particular": [1, 9, 21, 24, 28], "pass": [6, 7, 9, 18, 19, 21, 22, 23, 27, 28], "past": 4, "patch": [1, 31], "path": [9, 19, 23, 27, 28], "paus": 28, "payload": [22, 32], "perform": [1, 22, 27, 30], "performedsideeffect": [1, 7, 22, 24], "pertain": [1, 24], "pick": 27, "pickl": 19, "piec": 9, "place": [3, 27], "placehold": [0, 24], "plu": 4, "plugin": 21, "point": [1, 9, 19, 22, 28], "pointer": [1, 18, 21, 24, 28], "pop": 4, "port": [10, 20], "portion": [1, 24, 27], "posit": 24, "possibl": [1, 3, 9, 24, 28, 29, 32], "post": [1, 10, 20, 21, 22, 32], "post_patch": 1, "post_patch_st": 1, "postcondit": [9, 21, 24, 28], "postcondition_trigg": 21, "postconditionfailedst": [21, 24], "postconditions_fail": [21, 24, 28], "postpatch": [7, 10, 20, 27, 28, 31], "postpatched_result": [27, 28, 31], "practic": 28, "pre": [1, 3, 6, 10, 20, 27], "pre_patch": 1, "pre_patch_st": 1, "precis": 9, "precondit": [9, 28], "predic": 28, "prefer": [21, 31], "preorder": 11, "preorder_fold": 11, "preorder_mapfold": 11, "prepatch": [7, 10, 20, 27, 28, 31], "prepatched_result": [27, 28, 31], "presenc": 28, "present": 10, "pretti": [9, 10], "prevent": 28, "previou": 3, "previous": [1, 22, 28], "primari": [27, 31, 32], "primit": [16, 26, 28, 30], "print": [1, 9, 10, 22, 24, 28], "printf": 28, "prior": 21, "problemat": 29, "proce": 27, "procedur": 19, "process": [1, 10, 21, 22, 32], "process_command": 32, "process_sensor_data": 27, "produc": [24, 32], "program": [1, 3, 6, 9, 10, 19, 21, 24, 27, 28, 29, 32], "proj": [10, 21], "proj_a": 10, "proj_b": 10, "proj_postpatch": [27, 28, 31], "proj_prepatch": [27, 28, 31], "project": [1, 4, 9, 10, 16, 21, 24, 26, 27, 28, 29, 30, 31], "promis": 27, "properli": 29, "properti": [9, 19, 21, 22, 24], "proposit": [9, 18], "prototyp": 19, "provid": [3, 9, 22, 28, 29], "pseudocod": 28, "purpos": [1, 22], "put": [1, 24], "py": 27, "python": [1, 7, 9, 10, 11, 18, 22, 23, 24, 28, 32], "qemu": 3, "quantif": 9, "queri": [3, 20], "quit": [1, 28], "r": 32, "rang": [1, 4, 7, 19, 24, 28, 32], "rather": [1, 27], "rax": [1, 28], "re": 28, "reach": [4, 9, 21, 24, 27, 28], "read": 10, "readabl": [1, 9, 10, 18, 21, 22, 28], "readi": [21, 27, 31], "rebas": 19, "recal": 28, "reconstruct_bbl_addr_graph": 10, "reconstruct_bbl_pp_graph": 10, "record": 4, "recov": 10, "recurs": [1, 4], "redirect": 32, "refer": [19, 28, 30], "reg": [9, 28], "reg_diff": [1, 7], "regist": [1, 7, 10, 25, 28, 31], "register_typ": [25, 31], "relev": [10, 27], "rememb": 27, "remov": 3, "render": 10, "repl": 28, "replac": [1, 7, 10, 19, 24, 29, 31], "replic": 9, "report": [1, 21, 28], "report_asserts_fail": [21, 28], "report_error": 21, "report_postconditions_fail": 21, "report_spin": 21, "repositori": [28, 31], "repres": [1, 9, 10, 19, 22, 23, 28, 31, 32], "represent": [1, 18, 24], "request": [20, 22], "request_concol": 0, "request_concolic_complet": 0, "request_dump": 0, "request_dump_nam": 0, "request_function_nam": 0, "request_hook": 0, "request_postpatch": 0, "request_prepatch": 0, "request_script_nam": 0, "request_signatur": 0, "request_textual_report": 0, "request_visu": 0, "requir": [6, 21, 22, 27, 28, 32], "respect": [1, 3], "restrict": 28, "result": [0, 1, 3, 6, 9, 10, 21, 24, 27, 28, 31], "resum": 9, "ret_addr": 21, "ret_addr_left": 6, "ret_addr_right": 6, "ret_expr": 32, "retain": 28, "retriev": [1, 22], "return": [1, 2, 3, 6, 9, 10, 11, 18, 19, 21, 22, 23, 24, 25, 27, 28, 31, 32], "reveal": 28, "revers": 31, "rflag": 28, "right": [1, 3, 6], "right_bodi": 1, "right_explor": 3, "right_leaf": 1, "right_s": 1, "right_side_effect": 7, "roughli": 28, "rover_message_struct": 31, "rover_process": 31, "roverdata_t": 31, "rovermessage_t": 31, "rslt_a": 10, "rslt_b": 10, "rtype": 21, "run": [3, 4, 6, 7, 9, 13, 14, 15, 19, 21, 23, 27, 28, 31, 32], "run_result": 28, "runresult": [1, 6, 10, 21, 24, 27, 28, 31], "runtimeerror": 21, "s0": 28, "safe": 3, "sai": 28, "same": [1, 3, 7, 9, 19, 27, 28, 29, 32], "sat": 9, "satisfi": [1, 3, 9, 21, 24, 28], "save": [1, 9, 10], "save_ptr": 15, "scan": 10, "scenario": 1, "screen": [0, 9], "script": 28, "scrub": 9, "search": [4, 30], "second": [1, 3, 6, 10, 11, 24, 28, 32], "see": [10, 27, 28, 29], "seek": 4, "seen": 4, "select": [4, 27], "self": [21, 23, 32], "send": 22, "sent": 22, "separ": 24, "seri": [2, 25], "serial": [10, 32], "serv": [10, 20], "server": [10, 16, 26, 30], "sess": [4, 31], "sess_explor": 21, "sess_left": 6, "sess_orig": 27, "sess_postpatch": [27, 28], "sess_prepatch": [27, 28], "sess_right": 6, "session": [1, 3, 4, 5, 9, 10, 16, 19, 26, 27, 28, 30, 31], "set": [1, 2, 3, 4, 6, 9, 11, 17, 21, 27, 28, 29, 32], "set_concret": 3, "set_confirm_script_clobb": 0, "set_confirm_start": 0, "set_level": 17, "set_request_concol": 0, "set_request_concolic_complet": 0, "set_request_dump": 0, "set_request_dump_nam": 0, "set_request_function_nam": 0, "set_request_hook": 0, "set_request_postpatch": 0, "set_request_prepatch": 0, "set_request_script_nam": 0, "set_request_signatur": 0, "set_request_textual_report": 0, "set_request_visu": 0, "set_stag": 0, "setup": 3, "should": [1, 3, 4, 9, 11, 18, 22, 25, 28, 29, 31, 32], "show": [0, 1, 21], "shown": [9, 32], "side": [1, 7, 9, 10, 22, 30], "side_effect": [1, 7, 16, 24, 26, 30, 32], "side_effect_diff": 1, "sign": [9, 18], "signatur": [19, 21, 28, 31], "silent": 19, "sim_manag": [6, 21, 24], "sim_procedur": [19, 32], "sim_typ": 25, "simerror": 24, "simfil": 21, "simgr": [3, 4, 21], "simgr_left": 3, "simgr_right": 3, "similar": 1, "simpl": [20, 28], "simple_strtok": 29, "simplehttprequesthandl": 20, "simpli": [9, 21], "simplif": 2, "simplifi": [1, 2, 28], "simplify_kb": 2, "simproc_class": 19, "simprocedur": [10, 13, 14, 15, 19, 23, 32], "simstat": [1, 3, 4, 6, 9, 10, 21, 22, 24, 28, 29, 32], "simstatehistori": [10, 22], "simtyp": 25, "simul": [3, 21, 32], "simulationmanag": [3, 6, 21], "simultan": 11, "sinc": [1, 27, 28, 29, 31], "singl": [1, 19, 24, 28], "singleton": [7, 24], "site": [22, 32], "size": [18, 31], "sl": 1, "slice": [1, 10, 21], "slightli": 27, "small": 4, "smt": 28, "so": [1, 3, 20, 27, 28, 31, 32], "sole": 28, "solut": 3, "solver": [7, 9, 28, 32], "some": [1, 2, 3, 4, 9, 10, 21, 27, 28, 29, 31], "somewher": 4, "sort": 11, "sourc": [23, 28, 31], "space": [18, 28], "special": 9, "specif": [1, 4, 18, 22, 24, 28, 29], "specifi": 19, "speed": [1, 19], "sphinx": 26, "spin": [21, 24, 28], "spinningst": [21, 24], "sr": 1, "st": 1, "stack": 1, "stack_chang": 1, "stage": 0, "standalon": 27, "start": [4, 9, 19, 21, 30, 31], "start_fun": [19, 21], "start_fun_addr": 21, "start_viz_serv": 20, "stash": [3, 21, 27], "state": [1, 3, 4, 6, 7, 9, 10, 21, 22, 24, 27, 28, 29, 31, 32], "state_bundl": 7, "state_histori": 22, "state_id": 24, "state_left": 1, "state_plugin": [10, 22], "state_right": 1, "state_tag": 24, "state_type_str": 24, "statediff": 1, "static": [4, 9], "std_err": 24, "std_out": 24, "stderr": [1, 10, 24, 32], "stdio": 28, "stdout": [1, 10, 24, 32], "stem": 29, "step": [6, 28], "storag": 21, "store": [1, 3, 6, 7, 10, 18, 19, 21, 24, 28, 31], "store_f": 21, "str": [1, 7, 9, 10, 18, 19, 21, 22, 23, 24, 25], "str_ptr": 15, "str_strlen": 15, "straightforward": 32, "strategi": [27, 29], "string": [0, 1, 3, 10, 20, 21, 22, 23, 24, 25, 32], "strlen": [12, 26, 29, 32], "strncmp": [12, 26], "strstr": 29, "strtok_r": [12, 26, 29], "struct": 31, "stub": [16, 26, 30], "stubber": 23, "style": 25, "subcompon": 1, "subdirectori": 20, "subfield": 1, "subject": 3, "submit": 0, "submodul": 30, "subpackag": 30, "subpart": 1, "subregist": 1, "subset": 28, "substitut": [3, 27], "subsystem": 32, "success": 1, "suitabl": [1, 10], "sum": 4, "summari": [1, 28], "superior": 32, "support": 28, "suppos": 28, "swap": 31, "sym": 2, "sym_nam": 19, "sym_ptr": [18, 28], "sym_ptr_constraint": [18, 28], "symbol": [1, 3, 6, 7, 9, 10, 18, 19, 22, 23, 24, 27, 28, 29, 31, 32], "symbol_nam": 19, "symbolic_ptr": 18, "symbolic_ptr_i": 18, "syntax": 28, "syscal": [10, 19], "syscall_nam": 19, "t": [1, 11], "take": [1, 3, 9, 11, 22, 28, 32], "target": [28, 31], "task": 31, "techniqu": [3, 27], "tell": [3, 4, 28], "temp": 31, "temperatur": 31, "term": 28, "termin": [3, 4, 6, 9, 21, 24, 27, 28], "terminal_st": [1, 16, 21, 26, 30], "terminalst": [1, 7, 24], "terminalstateinput": [7, 24], "termination_fun_left": 3, "termination_fun_right": 3, "termination_heuristic_left": [6, 27], "termination_heuristic_right": [6, 27], "test": [9, 24, 28], "test_levenshtein_align": 22, "test_program": [23, 28], "text": 0, "textual": 0, "than": [1, 27], "thei": [1, 9, 10, 21, 24, 28], "them": [3, 28], "themselv": 1, "therefor": [24, 28], "thi": [0, 1, 2, 3, 4, 6, 9, 10, 11, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 31, 32], "thing": [1, 21], "think": 28, "third": [1, 32], "those": [23, 28], "through": [22, 27, 28], "thrown": [3, 21, 24], "ti": 21, "time": [3, 21, 28], "to_str": 0, "to_twos_comp": 18, "togeth": [2, 28], "tool": [28, 31], "total": 4, "traceback": 24, "track": 32, "transform": [1, 22], "transit": [4, 27], "travers": [1, 11], "tree": [1, 9, 28], "trigger": [9, 21, 24, 28], "true": [1, 3, 4, 6, 10, 11, 18, 19, 21, 27, 28, 31], "tupl": [1, 6, 7, 10, 11, 21, 22, 24, 28, 32], "two": [1, 3, 6, 10, 11, 18, 21, 22, 23, 27, 28, 29, 31], "type": [1, 2, 3, 6, 9, 11, 16, 18, 19, 20, 21, 22, 23, 24, 26, 30, 32], "type_definit": 25, "typic": [1, 2, 9, 27, 28, 32], "u": [11, 28], "under": [1, 9, 23], "underli": 19, "understand": [1, 29], "unequ": 1, "unicorn": [3, 27], "uniqu": [4, 24, 27], "univers": 9, "unknown": 9, "unpack": 1, "unrun": 4, "unsat": 1, "unsign": 31, "unstripped_binary_path": 23, "until": [4, 28], "up": [1, 4, 19, 21, 27], "upper": [3, 4, 6, 21, 24], "url": 20, "us": [1, 2, 3, 4, 6, 9, 10, 18, 19, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31, 32], "use_unsat_cor": 1, "user": [1, 9, 21, 22, 31, 32], "usual": 1, "util": 21, "v": 11, "val": 18, "val0": [1, 11], "valid": [1, 28], "valu": [0, 1, 3, 4, 6, 7, 9, 10, 11, 18, 22, 24, 27, 28, 29, 31, 32], "valueerror": 3, "variabl": [1, 3, 7, 9, 10, 19, 21, 23, 24, 28, 29, 31], "vector": 9, "veri": [1, 28, 29], "verif": 1, "verifi": [1, 9, 28], "verification_assert": 1, "version": [1, 2, 7, 9, 10, 21, 22, 28, 32], "vex": 10, "via": [3, 9, 28], "view": [10, 22, 28], "virtual": [9, 10, 21, 22, 24], "virtual_print": 24, "virtualprint": [9, 24], "virut": 9, "visit": 4, "visual": [6, 10, 20, 21, 27, 30, 32], "visualize_comparison": [10, 27, 31], "viz": [10, 20], "vizhandl": 20, "void": 28, "wa": [1, 6, 21, 22, 24, 28], "wai": [28, 32], "walk": [27, 28], "walkthrough": 30, "want": [1, 9, 22, 31, 32], "warn": [17, 19, 21], "wchar": [13, 14], "we": [1, 3, 4, 6, 9, 10, 22, 24, 27, 28, 29, 31, 32], "web": 31, "well": [28, 31], "went": 28, "were": [21, 28], "what": [1, 4, 6, 9, 19, 21, 27, 28], "when": [1, 3, 4, 9, 21, 22, 24, 27, 28, 29, 32], "whenev": 3, "where": [1, 6, 9, 21], "wherea": 1, "whether": 10, "which": [1, 2, 3, 4, 6, 9, 10, 11, 18, 19, 21, 22, 24, 27, 28, 29, 31, 32], "while": [1, 24], "whose": [4, 18, 22, 32], "why": 1, "widget": 0, "window": 31, "wish": [10, 27, 31, 32], "within": [1, 31], "without": [3, 28], "wizard": 0, "work": 28, "workflow": 27, "would": [9, 22, 32], "write": [1, 10, 24, 28, 32], "written": [1, 24, 28, 32], "wrong": 28, "wrote": [1, 24, 28], "x": 9, "x64": [1, 18, 28], "yet": [4, 22], "you": [1, 9, 19, 21, 22, 27, 28, 29, 32], "your": [9, 19, 31], "z3": 1}, "titles": ["cozy.__main__", "cozy.analysis", "cozy.claripy_ext", "cozy.concolic.exploration", "cozy.concolic.heuristics", "cozy.concolic", "cozy.concolic.session", "cozy.concrete", "cozy.constants", "cozy.directive", "cozy.execution_graph", "cozy.functools_ext", "cozy.hooks", "cozy.hooks.strlen", "cozy.hooks.strncmp", "cozy.hooks.strtok_r", "cozy", "cozy.log", "cozy.primitives", "cozy.project", "cozy.server", "cozy.session", "cozy.side_effect", "cozy.stubs", "cozy.terminal_state", "cozy.types", "API Reference", "Using Concolic Execution", "Getting Started", "Dealing with Hooks", "Welcome to cozy\u2019s documentation!", "Launching a Visualization", "Modeling I/O Side Effects"], "titleterms": {"": 30, "__main__": 0, "analysi": 1, "api": 26, "assert": 28, "assum": 28, "attribut": [8, 11, 13, 14, 15, 17, 18, 21, 23], "claripy_ext": 2, "class": [0, 1, 3, 4, 6, 7, 9, 10, 13, 14, 15, 19, 20, 21, 22, 23, 24], "compar": 31, "comparison": 28, "concol": [3, 4, 5, 6, 27], "concret": 7, "constant": 8, "content": [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25, 30], "cozi": [0, 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, 28, 30], "custom": 31, "deal": 29, "defin": 31, "direct": [9, 28], "document": 30, "effect": 32, "exampl": 28, "execut": 27, "execution_graph": 10, "explor": 3, "function": [1, 2, 7, 10, 11, 17, 18, 20, 21, 22, 25], "functools_ext": 11, "further": 28, "get": 28, "heurist": 4, "hook": [12, 13, 14, 15, 29], "how": 28, "i": 32, "indic": 30, "launch": 31, "log": 17, "make": 28, "model": 32, "modul": [0, 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 13, 14, 15, 17, 18, 19, 20, 21, 22, 23, 24, 25], "o": 32, "perform": 32, "primit": 18, "project": 19, "refer": 26, "server": 20, "session": [6, 21], "side": 32, "side_effect": 22, "start": 28, "strlen": 13, "strncmp": 14, "strtok_r": 15, "stub": 23, "submodul": [5, 12, 16], "subpackag": 16, "tabl": 30, "terminal_st": 24, "type": [25, 31], "us": 27, "visual": 31, "walkthrough": 28, "welcom": 30}}) \ No newline at end of file diff --git a/sideeffects.html b/sideeffects.html new file mode 100644 index 0000000..559f5db --- /dev/null +++ b/sideeffects.html @@ -0,0 +1,157 @@ + + + + + + + + Modeling I/O Side Effects — cozy 1.1 documentation + + + + + + + + + + + + + + + + + + + + +
    +
    +
    + + +
    + +
    +

    Modeling I/O Side Effects

    +

    Many programs of interest that we wish to simulate produce side effects, which we would like to be available for comparison in our analysis. +To enable this use case, cozy has a subsystem for producing IO side effects. Common examples of IO side effects we have found in example programs +include writing to stdout/stderr, writing to the network, or writing over a serial connection.

    +

    Modeling IO side effects is typically straightforward, and can be accomplished by hooking side effect producing functions and instead redirecting +the side effect payload to a list attached to the current state. When a child state is forked from its parent, it obtains a copy of side effects +from its parent. cozy keeps track of IO side effects over different channels (ie, a channel for stdout, network, etc.) and attempts to +intelligently align side effects in the visualization interface.

    +

    Note that by default, angr automatically concretizes data written to stdout/stderr. cozy side effects keeps the data symbolic and avoids the concretization. +In this way cozy’s side effects interface is superior to the angr default.

    +
    +

    Performing a Side Effect

    +

    The primary function to take a look at is cozy.side_effect.perform(). The first argument is the angr.SimState that the side effect +will attach to. This argument can be obtained by hooking a side effect function, whose angr.SimProcedure.run() method takes in a +angr.SimState object. Alternatively you can set a breakpoint using cozy.directive.Breakpoint and obtain the angr.SimState +object in the breakpoint’s breakpoint_fun callback.

    +

    Here is an example of the use of cozy.side_effect.perform() in a custom angr.SimProcedure hook:

    +
    # Here we are hooking a function called process_command,
    +# so we need to make a class that inherits from SimProcedure
    +class process_command(angr.SimProcedure):
    +    def run(self, cmd_str):
    +        strlen = angr.SIM_PROCEDURES["libc"]["strlen"]
    +        max_len = self.state.solver.max(self.inline_call(strlen, cmd_str).ret_expr)
    +        # Here we construct the side effect payload. Here it is a bunch of symbolic data.
    +        cmd = [self.state.memory.load(cmd_str + i, 1) for i in range(max_len)]
    +        def concrete_post_processor(concrete_cmd):
    +            return [chr(r.concrete_value) for r in concrete_cmd]
    +        cozy.side_effect.perform(self.state, "process_command", cmd, concrete_post_processor=concrete_post_processor)
    +
    +
    +

    The second argument is the side effect channel. Different types of side effects should be performed over different channels. For example, +you may have a channel for networked output and a channel for stdout.

    +

    The third argument is the side effect body. The body must be a mixture of string-keyed Python dictionaries, Python lists, Python tuples, +claripy concrete values, and claripy symbolic values. This should represent the payload of the side effect.

    +

    The fourth argument is an optional post processing function to apply to concretized versions of the side effect’s body if post processing is required. +In this example we use the Python chr function to convert the integer to Python characters, which will be shown in the visualization +user interface.

    +

    The fifth argument is an optional label used to aid alignment in the user interface. For example, if you have multiple sites that produce +side effects on the same channel, you will want to label the different sites with different labels. This aids the alignment algorithm to intelligently +compare the produced side effects. One possible label is the code address location that the side effect is produced at.

    +
    +
    + + +
    + +
    +
    + +
    +
    + + + + + + + \ No newline at end of file