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(
+ '
StateDiff encapsulates the memoized state used by the difference method. This class is used internally by Comparison and is typically not for external use.
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.
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).
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.
StateDiff encapsulates the memoized state used by the difference method. This class is used internally by Comparison and is typically not for external use.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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
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.
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.
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.
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.
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
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.
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.
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
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:
+
+
A termination heuristic, which determines when to halt concolic execution.
+
A candidate heuristic, which determines which deferred state to explore next.
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:
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:
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)
+
+ `)
+ }
+
+ 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`
+
`
+ }
+}
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`
`
+ 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`
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):
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.
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.
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:
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:
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().
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:
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:
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:
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.
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.
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:
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:
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:
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.
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 we are hooking a function called process_command,
+# so we need to make a class that inherits from SimProcedure
+classprocess_command(angr.SimProcedure):
+ defrun(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)foriinrange(max_len)]
+ defconcrete_post_processor(concrete_cmd):
+ return[chr(r.concrete_value)forrinconcrete_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.