[
  {
    "path": ".editorconfig",
    "content": "# https://editorconfig.org/\n\nroot = true\n\n[*]\nindent_style = space\nindent_size = 4\ninsert_final_newline = true\ntrim_trailing_whitespace = true\nend_of_line = lf\ncharset = utf-8\nmax_line_length = 100\n\n[*.json]\nindent_size = 2\nkeep_blank_lines_in_code = 0\nkeep_indents_on_empty_lines = false\nkeep_line_breaks = true\nspace_after_colon = true\nspace_after_comma = true\nspace_before_colon = true\nspace_before_comma = false\nspaces_within_braces = false\nspaces_within_brackets = false\nwrap_long_lines = false\ninsert_final_newline = ignore\n\n[Makefile]\nindent_style = tab\n\n[{*.bash,*.zsh,*.sh,*.bats}]\ntab_width = 4\nbinary_ops_start_line = false\nkeep_column_alignment_padding = false\nminify_program = false\nredirect_followed_by_space = false\nswitch_cases_indented = false\n\n[{*.yml,*.yaml}]\nindent_size = 2\nkeep_indents_on_empty_lines = false\nkeep_line_breaks = true\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "*       @OleksiiOleksenko\n"
  },
  {
    "path": ".github/workflows/kmodule-build.yaml",
    "content": "# This workflow will build the kernel module on multiple Ubuntu versions\nname: Kmodule Build\n\non:\n  push:\n    branches:\n      - main\n      - main-fixes\n      - pre-release\n      - dev\n  pull_request:\n    branches:\n      - main\n      - main-fixes\n      - pre-release\n      - dev\n\njobs:\n  km_build:\n    permissions:\n      contents: read\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - runner: ubuntu-latest\n            name: x86_latest\n          - runner: ubuntu-22.04\n            name: x86_backward_compatible\n          - runner: ubuntu-24.04-arm\n            name: arm_latest\n          - runner: ubuntu-22.04-arm\n            name: arm_backward_compatible\n    runs-on: ${{ matrix.runner }}\n    name: km_build_${{ matrix.name }}\n    steps:\n    - uses: actions/checkout@v4\n    - name: Install kernel headers\n      run: sudo apt-get update && sudo apt-get install -y linux-headers-$(uname -r) linux-headers-generic\n    - name: Build kernel module\n      run: |\n        set -o pipefail\n        cd rvzr/executor_km\n        make VMBUILD=1 2>&1 | tee build.log\n        if grep -q \"Error\" build.log; then\n          echo \"Build failed\"\n          exit 1\n        fi\n"
  },
  {
    "path": ".github/workflows/python-lint-and-test.yaml",
    "content": "# This workflow will install Python dependencies, run tests and lint with a variety of Python versions\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python\n\nname: Python Lint and Test\npermissions:\n  contents: read\n\non:\n  push:\n    branches:\n      - main\n      - main-fixes\n      - pre-release\n      - dev\n  pull_request:\n    branches:\n      - main\n      - main-fixes\n      - pre-release\n      - dev\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        python-version: [\"3.9\", \"3.13\"]\n\n    steps:\n    - uses: actions/checkout@v4\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v5\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        python -m pip install flake8 mypy pylint\n        python -m pip install .\n    - name: Run\n      run: |\n        ./tests/runtests.sh --skip-km-tests\n"
  },
  {
    "path": ".gitignore",
    "content": "cmake-build-*/\nbuild/\n.vscode/\n.mypy_cache/\n.lsync*\nvenv/\n**/__pycache__/\nbase.json\nrvzr/arch/x86/*.json\n*.code-workspace\n*.o\nrvzr/generated.asm\ngenerated.asm\ngenerated\nrvzr/executor_km/.cache.mk\nrvzr/executor_km/measurement.o.ur-safe\ndbg/\nsite\ndist/\n.cache/\n.claude/\n"
  },
  {
    "path": ".gitmodules",
    "content": ""
  },
  {
    "path": ".pylintrc",
    "content": "[MAIN]\n\n# Analyse import fallback blocks. This can be used to support both Python 2 and\n# 3 compatible code, which means that the block might have code that exists\n# only in one or another interpreter, leading to false positives when analysed.\nanalyse-fallback-blocks=no\n\n# Clear in-memory caches upon conclusion of linting. Useful if running pylint\n# in a server-like mode.\nclear-cache-post-run=no\n\n# Load and enable all available extensions. Use --list-extensions to see a list\n# all available extensions.\n#enable-all-extensions=\n\n# In error mode, messages with a category besides ERROR or FATAL are\n# suppressed, and no reports are done by default. Error mode is compatible with\n# disabling specific errors.\n#errors-only=\n\n# Always return a 0 (non-error) status code, even if lint errors are found.\n# This is primarily useful in continuous integration scripts.\n#exit-zero=\n\n# A comma-separated list of package or module names from where C extensions may\n# be loaded. Extensions are loading into the active Python interpreter and may\n# run arbitrary code.\nextension-pkg-allow-list=\n\n# A comma-separated list of package or module names from where C extensions may\n# be loaded. Extensions are loading into the active Python interpreter and may\n# run arbitrary code. (This is an alternative name to extension-pkg-allow-list\n# for backward compatibility.)\nextension-pkg-whitelist=\n\n# Return non-zero exit code if any of these messages/categories are detected,\n# even if score is above --fail-under value. Syntax same as enable. Messages\n# specified are enabled, while categories only check already-enabled messages.\nfail-on=\n\n# Specify a score threshold under which the program will exit with error.\nfail-under=10\n\n# Interpret the stdin as a python script, whose filename needs to be passed as\n# the module_or_package argument.\n#from-stdin=\n\n# Files or directories to be skipped. They should be base names, not paths.\nignore=CVS\n\n# Add files or directories matching the regular expressions patterns to the\n# ignore-list. The regex matches against paths and can be in Posix or Windows\n# format. Because '\\\\' represents the directory delimiter on Windows systems,\n# it can't be used as an escape character.\nignore-paths=\n\n# Files or directories matching the regular expression patterns are skipped.\n# The regex matches against base names, not paths. The default value ignores\n# Emacs file locks\nignore-patterns=^\\.#\n\n# List of module names for which member attributes should not be checked and\n# will not be imported (useful for modules/projects where namespaces are\n# manipulated during runtime and thus existing member attributes cannot be\n# deduced by static analysis). It supports qualified module names, as well as\n# Unix pattern matching.\nignored-modules=\n\n# Python code to execute, usually for sys.path manipulation such as\n# pygtk.require().\n#init-hook=\n\n# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the\n# number of processors available to use, and will cap the count on Windows to\n# avoid hangs.\njobs=1\n\n# Control the amount of potential inferred values when inferring a single\n# object. This can help the performance when dealing with large functions or\n# complex, nested conditions.\nlimit-inference-results=100\n\n# List of plugins (as comma separated values of python module names) to load,\n# usually to register additional checkers.\nload-plugins=\n\n# Pickle collected data for later comparisons.\npersistent=yes\n\n# Resolve imports to .pyi stubs if available. May reduce no-member messages and\n# increase not-an-iterable messages.\nprefer-stubs=no\n\n# Minimum Python version to use for version dependent checks. Will default to\n# the version used to run pylint.\npy-version=3.12\n\n# Discover python modules and packages in the file system subtree.\nrecursive=no\n\n# Add paths to the list of the source roots. Supports globbing patterns. The\n# source root is an absolute path or a path relative to the current working\n# directory used to determine a package namespace for modules located under the\n# source root.\nsource-roots=\n\n# Allow loading of arbitrary C extensions. Extensions are imported into the\n# active Python interpreter and may run arbitrary code.\nunsafe-load-any-extension=no\n\n# In verbose mode, extra non-checker-related info will be displayed.\n#verbose=\n\n\n[BASIC]\n\n# Naming style matching correct argument names.\nargument-naming-style=snake_case\n\n# Regular expression matching correct argument names. Overrides argument-\n# naming-style. If left empty, argument names will be checked with the set\n# naming style.\n#argument-rgx=\n\n# Naming style matching correct attribute names.\nattr-naming-style=snake_case\n\n# Regular expression matching correct attribute names. Overrides attr-naming-\n# style. If left empty, attribute names will be checked with the set naming\n# style.\n#attr-rgx=\n\n# Bad variable names which should always be refused, separated by a comma.\nbad-names=foo,\n          bar,\n          baz,\n          toto,\n          tutu,\n          tata\n\n# Bad variable names regexes, separated by a comma. If names match any regex,\n# they will always be refused\nbad-names-rgxs=\n\n# Naming style matching correct class attribute names.\nclass-attribute-naming-style=any\n\n# Regular expression matching correct class attribute names. Overrides class-\n# attribute-naming-style. If left empty, class attribute names will be checked\n# with the set naming style.\n#class-attribute-rgx=\n\n# Naming style matching correct class constant names.\nclass-const-naming-style=UPPER_CASE\n\n# Regular expression matching correct class constant names. Overrides class-\n# const-naming-style. If left empty, class constant names will be checked with\n# the set naming style.\n#class-const-rgx=\n\n# Naming style matching correct class names.\nclass-naming-style=PascalCase\n\n# Regular expression matching correct class names. Overrides class-naming-\n# style. If left empty, class names will be checked with the set naming style.\n#class-rgx=\n\n# Naming style matching correct constant names.\nconst-naming-style=UPPER_CASE\n\n# Regular expression matching correct constant names. Overrides const-naming-\n# style. If left empty, constant names will be checked with the set naming\n# style.\n#const-rgx=\n\n# Minimum line length for functions/classes that require docstrings, shorter\n# ones are exempt.\ndocstring-min-length=-1\n\n# Naming style matching correct function names.\nfunction-naming-style=snake_case\n\n# Regular expression matching correct function names. Overrides function-\n# naming-style. If left empty, function names will be checked with the set\n# naming style.\n#function-rgx=\n\n# Good variable names which should always be accepted, separated by a comma.\ngood-names=i,\n           j,\n           k,\n           ex,\n           Run,\n           _\n\n# Good variable names regexes, separated by a comma. If names match any regex,\n# they will always be accepted\ngood-names-rgxs=\n\n# Include a hint for the correct naming format with invalid-name.\ninclude-naming-hint=no\n\n# Naming style matching correct inline iteration names.\ninlinevar-naming-style=any\n\n# Regular expression matching correct inline iteration names. Overrides\n# inlinevar-naming-style. If left empty, inline iteration names will be checked\n# with the set naming style.\n#inlinevar-rgx=\n\n# Naming style matching correct method names.\nmethod-naming-style=snake_case\n\n# Regular expression matching correct method names. Overrides method-naming-\n# style. If left empty, method names will be checked with the set naming style.\n#method-rgx=\n\n# Naming style matching correct module names.\nmodule-naming-style=snake_case\n\n# Regular expression matching correct module names. Overrides module-naming-\n# style. If left empty, module names will be checked with the set naming style.\n#module-rgx=\n\n# Colon-delimited sets of names that determine each other's naming style when\n# the name regexes allow several styles.\nname-group=\n\n# Regular expression which should only match function or class names that do\n# not require a docstring.\nno-docstring-rgx=^_\n\n# List of decorators that produce properties, such as abc.abstractproperty. Add\n# to this list to register other decorators that produce valid properties.\n# These decorators are taken in consideration only for invalid-name.\nproperty-classes=abc.abstractproperty\n\n# Regular expression matching correct type alias names. If left empty, type\n# alias names will be checked with the set naming style.\n#typealias-rgx=\n\n# Regular expression matching correct type variable names. If left empty, type\n# variable names will be checked with the set naming style.\n#typevar-rgx=\n\n# Naming style matching correct variable names.\nvariable-naming-style=snake_case\n\n# Regular expression matching correct variable names. Overrides variable-\n# naming-style. If left empty, variable names will be checked with the set\n# naming style.\n#variable-rgx=\n\n[CLASSES]\n\n# Warn about protected attribute access inside special methods\ncheck-protected-access-in-special-methods=no\n\n# List of method names used to declare (i.e. assign) instance attributes.\ndefining-attr-methods=__init__,\n                      __new__,\n                      setUp,\n                      asyncSetUp,\n                      __post_init__\n\n# List of member names, which should be excluded from the protected access\n# warning.\nexclude-protected=_asdict,_fields,_replace,_source,_make,os._exit\n\n# List of valid names for the first argument in a class method.\nvalid-classmethod-first-arg=cls\n\n# List of valid names for the first argument in a metaclass class method.\nvalid-metaclass-classmethod-first-arg=mcs\n\n\n[DESIGN]\n\n# List of regular expressions of class ancestor names to ignore when counting\n# public methods (see R0903)\nexclude-too-few-public-methods=\n\n# List of qualified class names to ignore when counting class parents (see\n# R0901)\nignored-parents=\n\n# Maximum number of arguments for function / method.\nmax-args=9  # NOTE: non-default (5) because we rely on data classes with many attributes\n\n# Maximum number of attributes for a class (see R0902).\nmax-attributes=12  # NOTE: non-default (5) because we rely on data classes with many attributes\n\n# Maximum number of boolean expressions in an if statement (see R0916).\nmax-bool-expr=5\n\n# Maximum number of branch for function / method body.\nmax-branches=12\n\n# Maximum number of locals for function / method body.\nmax-locals=15\n\n# Maximum number of parents for a class (see R0901).\nmax-parents=7\n\n# Maximum number of public methods for a class (see R0904).\nmax-public-methods=20\n\n# Maximum number of return / yield for function / method body.\nmax-returns=6\n\n# Maximum number of statements in function / method body.\nmax-statements=50\n\n# Minimum number of public methods for a class (see R0903).\nmin-public-methods=1\n\n\n[EXCEPTIONS]\n\n# Exceptions that will emit a warning when caught.\novergeneral-exceptions=builtins.BaseException,builtins.Exception\n\n\n[FORMAT]\n\n# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.\nexpected-line-ending-format=\n\n# Regexp for a line that is allowed to be longer than the limit.\nignore-long-lines=^\\s*(# )?<?https?://\\S+>?$\n\n# Number of spaces of indent required inside a hanging or continued line.\nindent-after-paren=4\n\n# String used as indentation unit. This is usually \"    \" (4 spaces) or \"\\t\" (1\n# tab).\nindent-string='    '\n\n# Maximum number of characters on a single line.\nmax-line-length=100\n\n# Maximum number of lines in a module.\nmax-module-lines=1000\n\n# Allow the body of a class to be on the same line as the declaration if body\n# contains single statement.\nsingle-line-class-stmt=no\n\n# Allow the body of an if to be on the same line as the test if there is no\n# else.\nsingle-line-if-stmt=no\n\n\n[IMPORTS]\n\n# List of modules that can be imported at any level, not just the top level\n# one.\nallow-any-import-level=\n\n# Allow explicit reexports by alias from a package __init__.\nallow-reexport-from-package=no\n\n# Allow wildcard imports from modules that define __all__.\nallow-wildcard-with-all=no\n\n# Deprecated modules which should not be used, separated by a comma.\ndeprecated-modules=\n\n# Output a graph (.gv or any supported image format) of external dependencies\n# to the given file (report RP0402 must not be disabled).\next-import-graph=\n\n# Output a graph (.gv or any supported image format) of all (i.e. internal and\n# external) dependencies to the given file (report RP0402 must not be\n# disabled).\nimport-graph=\n\n# Output a graph (.gv or any supported image format) of internal dependencies\n# to the given file (report RP0402 must not be disabled).\nint-import-graph=\n\n# Force import order to recognize a module as part of the standard\n# compatibility libraries.\nknown-standard-library=\n\n# Force import order to recognize a module as part of a third party library.\nknown-third-party=enchant\n\n# Couples of modules and preferred modules, separated by a comma.\npreferred-modules=\n\n\n[LOGGING]\n\n# The type of string formatting that logging methods do. `old` means using %\n# formatting, `new` is for `{}` formatting.\nlogging-format-style=old\n\n# Logging modules to check that the string format arguments are in logging\n# function parameter format.\nlogging-modules=logging\n\n\n[MESSAGES CONTROL]\n\n# Only show warnings with the listed confidence levels. Leave empty to show\n# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,\n# UNDEFINED.\nconfidence=HIGH,\n           CONTROL_FLOW,\n           INFERENCE,\n           INFERENCE_FAILURE,\n           UNDEFINED\n\n# Disable the message, report, category or checker with the given id(s). You\n# can either give multiple identifiers separated by comma (,) or put this\n# option multiple times (only on the command line, not in the configuration\n# file where it should appear only once). You can also use \"--disable=all\" to\n# disable everything first and then re-enable specific checks. For example, if\n# you want to run only the similarities checker, you can use \"--disable=all\n# --enable=similarities\". If you want to run only the classes checker, but have\n# no Warning level messages displayed, use \"--disable=all --enable=classes\n# --disable=W\".\ndisable=W0511,  # disable warnings on FIXME tag\n        # invalid-name: we actively use Final to define read-only attributes,\n        # so using UPPERCASE everywhere would lead to messy code\n        c0103,\n        # use-yield-from: the replacement does not always produce the same result functionally\n        # and it breaks the code, so we disable this warning\n        r1737,\n        # unspecified-encoding: Revizor runs only on Linux, so we don't need to specify encoding\n        w1514,\n        # too-many-positional-arguments  # NOTE: we use data classes with many attributes\n        r0917,\n        # too-few-public-methods: we use data classes with many attributes\n        r0903,\n        # rise-missing-from\n        w0707,\n        # consider-using-sys-exit: just meh\n        r1722,\n\n\n# Enable the message, report, category or checker with the given id(s). You can\n# either give multiple identifier separated by comma (,) or put this option\n# multiple time (only on the command line, not in the configuration file where\n# it should appear only once). See also the \"--disable\" option for examples.\nenable=\n\n\n[METHOD_ARGS]\n\n# List of qualified names (i.e., library.method) which require a timeout\n# parameter e.g. 'requests.api.get,requests.api.post'\ntimeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request\n\n\n[MISCELLANEOUS]\n\n# List of note tags to take in consideration, separated by a comma.\nnotes=FIXME,\n      XXX,\n      TODO\n\n# Regular expression of note tags to take in consideration.\nnotes-rgx=\n\n\n[REFACTORING]\n\n# Maximum number of nested blocks for function / method body\nmax-nested-blocks=5\n\n# Complete name of functions that never returns. When checking for\n# inconsistent-return-statements if a never returning function is called then\n# it will be considered as an explicit return statement and no message will be\n# printed.\nnever-returning-functions=sys.exit,argparse.parse_error\n\n# Let 'consider-using-join' be raised when the separator to join on would be\n# non-empty (resulting in expected fixes of the type: ``\"- \" + \" -\n# \".join(items)``)\nsuggest-join-with-non-empty-separator=yes\n\n\n[REPORTS]\n\n# Python expression which should return a score less than or equal to 10. You\n# have access to the variables 'fatal', 'error', 'warning', 'refactor',\n# 'convention', and 'info' which contain the number of messages in each\n# category, as well as 'statement' which is the total number of statements\n# analyzed. This score is used by the global evaluation report (RP0004).\nevaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))\n\n# Template used to display messages. This is a python new-style format string\n# used to format the message information. See doc for all details.\nmsg-template=\n\n# Set the output format. Available formats are: text, parseable, colorized,\n# json2 (improved json format), json (old json format) and msvs (visual\n# studio). You can also give a reporter class, e.g.\n# mypackage.mymodule.MyReporterClass.\n#output-format=\n\n# Tells whether to display a full report or only the messages.\nreports=no\n\n# Activate the evaluation score.\nscore=yes\n\n\n[SIMILARITIES]\n\n# Comments are removed from the similarity computation\nignore-comments=yes\n\n# Docstrings are removed from the similarity computation\nignore-docstrings=yes\n\n# Imports are removed from the similarity computation\nignore-imports=yes\n\n# Signatures are removed from the similarity computation\nignore-signatures=yes\n\n# Minimum lines number of a similarity.\nmin-similarity-lines=8\n\n\n[SPELLING]\n\n# Limits count of emitted suggestions for spelling mistakes.\nmax-spelling-suggestions=4\n\n# Spelling dictionary name. No available dictionaries : You need to install\n# both the python package and the system dependency for enchant to work.\nspelling-dict=\n\n# List of comma separated words that should be considered directives if they\n# appear at the beginning of a comment and should not be checked.\nspelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:\n\n# List of comma separated words that should not be checked.\nspelling-ignore-words=\n\n# A path to a file that contains the private dictionary; one word per line.\nspelling-private-dict-file=\n\n# Tells whether to store unknown words to the private dictionary (see the\n# --spelling-private-dict-file option) instead of raising a message.\nspelling-store-unknown-words=no\n\n\n[STRING]\n\n# This flag controls whether inconsistent-quotes generates a warning when the\n# character used as a quote delimiter is used inconsistently within a module.\ncheck-quote-consistency=no\n\n# This flag controls whether the implicit-str-concat should generate a warning\n# on implicit string concatenation in sequences defined over several lines.\ncheck-str-concat-over-line-jumps=no\n\n\n[TYPECHECK]\n\n# List of decorators that produce context managers, such as\n# contextlib.contextmanager. Add to this list to register other decorators that\n# produce valid context managers.\ncontextmanager-decorators=contextlib.contextmanager\n\n# List of members which are set dynamically and missed by pylint inference\n# system, and so shouldn't trigger E1101 when accessed. Python regular\n# expressions are accepted.\ngenerated-members=\n\n# Tells whether to warn about missing members when the owner of the attribute\n# is inferred to be None.\nignore-none=yes\n\n# This flag controls whether pylint should warn about no-member and similar\n# checks whenever an opaque object is returned when inferring. The inference\n# can return multiple potential results while evaluating a Python object, but\n# some branches might not be evaluated, which results in partial inference. In\n# that case, it might be useful to still emit no-member and other checks for\n# the rest of the inferred objects.\nignore-on-opaque-inference=yes\n\n# List of symbolic message names to ignore for Mixin members.\nignored-checks-for-mixins=no-member,\n                          not-async-context-manager,\n                          not-context-manager,\n                          attribute-defined-outside-init\n\n# List of class names for which member attributes should not be checked (useful\n# for classes with dynamically set attributes). This supports the use of\n# qualified names.\nignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace\n\n# Show a hint with possible names when a member name was not found. The aspect\n# of finding the hint is based on edit distance.\nmissing-member-hint=yes\n\n# The minimum edit distance a name should have in order to be considered a\n# similar match for a missing member name.\nmissing-member-hint-distance=1\n\n# The total number of similar names that should be taken in consideration when\n# showing a hint for a missing member.\nmissing-member-max-choices=1\n\n# Regex pattern to define which classes are considered mixins.\nmixin-class-rgx=.*[Mm]ixin\n\n# List of decorators that change the signature of a decorated function.\nsignature-mutators=\n\n\n[VARIABLES]\n\n# List of additional names supposed to be defined in builtins. Remember that\n# you should avoid defining new builtins when possible.\nadditional-builtins=\n\n# Tells whether unused global variables should be treated as a violation.\nallow-global-unused-variables=yes\n\n# List of names allowed to shadow builtins\nallowed-redefined-builtins=\n\n# List of strings which can identify a callback function by name. A callback\n# name must start or end with one of those strings.\ncallbacks=cb_,\n          _cb\n\n# A regular expression matching the name of dummy variables (i.e. expected to\n# not be used).\ndummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_\n\n# Argument names that match this expression will be ignored.\nignored-argument-names=_.*|^ignored_|^unused_\n\n# Tells whether we should check for unused import in __init__ files.\ninit-import=no\n\n# List of qualified module names which can have objects that can redefine\n# builtins.\nredefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io\n"
  },
  {
    "path": "AUTHORS",
    "content": "Here is an inevitably incomplete list of MUCH-APPRECIATED CONTRIBUTORS:\n\nOleksii Oleksenko\nBoris Koepf\nEmanuele Vannacci\nJana Hofmann\nConnor Shugg\nMarco Guarnieri\nFlavien Solt\nBrian Fu\nAlvise de Faveri Tron\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\nAll notable changes to Revizor will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [2.0.0] - 2026-01-10\n\n### TL;DR\n\nThis release contains a major refactoring of the codebase, including many of the core modules. This breaks compatibility with previous versions, hence the major version bump.\n\nIn addition, several significant enhancements have been made:\n\n- ARM64 is now fully supported.\n- New DynamoRIO-based model backend has been added, which vastly improves ISA coverage on x86.\n- The documentation has been fully restructured and expanded.\n\n### Added\n\n#### ARM64 Support\n- Full hardware tracing support for ARM64 CPUs (#137)\n- ARM64 executor, fuzzer, and code generator implementations\n- ARM64 test suite with acceptance and unit tests\n- ARM64 ISA specification and target description\n\n#### DynamoRIO Model Backend\n- New DynamoRIO-based model backend added, which completely re-implements the leakage modeling functionality\n- New tracers: indirect memory access (IND) tracer and poisoning of faulty loads (#133)\n- Contract-based input generation for DynamoRIO backend (#138)\n\n#### Documentation\n- Complete documentation restructure with tutorials, reference guides, and topic guides\n- Five comprehensive tutorials covering first fuzzing campaign, vulnerability detection, fault handling, isolation, and extending Revizor\n- Detailed primer on contracts and leakage models\n- In-depth guides on choosing contracts, designing campaigns, interpreting results, and root-causing violations\n- Architecture overview with detailed diagrams\n- DynamoRIO backend instrumentation diagrams\n- Sandbox and binary format documentation\n- Actor and test case generation topics\n- Glossary of key terms\n\n#### Demos and Examples\n- TSA-L1D demo configuration and template\n- TSA-SQ demo files\n- Improved detection demos for various Spectre variants\n\n#### Testing and Development\n- Unified tests for Unicorn and DynamoRIO backends\n- Unit tests for traces, stats, and test case components\n- Utility scripts for generating RCBF/RDBF test files\n- Interface to run individual testing stages\n- Improved test coverage and CI integration\n\n#### Misc. Features\n- Special value generation option for input data (not just random values)\n- More verbose configuration error messages\n- Better visibility for warnings in logger output\n- Support for FS/GS segment register instructions in ISA specification\n- Input differential minimization for observer actors\n\n### Changed\n\n**WARNING**: This release contains breaking changes! The release introduces a complete refactoring of the code structure, including many of the core modules. See docs/internals/architecture/overview.md for details.\n\n#### Code Structure\n- Renamed source directory from src/ to rvzr/ for better compliance with Python packaging standards\n- Encapsulated all core components into dedicated modules (sandbox.py, actor.py, etc)\n- Moved all test case components into a dedicated directory rvzr/tc_components\n- Refactored fuzzer.py to isolate the multi-stage filtering logic into a dedicated class\n- Isolated utility classes into dedicated modules stats.py and logs.py\n- Unicorn-based backend split into logical classes: Tracer, Speculator, TaintTracker, etc. (rvzr/model_unicorn)\n- Reorganized into architecture-specific subdirectories (rvzr/arch/x86, rvzr/arch/arm64)\n- Minimizer refactored to encapsulate each pass into a separate class (rvzr/postprocessing)\n- Executor KM is now shared between x86 and ARM to avoid code duplication\n- Consistent naming conventions for generators across architectures\n- Improved code style and formatting\n\n#### Configuration Options\n- Many config options have been renamed during the refactoring process\n- Refer to the updated documentation (`docs/ref/config.md`)for the new option names and their usage.\n\n#### ISA Spec Format\n- Renamed several fields in the json produced by the download_spec command\n\n#### Testing Infrastructure\n- Cleaner interface for test scripts\n- GitHub Actions aligned with internal test scripts\n\n#### Documentation Structure\n- Reorganized into intro/, howto/, ref/, topics/, and internals/ sections\n- Split architecture documentation into per-module pages\n- Updated navigation structure in MkDocs\n\n\n\n### Deprecated\n\n- MPX support\n\n---\n\n## [1.3.2] - 2024-09-12\n\nSee git history for changes in version 1.3.2 and earlier.\n\n[1.3.3]: https://github.com/microsoft/side-channel-fuzzer/compare/v1.3.2...v1.3.3\n[1.3.2]: https://github.com/microsoft/side-channel-fuzzer/releases/tag/v1.3.2\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Microsoft Open Source Code of Conduct\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\n\nResources:\n\n- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)\n- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)\n- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nAs an open source project, Revizor welcomes contributions and suggestions.\n\n## Contributor License Agreement and Code of Conduct\n\nMost contributions require you to agree to a\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\nthe rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.\n\nWhen you submit a pull request, a CLA bot will automatically determine whether you need to provide\na CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\nprovided by the bot. You will only need to do this once across all repos using our CLA.\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n\n## Contribution Guidelines\n\nPlease refer to the [Guide to Contributing](https://microsoft.github.io/side-channel-fuzzer/internals/contributing/overview/) for an overview of how to contribute.\n"
  },
  {
    "path": "LICENSE",
    "content": "    MIT License\n\n    Copyright (c) Microsoft Corporation.\n\n    Permission is hereby granted, free of charge, to any person obtaining a copy\n    of this software and associated documentation files (the \"Software\"), to deal\n    in the Software without restriction, including without limitation the rights\n    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n    copies of the Software, and to permit persons to whom the Software is\n    furnished to do so, subject to the following conditions:\n\n    The above copyright notice and this permission notice shall be included in all\n    copies or substantial portions of the Software.\n\n    THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n    SOFTWARE\n"
  },
  {
    "path": "README.md",
    "content": "# Revizor\n\n![GitHub](https://img.shields.io/github/license/microsoft/side-channel-fuzzer)\n![GitHub all releases](https://img.shields.io/github/downloads/microsoft/side-channel-fuzzer/total)\n![GitHub contributors](https://img.shields.io/github/contributors/microsoft/side-channel-fuzzer)\n![PyPI](https://img.shields.io/pypi/v/revizor-fuzzer?label=PyPI)\n![PyPI - Downloads](https://img.shields.io/pypi/dm/revizor-fuzzer?label=%22PyPI%20Downloads%22)\n\nRevizor is a security-oriented fuzzer for detecting information leaks in CPUs, such as [Spectre and Meltdown](https://meltdownattack.com/).\nIt tests CPUs against [Leakage Contracts](https://arxiv.org/abs/2006.03841) and searches for unexpected leaks.\n\n<!-- For more details, see our [Paper](https://dl.acm.org/doi/10.1145/3503222.3507729) (open access [here](https://arxiv.org/abs/2105.06872)), and the follow-up papers ([1](https://arxiv.org/pdf/2301.07642.pdf), [2](https://www.usenix.org/conference/usenixsecurity23/presentation/hofmann)). -->\n\n## Getting Started and Documentation\n\nYou can find a quick start guide at [Quick Start](https://microsoft.github.io/side-channel-fuzzer/intro/start-here/).\n\nFor detailed information on how to use Revizor, see [Documentation Pages](https://microsoft.github.io/side-channel-fuzzer/structure/).\n\nFor information on how to contribute to Revizor, see [CONTRIBUTING.md](CONTRIBUTING.md).\n\n## Need Help with Revizor?\n\nIf you find a bug in Revizor, don't hesitate to [open an issue](https://github.com/microsoft/side-channel-fuzzer/issues).\n\nIf something is confusing or you need help in using Revizor, we have a [discussion page](https://github.com/microsoft/side-channel-fuzzer/discussions).\n\n## Citing Revizor\n\nTo cite this project, you can use any of the following references:\n\n1. Original paper that introduced the concept of Model-based Relation Testing as well as the Revizor tool:\n\n    Oleksii Oleksenko, Christof Fetzer, Boris Köpf, Mark Silberstein. \"[Revizor: Testing Black-box CPUs against Speculation Contracts](https://www.microsoft.com/en-us/research/publication/revizor-testing-black-box-cpus-against-speculation-contracts/)\" in Proceedings of the 27th ACM International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS), 2022.\n\n2. Theoretical foundations of leakage contract:\n\n    Marco Guarnieri, Boris Köpf, Jan Reineke, and Pepe Vila. \"[Hardware-software contracts for secure speculation](https://www.microsoft.com/en-us/research/publication/hardware-software-contracts-for-secure-speculation/)\" in Proceedings of the 2021 IEEE Symposium on Security and Privacy (SP), 2021.\n\n3. Accessible summary of the two papers above, in a journal format:\n\n    Oleksii Oleksenko, Christof Fetzer, Boris Köpf, Mark Silberstein. \"Revizor: Testing Black-box CPUs against Speculation Contracts\". In IEEE Micro, 2023.\n\n4. Paper that introduced speculation filtering, observation filtering, and contract-based input generation:\n\n    Oleksii Oleksenko, Marco Guarnieri, Boris Köpf, and Mark Silberstein. \"[Hide and Seek with Spectres: Efficient discovery of speculative information leaks with random testing](https://www.microsoft.com/en-us/research/publication/hide-and-seek-with-spectres-efficient-discovery-of-speculative-information-leaks-with-random-testing/)\" in Proceedings of the 2023 IEEE Symposium on Security and Privacy (SP), 2022.\n\n5. Paper that introduced exception-based testing (i.e., focus on Meltdown, Foreshadow) into Revizor:\n\n    Jana Hofmann, Emanuele Vannacci, Cédric Fournet, Boris Köpf, and Oleksii Oleksenko. \"[Speculation at Fault: Modeling and Testing Microarchitectural Leakage of CPU Exceptions.](https://www.usenix.org/conference/usenixsecurity23/presentation/hofmann)\" in Proceedings of 32nd USENIX Security Symposium (USENIX Security), 2023.\n\n6. Paper that introduced testing of cross-VM and user-kernel leaks in Revizor, as well as presented TSA attacks on AMD CPUs:\n\n    Oleksii Oleksenko, Flavien Solt, Cédric Fournet, Jana Hofmann, Boris Köpf, and Stavros Volos. \"[Enter, Exit, Page Fault, Leak: Testing Isolation Boundaries for Microarchitectural Leaks](https://www.microsoft.com/en-us/research/wp-content/uploads/2025/07/Enter-Exit-SP26.pdf)\" (to be published) in Proceedings of the 2026 IEEE Symposium on Security and Privacy (SP), 2026.\n\n## Trademarks\n\nThis project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft\ntrademarks or logos is subject to and must follow\n[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general).\nUse of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship.\nAny use of third-party trademarks or logos are subject to those third-party's policies.\n"
  },
  {
    "path": "SECURITY.md",
    "content": "<!-- BEGIN MICROSOFT SECURITY.MD V0.0.5 BLOCK -->\n\n## Security\n\nMicrosoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).\n\nIf you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.\n\n## Reporting Security Issues\n\n**Please do not report security vulnerabilities through public GitHub issues.**\n\nInstead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).\n\nIf you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com).  If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).\n\nYou should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). \n\nPlease include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:\n\n  * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)\n  * Full paths of source file(s) related to the manifestation of the issue\n  * The location of the affected source code (tag/branch/commit or direct URL)\n  * Any special configuration required to reproduce the issue\n  * Step-by-step instructions to reproduce the issue\n  * Proof-of-concept or exploit code (if possible)\n  * Impact of the issue, including how an attacker might exploit the issue\n\nThis information will help us triage your report more quickly.\n\nIf you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.\n\n## Preferred Languages\n\nWe prefer all communications to be in English.\n\n## Policy\n\nMicrosoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).\n\n<!-- END MICROSOFT SECURITY.MD BLOCK -->"
  },
  {
    "path": "demo/README.md",
    "content": "This directory contains a set of demo configurations for fuzzing various known CPU vulnerabilities using Revizor.\nEach config here is intentionally made to detect only one type of vulnerabilities.\n\nFor example, if you fuzz an Intel CPU with `detect-v1.yaml`, you will likely detect an instance of Spectre V1.\n(of course, there is always a chance that you will find a new previously-unknown vulnerability with this config, but the likelihood is rather low).\n\nThe commands below assume that the ISA spec (downloaded via `rvzr download_spec`) is stored in `base.json`.\n\n## [Spectre V1](https://meltdownattack.com/)\n\n```\nrvzr fuzz -s base.json -c demo/detect-v1.yaml -i 50 -n 10000\n```\nExpected duration - several seconds.\n\n## Spectre V1 (store variant)\n\n```\nrvzr fuzz -s base.json -c demo/detect-v1-store.yaml -i 50 -n 10000\n```\nExpected duration - several seconds.\n\n## Spectre V1-Var ([description](https://dl.acm.org/doi/10.1145/3503222.3507729) and [here](https://eprint.iacr.org/2022/715.pdf))\n\n```\nrvzr fuzz -s base.json -c demo/detect-v1-var.yaml -i 50 -n 10000\n```\nExpected duration - several hours.\n\n## [MDS](https://mdsattacks.com/) or [LVI-Null](https://lviattack.eu/), depending on the CPU model\n\nNote: only Intel CPUs.\n\n```\nrvzr fuzz -s base.json -c demo/detect-mds.yaml -i 50 -n 10000\n```\nExpected duration - several minutes.\n\n## Spectre V4 ([description](https://www.cyberus-technology.de/posts/2018-05-22-intel-store-load-spectre-vulnerability.html))\n```\nrvzr fuzz -s base.json -c demo/detect-v4.yaml -i 50 -n 10000\n```\nExpected duration - 5-20 minutes.\n\n## Zero Divisor Injection (ZDI)\n\nNote: only Intel CPUs.\n\n```\nrvzr fuzz -s base.json -c demo/detect-zdi.yaml -i 50 -n 10000\n```\nExpected duration - several minutes.\n\n## String Comparison Overrun (SCO)\n\n```\nrvzr fuzz -s base.json -c demo/detect-sco.yaml -i 50 -n 10000\n```\nExpected duration - several minutes.\n\n## Foreshadow (simplified version)\n\nNote: only Intel CPUs.\n\n```\nrvzr fuzz -s base.json -c demo/detect-foreshadow.yaml -i 50 -n 10000\n```\nExpected duration - several minutes.\n\n## Transient Scheduler Attack, Store Queue variant (TSA-SQ)\n\nNote: only AMD CPUs vulnerable to TSA.\n\n```\nrvzr tfuzz -s base.json -c demo/tsa-sq/config.yaml -t demo/tsa-sq/template.asm -i 50 -n 10000\n```\nExpected duration - several minutes.\n\n## Transient Scheduler Attack, L1D Cache variant (TSA-L1D)\n\nNote: only AMD CPUs vulnerable to TSA.\n\n```\nrvzr tfuzz -s base.json -c demo/tsa-l1d/config.yaml -t demo/tsa-l1d/template.asm -i 50 -n 10000\n``\nExpected duration - several minutes.\n"
  },
  {
    "path": "demo/big-fuzz.yaml",
    "content": "instruction_set: x86-64\n\n# Model\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - seq\n\n# Actors\nactors:\n  - main:\n    - data_properties:\n      - present: true\n\n# Executor\nexecutor_mode: P+P\nx86_executor_enable_ssbp_patch: true\n\n# Program generator\nprogram_size: 64\navg_mem_accesses: 16\nmax_bb_per_function: 1  # straight-line code only\nmin_bb_per_function: 1\nmin_successors_per_bb: 1\nmax_successors_per_bb: 1\n\ninstruction_categories:\n  - BASE-BINARY\n  - BASE-BITBYTE\n  - BASE-CMOV\n  - BASE-COND_BR\n  - BASE-CONVERT\n  - BASE-DATAXFER\n  - BASE-FLAGOP\n  - BASE-LOGICAL\n  - BASE-MISC\n  - BASE-NOP\n  - BASE-WIDENOP\n  - BASE-POP\n  - BASE-PUSH\n  - BASE-SEMAPHORE\n  - BASE-SETCC\n  # - BASE-STRINGOP  # commented out as it triggers a known information leak\n  - LONGMODE-CONVERT\n  - LONGMODE-DATAXFER\n  - LONGMODE-SEMAPHORE\n  # - LONGMODE-STRINGOP  # commented out as it triggers a known information leak\n  - SSE-DATAXFER\n  - SSE-LOGICAL_FP\n  - SSE-MISC\n  - SSE-SSE\n\n# Input generator\ndata_generator_entropy_bits: 24\ninputs_per_class: 2\n\n# Fuzzer\nenable_speculation_filter: true\nenable_observation_filter: true\nenable_fast_path_model: true\ncoverage_type: model_instructions\n\n# Output\ncolor: true\nlogging_modes:\n  - info\n  - stat\n  - dbg_generator\n  # - dbg_timestamp\n  # - dbg_violation\n  # - dbg_dump_htraces\n  # - dbg_dump_ctraces\n  # - dbg_dump_traces_unlimited\n  # - dbg_model\n  - dbg_coverage\n  # - dbg_priming\n  # - dbg_executor_raw\n\n"
  },
  {
    "path": "demo/detect-foreshadow.yaml",
    "content": "# This demo illustrates detection of Foreshadow\n\n# contract\ncontract_observation_clause: loads+stores+pc\ncontract_execution_clause:\n  - delayed-exception-handling\n\n# tested instructions\ninstruction_categories:\n  - BASE-BINARY\n  - BASE-BITBYTE\n  - BASE-CMOV\n  - BASE-CONVERT\n  - BASE-DATAXFER\n  - BASE-LOGICAL\n  - BASE-MISC\n  - BASE-NOP\n  - BASE-POP\n  - BASE-PUSH\n  - BASE-SETCC\n\ninstruction_blocklist_append:\n  - DIV\n  - IDIV\n\nactors:\n  - main:\n    - data_properties:\n      - present: false\n      - writable: false\n\n# misc. fuzzing configuration\nenable_speculation_filter: true\nenable_observation_filter: true\nprogram_size: 16\navg_mem_accesses: 8\ninputs_per_class: 2\nexecutor_warmups: 2\nx86_disable_div64: false\n"
  },
  {
    "path": "demo/detect-mds.yaml",
    "content": "# contract\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - seq-assist\n\n# tested instructions\ninstruction_categories:\n  - BASE-BITBYTE\n  - BASE-CMOV\n  - BASE-LOGICAL\n\n# environment\nactors:\n  - main:\n    - data_properties:\n      - accessed: False\n\n# fuzzing configuration\nenable_speculation_filter: true\nenable_observation_filter: true\nprogram_size: 20\navg_mem_accesses: 10\ninputs_per_class: 2\n\nprogram_generator_seed: 955240\n"
  },
  {
    "path": "demo/detect-sco.yaml",
    "content": "# contract\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - seq\n\n# tested instructions\ninstruction_categories:\n  - BASE-BITBYTE\n  - BASE-CMOV\n  - BASE-LOGICAL\n  - BASE-STRINGOP\n  - BASE-FLAGOP\n\n# fuzzing configuration\nenable_speculation_filter: true\nenable_observation_filter: true\nprogram_size: 20\navg_mem_accesses: 10\ninputs_per_class: 2\n\n\nprogram_generator_seed: 910000\n"
  },
  {
    "path": "demo/detect-v1-store.yaml",
    "content": "file: !include detect-v1.yaml\n\n# prevent speculative stores from being observed\ncontract_observation_clause: ct-nonspecstore\ncontract_execution_clause:\n  - cond\n"
  },
  {
    "path": "demo/detect-v1-var.yaml",
    "content": "file: !include detect-v1.yaml\n\n# contract\n# contract_observation_clause: ct\ncontract_execution_clause:\n  - cond\n# analyser_subsets_is_violation: false\n\n# # tested instructions\n# instruction_categories:\n#   - BASE-BITBYTE\n#   - BASE-COND_BR\n#   - BASE-CMOV\n#   - BASE-LOGICAL\n\n# # fuzzing configuration\n# enable_speculation_filter: true\n# enable_observation_filter: true\n# data_generator_entropy_bits: 16\n# min_bb_per_function: 2\n# max_bb_per_function: 2\n# program_size: 20\n# avg_mem_accesses: 10\n# inputs_per_class: 2\n"
  },
  {
    "path": "demo/detect-v1.yaml",
    "content": "# contract\ncontract_observation_clause: loads+stores+pc\ncontract_execution_clause:\n  - no_speculation\n\n# tested instructions\ninstruction_categories:\n  - BASE-BINARY\n  - BASE-BITBYTE\n  - BASE-CMOV\n  - BASE-COND_BR\n  - BASE-CONVERT\n  - BASE-DATAXFER\n  - BASE-LOGICAL\n  - BASE-MISC\n  - BASE-NOP\n  - BASE-POP\n  - BASE-PUSH\n  - BASE-SETCC\n\n# fuzzing configuration\nenable_speculation_filter: true\nenable_observation_filter: true\nprogram_size: 16\navg_mem_accesses: 8\ninputs_per_class: 2\n\nprogram_generator_seed: 100\n"
  },
  {
    "path": "demo/detect-v4.yaml",
    "content": "# contract\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - seq\n\n# tested instructions\ninstruction_categories:\n  - BASE-BITBYTE\n  - BASE-CMOV\n  - BASE-LOGICAL\n\n# environment\nx86_executor_enable_ssbp_patch: false\n\n# fuzzing configuration\nenable_speculation_filter: true\nenable_observation_filter: true\nprogram_size: 20\navg_mem_accesses: 10\ninputs_per_class: 2\n\n# reduce entropy (not strictly required for detection, but makes the demo finish faster)\ndata_generator_entropy_bits: 10\n\nprogram_generator_seed: 1000000\n"
  },
  {
    "path": "demo/detect-zdi.yaml",
    "content": "# contract\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - seq\n\n# tested instructions\ninstruction_categories:\n  - BASE-BITBYTE\n  - BASE-BINARY\n  - BASE-CMOV\n  - BASE-LOGICAL\n\n# fuzzing configuration\nenable_speculation_filter: true\nenable_observation_filter: true\nprogram_size: 64\navg_mem_accesses: 24\ninputs_per_class: 2\n\nprogram_generator_seed: 252633\nx86_disable_div64: false\n"
  },
  {
    "path": "demo/tsa-l1d/config.yaml",
    "content": "instruction_set: x86-64\ninstruction_categories:\n  - BASE-BINARY\n  - BASE-BITBYTE\n  - BASE-CMOV\n  - BASE-COND_BR\n  - BASE-CONVERT\n  - BASE-DATAXFER\n  - BASE-FLAGOP\n  - BASE-LOGICAL\n  - BASE-MISC\n  - BASE-NOP\n  - BASE-POP\n  - BASE-PUSH\n  - BASE-SEMAPHORE\n  - BASE-SETCC\n  - BASE-WIDENOP\n\nactors:\n  - main:\n    - mode: \"host\"\n    - privilege_level: \"kernel\"\n  - vmvictim:\n    - mode: \"guest\"\n    - privilege_level: \"kernel\"\n  - vm:\n    - mode: \"guest\"\n    - observer: true\n    - privilege_level: \"kernel\"\n    - data_properties:\n      - writable: false\n\ncontract_observation_clause: ct-ni\n\nmax_bb_per_function: 1\n\nexecutor_mode: F+R\nexecutor_sample_sizes:\n  - 15\n  - 40\n  - 160\n  - 320\n\nexecutor_filtering_repetitions: 5\nx86_enable_hpa_gpa_collisions: true\n\nprogram_generator_seed: 20000000\ndata_generator_seed: 1000000\ninputs_per_class: 2\n\nanalyser_stat_threshold: 0.1\n\n# enable_speculation_filter: true\nenable_observation_filter: true\nenable_fast_path_model: true\n\n# color: true\nlogging_modes:\n  - info\n  # - stat\n"
  },
  {
    "path": "demo/tsa-l1d/template.asm",
    "content": ".intel_syntax noprefix\n\n# ----------------------------- Hypervisor (Host) ----------------------------\n.section .data.main\n.function_main_0:\n    # observer start\n    .macro.set_h2g_target.vm.function_vm_0:\n    .macro.set_g2h_target.main.function_main_1:\n    .macro.switch_h2g.vm.0:\n\n\n.function_main_1:\n    .macro.landing_g2h.main_1:\n\n    .macro.set_h2g_target.vmvictim.function_vmvictim_0:\n    .macro.set_g2h_target.main.function_main_2:\n    .macro.switch_h2g.vmvictim.0:\n\n.function_main_2:\n    .macro.landing_g2h.main_2:\n    .macro.set_h2g_target.vm.function_vm_1:\n    .macro.set_g2h_target.main.function_main_3:\n\n    xor rax, rax  # noremove\n    xor rbx, rbx  # noremove\n    xor rcx, rcx  # noremove\n    xor rdx, rdx  # noremove\n    xor rsi, rsi  # noremove\n    xor rdi, rdi  # noremove\n    # insert flushing patches here\n.patch_placeholder:\n\n    .macro.switch_h2g.vm.1:\n\n.function_main_3:\n    .macro.landing_g2h.main_3:\n\n.macro.fault_handler:\n.patch_placeholder_fault_handler:\n\n    .macro.set_h2g_target.vm.function_vm_2:\n    .macro.set_g2h_target.main.function_main_4:\n    .macro.switch_h2g.vm.2:\n\n.function_main_4:\n    .macro.landing_g2h.main_4:\n    nop\n\n# ----------------------------- VM - Victim ----------------------------------\n.section .data.vmvictim\n.function_vmvictim_0:\n    .macro.landing_h2g.vmvictim_0:\n\n    # secret injection\n    .macro.random_instructions.64.32.main_1:\n\n    .macro.switch_g2h.main.vmvictim_0:\n    lfence\n\n\n# ----------------------------- VM - Observer --------------------------------\n.section .data.vm\n.function_vm_0:\n    .macro.landing_h2g.vm_0:\n    .macro.measurement_start:\n    .macro.switch_g2h.main.vm_0:\n    lfence\n\n\n.function_vm_1:\n    .macro.landing_h2g.vm_1:\n    xor rax, rax  # noremove\n    mov rax, qword ptr [r14 + 0x2000] # noremove\n    mov rbx, qword ptr [r14 + 0x2008] # noremove\n    mov rcx, qword ptr [r14 + 0x2010] # noremove\n    mov rdx, qword ptr [r14 + 0x2018] # noremove\n    mov rsi, qword ptr [r14 + 0x2020] # noremove\n    mov rdi, qword ptr [r14 + 0x2028] # noremove\n    mfence # noremove\n\n    # secret retrieval\n    .macro.random_instructions.64.32.vm_1:\n\n    # make sure the model doesn't attempt to go further than this point\n    lfence  # noremove\n\n    .macro.measurement_end.vm_1:\n    .macro.switch_g2h.main.1:\n    lfence\n\n\n.function_vm_2:\n    .macro.landing_h2g.vm_2:\n    .macro.measurement_end.vm_2:\n    .macro.switch_g2h.main.2:\n    lfence\n\n\n# ----------------------------- Exit -----------------------------------------\n.section .data.main\n.test_case_exit:\n"
  },
  {
    "path": "demo/tsa-sq/config.yaml",
    "content": "instruction_set: x86-64\ninstruction_categories:\n  - BASE-BINARY\n  - BASE-BITBYTE\n  - BASE-CMOV\n  - BASE-COND_BR\n  - BASE-CONVERT\n  - BASE-DATAXFER\n  - BASE-FLAGOP\n  - BASE-LOGICAL\n  - BASE-MISC\n  - BASE-NOP\n  - BASE-POP\n  - BASE-PUSH\n  - BASE-SEMAPHORE\n  - BASE-SETCC\n  - BASE-WIDENOP\n\n\nfaults_allowlist:\n  - user-to-kernel-access\n\nactors:\n  - main:\n    - mode: \"host\"\n    - privilege_level: \"kernel\"\n    - fault_blocklist:\n      - user-to-kernel-access\n  - user:\n    - mode: \"host\"\n    - observer: true\n    - privilege_level: \"user\"\n    - data_properties:\n      - present: true\n\ncontract_observation_clause: ct-ni\n\nmax_bb_per_function: 1\n\nexecutor_mode: F+R\nexecutor_sample_sizes:\n  - 15\n  - 40\n  - 160\n  - 320\n\nexecutor_filtering_repetitions: 5\nx86_enable_hpa_gpa_collisions: true\n\nprogram_generator_seed: 20000000\ndata_generator_seed: 1000000\ninputs_per_class: 2\n\nanalyser_stat_threshold: 0.2\n\n# enable_speculation_filter: true\nenable_observation_filter: true\nenable_fast_path_model: true\n\n# color: true\nlogging_modes:\n  - info\n  # - stat\n"
  },
  {
    "path": "demo/tsa-sq/template.asm",
    "content": ".intel_syntax noprefix\n\n# ----------------------------- Kernel-mode Actor (Victim) -------------------\n.section .data.main\n.function_main_0:\n    # observer start\n    .macro.set_k2u_target.user.function_user_0:\n    .macro.set_u2k_target.main.function_main_1:\n    .macro.switch_k2u.user.0:\n\n\n.function_main_1:\n    .macro.landing_u2k.main_1:\n\n    # secret injection\n    .macro.random_instructions.64.32.main_1:\n\n    .macro.set_k2u_target.user.function_user_1:\n    .macro.set_u2k_target.main.function_main_2:\n    .macro.switch_k2u.user.1:\n\n.function_main_2:\n    .macro.landing_u2k.main_2:\n\n.macro.fault_handler:\n    .macro.set_k2u_target.user.function_user_2:\n    .macro.set_u2k_target.main.function_main_3:\n    .macro.switch_k2u.user.2:\n\n.function_main_3:\n    .macro.landing_u2k.main_3:\n    nop\n\n# ----------------------------- User-mode Actor ------------------------------\n.section .data.user\n.function_user_0:\n    .macro.landing_k2u.user_0:\n    .macro.measurement_start:\n    .macro.switch_u2k.main.user_0:\n    lfence\n\n\n.function_user_1:\n    .macro.landing_k2u.user_1:\n    xor rax, rax  # noremove\n    mov rax, qword ptr [r14 + 0x2000] # noremove\n    mov rbx, qword ptr [r14 + 0x2008] # noremove\n    mov rcx, qword ptr [r14 + 0x2010] # noremove\n    mov rdx, qword ptr [r14 + 0x2018] # noremove\n    mov rsi, qword ptr [r14 + 0x2020] # noremove\n    mov rdi, qword ptr [r14 + 0x2028] # noremove\n    lfence\n\n    # secret retrieval\n    .macro.random_instructions.64.32.user_1:\n\n    # make sure the model doesn't attempt to go further than this point\n    lfence  # noremove\n\n    .macro.measurement_end.user_1:\n    .macro.switch_u2k.main.1:\n    lfence\n\n\n.function_user_2:\n    .macro.landing_k2u.user_2:\n    .macro.measurement_end.user_2:\n    .macro.switch_u2k.main.2:\n    lfence\n\n\n# ----------------------------- Exit -----------------------------------------\n.section .data.main\n.test_case_exit:\n"
  },
  {
    "path": "docs/assets/branches.drawio",
    "content": "<mxfile host=\"Electron\" modified=\"2024-07-30T08:53:34.216Z\" agent=\"5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.5.1 Chrome/96.0.4664.110 Electron/16.0.7 Safari/537.36\" etag=\"Hyny0YfqRxkr2HCByzIt\" version=\"16.5.1\" type=\"device\"><diagram id=\"RTthD5nFo_tmHNPfYv7S\" name=\"Page-1\">3VlZj5swEP41kdpKWQVzJPvYPdqqdxVVbR6d4IBXgFNjNqS/vkMYMARytJtC2pfEHt8z830zNgPzNkxfS7ryPwiXBQMyctOBeTcgxHIM+M0Em1wwGdu5wJPczUWGFkz5T4bCEUoT7rK41lEJESi+qgsXIorYQtVkVEqxrndbiqC+6op6rCGYLmjQlH7jrvJR6tiWbnjDuOfj0sQ0nbwlpEVvPErsU1esKyLzfmDeSiFUXgrTWxZkyisUk497tae13JlkkTplwHRKPw4d/vbNbDE22dcv3rvw8xBneaRBgiceECeA+W6WAqaFXasN6sL5kYiiYRhvLfUSOhjjVaoboeTh/3aWeSEIKY8KIexxvtsRZPmKO+NlIXkWKzqHrZBbWFSygNGYuc9RteUmiRRJ5LLsyAYMWvtcsemKLrLWNXgoyHwVBtjcVGGhDyYVSysiVOlrJkKm5Aa6YKtpoXnRwc0R1tcVdylcwK94ygRlFD3UK6fWNoQCmvE3THrd0AlzwaexKqTyhSciGtxr6Y3W2ghqus97IVaoqwem1AYBShMl6poEBcrN92z8FbGL+qzaeJfi7Hltg7W9JohFIhfswDkJ0gGVHlPHXTxTwkGDgk9RxR/rwD+7dUgTcSGPhGw6PPp4qzXf0znwbM0CNOBeBOUFKJJJEGQ+zIHJXmJDyF03NzYD9G6xlJtgJXiktue0bwb2HUIcTW2MW410yPMa4CnpGRetEWAbqIajK9MgSKQn2wWn+5wdpzIXMWsALevFFGK5jMGBdg1b7uokW8/u0iEXE/fTw718HKbfflzbaeF5HSMx5ep7pTzbgtLGmkZhVilAqNFbhS6OOgBel8Z+ybWtMPu70DZ7RXKXoXO45CnkQQcCqCaOPSH1WRLnARTQGIYcEJ/lUvATsVRtExbAzZUOrr2HVuLUQ6tF+g6tRVbaW2wddxRbzX8ztjYhGdKH/yO2Fq53luBKRrtB0ThLrCXXHYTaQw7bQsYNonwaO68kG5bec+LVpY2229h4mbmqpuOt5xZs3P9Np6Tfy6HjXhKsP2dV63fSmothVasraLns8WmQikS2+jIJgkw7isUK7JwBaq1h1j+O7ItLawqa7ueioi8ns0rLkYtKmfrkFxVy7KZyPOqeCmL7RBBbFwViey+Iz3xZWTKqEoiRL5722Jf5lsxzm1jJZKG4iEoo4xrlBQaIo39YO9c7D4FW77Du57bSCuvRQVjvonMfzMfHYP7nsHb6gnV7Jm1N6t7kjHa8JN8ojjp/Pu10xRfzxPtLXIEvJhdMEXbv3wqMSZ8U0cUT5fkC//hEhiAXFfjHXQHZFwoQp7F8OFff+0Z5Apazh8r+wWwY3aXxUNXfiXOK11/bzftf</diagram></mxfile>"
  },
  {
    "path": "docs/assets/dr-instrumentation.drawio",
    "content": "<mxfile host=\"Electron\" agent=\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/27.0.9 Chrome/134.0.6998.205 Electron/35.4.0 Safari/537.36\" version=\"27.0.9\">\n  <diagram name=\"Page-1\" id=\"Q5S100K9i7V5bAyz_BIk\">\n    <mxGraphModel dx=\"1376\" dy=\"1102\" grid=\"1\" gridSize=\"10\" guides=\"1\" tooltips=\"1\" connect=\"1\" arrows=\"1\" fold=\"1\" page=\"1\" pageScale=\"1\" pageWidth=\"827\" pageHeight=\"1169\" math=\"0\" shadow=\"0\">\n      <root>\n        <mxCell id=\"0\" />\n        <mxCell id=\"1\" parent=\"0\" />\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-28\" value=\"Dispatcher.cpp\" style=\"rounded=1;whiteSpace=wrap;html=1;arcSize=5;verticalAlign=top;fontFamily=JetBrains Mono;fontSize=15;fontStyle=1\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"560\" y=\"110\" width=\"380\" height=\"290\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-5\" value=\"Model.cpp\" style=\"rounded=1;whiteSpace=wrap;html=1;arcSize=5;verticalAlign=top;fontFamily=JetBrains Mono;fontSize=15;fontStyle=1\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"40\" y=\"110\" width=\"470\" height=\"290\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-7\" value=\"namespace dr_model\" style=\"rounded=1;whiteSpace=wrap;html=1;arcSize=7;verticalAlign=top;fontFamily=JetBrains Mono;fontStyle=1;fillColor=#FFF2CC;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"70\" y=\"180\" width=\"430\" height=\"160\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-27\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;endArrow=none;endFill=1;fontFamily=JetBrains Mono;curved=0;dashed=1;dashPattern=1 1;strokeColor=#666666;startArrow=blockThin;startFill=0;\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-10\" target=\"VMTqDSAsTNkDB5RHAwfX-19\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"160\" y=\"276\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-72\" value=\"reads\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"VMTqDSAsTNkDB5RHAwfX-27\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"0.1613\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-10\" value=\"&lt;div&gt;instrumented_func.exit_pc&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=center;fontFamily=JetBrains Mono;fontSize=9;fontStyle=2\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"80\" y=\"235\" width=\"160\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-23\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=none;endFill=1;fontFamily=JetBrains Mono;dashed=1;dashPattern=1 1;strokeColor=#666666;startArrow=blockThin;startFill=0;\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-11\" target=\"VMTqDSAsTNkDB5RHAwfX-18\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-68\" value=\"reads\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"VMTqDSAsTNkDB5RHAwfX-23\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"-0.4408\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"11\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-11\" value=\"&lt;div&gt;instrumented_func.name&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=center;fontFamily=JetBrains Mono;fontSize=9;fontStyle=2\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"80\" y=\"215\" width=\"160\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-12\" value=\"&lt;div&gt;glob_dispatcher&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=center;fontFamily=JetBrains Mono;fontSize=9;fontStyle=2\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"80\" y=\"150\" width=\"160\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-14\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=blockThin;endFill=1;fontFamily=JetBrains Mono;dashed=1;\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-13\" target=\"VMTqDSAsTNkDB5RHAwfX-12\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-16\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=blockThin;endFill=1;fontFamily=JetBrains Mono;dashed=1;\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-13\" target=\"VMTqDSAsTNkDB5RHAwfX-11\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-17\" value=\"initializes\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontFamily=JetBrains Mono;fontStyle=2;fontSize=9;\" parent=\"VMTqDSAsTNkDB5RHAwfX-16\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"-0.3834\" y=\"-2\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"31\" y=\"11\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-26\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.75;entryY=1;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;endArrow=blockThin;endFill=1;dashed=1;\" edge=\"1\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-13\" target=\"VMTqDSAsTNkDB5RHAwfX-7\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-13\" value=\"&lt;div&gt;dr_client_main()&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=center;fontFamily=JetBrains Mono;fontStyle=1\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"90\" y=\"360\" width=\"160\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-25\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;endArrow=blockThin;endFill=1;fontFamily=JetBrains Mono;curved=0;dashed=1;\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-18\" target=\"VMTqDSAsTNkDB5RHAwfX-20\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"490\" y=\"225\" />\n              <mxPoint x=\"490\" y=\"245\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-73\" value=\"inserts\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"VMTqDSAsTNkDB5RHAwfX-25\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"-0.1333\" y=\"1\" relative=\"1\" as=\"geometry\">\n            <mxPoint y=\"-13\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-18\" value=\"&lt;div&gt;event_module_load()&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=left;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=#FFCE9F;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"310\" y=\"215\" width=\"160\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-82\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;endArrow=blockThin;endFill=1;\" parent=\"1\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"578.4444444444445\" y=\"261\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"470\" y=\"276\" />\n              <mxPoint x=\"504\" y=\"276\" />\n              <mxPoint x=\"504\" y=\"261\" />\n            </Array>\n            <mxPoint x=\"470\" y=\"280\" as=\"sourcePoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-83\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=blockThin;endFill=1;\" parent=\"1\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"577.8888888888889\" y=\"276.33333333333337\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"470\" y=\"276\" />\n            </Array>\n            <mxPoint x=\"470\" y=\"278\" as=\"sourcePoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-19\" value=\"&lt;div&gt;event_bb_instrumentation()&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=left;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=#FFCE9F;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"310\" y=\"266\" width=\"160\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-26\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;endArrow=blockThin;endFill=1;fontFamily=JetBrains Mono;dashed=1;dashPattern=1 1;strokeColor=#666666;\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-20\" target=\"VMTqDSAsTNkDB5RHAwfX-10\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-71\" value=\"writes\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"VMTqDSAsTNkDB5RHAwfX-26\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"0.0599\" y=\"-3\" relative=\"1\" as=\"geometry\">\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-20\" value=\"&lt;div&gt;event_instrumentation_start()&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=left;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=#FFD966;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"310\" y=\"235\" width=\"160\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-21\" value=\"&lt;div&gt;event_signal()&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=left;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=#FFCE9F;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"310\" y=\"286\" width=\"160\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-22\" value=\"&lt;div&gt;event_exit()&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=left;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=#FFCE9F;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"310\" y=\"306\" width=\"160\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-38\" value=\"Dispatcher\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;whiteSpace=wrap;html=1;fontFamily=JetBrains Mono;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"572\" y=\"151\" width=\"160\" height=\"190\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-39\" value=\"+ logger&lt;div&gt;+ tracer&lt;/div&gt;&lt;div&gt;+ speculator&lt;/div&gt;\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fontFamily=JetBrains Mono;fontSize=9;\" parent=\"VMTqDSAsTNkDB5RHAwfX-38\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"160\" height=\"44\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-40\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;strokeColor=inherit;fontFamily=JetBrains Mono;\" parent=\"VMTqDSAsTNkDB5RHAwfX-38\" vertex=\"1\">\n          <mxGeometry y=\"70\" width=\"160\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-61\" value=\"\" style=\"endArrow=blockThin;html=1;rounded=0;endFill=1;fontFamily=JetBrains Mono;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;\" parent=\"VMTqDSAsTNkDB5RHAwfX-38\" target=\"VMTqDSAsTNkDB5RHAwfX-45\" edge=\"1\">\n          <mxGeometry width=\"50\" height=\"50\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"110\" y=\"125\" as=\"sourcePoint\" />\n            <mxPoint x=\"207\" y=\"117\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-41\" value=\"&lt;div&gt;+ start()&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;+ instrument_instruction()&lt;div&gt;+ instrument_exit()&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;+ handle_exception()&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;+ finalize()&lt;/div&gt;\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;whiteSpace=wrap;html=1;fontFamily=JetBrains Mono;fontSize=9;\" parent=\"VMTqDSAsTNkDB5RHAwfX-38\" vertex=\"1\">\n          <mxGeometry y=\"78\" width=\"160\" height=\"112\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-63\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;endArrow=blockThin;endFill=1;entryX=1.003;entryY=0.896;entryDx=0;entryDy=0;entryPerimeter=0;fontFamily=JetBrains Mono;curved=0;\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-44\" target=\"VMTqDSAsTNkDB5RHAwfX-39\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"653.5999999999999\" y=\"198.60000000000002\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"860\" y=\"217\" />\n              <mxPoint x=\"732\" y=\"217\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-44\" value=\"&lt;div&gt;dispatch_callback()&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=center;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=#F19C99;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"780\" y=\"250.5\" width=\"120\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-62\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;endArrow=blockThin;endFill=1;fontFamily=JetBrains Mono;curved=0;\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-45\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"646\" y=\"319\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"840\" y=\"319\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-64\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=1;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;fontFamily=JetBrains Mono;curved=0;endArrow=blockThin;endFill=1;\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-45\" target=\"VMTqDSAsTNkDB5RHAwfX-39\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"659.2\" y=\"210.20000000000005\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"840\" y=\"318\" />\n              <mxPoint x=\"920\" y=\"318\" />\n              <mxPoint x=\"920\" y=\"199\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-45\" value=\"&lt;div&gt;exit_callback()&lt;/div&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;align=center;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=#F19C99;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"780\" y=\"270.5\" width=\"120\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-59\" value=\"\" style=\"endArrow=blockThin;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endFill=1;fontFamily=JetBrains Mono;dashed=1;\" parent=\"1\" target=\"VMTqDSAsTNkDB5RHAwfX-44\" edge=\"1\">\n          <mxGeometry width=\"50\" height=\"50\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"721\" y=\"264\" as=\"sourcePoint\" />\n            <mxPoint x=\"782\" y=\"240\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-75\" value=\"exit_pc?\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"1\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"544\" y=\"266\" as=\"geometry\">\n            <mxPoint x=\"-7\" y=\"10\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-76\" value=\"calls\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"1\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"543\" y=\"250\" as=\"geometry\">\n            <mxPoint x=\"-3\" y=\"73\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-77\" value=\"&lt;font color=&quot;#ffffff&quot;&gt;3&lt;/font&gt;\" style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;fontFamily=JetBrains Mono;strokeWidth=6;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"470\" y=\"200\" width=\"10\" height=\"10\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-80\" value=\"&lt;font style=&quot;color: rgb(255, 255, 255);&quot;&gt;4&lt;/font&gt;\" style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;fontFamily=JetBrains Mono;strokeWidth=6;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"295\" y=\"238\" width=\"10\" height=\"10\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-85\" value=\"\" style=\"endArrow=blockThin;html=1;rounded=0;endFill=1;fontFamily=JetBrains Mono;\" parent=\"1\" edge=\"1\">\n          <mxGeometry width=\"50\" height=\"50\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"470\" y=\"297\" as=\"sourcePoint\" />\n            <mxPoint x=\"575\" y=\"296\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-88\" value=\"&lt;font style=&quot;color: rgb(255, 255, 255);&quot;&gt;5&lt;/font&gt;\" style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;fontFamily=JetBrains Mono;strokeWidth=6;\" parent=\"1\" vertex=\"1\">\n          <mxGeometry x=\"479\" y=\"270\" width=\"10\" height=\"10\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-89\" value=\"\" style=\"endArrow=blockThin;html=1;rounded=0;endFill=1;fontFamily=JetBrains Mono;\" parent=\"1\" edge=\"1\">\n          <mxGeometry width=\"50\" height=\"50\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"470\" y=\"317\" as=\"sourcePoint\" />\n            <mxPoint x=\"576\" y=\"317\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-90\" value=\"calls\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"1\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"541\" y=\"296\" as=\"geometry\">\n            <mxPoint y=\"5\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-91\" value=\"inserts\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"1\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"750\" y=\"237\" as=\"geometry\">\n            <mxPoint x=\"1\" y=\"52\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-92\" value=\"inserts\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"1\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"752\" y=\"255\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-93\" value=\"uses\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"1\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"262\" y=\"227\" as=\"geometry\">\n            <mxPoint x=\"555\" y=\"-2\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-96\" value=\"n: finalize()\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"1\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"780\" y=\"305\" as=\"geometry\">\n            <mxPoint x=\"-8\" y=\"11\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-97\" value=\"y: rollback()\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"1\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"900\" y=\"306\" as=\"geometry\">\n            <mxPoint x=\"-18\" y=\"13\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"VMTqDSAsTNkDB5RHAwfX-98\" value=\"is speculative?\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" parent=\"1\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"907\" y=\"306\" as=\"geometry\">\n            <mxPoint x=\"-48\" y=\"-6\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-1\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=blockThin;endFill=1;dashed=1;\" edge=\"1\" parent=\"1\" source=\"VMTqDSAsTNkDB5RHAwfX-12\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"574\" y=\"159\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-2\" value=\"&lt;i&gt;points to&lt;/i&gt;\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontFamily=JetBrains Mono;fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"jZhf56UUiF3wu5g5-CzG-1\">\n          <mxGeometry x=\"-0.5431\" y=\"4\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"23\" y=\"-5\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-3\" value=\"&lt;font style=&quot;color: rgb(255, 255, 255);&quot;&gt;2&lt;/font&gt;\" style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;fontFamily=JetBrains Mono;strokeWidth=6;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"45\" y=\"345\" width=\"10\" height=\"10\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-15\" value=\"\" style=\"endArrow=blockThin;html=1;rounded=0;endFill=1;\" edge=\"1\" parent=\"1\">\n          <mxGeometry width=\"50\" height=\"50\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"470\" y=\"251\" as=\"sourcePoint\" />\n            <mxPoint x=\"578\" y=\"243\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-16\" value=\"calls\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" vertex=\"1\" connectable=\"0\" parent=\"1\">\n          <mxGeometry x=\"541\" y=\"232.5\" as=\"geometry\">\n            <mxPoint y=\"5\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-17\" value=\"started?\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontStyle=2;fontFamily=JetBrains Mono;fontSize=8;\" vertex=\"1\" connectable=\"0\" parent=\"1\">\n          <mxGeometry x=\"544\" y=\"250\" as=\"geometry\">\n            <mxPoint x=\"-7\" y=\"10\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-18\" value=\"\" style=\"rounded=1;whiteSpace=wrap;html=1;align=left;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=#FFCE9F;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"70\" y=\"430\" width=\"20\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-19\" value=\"\" style=\"rounded=1;whiteSpace=wrap;html=1;align=left;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=#FFD966;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"210\" y=\"430\" width=\"20\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-20\" value=\"\" style=\"rounded=1;whiteSpace=wrap;html=1;align=left;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=#F19C99;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"360\" y=\"430\" width=\"20\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-21\" value=\"\" style=\"rounded=1;whiteSpace=wrap;html=1;align=left;fontFamily=JetBrains Mono;fontSize=9;fontStyle=1;fillColor=none;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"500\" y=\"430\" width=\"20\" height=\"20\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-22\" value=\"initial callbacks\" style=\"edgeLabel;html=1;align=left;verticalAlign=middle;resizable=0;points=[];fontFamily=JetBrains Mono;fontStyle=2;fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"1\">\n          <mxGeometry x=\"100\" y=\"440\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-23\" value=\"dynamically-added&lt;div&gt;callbacks&lt;/div&gt;\" style=\"edgeLabel;html=1;align=left;verticalAlign=middle;resizable=0;points=[];fontFamily=JetBrains Mono;fontStyle=2;fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"1\">\n          <mxGeometry x=\"240\" y=\"440\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-24\" value=\"execution-time&lt;div&gt;clean calls&lt;/div&gt;\" style=\"edgeLabel;html=1;align=left;verticalAlign=middle;resizable=0;points=[];fontFamily=JetBrains Mono;fontStyle=2;fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"1\">\n          <mxGeometry x=\"391\" y=\"440\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-25\" value=\"other components\" style=\"edgeLabel;html=1;align=left;verticalAlign=middle;resizable=0;points=[];fontFamily=JetBrains Mono;fontStyle=2;fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"1\">\n          <mxGeometry x=\"531\" y=\"440\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-27\" value=\"installs callbacks\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontFamily=JetBrains Mono;fontStyle=2;fontSize=9;\" vertex=\"1\" connectable=\"0\" parent=\"1\">\n          <mxGeometry x=\"337\" y=\"361\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-28\" value=\"&lt;font style=&quot;color: rgb(255, 255, 255);&quot;&gt;1&lt;/font&gt;\" style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;fontFamily=JetBrains Mono;strokeWidth=6;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"270\" y=\"356\" width=\"10\" height=\"10\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-29\" value=\"&lt;font style=&quot;color: rgb(255, 255, 255);&quot;&gt;6&lt;/font&gt;\" style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;fontFamily=JetBrains Mono;strokeWidth=6;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"840\" y=\"232.5\" width=\"10\" height=\"10\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"jZhf56UUiF3wu5g5-CzG-30\" value=\"&lt;font style=&quot;color: rgb(255, 255, 255);&quot;&gt;7&lt;/font&gt;\" style=\"ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#000000;fontFamily=JetBrains Mono;strokeWidth=6;\" vertex=\"1\" parent=\"1\">\n          <mxGeometry x=\"804\" y=\"295\" width=\"10\" height=\"10\" as=\"geometry\" />\n        </mxCell>\n      </root>\n    </mxGraphModel>\n  </diagram>\n</mxfile>\n"
  },
  {
    "path": "docs/assets/dr-model.drawio",
    "content": "<mxfile host=\"Electron\" agent=\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/27.0.9 Chrome/134.0.6998.205 Electron/35.4.0 Safari/537.36\" version=\"27.0.9\">\n  <diagram id=\"C5RBs43oDa-KdzZeNtuy\" name=\"Page-1\">\n    <mxGraphModel dx=\"1678\" dy=\"2377\" grid=\"1\" gridSize=\"10\" guides=\"1\" tooltips=\"1\" connect=\"1\" arrows=\"1\" fold=\"1\" page=\"1\" pageScale=\"1\" pageWidth=\"827\" pageHeight=\"1169\" math=\"0\" shadow=\"0\">\n      <root>\n        <mxCell id=\"WIyWlLk6GJQsqaUBKTNV-0\" />\n        <mxCell id=\"WIyWlLk6GJQsqaUBKTNV-1\" parent=\"WIyWlLk6GJQsqaUBKTNV-0\" />\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-14\" value=\"&lt;font style=&quot;font-size: 18px&quot;&gt;DynamoRIO Tool&lt;/font&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;fontSize=11;verticalAlign=top;arcSize=6;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"260\" y=\"320\" width=\"930\" height=\"580\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-70\" value=\"\" style=\"rounded=1;whiteSpace=wrap;html=1;fontSize=15;arcSize=8;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"840\" y=\"360\" width=\"330\" height=\"520\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-12\" value=\"&lt;font style=&quot;font-size: 18px&quot;&gt;Python Adapter&lt;/font&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;fontSize=11;verticalAlign=top;arcSize=11;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"201\" y=\"-100\" width=\"550\" height=\"370\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-1\" value=\"DynamoRIOModel\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"391\" y=\"60\" width=\"300\" height=\"90\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-3\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;\" parent=\"QmN-cfn-Gxa06QFyi7zT-1\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"300\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-4\" value=\"+ configure_clauses(...)&#xa;+ load_test_case(TestCaseProgram)&#xa;+ trace_test_case(List[InputData],...) -&gt; List[CTrace]&#xa;\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;\" parent=\"QmN-cfn-Gxa06QFyi7zT-1\" vertex=\"1\">\n          <mxGeometry y=\"34\" width=\"300\" height=\"56\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-10\" value=\"&lt;b&gt;&lt;font style=&quot;font-size: 18px&quot;&gt;Fuzzer&lt;/font&gt;&lt;/b&gt;\" style=\"text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"241\" y=\"-80\" width=\"60\" height=\"30\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-11\" value=\"&lt;font style=&quot;font-size: 11px&quot;&gt;TestCaseProgram&lt;br&gt;List[InputData]&lt;/font&gt;\" style=\"html=1;verticalAlign=bottom;endArrow=block;rounded=0;fontSize=25;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-10\" target=\"QmN-cfn-Gxa06QFyi7zT-4\" edge=\"1\">\n          <mxGeometry x=\"0.0186\" y=\"13\" width=\"80\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"381\" y=\"-50\" as=\"sourcePoint\" />\n            <mxPoint x=\"461\" y=\"-50\" as=\"targetPoint\" />\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-13\" value=\"&lt;font style=&quot;font-size: 18px&quot;&gt;Test Case Loader&lt;/font&gt;\" style=\"rounded=1;whiteSpace=wrap;html=1;fontSize=11;verticalAlign=top;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"843\" y=\"-100\" width=\"300\" height=\"350\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-46\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;fontSize=12;dashed=1;exitX=0.588;exitY=0.001;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" target=\"QmN-cfn-Gxa06QFyi7zT-13\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"869.8400000000001\" y=\"320.62000000000035\" as=\"sourcePoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"870\" y=\"290\" />\n              <mxPoint x=\"993\" y=\"290\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-47\" value=\"Instrument Binary\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-46\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"0.2028\" y=\"-2\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-22\" y=\"-7\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-19\" value=\"parser.c\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"873\" y=\"-20\" width=\"230\" height=\"70\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-21\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-19\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"230\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-22\" value=\"+ parse_rcbf(file): rcbf_t*&#xa;+ parse_rdbf(file): rdbf_t*\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-19\" vertex=\"1\">\n          <mxGeometry y=\"34\" width=\"230\" height=\"36\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-26\" value=\"sandbox.c\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"873\" y=\"70\" width=\"230\" height=\"70\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-27\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-26\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"230\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-28\" value=\"+ load_code_in_sandbox(rcbf_t*)&#xa;+ load_data_in_sandbox(rdbf_t*)\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-26\" vertex=\"1\">\n          <mxGeometry y=\"34\" width=\"230\" height=\"36\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-30\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;fontSize=12;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-22\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"873\" y=\"110\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"853\" y=\"32\" />\n              <mxPoint x=\"853\" y=\"110\" />\n              <mxPoint x=\"873\" y=\"110\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-31\" value=\"test_case_entry.c\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"873\" y=\"160\" width=\"230\" height=\"60\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-32\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-31\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"230\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-33\" value=\"+ test_case_entry(sandbox_t*)\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-31\" vertex=\"1\">\n          <mxGeometry y=\"34\" width=\"230\" height=\"26\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-38\" value=\"&lt;b&gt;&lt;font style=&quot;font-size: 18px&quot;&gt;Postprocessor&lt;/font&gt;&lt;/b&gt;\" style=\"text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"241\" y=\"200\" width=\"60\" height=\"30\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-39\" value=\"&lt;font style=&quot;font-size: 11px&quot;&gt;TestCaseProgram&lt;br&gt;List[InputData]&lt;/font&gt;\" style=\"html=1;verticalAlign=bottom;endArrow=block;rounded=0;fontSize=25;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-38\" target=\"QmN-cfn-Gxa06QFyi7zT-4\" edge=\"1\">\n          <mxGeometry x=\"-0.2384\" y=\"-12\" width=\"80\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"281\" y=\"-40\" as=\"sourcePoint\" />\n            <mxPoint x=\"351\" y=\"121.02400000000034\" as=\"targetPoint\" />\n            <mxPoint y=\"-1\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-40\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;fontSize=12;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-4\" target=\"QmN-cfn-Gxa06QFyi7zT-13\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-42\" value=\"RCBF File&lt;br&gt;RDBF File\" style=\"text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=12;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"690\" y=\"90\" width=\"60\" height=\"30\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-48\" value=\"model.cpp\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"934\" y=\"405\" width=\"200\" height=\"110\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-49\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-48\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"200\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-50\" value=\"+ event_bb_instrumentation(...)&#xa;+ event_instrumentation_start(...)&#xa;+ event_instrumentation_end(...)&#xa;+ event_signal()\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-48\" vertex=\"1\">\n          <mxGeometry y=\"34\" width=\"200\" height=\"76\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-52\" value=\"Dispatcher\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"934\" y=\"565\" width=\"200\" height=\"110\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-53\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-52\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"200\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-54\" value=\"+ instrument_instruction(...)&#xa;+ start(...)&#xa;+ finalize(...)&#xa;+ handle_exception(...)\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-52\" vertex=\"1\">\n          <mxGeometry y=\"34\" width=\"200\" height=\"76\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-57\" value=\"Instrumentation Components\" style=\"text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=15;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"907\" y=\"360\" width=\"210\" height=\"30\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-61\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontSize=15;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-28\" target=\"QmN-cfn-Gxa06QFyi7zT-33\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-62\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fontSize=15;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-50\" target=\"QmN-cfn-Gxa06QFyi7zT-52\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"1150\" y=\"467\" />\n              <mxPoint x=\"1150\" y=\"530\" />\n              <mxPoint x=\"1034\" y=\"530\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-69\" value=\"\" style=\"rounded=1;whiteSpace=wrap;html=1;fontSize=15;arcSize=7;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"280\" y=\"360\" width=\"550\" height=\"520\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-63\" value=\"Execution-time Components\" style=\"text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=15;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"460\" y=\"360\" width=\"210\" height=\"30\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-64\" value=\"dispatcher.cpp\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"633\" y=\"390\" width=\"170\" height=\"100\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-65\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-64\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"170\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-66\" value=\"+ dispatch_callback(...)&#xa;+ mem_access_dispatch(...)&#xa;+ instruction_dispatch(...)\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-64\" vertex=\"1\">\n          <mxGeometry y=\"34\" width=\"170\" height=\"66\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-68\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;fontSize=15;dashed=1;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-54\" target=\"QmN-cfn-Gxa06QFyi7zT-66\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"934\" y=\"587\" />\n              <mxPoint x=\"850\" y=\"587\" />\n              <mxPoint x=\"850\" y=\"457\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-71\" value=\"&lt;font style=&quot;font-size: 8px;&quot;&gt;add calls to&lt;br style=&quot;font-size: 8px;&quot;&gt;dispatch_callback()&lt;br style=&quot;font-size: 8px;&quot;&gt;forevery instruction&lt;br style=&quot;font-size: 8px;&quot;&gt;&lt;/font&gt;\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=8;\" parent=\"QmN-cfn-Gxa06QFyi7zT-68\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"-0.6126\" y=\"1\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-44\" y=\"-37\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-74\" value=\"TracerABC\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"301\" y=\"537\" width=\"230\" height=\"120\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-75\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-74\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"230\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-76\" value=\"+ tracing_star(...)&#xa;+ tracing_finalize(...)&#xa;+ observe_instruction(...)&#xa;+ observe_mem_access(...)&#xa;+ observe_exception(...)\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-74\" vertex=\"1\">\n          <mxGeometry y=\"34\" width=\"230\" height=\"86\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-77\" value=\"SpeculatorABC\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"590\" y=\"540\" width=\"170\" height=\"120\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-78\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-77\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"170\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-79\" value=\"+ handle_instruction(...)&#xa;+ handle_mem_access(...)&#xa;+ handle_exception(...)&#xa;- checkpoint()&#xa;- rollback()\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-77\" vertex=\"1\">\n          <mxGeometry y=\"34\" width=\"170\" height=\"86\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-81\" value=\"\" style=\"html=1;verticalAlign=bottom;endArrow=block;rounded=0;fontSize=8;exitX=0.235;exitY=1.031;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;curved=1;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-66\" target=\"QmN-cfn-Gxa06QFyi7zT-74\" edge=\"1\">\n          <mxGeometry width=\"80\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"660\" y=\"560\" as=\"sourcePoint\" />\n            <mxPoint x=\"740\" y=\"560\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-82\" value=\"dispatch instruction/mem. access\" style=\"html=1;verticalAlign=bottom;endArrow=block;rounded=0;fontSize=9;exitX=0.245;exitY=1.01;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-66\" target=\"QmN-cfn-Gxa06QFyi7zT-77\" edge=\"1\">\n          <mxGeometry x=\"-0.2193\" y=\"-25\" width=\"80\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"699.94\" y=\"512.0160000000001\" as=\"sourcePoint\" />\n            <mxPoint x=\"445\" y=\"550\" as=\"targetPoint\" />\n            <mxPoint as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-83\" value=\"TracerCT\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"301\" y=\"697\" width=\"230\" height=\"80\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-84\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-83\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"230\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-85\" value=\"+ observe_instruction(...)&#xa;+ observe_mem_access(...)\" style=\"text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-83\" vertex=\"1\">\n          <mxGeometry y=\"34\" width=\"230\" height=\"46\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-86\" value=\"\" style=\"endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;fontSize=8;entryX=0.5;entryY=1;entryDx=0;entryDy=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-83\" target=\"QmN-cfn-Gxa06QFyi7zT-74\" edge=\"1\">\n          <mxGeometry width=\"160\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"461\" y=\"607\" as=\"sourcePoint\" />\n            <mxPoint x=\"621\" y=\"607\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-90\" value=\"SpeculatorSeq\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"566\" y=\"713\" width=\"140\" height=\"34\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-91\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-90\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"140\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-93\" value=\"SpeculatorCond\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"588\" y=\"763\" width=\"160\" height=\"34\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-94\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-93\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"160\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-95\" value=\"SpeculatorBpas\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"643\" y=\"813\" width=\"160\" height=\"34\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-96\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"QmN-cfn-Gxa06QFyi7zT-95\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"160\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-97\" value=\"\" style=\"endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;fontSize=8;exitX=0.5;exitY=0;exitDx=0;exitDy=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-90\" edge=\"1\">\n          <mxGeometry width=\"160\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"680.71\" y=\"660\" as=\"sourcePoint\" />\n            <mxPoint x=\"676\" y=\"660\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"636\" y=\"680\" />\n              <mxPoint x=\"676\" y=\"680\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-98\" value=\"\" style=\"endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;fontSize=8;exitX=0.829;exitY=0.013;exitDx=0;exitDy=0;exitPerimeter=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-93\" edge=\"1\">\n          <mxGeometry width=\"160\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"646\" y=\"670\" as=\"sourcePoint\" />\n            <mxPoint x=\"676\" y=\"660\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"721\" y=\"680\" />\n              <mxPoint x=\"676\" y=\"680\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-99\" value=\"\" style=\"endArrow=block;dashed=1;endFill=0;endSize=12;html=1;rounded=0;fontSize=8;exitX=0.75;exitY=0;exitDx=0;exitDy=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" source=\"QmN-cfn-Gxa06QFyi7zT-95\" edge=\"1\">\n          <mxGeometry width=\"160\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"731\" y=\"720\" as=\"sourcePoint\" />\n            <mxPoint x=\"676\" y=\"660\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"766\" y=\"680\" />\n              <mxPoint x=\"676\" y=\"680\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-100\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontSize=12;dashed=1;exitX=0.004;exitY=0.379;exitDx=0;exitDy=0;exitPerimeter=0;endArrow=block;endFill=1;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"296.97999999999985\" y=\"603.594\" as=\"sourcePoint\" />\n            <mxPoint x=\"534.06\" y=\"150\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"245.06\" y=\"604\" />\n              <mxPoint x=\"245.06\" y=\"297\" />\n              <mxPoint x=\"534.06\" y=\"297\" />\n            </Array>\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"QmN-cfn-Gxa06QFyi7zT-101\" value=\"&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; TRACE File&amp;nbsp;&amp;nbsp;\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=12;\" parent=\"QmN-cfn-Gxa06QFyi7zT-100\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"0.2028\" y=\"-2\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"66\" y=\"-3\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-20\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;endArrow=none;startFill=0;strokeColor=#666666;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" target=\"uKMc0L07USMBdFAgg4Kt-19\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"421\" y=\"340\" />\n              <mxPoint x=\"585\" y=\"340\" />\n            </Array>\n            <mxPoint x=\"421\" y=\"410\" as=\"sourcePoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-3\" value=\"Logger\" style=\"swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;fontSize=18;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"379\" y=\"410\" width=\"120\" height=\"34\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-4\" value=\"\" style=\"line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;fontSize=18;\" parent=\"uKMc0L07USMBdFAgg4Kt-3\" vertex=\"1\">\n          <mxGeometry y=\"26\" width=\"120\" height=\"8\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-11\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;dashed=1;endArrow=block;endFill=1;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"550\" y=\"416\" />\n              <mxPoint x=\"550\" y=\"416\" />\n            </Array>\n            <mxPoint x=\"633\" y=\"416\" as=\"sourcePoint\" />\n            <mxPoint x=\"499\" y=\"416\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-12\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.042;entryDx=0;entryDy=0;dashed=1;endArrow=block;endFill=1;entryPerimeter=0;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <Array as=\"points\">\n              <mxPoint x=\"549\" y=\"616\" />\n              <mxPoint x=\"549\" y=\"438\" />\n              <mxPoint x=\"499\" y=\"438\" />\n            </Array>\n            <mxPoint x=\"531\" y=\"616\" as=\"sourcePoint\" />\n            <mxPoint x=\"499\" y=\"438.336\" as=\"targetPoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-13\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;dashed=1;endArrow=block;endFill=1;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"499\" y=\"427\" as=\"targetPoint\" />\n            <Array as=\"points\">\n              <mxPoint x=\"569\" y=\"617\" />\n              <mxPoint x=\"569\" y=\"427\" />\n            </Array>\n            <mxPoint x=\"589\" y=\"617\" as=\"sourcePoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-15\" value=\"&lt;font&gt;Log Events&lt;/font&gt;\" style=\"edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=9;\" parent=\"uKMc0L07USMBdFAgg4Kt-13\" vertex=\"1\" connectable=\"0\">\n          <mxGeometry x=\"-0.4384\" y=\"-2\" relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"-31\" y=\"-153\" as=\"offset\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-16\" value=\"\" style=\"sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#232F3D;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.document;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"386\" y=\"285\" width=\"17.54\" height=\"24\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-19\" value=\"\" style=\"sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#666666;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.document;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"578\" y=\"285\" width=\"16.08\" height=\"22\" as=\"geometry\" />\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-21\" style=\"edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;dashed=1;endArrow=block;endFill=1;strokeColor=#666666;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" edge=\"1\">\n          <mxGeometry relative=\"1\" as=\"geometry\">\n            <mxPoint x=\"585.8\" y=\"150.5\" as=\"targetPoint\" />\n            <mxPoint x=\"585.8\" y=\"283.5\" as=\"sourcePoint\" />\n          </mxGeometry>\n        </mxCell>\n        <mxCell id=\"uKMc0L07USMBdFAgg4Kt-22\" value=\"&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;Debug Trace&lt;/span&gt;&lt;div&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;(optional)&lt;/span&gt;&lt;/div&gt;\" style=\"text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;fontColor=#666666;\" parent=\"WIyWlLk6GJQsqaUBKTNV-1\" vertex=\"1\">\n          <mxGeometry x=\"594.08\" y=\"276\" width=\"90\" height=\"40\" as=\"geometry\" />\n        </mxCell>\n      </root>\n    </mxGraphModel>\n  </diagram>\n</mxfile>\n"
  },
  {
    "path": "docs/assets/fuzzing-flow.drawio",
    "content": "<mxfile host=\"Electron\" modified=\"2025-02-14T10:47:35.820Z\" agent=\"5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.5.1 Chrome/96.0.4664.110 Electron/16.0.7 Safari/537.36\" etag=\"RKLjlPN-6zBEMbTxfot0\" version=\"16.5.1\" type=\"device\"><diagram id=\"C5RBs43oDa-KdzZeNtuy\" name=\"Page-1\">7V1bd+K2Fv41rJXzQJav4DwGkplOD+1JJ5mm85Sl2ALcMRa1RQLz64+EZWNL8gWwIEnd1dViIWRb37582tra6ZnjxfpzBJbz35AHg56heeueedMzDMOxHfI/2rJJWnTD1pKWWeR7rG3XcO//hKwx7bbyPRgXOmKEAuwvi40uCkPo4kIbiCL0Wuw2RUHxrkswg0LDvQsCsfXR9/A8aXWM4a79F+jP5umd9cFV8s0CpJ3Zm8Rz4KHXXJN52zPHEUI4+bRYj2FAZy+dl8cvm8dg8mPw+dc/4n/At9F/H37/s58M9mmfn2SvEMEQHzz094enyQj8sL3RYGpF3vL201pjP9FeQLBi8+UG/uVyw94Yb9JpjF/9RQBCcjWaohDfs290cg0CfxaSzy55OhiRhhcYYZ8gcM2+wGhJWt25H3gTsEEr+g4xBu6P9Go0R5H/kwwLAjYm+TrCTJiMQaHHPf0ladZIawRj0ucunRida/oNrAsdJyDGrMFFQQCWsf+cvcYCRDM/HCGM0YJ1ajjvDB/62nCdkzqGw2eIFhBHZE619FuLzTtTqvTydSeghsba5jnhHLA2wHRilo28w518YNDvIQamIAY9Y7TVAT+8+I8gDOQ18RakCP2AYxQggvpNiBLp8IOAa0oFJIBTXCoe8RK4fjibbPvcWLuWr+z9aRMiv50GWyWc+54HQwotwgCDBEcK2hL5Id5OkD0i/5IpG2uXds8mDz4m1/rumvxLu0d4jELyLuRd6X0gEZJXSAVFgn+lEtXjz/AmEt0I77Rf63gbErw5jAN/i12CcWo59YMAXhCoArhD9IECftPXBdRNEXVTgnAAnmFwh2If+4iOHyV9OeTPBa5tNAPXUYStNagHF3rER7JLMkVzNEMhCG53rWTOV6EHPTbjuz4TtAWPYvc3xHjDbDRYYUStNF6kFpzMarT5K3/xnQ52adjp9U1qnJOrDbvaupdkVH2wtxGmb9bEBPfJkxjpzDDk+ilniWAAsP9SZBAyoNjwd1Twys17RpnSIWK0ilzIfpV32fxA2qWW+8dISUs67hU3LnGaM4iFca+jCGxy3Zie7P38pY/J978q9Ccfkifgfp0+DppOY4h7vNxnmDVThWB95/cfvz0+j/94+Pb39Ffv6jp1aycW/LWP/9qKus2uvue+2Qk9vZDIvJPXHTZGpj01qiNVgea6k4hPhWnRbblOHakvhu60oy/8QLauRkH4+5iDagVxtKruRf1oS/ZFL9Anlw/E941BDO8iNIvAomcMAuoLnyPyaZZ4xaTF818KmjP4Z0XXOSNK/frM31+THluXn32bjkJvNfGJm7VHX8LlCt8Q/71lYMng5IW246e3lCjphPr4omLVrjMynrFl+6WU8EZUuBIfn61K2Vi9bC1YoA2lpqfK+9jDYdGa6634nr5V9GmWWRxBnbHVB2elGVrvYJrhnMB0ltGRI+E2D6Qa+1q8svuUWTyhfxUlaEsC0+njbB4zROOHCLgwZ4V2Ri/X65ddrw9olRIdrSTF2qBVs9Su3ZGudwTQp6ufP2FEo1jm9aftZwHMLpzVZjjLLCq77kjiWYZkCTxUFc8aCjKRxLO2OFxcXl52Ma2asIfVWAgqYloyzE1VMa0GYY8upnUguANJTEsGrrKY1llX8unnhFmmq/ralfwB0auErVVMhN40zHXsMmJwqQ9zoaehWbDwpnZoRItzFVlIq+0QVsl9lPLPK8ECCcvtd84py3WzOtB6HIdMR7oq/kIdpXQEIF3kwacZDGEEMGLUckzaPqdNHcNUyTD7GVWsopiOxCMp2zJNQ4sd36g3EU5jvCvIpAxdVXxDFyMJyQLCjSDA8AmT139yiWFnawmtvwtmijHWbqXRqnCYVjPhaGX3XObnNQHSN0pGc/tIRmEjKYucqtlIqqexDMLasKmlhN1myWllUdO2WKhjF2kz8xFKWajMdCUBT8pDerJNHfpFP95KHt3TcZZrcUunejsnGfp8+znH0dpSJa9itUPrWF6bpR2citfqhiAbHoGSJ7YU3o7YnojY8qZoaIveTRZqqSK27HZfqeCHM/Jeu/vZxdtZRsNQLe9MQUAQDgkXGlHHFiuRVllWYsewqzW79XitMoot7tskFJsZI3jB82qpB+q4dbtiISPXTexBa+TaPie5Jl49n7SVEeo6dr0XuW4386CeaytK2uIirEKuVeOkLT5Uq4qMc67PLKYk1D6XeYoUBjHyKGYvvPMYcqnSV5Ftx3CuCmi0lC1lFAdVkixVrpQFoBf07FVCwNkxrI54KyTeA54IX4mOTxpyVBZQLktaCBDwchFHLrxIWBJxxaaeTBbtj6mlyP1AwptKqFW5kel4lVx9W49oW6rSI/QuP0IhvEOjofVQha5IHOAauqsspnPLrjqvotKrGG/Pq4hJCZ3a1ynRu9mnTAn2GTlDecJ0xxmOlK6mG53KOIMkoR6EINhk2dXX9Cru8qvVehVrWL9HYO67R3CcYIgbSZ1XqVOiY7yKFF5lXqWsHARBjqjs04uPaNSFTMuFuHAc51wDLrRKUmX+TEfqnEfbQiRxHlIhUldjQtzgCdAs3h3LmaDZrHMdp3UdsrxJqVgoO5pjiJHPznXUqdD7cR2yKNNoG6in87FzHReZ6e+OYrUuBU1tv7KzWGaD1A0Yete0vhu15wGIY9/tFTawipu6h5bEOKQax37GuHYDlk18s1xHo/5oTw5VWwJq2nbs/i53gls/dH+Xd0GZJNbs77a11WaKPISe9/5yT1Md4yV0t1iTGc0dCz8wR5KO66Jw6s+yMeWmLSfmLVm5zM+9zn0M75dgK5KvEVgKqiTdzVUQC+UFSBOphyyaYapiHuZ5ClQwA9SntZyMXj5529CHlXaorCrQvhW16KB3MPLJNFLWrMLGNc3ndhrauGMrcHGid2Wf1uTItnJzdXUSa+HH4Inan2RF9IVyhJVLGdE9zTloo1JOSk7bTv2opSypqjXOEanK/Ui5A5cufaSIWFbxF+pyPUxxb04XkQwCfxmXGfActvwBY36NnHcdPcOcOi50XcHPkG+eHduylW6GaZwWGpKw5UkdgLgXJqGlHw8IblNSt0+Ig7ROjwCD5Hj/uWGQmK5myJTCoFtc+caBJBZzShxEYmz9K3DQuFNZsu2UU+KgS9yBemL6XmpOOkoYIm8UD6/RynHNLOWj7ZqTJQ9cmr7M9a+rUWlplf0VnVUUXUFXpfKU9eBqiyQrKlNp6UWl4ZRPYZXKM9cENpqfLzn1ORE1hlbnU+JMzj42Pidi11jslgytWX3uo/a59u3PGXJFhlbkev+Ogpe1xYne88kS6RuL+6uSI3Ufj9WbvBoOJVuhJ2X1w7N6mkKkuXGZkOFbPsmYhgmUVw05Lroh7n3m0py0/30l//md7ul8SHs7rLO3A61Vc3sCgyqGbyUbSR/QoHJhEsMWzzKcNmwochiJif2AQHBVhLKqQmcD4jxn9Esq/b+1Qv8lJdaPLfTPrxpMozhE43QM/o+R8KVc21pGlTyw2r8AIKaflacYfwR/a9SenH9LBf3J5e7PZSbdd3911Lz9Pw==</diagram></mxfile>"
  },
  {
    "path": "docs/assets/tsa-sq-template.drawio",
    "content": "<mxfile host=\"Electron\" modified=\"2025-07-21T10:25:41.035Z\" agent=\"5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.5.1 Chrome/96.0.4664.110 Electron/16.0.7 Safari/537.36\" etag=\"BbGekvxkDWImu01GMpuU\" version=\"16.5.1\" type=\"device\"><diagram id=\"VUT32xMhq3pvxhd0Flhl\" name=\"Page-1\">7Vtdc5s4FP01nmkfkjESYPvRcZJmptnZzma7m33qyCAbpoBcIcf2/vqVQHxJ2CYOJiTZvsS6EgKde+7RvYIO4CzcfqFo5f1GXBwMwNDdDuD1AADDHkH+R1h20jIEk9SypL4rbYXhwf8XZwOlde27OK4MZIQEzF9VjQ6JIuywig1RSjbVYQsSVO+6QkusGR4cFOjWv32XednCLLPouMP+0pO3BhDaaU+IstFyKbGHXLIpmeDNAM4oISz9FW5nOBDwZcCk193u6c2fjOKINblgeDe9uDccYzeZjR7t9TSOx8sLMEqneULBWi5ZPi3bZRhQso5cLGYxBvBq4/kMP6yQI3o33O3c5rEwkN0LPwhmJCA0uRYuFgvgONweM0p+4lKPa89ty+Y9+jrk0p4wZXhbMsl1fcEkxIzu+BDZC22JcUazDPNN4TMwlDav5C4zuxBJnizzuQsk+Q8J5nOAtc8LrGvhsWvWATsGc2i3BKzRP2ANDdevmEaJ7qTgATvgD3E1p/zXUvz6hBwmwJkmUelHnzVHcDRYFe0qqhGJsOICaUKBv4x40+EYY26/Etj6XD+msiP0XVfcpta9BQGGYnoSsQf5UDXR/Wzn5cqTOQ+amvPGNb6DZ4sJzXXfYw4aGIqN47Df1nzgR/GbaRz3m1EXdGdznFWjZdJbmbNEYP0YZmZ+l7zngOwNj8veUUcxsmpH63Id2yntMuygS9jrtpAUX9d/KuBNTfEKRaot94EIn33eyW11M3Bz6V63998f7hJnirRJD9gVxcIFIh9DjofTkQzrgdtTCpgqBWBDCpxtt2uQnuHInYpMVyAUoDj2nSp8Vaw5OHT3WG78IxqXVta83pY7r3eytRddhugSswNLkOKB3UqmrfughLFVA3FmozhAzH+q5ud1uMs7fCM+f+Iio1HEFRhWdYqYrKmD5VXldFqdCFrVicYKB1JgtIkSGuTLPp0ZY40Zf+JYPN8MxSLybqKMf/3bMVsI1bGyR471SLW71OqJ5o5kRzR0lawKdsk59q+1qAWTfOJiIx9aJD8RoSEKigHZTH/5DvPDpPqtzZ8oilwS8pra/bxX5jsQ5tbzWlWlJ6+t0pmmqN4/VPK9HXzzfPT18NWLvgRf+C7wtZpmGWcTL0OvzJKMEbShXklSpUrXHzf3v0+v96eSR9LSl2vnJ74RUl5gCmiihRjEfBK9K51Us1lz9OpxrBeSC7QO2A+P71SBOAp4gzBrcmm+eji3XDgag5cWjlPGkPOzYabyNmtFaL16dLVfLG599ih7xO9SqchbRaUoGlmhuK/ABCdXmGlFdmDhUlWOVqIyLHpSidpgjzw/txK1R8pEQJnozJWooZeiLfFuWOHdqCHxjM6IZzck3qRXxINQUa7hiUcg6kE1BMpE5yaeXnSfRfCaEq87xZs0JJ7RL8lTmZfvoS9lXteSl93u40me0VTzMg/1lHoQtiV6ZreiB/SzkLNQ7zTmHSOe9rXC7S3k/0RlgmIv/xaiC372672ERqvRifzUNbZjfupnSRo/s6LsHs1x8I3Evjh74V1zwhgJ91Zt5bcWHlqJycLtUnx5djlHnOSXC052b6B9K5NzbC+tnlP2mYqfBOnVwm+k88SA+ynxsq8q4EfNgoymhV92vvl/rLcc62Yf9qIXEMjoFS+AUsgbp54IqC+5ra5zFP2cV3k5zf2nMeW9vpu2at6fdfpuGpytTu59tQKa7hD9UgJN2EfgsqV6xZy0pQW8WXy9ng4v/hcAvPkP</diagram></mxfile>"
  },
  {
    "path": "docs/assets/unicorn-model-state-machine.drawio",
    "content": "<mxfile host=\"Electron\" modified=\"2024-11-12T17:35:30.054Z\" agent=\"5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.5.1 Chrome/96.0.4664.110 Electron/16.0.7 Safari/537.36\" etag=\"H7DxQGuZ2p2YCU2e7pUZ\" version=\"16.5.1\" type=\"device\"><diagram id=\"Uj5UJ1iNXtTnnK9jLXeE\" name=\"Page-1\">7Vxbc5s4FP41nml3phmQuD4mTtzONOtmm3S23ZeOYmSbLUZeITdxf/0KEBcDBmyMgaTuTApHEhyk7zs6R7cRHK+e31O0Xv5JLOyMgGQ9j+D1CAAgQZn/50u2oUQGqhRKFtS2hCwR3Nu/sBBG2Ta2hb2djIwQh9nrXeGMuC6esR0ZopQ87WabE2f3rWu0wDnB/Qw5eenftsWWQqqpSpLwAduLpXg1gFALU1Yoyi0+xVsiizylRPBmBMeUEBZerZ7H2PGrL6qYsNxkT2qsGcUuq1Pgr6+f7pzF9uP0++zLP9PF+8ndVnkn60I5to0+GVu8BsQtoWxJFsRFzk0ivaJk41rYf6zE75I8t4SsuVDmwn8xY1vRnGjDCBct2coRqfjZZl/94hequPuWSrl+Fk8ObrbRjcvoNiwE1Oj+WzoxKRfcRQU9RskPPCYOocEHwskE8h9PCT/d/969VSpEHtnQGS6rR4FNRBeYleTT4obnnMFkhbmmvBzFDmL2z109kMDuIs6XtC6/EA18SGOHz/2JnI140x/89o4/TJoSukL8AZrDtb96pPxq4V/5GS5dX8X7NZ7lsLKLhKelzfD9GgU19cQtwm6rI8deuPzawXMW1/5PTBl+Lq//fH2JAtAU8BXmRYm49pTiaiRbpmiqSS1VsV9+cHxqRidJmkwk6bR0AjXppHdJJ7CfTje8povJ1EcaGVFv2xsaRX7DgGgkNyIR5DTStNOSCNYkkaw0ZFFQ9JJStE1lWBPbZV7qyXe+IAGdLIFd1KlSxn/JFlDkhgUMpaIA0EBZAX4RfmUC7Li6GmD9GDMyJe47r4emxNT6ZkqUIfbIDT3cNrpkZQhdsrKfSx+QazmYDqdX1vS+UQkMr1du6ty2ESuqNZnUaayoHsmkvnZMKuwdm7TfbDoBm7SabDK7cHLVjA3X5HIPVDFBWf52HFAwxDHA4QJRlnqBRLUciZoBy/K3hERjKEjsF6L2dH1nRhSsiK5lGZYVaAlS5m9IHQMp2AtIVQzYmFlEnWW8RhoKol5Gdwl6gcTs5GrWuMEsFMEZoCjqMBWkff4yzaEzwZ5cHXnNbcdJtfx8PgezWREmLO1RU5MR62bBWXZkF3YdnCmd+sRyiuLRYOBAhw31mjTvpL8x1QzuFLW8v8nkVypmIEyzNH87RkHp1Ik+Frr6cKHbSQfVFLrQqICuVJq/Jeh26qy/Quh2MvPbGLpaOXR1rTR/O9BVO40KXiF01UFCt2L8VwOl+VuCbqfTeq8Qup0M1zWFLjArpi4MvSx/O9AVr0wHwJ9ub68uxx9zkO5/FKzD3kXBPRrqkuoahoZBsKZdgcnktIbB6LM7BjOwgxWDrno2MjjHoKswVsNC4nCB2IlzlQNihZ+vmh34+UqPVm28BpPYia+UQ2LFYIkC9LL87SDRyPk+X+6uLx9uRuEyrSbuj4MesXOFZj8WQbGdmaDJZDzuykMCet88JKD0xxwcNxkoH2oN/N+prYFZ0xoYDa1Bo7Y2c4QLlkRi17LdBb+aoI2zZ7n+Mlgy+X0e5pBc4v+1Xf7nDUemv6jSb6u3Z91/l2WwMcPFDH40eKwonYbB2S0ZmlHAYA2ek8E98iyP3B9wMIXbGPyIGrb1DQJH9ejZ5Wz6gWPI+jl8y6gKT2Bh6tqWl2FVtDpWRdEu1DPaFQUOz640ihLa2MIo191X39EexuySRl2vWgOpG2UFWrIrezb+t2hXWtkG0oltkSWlf8YFDtC4NBwOa8W8gHOZl2bndoBC/k6J/9kxief7SfyC2Aj6yMbXNwjQSgRR98CCTiKI7FrzygjC7CKCKD5MoL6leBnxgNFDIyHn96bfTK/zhsNx7LWHq+u5YJTe/xVUuer/8+XEZZlxvNSi/mZmWc7Ey3ELVI3MGq3FX4M3ykfM0/hO0omNct3d6k0napqRK79d/f7h8vPDC6FX9pyzmG2dsUvNdzS3eMF7mBG8LO5Z+PV/G/+sw6vdA+eEkIve+TBb4qDBEGVhXxVktnm/LLnY5qk0zoSDw3EkZFkUe0EGkiQmUjKPhVEQuyw7wCCjZ3LYQR1FV4jNltg7vRriJKBjdMjVU0oVhj0/bYY4JSrUSM9CFOvBkzac7EErBC0WN1gwaoBSeXyDwBWhQuyK+CRJ4h/FKlU6SJk9OtR6URI/HfqyI78sPzSTey/F0ctQkXMnoTlLk2UVK+gxsvaqv7jooXu0QMFft6CI11iR+jNnRS1DMdtQN7ZTAvseduYX39NPfvM2b2Wk2KQE/Kmi1UGfUfEBxyq/o3t91fe6+8cvFeC4x6W9aEWYUNWpelwhjrPboPS1LJ+mnwU6vIjmAyNHVs53tdzNKgodgHR4b8tvkxOAwygwOUkZ3vwP</diagram></mxfile>"
  },
  {
    "path": "docs/faq/general.md",
    "content": "# General FAQ\n\n## Overview\n\n#### What is Revizor? {#what-is-revizor}\n\n:   Revizor is a security-oriented fuzzer designed to detect microarchitectural information leaks in CPUs. It automatically generates random test programs, executes them on real hardware, and compares the observed behavior against a formal model to identify unexpected information leakage through side channels like those exploited by Spectre and Meltdown attacks.\n\n#### Who is Revizor for? {#who-uses-revizor}\n\n:   Revizor is primarily designed for CPU security researchers and hardware vendors interested in identifying and mitigating microarchitectural vulnerabilities. It may also be useful for system developers and security professionals who want to assess the security of the hardware platforms they work with.\n\n#### How does Revizor differ from other hardware fuzzers (e.g., SiliFuzz)? {#how-does-revizor-differ-from-other-hardware-fuzzers}\n\n:   Most of the existing hardware fuzzers focus on finding functional bugs, such as incorrect instruction execution or crashes. Revizor, on the other hand, is specifically designed to find security vulnerabilities related to microarchitectural side channels. It uses a model-based approach to define what information is allowed to leak and tests whether the CPU adheres to these specifications.\n\n:   See [Revizor at a Glance](../intro/01-overview.md) for a more detailed introduction.\n\n#### How is Revizor different from constant-time testing tools (e.g., Microwalk)? {#how-does-revizor-differ-from-ct-testing-tools}\n\n:   Constant-time testing tools like Microwalk focus on verifying that software implementations do not leak sensitive information through timing variations. They analyze the execution of programs to ensure that their timing behavior is independent of secret data.\n\n:   Revizor, in contrast, tests the CPU hardware itself for microarchitectural information leaks. It tests whether the CPU behaves as expected, regardless of the software running on it.\n\n#### What CPUs does Revizor support? {#supported-cpus}\n\n:   Revizor currently supports testing on x86-64 CPUs from Intel and AMD, as well as ARM CPUs.\n\n#### Does Revizor detect only those leaks that are described in the contract? {#leaks-described-in-contract}\n\n:   No! It is a common misconception that Revizor can only find leaks that are explicitly described in the contract. In reality, it is the opposite: The contract defines what the Revizor should *not* report as a leak, which allows the tool to filter out the known types of leakage and focus on finding unexpected leaks that violate the contract. This is how Revizor is able to discover new vulnerabilities even in completely black-box CPUs.\n\n---\n\n## Installing Revizor\n\n#### What operating system is required to run Revizor? {#required-os}\n\n:   You will need Linux.\n\n#### Do I need a specific Linux distribution/version? {#specific-linux-distro}\n\n:    No, Revizor should work on any reasonably recent Linux. If you encounter issues, that's most likely a bug that we would like to hear about. Please report any problems on our [GitHub Issues page](https://github.com/microsoft/side-channel-fuzzer/issues).\n\n#### Does Revizor require root or administrator privileges? {#requires-root}\n\n:   Yes. Revizor's executor is implemented as a kernel module that requires loading into the kernel and accessing hardware performance counters. Both operations require root privileges. Additionally, some system configuration steps recommended for optimal performance (like disabling hyperthreading) require administrative access.\n\n#### Can I run Revizor in a virtual machines? {#run-on-vms}\n\n:   Unfortunately, not. Revizor requires direct access to the CPU's PMU to accurately measure side-channel leakage. Running Revizor inside a virtual machine would introduce additional layers of abstraction and interference that could distort the measurements and lead to inaccurate results. You need to run Revizor on a bare-metal installation of Linux.\n\n---\n\n## Running Revizor\n\n#### Can Revizor affect system stability? {#safety}\n\n:   Although extremely unlikely, Revizor could potentially affect the host operating system. Revizor executes randomly-generated code in kernel space, which means that a misconfiguration or bug can crash the system and potentially lead to data loss. However, it does not intentionally perform any operations that would damage hardware.\n\n:   You should never run Revizor on production machines or systems containing important data without backups. Always use a dedicated testing machine.\n\n#### How long does it take to find a vulnerability? {#time-to-find}\n\n:   This varies significantly, based on the complexity of the experiment. Typical numbers range from minutes to weeks.\n\n#### Can Revizor test my own assembly programs or does it only generate random ones? {#test-custom-programs}\n\n:   Yes, Revizor can test custom assembly programs using the `-t` flag. You can provide your own test case program in assembly format, and Revizor will execute it with randomly-generated inputs to check for contract violations. This is useful when you want to verify specific code patterns or investigate potential vulnerabilities in particular instruction sequences.\n\n:   See the [CLI Reference](../ref/cli.md) for details on the `-t` option.\n\n#### How much computational resources does a typical fuzzing campaign require? {#resource-requirements}\n\n:   Resource requirements vary significantly based on the fuzzing configuration. A typical campaign runs continuously for hours to weeks. The primary variables affecting performance are the number of inputs per test case, sample sizes for hardware measurements, and the complexity of the ISA subset being tested. Larger sample sizes increase accuracy but reduce throughput. Most campaigns run on standard server or workstation hardware without specialized requirements beyond the supported CPU architecture.\n\n:   See [How to Design a Fuzzing Campaign](../howto/design-campaign.md) for guidance on balancing performance and detection effectiveness.\n\n---\n\n## Violations\n\n#### Are false positives common? How does Revizor handle them? {#false-positives}\n\n:   No, unless it is misconfigured. Revizor uses a multi-stage filtering pipeline to eliminate false positives caused by noise and non-deterministic hardware behavior. This removes the vast majority of spurious violations. However, if Revizor is misconfigured (e.g., insufficient sample sizes), false positives can still occur due to noise in hardware measurements. These are relatively easy to identify as they tend to be unstable and non-reproducible.\n\n:   See [How to Interpret Violation Results](../howto/interpret-results.md#evaluating-violation-quality) for guidance on evaluating violation quality and handling false positives.\n\n#### Can Revizor automatically generate exploits or proof-of-concept code? {#generate-exploits}\n\n:   No. Revizor detects violations of the leakage contract by identifying test cases where hardware behavior differs from the contract's predictions. While it provides the test program and inputs that trigger the violation, it does not automatically generate working exploits. The violation artifacts serve as evidence of unexpected leakage and a starting point for manual security analysis. You can use the minimization feature to simplify the test case, making it easier to understand and potentially develop into a proof-of-concept.\n\n:   See [How to Minimize Test Cases](../howto/minimize.md) for details on simplifying violations.\n\n#### How do I know if a detected violation is actually exploitable? {#exploitability}\n\n:   Determining exploitability requires manual analysis of the violation. Start by reproducing the violation to confirm it's stable, then use the minimization feature to simplify the test case. Next, analyze the minimized program to understand what information is leaking and through which side channel. Root-cause analysis involves examining the assembly code, understanding the data dependencies, and determining whether an attacker could control the leaked information to extract sensitive data. Not all violations are practically exploitable, but all indicate deviation from the specified security contract.\n\n:   See [How to Root-Cause a Violation](../howto/root-cause-a-violation.md) for systematic analysis techniques.\n\n#### Is Revizor deterministic? Can I reproduce results? {#reproducibility}\n\n:   Contract traces are fully deterministic—the same program with the same inputs always produces identical contract traces. Hardware traces, however, contain inherent non-determinism due to timing variations, cache state, and other microarchitectural effects. Revizor handles this through statistical analysis of multiple samples. Violations are reproducible when the same test program and inputs consistently show the same distributional differences in hardware traces. The violation artifact includes all necessary files (program, inputs, configuration) to reproduce detected violations, and Revizor provides a dedicated reproduce mode for verification.\n\n:   See [Execution Modes](../ref/modes.md) for details on the reproduce mode.\n\n---\n\n## Development and Contribution\n\n#### Is Revizor actively maintained? {#maintenance-status}\n\n:   Yes. Revizor is actively maintained and continues to receive updates, bug fixes, and new features. The project has an active GitHub repository with recent commits and ongoing development.\n\n#### Can I contribute to Revizor? {#contributing}\n\n:   Yes, we welcome contributions from the community! You can contribute by reporting issues, suggesting new features, improving documentation, or submitting code changes through pull requests. Please refer to our [Contribution Guidelines](../internals/index.md) for instructions on how to get started.\n\n"
  },
  {
    "path": "docs/glossary.md",
    "content": "# Glossary\n\nThis glossary defines key terms used throughout the Revizor documentation. The entries are ordered in such a way that more fundamental concepts appear first, building up to more complex ideas. So, you can should be able to get a good understanding of the terminology by reading the glossary top-down.\n\n---\n\n####<a name=\"noninterference\"></a>Noninterference\n: A formal property that captures perfect confidentiality, stating that changes in secret data have no observable effect on public outputs. A program satisfies noninterference if variations in secret inputs cause no differences in public outputs. In Revizor's context, this property is checked with respect to side-channel observations and speculation contracts.\n\n!!! info \"Related Documentation\"\n    - [Primer: Information-Flow Properties](intro/03-primer.md#information-flow-properties)\n    - [Primer: Noninterference Definition](intro/03-primer.md#noninterference-definition-and-examples)\n\n---\n\n####<a name=\"information-flow\"></a>Information Flow\n: The movement of data through a computation. Information-flow security is concerned with how data moves through a system and how it can be observed by an attacker. For example, if a program contains a data-dependent memory access `array[secret_index]`, the value of `secret_index` influences which memory location is accessed. In turn, if the attacker can observe the cache lines being accessed by this program, the execution of the array access will reveal (leak) information about `secret_index` through side channels. This creates an information flow from the secret data (`secret_index`) to the attacker's observations (cache state).\n\n!!! info \"Related Documentation\"\n    - [Primer: Information-Flow Properties](intro/03-primer.md#information-flow-properties)\n    - [Primer: Side Channels](intro/03-primer.md#beyond-direct-outputs-side-channels)\n\n---\n\n####<a name=\"speculation-contract\"></a>Speculation Contract (aka Leakage Contract)\n: A formalization of how we expect the CPU to behave and what information we expect it to leak when any given program is executed. A simplified and deterministic model of CPU hardware designed to capture the information that a given program could leak over side channels when executed with given inputs. A speculation contract defines two key aspects for every instruction: an observation clause (describing what data is exposed) and an execution clause (describing how hardware optimizations like speculative execution affect the instruction). Speculation contracts intentionally overestimate possible leaks to ensure conservative and deterministic traces.\n\n!!! info \"Related Documentation\"\n    - [Topic: Contracts](topics/contracts.md)\n    - [Primer: Speculation Contracts](intro/03-primer.md#speculation-contracts-dealing-with-the-complexity-of-modern-hardware)\n    - [How-to: Choose a Contract](howto/choose-contract.md)\n\n---\n\n####<a name=\"observation-clause\"></a>Observation Clause\n: Part of a speculation contract that specifies what information an instruction exposes through side channels when executed. For example, an observation clause might specify that a load instruction exposes the memory address it accesses.\n\n!!! info \"Related Documentation\"\n    - [Topic: Contracts - Contract Structure](topics/contracts.md#contract-structure)\n    - [Primer: Speculation Contracts](intro/03-primer.md#speculation-contracts-dealing-with-the-complexity-of-modern-hardware)\n\n---\n\n####<a name=\"execution-clause\"></a>Execution Clause\n: Part of a speculation contract that specifies how hardware optimizations (particularly speculative execution) affect an instruction's semantics. For example, an execution clause might specify that a conditional branch may mispredict its target and execute down the wrong path.\n\n!!! info \"Related Documentation\"\n    - [Topic: Contracts - Contract Structure](topics/contracts.md#contract-structure)\n    - [Primer: Speculation Contracts](intro/03-primer.md#speculation-contracts-dealing-with-the-complexity-of-modern-hardware)\n\n---\n\n####<a name=\"leakage-model\"></a>Leakage Model\n: An implementation of a speculation contract. This model is used to compare the actual CPU behavior against the specification defined by the contract. It predicts what information flow is allowed through side channels for any given test case.\n\n!!! info \"Related Documentation\"\n    - [Topic: Leakage Models](topics/models.md)\n    - [Internals: Model Architecture](internals/architecture/model.md)\n    - [Internals: Unicorn Backend](internals/model-backends/model-unicorn.md)\n    - [Internals: DynamoRIO Backend](internals/model-backends/model-dr.md)\n\n---\n\n####<a name=\"contract-trace\"></a>Contract Trace (CTrace)\n: The output of a leakage model. A CTrace is a recording of all exposed information when a given program is executed on the leakage model (e.g., a sequence of memory addresses accessed). This trace represents the expected information flow according to the contract.\n\n!!! info \"Related Documentation\"\n    - [Topic: Contracts - Contract Traces](topics/contracts.md#contract-traces)\n    - [Topic: Leakage Models - Trace Representation](topics/models.md#trace-representation)\n    - [Topic: Trace Analysis](topics/trace-analysis.md)\n\n---\n\n####<a name=\"executor\"></a>Executor\n: The component responsible for running programs on real hardware and collecting attacker-observable microarchitectural changes. This component acts as the counterpart to the leakage model; that is, while the model represents our expectations of the CPU behavior, the executor captures the actual behavior of the CPU under test.\n\n!!! info \"Related Documentation\"\n    - [Internals: Executor Architecture](internals/architecture/exec.md)\n    - [Reference: Configuration Options](ref/config.md)\n\n---\n\n####<a name=\"hardware-trace\"></a>Hardware Trace (HTrace)\n: The output of the executor. An HTrace is a recording of microarchitectural state changes (like cache evictions, readings of the time stamp counter, etc.) observed during a program execution. These traces are used to capture the information flows on the CPU under test, both the expected and unexpected ones.\n\n!!! info \"Related Documentation\"\n    - [Topic: Trace Analysis](topics/trace-analysis.md)\n    - [Internals: Executor Architecture](internals/architecture/exec.md)\n\n---\n\n####<a name=\"test-case-program\"></a>Test Case Program\n: A small assembly program, either generated automatically by Revizor or written manually by the user. Test case programs are intended to be executed on the target CPU to collect hardware traces, and on the leakage model to collect contract traces.\n\n!!! info \"Related Documentation\"\n    - [Topic: Test Case Generation](topics/test-case-generation.md)\n    - [Internals: Code Generator Architecture](internals/architecture/code.md)\n    - [Reference: Binary Formats - RCBF](ref/binary-formats.md)\n\n---\n\n####<a name=\"test-case-data\"></a>Test Case Data (aka Test Case Input)\n: A blob of data used to initialize memory and registers for the execution of a test case program. Test case data can be generated automatically by Revizor or provided manually by the user.\n\n!!! info \"Related Documentation\"\n    - [Topic: Test Case Generation](topics/test-case-generation.md)\n    - [Internals: Data Generator Architecture](internals/architecture/data.md)\n    - [Reference: Binary Formats - RDBF](ref/binary-formats.md)\n\n---\n\n####<a name=\"sandbox\"></a>Sandbox (or Test Case Sandbox)\n: An isolated execution environment where test case programs are run on the target CPU and on the model. On the technical level, a sandbox constitutes of a dedicated region of memory where the test case program and data are loaded, as well as a set of mechanisms to isolate the test case execution from the rest of the system (e.g., by disabling interrupts, overriding MSRs, etc.).\n\n!!! info \"Related Documentation\"\n    - [Reference: Sandbox](ref/sandbox.md)\n    - [Reference: Registers](ref/registers.md)\n\n---\n\n####<a name=\"model-based-relational-testing\"></a>Model-based Relational Testing (MRT)\n: The core methodology of Revizor. It involves randomly generating test programs and inputs to them, executing them with the executor and the model, collecting the corresponding hardware and contract traces, identifying the information flows in both, and comparing them to find unexpected leaks.\n\n!!! info \"Related Documentation\"\n    - [Primer: Model-Based Relational Testing](intro/03-primer.md#model-based-relational-testing-and-revizor)\n    - [Topic: Trace Analysis](topics/trace-analysis.md)\n    - [Internals: Fuzzer Architecture](internals/architecture/fuzz.md)\n\n---\n\n####<a name=\"violation\"></a>Violation\n: A situation where hardware traces expose some information that is not exposed in the contract traces for the same test case. This indicates that the CPU is leaking some information not specified by the contract, which may represent a security vulnerability.\n\n!!! info \"Related Documentation\"\n    - [Topic: Trace Analysis](topics/trace-analysis.md)\n    - [Primer: Contract Violation](intro/03-primer.md#building-and-testing-speculation-contracts)\n    - [How-to: Root-Cause a Violation](howto/root-cause-a-violation.md)\n\n---\n\n####<a name=\"violation-artifact\"></a>Violation Artifact (aka Contract Counterexample)\n: A bundle consisting of a test case program, two inputs that trigger the violation (plus extra inputs to set the uarch state, if needed), the corresponding hardware and contract traces, and a collection of configuration files to reproduce the violation. Violation artifacts are generated automatically by Revizor when a violation is detected.\n\n!!! info \"Related Documentation\"\n    - [Reference: Binary Formats](ref/binary-formats.md)\n    - [How-to: Root-Cause a Violation](howto/root-cause-a-violation.md)\n    - [How-to: Minimize Test Cases](howto/minimize.md)\n\n---\n\n####<a name=\"minimization\"></a>Minimization\n: A post-processing mode that takes a violation artifact and performs transformation passes to simplify the program and data while preserving the violation. The goal is to produce a minimal artifact that is easier to understand and analyze, using program passes (instruction removal/simplification), input passes (sequence/diff minimization), and analysis passes (source analysis).\n\n!!! info \"Related Documentation\"\n    - [How-to: Minimize Test Cases](howto/minimize.md)\n    - [Reference: Minimization Passes](ref/minimization-passes.md)\n    - [Internals: Minimization Architecture](internals/architecture/mini.md)\n\n---\n\n####<a name=\"multi-stage-filtering\"></a>Multi-stage Filtering\n: A pipeline of validation stages applied to potential violations to rule out false positives. A violation must survive all stages to be reported.\n\n!!! info \"Related Documentation\"\n    - [Internals: Fuzzer Architecture](internals/architecture/fuzz.md)\n    - [Reference: Configuration Options](ref/config.md)\n\n---\n\n####<a name=\"priming-test\"></a>Priming Test\n: One of the most important validation stages. It is motivated by the following problem: when hardware traces are collected for a sequence of many inputs, the execution of the program with earlier inputs will affect the microarchitectural state for later inputs (e.g., the branch predictor state). This can lead to false positives, where two inputs that should be indistinguishable according to the contract produce different hardware traces simply because they were executed in different microarchitectural states (e.g., one input triggered a misprediction while the other did not). These case don't actually represent a violation because the difference in traces is not caused by the data difference, but rather by the sequence of executions.\n\nThe priming test mitigates this problem by re-executing the violating inputs in a different sequence, by swapping the order of inputs that trigger a violation. If the violation disappears when the order is swapped, it indicates that the difference in traces was due to inconsistent microarchitectural state rather than a true violation. Otherwise, we have evidence that the violation is genuine.\n\n!!! info \"Related Documentation\"\n    - [Reference: Configuration Options - enable_priming](ref/config.md#enable_priming)\n    - [Internals: Fuzzer Architecture](internals/architecture/fuzz.md)\n\n---\n\n####<a name=\"contract-compliance\"></a>Contract Compliance\n: A CPU complies with a speculation contract if, for all possible programs and input pairs that produce identical contract traces, the corresponding hardware traces are also identical. This ensures that the contract captures all information that the hardware can leak. While testing all possible programs is infeasible, Revizor approximates this by randomly sampling the search space with a large number of test cases.\n\n!!! info \"Related Documentation\"\n    - [Topic: Trace Analysis - Contract Compliance Property](topics/trace-analysis.md#contract-compliance-property)\n    - [Topic: Contracts - Contract Compliance](topics/contracts.md#contract-compliance)\n    - [Primer: Contract Compliance](intro/03-primer.md#building-and-testing-speculation-contracts)\n\n---\n\n####<a name=\"contract-equivalence-class\"></a>Contract Equivalence Class (ContractEqClass)\n: A group of inputs that produce identical contract traces for a given test case program. According to the leakage model, these inputs should be indistinguishable when executed.\n\n!!! info \"Related Documentation\"\n    - [Topic: Trace Analysis - Deterministic Trace Comparison](topics/trace-analysis.md#deterministic-trace-comparison)\n    - [Internals: Analyser Architecture](internals/architecture/analysis.md)\n\n---\n\n####<a name=\"hardware-equivalence-class\"></a>Hardware Equivalence Class (HardwareEqClass)\n: A group of inputs that produce statistically similar hardware traces for a given test case program. These inputs are actually indistinguishable on real hardware.\n\n!!! info \"Related Documentation\"\n    - [Topic: Trace Analysis - Statistical Trace Comparison](topics/trace-analysis.md#statistical-trace-comparison)\n    - [Internals: Analyser Architecture](internals/architecture/analysis.md)\n\n---\n\n####<a name=\"boosting\"></a>Boosting (aka Contract-driven Input Generation)\n: A data generation optimization technique that uses taint analysis to generate inputs more likely to trigger contract violations. The boosted generator identifies which input bytes affect the contract trace and generates new inputs by mutating the non-tainted bytes. This way, we can deterministically and efficiently create any number of inputs that produce the same contract trace (i.e., form one ContractEqClass), increasing the chances of finding violations.\n\n!!! info \"Related Documentation\"\n    - [Internals: Data Generator Architecture](internals/architecture/data.md)\n    - [Reference: Configuration Options](ref/config.md)\n\n---\n\n####<a name=\"fuzzer\"></a>Fuzzer\n: The main orchestrator in Revizor that manages core components (CodeGenerator, DataGenerator, Model, Executor, and Analyser) and coordinates the fuzzing loop. When a potential violation is found, the Fuzzer runs it through a multi-stage filtering pipeline to eliminate false positives.\n\n!!! info \"Related Documentation\"\n    - [Internals: Fuzzer Architecture](internals/architecture/fuzz.md)\n    - [Reference: Configuration Options](ref/config.md)\n\n---\n\n####<a name=\"analyser\"></a>Analyser\n: The component that compares contract traces with hardware traces to detect violations. It uses an equivalence class approach where it groups inputs by contract traces (ContractEqClasses) and then checks if they split into multiple hardware equivalence classes (HardwareEqClasses), which would indicate a violation.\n\n!!! info \"Related Documentation\"\n    - [Topic: Trace Analysis](topics/trace-analysis.md)\n    - [Internals: Analyser Architecture](internals/architecture/analysis.md)\n    - [Reference: Configuration Options - analyser](ref/config.md)\n\n---\n\n####<a name=\"actor\"></a>Actor\n: A partition of the sandbox representing a distinct execution context with specific isolation properties (e.g., a VM). An actor encompasses a code region, a data region with configurable permissions, and an execution context (CPU mode, privilege level, and system configuration). Actors enable testing for information leaks across different security domains.\n\n!!! info \"Related Documentation\"\n    - [Topic: Actors](topics/actors.md)\n    - [Reference: Sandbox](ref/sandbox.md)\n\n---\n\n####<a name=\"actor-non-interference\"></a>Actor Non-Interference\n: A specialized type mode of testing in Revizor, where, on top of testing for standard contract violations, the tool also checks that there are no information flows between different actors in a multi-actor test case. This mode is used to verify isolation properties between security domains, ensuring that secret data in one actor does not influence observable behavior in another actor.\n\n!!! info \"Related Documentation\"\n    - [Topic: Actors](topics/actors.md)\n\n---\n\n####<a name=\"observer-actor\"></a>Observer Actor\n: An actor marked as an observer in the configuration, representing an attacker that can observe data leaks in multi-actor testing scenarios. This is used in conjunction with the Actor Non-Interference mode to check that secret data in other actors does not influence the traces in the observer actor.\n\n!!! info \"Related Documentation\"\n    - [Topic: Actors](topics/actors.md)\n    - [Reference: Configuration Options](ref/config.md)\n\n---\n\n####<a name=\"rcbf\"></a>RCBF (Revizor Code Binary Format)\n: A custom binary format used to transfer test case programs between Revizor components. The format contains a header, actor table, symbol table, metadata, and code sections for each actor.\n\n!!! info \"Related Documentation\"\n    - [Reference: Binary Formats - RCBF](ref/binary-formats.md)\n\n---\n\n####<a name=\"rdbf\"></a>RDBF (Revizor Data Binary Format)\n: A custom binary format used to transfer input data between Revizor components. The format contains initialization data for sandbox memory and registers, and can combine multiple inputs into a single file for batch processing.\n\n!!! info \"Related Documentation\"\n    - [Reference: Binary Formats - RDBF](ref/binary-formats.md)\n\n---\n\n####<a name=\"template\"></a>Template\n: An assembly file that combines regular assembly instructions with placeholders to define a test case structure for the code generator. Such templates are used in a special template mode of Revizor, where the programs are generated by populating the placeholders with random instructions instead of generating programs from scratch.\n\n!!! info \"Related Documentation\"\n    - [How-to: Use Templates](howto/use-templates.md)\n    - [Reference: Configuration Options](ref/config.md)\n\n---\n\n####<a name=\"macro\"></a>Macro\n: A special pseudo-instruction in test case programs that can be treated differently depending on whether the test case is executed by the model or the executor. One prominent example is VM transition macros, which handle switching between actors. A special type of macro is also used to implement the placeholders in templates.\n\n!!! info \"Related Documentation\"\n    - [How-to: Use Macros](howto/use-macros.md)\n    - [Reference: Macro Reference](ref/macros.md)\n\n---\n\n"
  },
  {
    "path": "docs/howto/ask-a-question.md",
    "content": "# Ask a Question\n\nIf you have a question about Revizor, there are several ways to reach out to us:\n\n* For **any questions, no matter how big or small,** feel free to post them in our community [Zulip chat](https://rvzr.zulipchat.com/) where the community and developers can assist you.\n* Alternatively, you can start a discussion on our [GitHub Discussions page](https://github.com/microsoft/side-channel-fuzzer/discussions) (this is preferable for longer questions that may require more in-depth answers).\n\nBug reports should be submitted to our [GitHub Issues page](https://github.com/microsoft/side-channel-fuzzer/issues).\n\nFor general information about Revizor, please refer to our [FAQ](../faq/general.md) page.\n"
  },
  {
    "path": "docs/howto/choose-contract.md",
    "content": "# How to Choose a Contract\n\nThis guide helps you select the appropriate [contract](../glossary.md#speculation-contract) for your fuzzing campaign. The contract determines which microarchitectural leaks Revizor will report as violations, making it a critical configuration choice that affects both what you find and how efficiently you find it.\n\n!!! note \"Prerequisites\"\n    Before choosing a contract, you should understand what contracts are and how they work. Read the [Contracts](../topics/contracts.md) topic guide if you need background on contract structure and purpose.\n\n\n## Standard Fuzzing with CT-SEQ\n\nUse CT-SEQ for most fuzzing campaigns. This contract assumes nothing about the target CPU except the presence of CPU caches, making it a zero-knowledge baseline for detecting unknown vulnerabilities. With CT-SEQ, Revizor reports any information leaks beyond the most trivial non-speculative cache accesses.\n\nConfigure CT-SEQ by setting the [observation clause](../glossary.md#observation-clause) to `ct` and the [execution clause](../glossary.md#execution-clause) to `seq`:\n\n```yaml\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - seq\n```\n\nCT-SEQ provides the strictest security guarantees and will detect the widest range of vulnerabilities. Start with this contract unless you have specific reasons to use a different one.\n\n## Continuing After Finding a Violation\n\nWhen you find a violation with CT-SEQ and want to continue testing for additional vulnerabilities, you have two approaches.\n\nThe simpler and more efficient approach is to blocklist the instruction that triggered the violation. Use the [`instruction_blocklist_append`](../ref/config.md#instruction_blocklist_append) configuration option to exclude specific instructions from testing. For example, if a branch misprediction caused the violation, blocklist all conditional branch instructions:\n\n```yaml\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - seq\ninstruction_blocklist_append:\n  - jne\n  - je\n  # add other branch instructions\n```\n\nThis approach lets you continue using CT-SEQ's fast and efficient detection while avoiding repeated reports of the same root cause.\n\nAlternatively, you can incorporate the newly discovered speculation source into the contract by switching to a different execution clause. For violations caused by branch mispredictions, switch to the COND execution clause:\n\n```yaml\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - cond\n```\n\nThe CT-COND contract models speculative execution from branch mispredictions as expected behavior. Revizor will no longer report violations from this source, allowing you to search for other types of leaks in the same instruction set.\n\n## Testing with Exceptions\n\nIf your fuzzing campaign includes code that may raise exceptions such as page faults or general protection faults, these exceptions will likely cause trivial violations under CT-SEQ. Modern CPUs implement out-of-order execution, which means instructions after a faulting instruction may begin executing before the CPU recognizes the exception. These subsequent instructions can leak information not predicted by CT-SEQ's strictly sequential model.\n\nThese violations typically represent known artifacts of out-of-order execution rather than genuine security issues. To suppress such trivial reports, use the CT-DEH contract instead. This contract models delayed exception handling, allowing instructions after a faulting instruction to execute transiently before the exception is handled:\n\n```yaml\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - deh\n```\n\nCT-DEH remains strict about other speculation sources while accommodating the expected behavior around exceptions.\n\n## Testing Cross-Domain Isolation\n\nWhen testing isolation between security domains such as kernel versus user mode or host versus guest execution, use the Actor Non-Interference contract (CT-NI). This contract changes the security property being tested. Instead of only checking that inputs with identical [contract traces](../glossary.md#contract-trace) produce equivalent [hardware traces](../glossary.md#hardware-trace), CT-NI adds an additional requirement: the hardware traces observed by attacker actors must not depend on data from victim actors.\n\nConfigure CT-NI with the following observation clause:\n\n```yaml\ncontract_observation_clause: ct-ni\n```\n\nYou must also configure actors properly, designating which actors are observers (attackers) and which are victims. See [Actors](../topics/actors.md) for details on actor configuration.\n\n## Investigating Known Vulnerabilities\n\nWhen investigating variants of known vulnerabilities, use a contract that models the specific vulnerability class you are studying.\n\nFor Spectre V1 variant analysis, use the COND execution clause to model branch mispredictions as expected behavior:\n\n```yaml\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - cond\n```\n\nThis configuration lets you explore whether other instructions or gadget patterns can be exploited through branch misprediction without being distracted by the original Spectre V1 finding.\n\nFor other vulnerability classes, choose the execution clause that models the corresponding speculation mechanism. See the [Configuration Reference](../ref/config.md#contract_execution_clause) for a list of available execution clauses and their intended use cases.\n\n## What's Next?\n\n- Topic: [Contracts](../topics/contracts.md) - Understanding contract structure and behavior\n- How-to: [Design a Fuzzing Campaign](design-campaign.md) - Complete campaign planning including contract selection\n- Reference: [Configuration Options](../ref/config.md) - Complete list of contract and configuration parameters\n- Glossary: [Contract](../glossary.md#speculation-contract), [Observation Clause](../glossary.md#observation-clause), [Execution Clause](../glossary.md#execution-clause)\n"
  },
  {
    "path": "docs/howto/design-campaign.md",
    "content": "# How to Design a Fuzzing Campaign\n\nThis guide shows you how to design and configure a fuzzing campaign for detecting speculative execution vulnerabilities. A campaign consists of three components: a configuration file (YAML), command-line arguments, and optionally a template file (ASM).\n\n!!! note \"Prerequisites\"\n    - Revizor installed and the executor kernel module loaded\n    - Basic understanding of [contracts](../topics/contracts.md) and what you want to test\n\n## Select Instruction Set\n\nChoose which instruction subset to test. Smaller subsets are more effective because violations are found faster and root-cause analysis is simpler. For comprehensive ISA coverage, split testing into multiple targeted campaigns rather than running a single large campaign.\n\nSpecify instruction categories in your configuration file using `instruction_categories`:\n\n```yaml\ninstruction_categories:\n  - BASE-BINARY      # arithmetic instructions\n  - BASE-STRINGOP    # string operations\n  - BASE-LOGIC       # logical operations\n```\n\nVerify which instructions are included by enabling debug logging:\n\n```yaml\nlogging_modes: ['info', 'stat', 'dbg_generator']\n```\n\nFor fine-grained control over the instruction set, see the [Configuration Reference](../ref/config.md#instruction_categories).\n\n## Configure Exception Testing\n\nEnable exception testing using the `faults_allowlist` option:\n\n```yaml\nfaults_allowlist:\n  - div-by-zero              # division by zero exceptions\n```\n\nEnsure the corresponding instructions are included in your instruction set. For example, `div-by-zero` requires division instructions in the tested pool.\n\nFor testing Meltdown or Foreshadow-like vulnerabilities, configure memory access permissions through actor-specific `data_properties` and `data_ept_properties`:\n\n```yaml\nactors:\n  - main:\n      data_properties:\n        present: false     # trigger page faults\n        writable: false    # trigger write protection faults\n```\n\nSee the [Sandbox Reference](../ref/sandbox.md) for details on memory permissions and the [Configuration Reference](../ref/config.md#faults_allowlist) for all exception handling options.\n\n## Configure Actors for Multi-Domain Testing\n\nFor cross-domain leakage testing, define [actors](../glossary.md#actor) to represent different security domains:\n\n```yaml\nactors:\n  - main:\n      mode: host\n      privilege_level: kernel\n  - guest:\n      mode: guest\n      privilege_level: kernel\n      observer: true\n```\n\nCreate corresponding template files to specify transition sequences between actors. See [Actors](../topics/actors.md) for detailed instructions.\n\n## Select Contract\n\nChoose a [contract](../glossary.md#speculation-contract) that defines what execution behavior constitutes a violation. Contract selection depends on whether you are testing cross-domain leakage and which known vulnerabilities you want to filter out.\n\nFor detailed guidance on selecting the appropriate contract for your testing scenario, see [How to Choose a Contract](choose-contract.md).\n\nExample configuration:\n\n```yaml\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - seq\n```\n\nSee the [Configuration Reference](../ref/config.md#contract_observation_clause) for all available contract options.\n\n## Configure Noise Threshold\n\nAdjust noise tolerance based on your system characteristics. Higher thresholds and larger sample sizes reduce false positives but may miss subtle leaks and decrease performance. Lower thresholds increase sensitivity but may produce false positives on noisy systems.\n\nFor high-noise systems:\n\n```yaml\nanalyser_stat_threshold: 0.5      # conservative threshold\nexecutor_sample_sizes: [50, 100, 500, 1000]\n```\n\nFor low-noise systems:\n\n```yaml\nanalyser_stat_threshold: 0.1      # sensitive threshold\nexecutor_sample_sizes: [10, 50, 100]\n```\n\nStart with low-noise settings and increase thresholds if you encounter non-reproducible violations. See the [Trace Analysis Guide](../topics/trace-analysis.md#statistical-trace-comparison) for more information on noise handling.\n\n## Enable Reproducibility\n\nSet deterministic seeds to make the campaign reproducible:\n\n```yaml\nprogram_generator_seed: 12345     # deterministic program generation\ndata_generator_seed: 67890        # deterministic input generation\n```\n\nReproducible campaigns are essential for debugging and comparing results across different runs.\n\n## Configure Test Case Shape\n\nControl the structure of generated test cases:\n\n```yaml\nprogram_size: 64                  # instructions per program\navg_mem_accesses: 32              # average memory accesses\nmin_bb_per_function: 1            # minimum basic blocks per function\nmax_bb_per_function: 2            # maximum basic blocks per function\nmin_successors_per_bb: 1          # minimum successors per basic block\nmax_successors_per_bb: 1          # maximum successors per basic block\n```\n\nLarger programs may find more complex interactions but require longer analysis time. Start with smaller programs and increase size if needed.\n\n## Use Templates for Targeted Testing\n\nUse templates when targeting specific microarchitectural scenarios. Templates define fixed assembly structures with random instruction insertion, allowing you to focus on specific patterns while maintaining variability.\n\nExample template:\n\n```asm\n.section .data.main\n.function_main_0:\n    # Fixed initialization\n    mov rax, 0\n\n    # Random instruction sequence\n    .macro.random_instructions.32.0:\n\n    # Fixed measurement\n    .macro.measurement_start:\n    mov rbx, [r14]\n    .macro.measurement_end:\n\n.test_case_exit:\n```\n\nSee [How to Use Templates](use-templates.md) for detailed template syntax and the [Macro Reference](../ref/macros.md) for available macros.\n\n## Complete Example\n\nThis campaign tests whether division-by-zero exceptions cause unexpected information leakage on the target CPU. It focuses on simple arithmetic instructions to isolate exception handling behavior and answers the question: \"Does division by zero on this CPU leak information through microarchitectural side channels?\"\n\nThe configuration assumes a CPU with relatively low non-determinism, using moderate sample sizes and a conservative statistical threshold. The campaign uses the DEH (Delay Exception Handling) contract to filter out trivial cases of out-of-order handling of the exception. Test cases are kept small (32 instructions, no branches) to simplify analysis and accelerate violation detection. Each campaign iteration generates 100 different inputs per test case to explore various data-dependent behaviors around division operations.\n\n```yaml\n# Instruction selection\ninstruction_categories:\n  - BASE-BINARY\n\n# Exception handling\nfaults_allowlist:\n  - div-by-zero\n\n# Contract\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - deh\n\n# Noise handling\nanalyser_stat_threshold: 0.2\nexecutor_sample_sizes: [10, 50, 100, 500]\n\n# Reproducibility\nprogram_generator_seed: 12345\ndata_generator_seed: 67890\n\n# Test case shape: 32 instructions with no branches\nprogram_size: 32\navg_mem_accesses: 16\nmin_bb_per_function: 1\nmax_bb_per_function: 1\n\n# Single actor\nactors:\n  - main:\n      mode: host\n      privilege_level: kernel\n      data_properties:  # no page faults\n        present: true\n        writable: true\n\n# Debugging\nlogging_modes: ['info', 'stat', 'dbg_generator']\n```\n\nLaunch the campaign:\n\n```bash\nrvzr fuzz -s base.json -c config.yaml -n 100000 -i 100 -w ./violations --timeout 3600\n```\n\n\n## What's Next?\n\n- How-to: [Choose a Contract](choose-contract.md) - Select the appropriate contract for your testing scenario\n- How-to: [Use Templates](use-templates.md) - Create targeted test cases\n- How-to: [Interpret Results](interpret-results.md) - Understand fuzzing output\n- Topic: [Actors](../topics/actors.md) - Configure multi-domain testing\n- Topic: [Contracts](../topics/contracts.md) - Understanding leakage contracts\n- Topic: [Test Case Generation](../topics/test-case-generation.md) - How test cases are generated\n- Reference: [Configuration Options](../ref/config.md) - Complete configuration reference\n- Reference: [CLI Reference](../ref/cli.md) - Command-line interface reference\n"
  },
  {
    "path": "docs/howto/interpret-results.md",
    "content": "# How to Interpret Violation Results\n\nSo you've run a fuzzing campaign and found a violation. Now what?\n\nThis guide will help you understand and validate violations detected by Revizor. This guide explains the structure of violation artifacts, how to reproduce violations, and how to interpret the output to determine whether a violation is genuine and worth investigating.\n\n!!! info \"Prerequisites\"\n    Before starting, ensure you have:\n\n    - Revizor installed and functional on the target system\n    - A violation directory (`violation-<timestamp>`) produced during fuzzing\n    - The configuration file (`config.yaml`) used in the original fuzzing campaign\n    - Access to the same hardware where the violation was detected\n\n## Violation Message\n\nWhen Revizor detects a violation during fuzzing, it prints a summary message to the console similar to this:\n\n```plaintext\n(venv-3.12) main ➜  revizor ./revizor.py fuzz -s base.json -c demo/detect-v1.yaml -n 1000 -i 100 -w ./\n\nINFO: [prog_gen] Setting program_generator_seed to random value: 599740\n\nINFO: [fuzzer] Starting at 15:39:42\n17    ( 2%)| Stats: Cls:0/0,In:200,R:7,SF:10,OF:7,Fst:0,CN:0,CT:0,P1:0,CS:0,P2:0,V:0> Priming  27             . to 500\n\n================================ Violations detected ==========================\nViolation Details:\n\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:92  | ID:192|\n-----------------------------------------------------------------------------------\n^...^...................^...........^.........^................. | 497    | 0     |\n^...^........................................................... | 3      | 2     |\n^^..^...........................................^.........^..... | 0      | 498   |\n\n\n================================ Statistics ===================================\n\nTest Cases: 18\nInputs per test case: 200.0\nViolations: 1\nEffectiveness:\n  Total Cls: 98.0\n  Effective Cls: 98.0\nDiscarded Test Cases:\n  Speculation Filter: 10\n  Observation Filter: 7\n  Fast Path: 0\n  Max Nesting Check: 0\n  Tainting Check: 0\n  Early Priming Check: 0\n  Large Sample Check: 0\n  Priming Check: 0\n\nDuration: 40.5\nFinished at 15:40:23\n```\n\nMost of the output is statistics, and they are mostly irrelevant for interpreting the violation itself. You can find a detailed explanation of the runtime statistics in the [Statistics Reference](../ref/runtime-statistic.md).\n\nThe relevant part for interpreting the violation is the `Violation Details` section:\n\n```\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:92  | ID:192|\n-----------------------------------------------------------------------------------\n^...^...................^...........^.........^................. | 497    | 0     |\n^...^........................................................... | 3      | 2     |\n^^..^...........................................^.........^..... | 0      | 498   |\n```\n\nThis section summarizes the hardware trace samples recorded for the inputs that triggered the violation.\n\nLet's break it down.\n\n### Violating Inputs\n\n```\n| ID:92  | ID:192|\n```\n\nThis block tells us which inputs produced the violation. In this case, it's inputs 92 and 192. You can find them in the violation artifact directory as `input_92.bin` and `input_192.bin`.\n\n### Hardware Traces\n\n```\n^...^...................^...........^.........^.................\n^...^...........................................................\n^^..^...........................................^.........^.....\n```\n\nThis block shows a visual representation of all observed hardware traces for these inputs. In this example, we used Revizor's default P+P (Prime+Probe) cache side channel tracer, which records the state of L1D cache after a test case execution. The `^` character indicates that a cache line was accessed (evicted by the test case program), while the `.` character indicates that the cache line was not accessed. The complete line is a bitmap of all 64 L1D cache sets available on the target machine, numbered left to right from 0 to 63.\n\nAccordingly, the first line is interpreted as follows:\n\n```\n\n    Set 4 accessed                  Set 36 accessed\n    |                               |         Set 46 accessed\n    |                               |         |\n^...^...................^...........^.........^.................\n|                       |\nSet 0 accessed          Set 24 accessed\n```\n\nmeaning that cache sets with IDs 0, 4, 24, 36, and 46 were accessed in this hardware trace.\n\n\n!!! tip \"Colors!\"\n    Enable `color: true` in the configuration file to improve readability of hardware trace visualizations.\n\n### Trace Distribution\n\n```\n... | 497    | 0     |\n... | 3      | 2     |\n... | 0      | 498   |\n```\n\nFinally, this block shows the [statistical distribution](../topics/trace-analysis.md#statistical-trace-comparison) of hardware traces for each input. For example, input 92 produced the first hardware trace 497 times (out of the total of 500 measurements), while input 192 never produced that trace. Instead, input 192 produced the third hardware trace 498 times.\n\n### Analysis\n\nBy looking at this table, we can deduce two important facts about the violation:\n\n1. There is a clear difference in the sample distributions for the two inputs. This indicates a genuine violation rather than random noise.\n2. The dominant (most frequently observed) hardware trace for each input have evicted distinct sets of cache lines. This is an indirect clue that the test case had a data-dependent memory accesses pattern that was not predicted by the contract (likely due to speculative execution).\n\n## Violation Artifact\n\nWhen Revizor detects a violation, it creates a directory named `violation-<timestamp>`, with the following structure:\n\n```\nviolation-<timestamp>/\n├── program.asm\n├── input_0.bin\n├── input_1.bin\n├── ...\n├── report.txt\n├── org-config.yaml\n├── reproduce.yaml\n└── minimize.yaml\n```\n\n The `program.asm` file holds the test case program that triggered the violation. The `input_*.bin` files contain the input sequence that exposed the leak. The `report.txt` file provides additional details including hardware and contract traces. The configuration files include `org-config.yaml` (the original configuration), `reproduce.yaml` (for reproducing the violation), and `minimize.yaml` (for test case minimization).\n\nBefore proceeding with analysis, locate this directory and verify that all required files are present.\n\n## Reproducing the Violation\n\nIt is usually a good idea to first reproduce the violation outside of the fuzzing campaign. This confirms that the violation is stable and not a transient artifact of noise or a misconfiguration of the fuzzer.\n\n```bash\nrvzr reproduce -s base.json -c ./violation-<timestamp>/reproduce.yaml \\\n    -t ./violation-<timestamp>/program.asm -i ./violation-<timestamp>/input_*.bin\n```\n\nIf Revizor prints \"Violation detected\" in the output, the violation reproduced successfully. The distribution of hardware traces should roughly match the original violation. Significant differences may indicate a bug or misconfiguration in the fuzzer (e.g., random seeds).\n\nNon-reproducible violations should be rare, typically no more than one or two per machine per week of fuzzing. If your campaign produces more, adjust the configuration file to increase noise tolerance. See the [configuration options reference](../ref/config.md) for details on noise-related parameters.\n\n\n## Evaluating Violation Quality\n\nSeveral factors determine whether a violation is worth investigating further.\n\n*Reproducibility* is the most important criterion. Violations that consistently reproduce across multiple runs indicate stable, genuine leaks. Sporadic violations that appear and disappear may be false positives caused by noise. In such cases, consider adjusting noise tolerance settings ([`analyser_stat_threshold`](../ref/config.md#analyser_stat_threshold) and/or [`executor_sample_sizes`](../ref/config.md#executor_sample_sizes)) in the configuration file and rerunning the fuzzing campaign.\n\n*Trace distribution* provides additional insight. Clean violations show clear separation between inputs with consistent occurrence counts. Messy violations with overlapping traces or highly variable counts suggest non-determinism and may be harder to analyze. In such cases, consider collecting more samples per input by increasing the [`executor_sample_sizes`](../ref/config.md#executor_sample_sizes) configuration option (note: this will slow down fuzzing).\n\nFinally, *the hardware trace pattern* can be informative as well. There is no hard rule here, but if you see lots of accessed cache sets while the configuration is supposed to limit the number of memory accesses to only a few, that may indicate that some CPU feature creates additional noise, beyond the ability of the statistical analyzer to filter it out. In practice, this is often due to prefetchers. It is typically a good idea to disable them, unless you are specifically testing for prefetcher-related leaks.\n\n## Next Steps\n\nOnce you have confirmed that a violation is reproducible and worth investigating, proceed to minimize the violation artifacts and root-cause the leak. See the [How to Minimize Test Cases](minimize.md) and [How to Root-Cause a Violation](root-cause-a-violation.md) guides for detailed instructions.\n\n## See Also\n\n- [How to Root-Cause a Violation](root-cause-a-violation.md) - Systematic analysis of confirmed violations\n- [How to Design a Fuzzing Campaign](design-campaign.md) - Tuning fuzzer parameters for better results\n- [How to Minimize Test Cases](minimize.md) - Simplifying violation artifacts for analysis\n- [Configuration Options](../ref/config.md) - Detailed configuration parameter reference\n- [Execution Modes](../ref/modes.md) - Understanding reproduce mode and other execution modes\n- [Trace Analysis and Violation Detection](../topics/trace-analysis.md) - How Revizor detects and analyzes violations\n- [Contracts and Leakage Models](../topics/contracts.md) - Understanding contract semantics\n"
  },
  {
    "path": "docs/howto/minimize.md",
    "content": "# How to Minimize Test Cases\n\nThis guide discussed a process of test case minimization, which aims to reduce complexity of violation artifacts by simplifying test programs and input sequences while preserving the violation. This is typically a post-processing step performed after a fuzzing campaign has detected a violation, with the goal of producing a minimal test case suitable for human analysis and root-cause investigation.\n\nThe minimization is done by using Revizor's `minimize` mode, which post-processes a violation through a series of transformation passes that simplify both the test program and input sequence.\n\n!!! note \"Related Documentation\"\n    For a complete list of available passes and their detailed descriptions, see the [Minimization Passes reference](../ref/minimization-passes.md).\n\n!!! info \"Prerequisites\"\n    Before starting, ensure you have:\n\n    - Revizor installed and functional on the target system\n    - A violation directory (`violation-<timestamp>`) produced during fuzzing\n    - The configuration file (`config.yaml`) used in the original fuzzing campaign\n    - Access to the same hardware where the violation was detected\n\n## Basic Usage\n\nRun the minimizer with the following syntax:\n\n```bash\nrvzr minimize -s <spec_file> -c <config_file> -t <program_file> -o <output_file> \\\n    -i <num_inputs> --input-outdir <input_outdir> --num-attempts <num_attempts> \\\n    [pass_options]\n```\n\nParameters:\n\n- `-s`: Path to ISA specification (e.g., `base.json`)\n- `-c`: Path to configuration file (typically `minimize.yaml` from violation directory)\n- `-t`: Path to test program (typically `program.asm` from violation directory)\n- `-o`: Output path for minimized program\n- `-i`: Number of inputs in the sequence (must match the original fuzzing campaign)\n- `--input-outdir`: Directory to store minimized input files\n- `--num-attempts`: Number of minimization iterations to perform\n- `[pass_options]`: Enable specific minimization passes (see [Minimization Passes](../ref/minimization-passes.md))\n\nExample command (assuming a violation directory named `violation-0000-0000`):\n\n```bash\nrvzr minimize -s base.json -c violation-0000-0000/minimize.yaml -t violation-0000-0000/program.asm \\\n    -i 25 --input-outdir ./min-inputs --num-attempts 10 --enable-instruction-pass 1 \\\n    -o min.asm\n```\n\nThis command generates an input sequence of 25 inputs based on the seed in `violation-0000-0000/minimize.yaml`, applies the instruction removal pass 10 times to simplify `program.asm`, and writes the minimized program to `min.asm`. The simplified input sequence is stored in `./min-inputs`.\n\n## Interpreting the Output\n\nEach minimization pass prints progress indicators to the console as it executes. Understanding this output helps verify that minimization is progressing correctly.\n\n### Program Pass Output\n\nProgram passes display one character per instruction to indicate success or failure:\n\n- `.` indicates the pass succeeded on this instruction (e.g., instruction was successfully removed)\n- `-` indicates the pass failed on this instruction (e.g., removing this instruction breaks the violation)\n\nExample output when running `--enable-instruction-pass`:\n\n```\n[Pass 2] Instruction Removal Pass\n\n.............-.....--.-------..----\n```\n\nInterpret this output by reading from right to left, since the pass iterates from the end of the program to the beginning. In this example, the pass successfully removed the last 13 instructions, failed on the 14th instruction from the end, succeeded on the 15th, and so on.\n\n### Input Pass Output\n\nThe `input-diff` pass uses a memory-map visualization to show minimization progress. Each character represents one byte in the input sequence:\n\n- `.` indicates zeroing the byte succeeded\n- `+` indicates copying the byte from the first input to the second succeeded\n- `=` indicates the byte was already identical in both inputs\n- `^` indicates the pass could not minimize this byte (it remains different between inputs)\n\nExample output from `--enable-input-diff-pass`:\n\n```\nAddress    +0x0     +0x40    +0x80    +0xc0    +0x100   +0x140   +0x180   +0x1c0\n0x00000000 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000200 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000400 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000600 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000800 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000a00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000c00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000e00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001000 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001200 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001400 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001600 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001800 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001a00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001c00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001e00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00002000 ====^=..\n0x00002040 ........ ........ ........ ........\n  > Result: Leaked 1 bytes\n  > Addresses: ['0x2020']\n```\n\nThis output shows that the pass successfully minimized most input differences. The byte at address `0x2020` (marked with `^`) remains different between the two inputs and likely contributes to the violation. Bytes at addresses `0x2000-0x2018` and `0x2028` (marked with `=`) were already identical.\n\n### Comment Pass Output\n\nEnable `--enable-comment-pass` to annotate the minimized program with analysis information. The pass inserts comments indicating which memory accesses contributed to the violation, making it easier to identify the root cause.\n\nComment format:\n\n```\n# mem access: [input1_id] [load_addr]-[store_addr]\n  CL [cache_set_id]:[cache_line_offset] | [input2_id] [load_addr]-[store_addr]\n  CL [cache_set_id]:[cache_line_offset]\n```\n\nEach comment shows the memory addresses accessed by an instruction when executed with the two inputs that triggered the violation. The comment includes both virtual addresses and their corresponding L1D cache set IDs and line offsets.\n\nExample comment:\n\n```asm\n# mem access: [1] 0x800-0x800 CL 32:0 | [11] 0x710-0x710 CL 28:10\n```\n\nThis indicates that when executed with input 1, the instruction accessed address `0x800` (cache set 32, offset 0), and when executed with input 11, it accessed address `0x710` (cache set 28, offset 10). These different cache set accesses likely contributed to the violation.\n\n## Complete Workflow Example\n\nThis example demonstrates a typical minimization workflow. Assume a fuzzing campaign detected a violation:\n\n```bash\nrvzr fuzz -s base.json -c config.yaml -n 1000 -i 25 -w .\n```\n\nThe fuzzer created a violation directory (e.g., `violation-000000-000000`) containing the test case artifacts.\n\n### Step 1: Minimize the Program\n\nApply all program passes to simplify the test case while preserving the violation:\n\n```bash\nrvzr minimize -s base.json -c ./violation-000000-000000/minimize.yaml \\\n    -t ./violation-000000-000000/program.asm \\\n    -o min.asm -i 25 --num-attempts 3 \\\n    --enable-instruction-pass 1 \\\n    --enable-simplification-pass 1 \\\n    --enable-nop-pass 1 \\\n    --enable-constant-pass 1 \\\n    --enable-mask-pass 1 \\\n    --enable-label-pass 1\n```\n\n### Step 2: Verify Program Minimization\n\nConfirm the minimized program still triggers the violation:\n\n```bash\nrvzr fuzz -s base.json -c ./violation-000000-000000/minimize.yaml -t min.asm -i 25\n```\n\nIf the violation is no longer detected, reduce `--num-attempts` or disable some passes, then retry step 1.\n\n### Step 3: Minimize Inputs and Add Annotations\n\nApply input passes and analysis passes to further simplify the test case and add helpful comments:\n\n```bash\nrvzr minimize -s base.json -c ./violation-000000-000000/minimize.yaml \\\n    -t min.asm -o commented.asm -i 25 \\\n    --input-outdir ./inputs \\\n    --enable-input-diff-pass 1 \\\n    --enable-input-seq-pass 1 \\\n    --enable-comment-pass 1\n```\n\n### Step 4: Verify Complete Minimization\n\nReproduce the violation with the minimized program and inputs:\n\n```bash\nrvzr reproduce -s base.json -c ./violation-000000-000000/reproduce.yaml \\\n    -t commented.asm -i ./inputs/min_input*.bin\n```\n\nIf successful, the minimized test case in `commented.asm` and `./inputs/` is ready for detailed analysis. The annotated comments will help identify the root cause of the violation.\n\n!!! tip \"Troubleshooting Failed Minimization\"\n    If minimization breaks the violation, try these adjustments:\n\n    - Reduce `--num-attempts` to perform fewer iterations\n    - Disable aggressive passes like `--enable-simplification-pass`\n    - Minimize the program before minimizing inputs\n    - Check that `data_generator_seed` matches the original fuzzing campaign\n\n\n## What's Next?\n\nOnce a violation is minimized, the next step is typically to analyze it manually to understand the root cause. The [How to Root-Cause a Violation](root-cause-a-violation.md) guide is dedicated to this topic.\n\n## See Also\n\n- [Minimization Passes](../ref/minimization-passes.md) - Complete list of available passes and their options\n- [CLI Reference](../ref/cli.md) - Full command-line interface documentation\n- [Execution Modes](../ref/modes.md) - Overview of all Revizor execution modes\n- [Configuration Options](../ref/config.md) - Configuration file reference including `data_generator_seed`\n- [How to Design a Fuzzing Campaign](design-campaign.md) - Set up effective fuzzing campaigns\n- [How to Interpret Results](interpret-results.md) - Understand fuzzing outputs and violation reports\n- [Trace Analysis and Violation Detection](../topics/trace-analysis.md) - Understanding how violations are detected\n"
  },
  {
    "path": "docs/howto/root-cause-a-violation.md",
    "content": "# How to Root-Cause a Violation\n\nThis guide discussed in detail how to identify the root cause of confirmed contract violations. This guide shows a typical workflow and some useful techniques for analyzing violation artifacts and isolating the specific CPU behavior that leads to information leakage.\n\n!!! warning \"Art, Not Science\"\n    Root-causing violations is more art than science. The techniques described here are not guaranteed to work in every situation because violations can arise from a wide variety of complex CPU behaviors. Use your intuition and knowledge of microarchitecture to guide your analysis. Experiment with different approaches and document what works best for you.\n\n!!! info \"Prerequisites\"\n    The guide assume you have already finished a [fuzzing campaign](design-campaign.md) and [minimized the violation artifacts](minimize.md).\n\n## Locate the Violation Files\n\nWe will explore the root-cause analysis through a concrete example. The example will demonstrate a CT-SEQ contract violation on an x86-64 CPU.\n\nWe will be working with:\n\n- The violation artifact in `violation-0000-0000/` produced during fuzzing\n- A minimized version of the violation program in `min.asm` produced by the minimizer\n- A set of minimized input files in `./inputs/min_input_*.bin` produced by the minimizer\n- The configuration file `config.yaml` used during fuzzing\n\n## Gather Insights from Minimizer\n\nA good starting point is to examine the output of the minimizer, especially from input minimization passes. These passes attempt to reduce the differences between inputs that trigger the violation, and thus they often highlight the specific data values that leak and that impact the violation.\n\nBelow is an example of the printed summary from the differential input minimizer:\n\n```\n[PASS 2] Differential Input Minimizer\n  > Minimizing the difference between inputs 1 and 11\n\nAddress    +0x0     +0x40    +0x80    +0xc0    +0x100   +0x140   +0x180   +0x1c0\n0x00000000 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000200 ........ =....... ........ ........ ........ ........ ........ ........\n0x00000400 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000600 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000800 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000a00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000c00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000e00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001000 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001200 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001400 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001600 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001800 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001a00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001c00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001e00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00002000 .....^..\n0x00002040 ........ ........ ........ ........\n  > Result: Leaked 1 bytes\n  > Addresses: ['0x2028']\n```\n\nThe minimizer goes through the pair of inputs that trigger the violation - inputs #1 and #11 in this case - and tries to minimize the differences between them:\n\n* If both inputs already have identical values at a given address, the minimizer prints `=` for that address. In this example, this is the case for address `0x240`.\n* Next, the pass attempts to zero out one byte at a time in both inputs. If the violation persists, then the minimizer prints `.` for that address. In this example, most of the addresses are zeroed out.\n* Next, the pass attempts to copy one byte from input #1 into the same address in input #11. If the violation persists, then the minimizer prints `+` for that address. This example does not have such cases.\n* If both attempts fail, the pass restores the original values at the given address, prints `^`, and moves to the next address. In this example, the minimizer restored the original value at address `0x2028`.\n\nThe interpretation of these results is case-specific, but generally, the values with `+` or `=` are those that create conditions for leakage, and the values with `^` are the addresses whose value leaks.\n\nIn this example, the minimizer found that this test case leaks one byte at address `0x2028` (used to initialize RDI). The minimizer also found that the address `0x240` must contain specific non-zero values that must be the same in both inputs. This address in the input is used to initialize the corresponding offset in the sandbox of actor 0. See [Sandbox Memory Layout](https://microsoft.github.io/side-channel-fuzzer/user/sandbox/) for more details about register and memory initialization.\n\n!!! tip \"Minimizer Behavior\"\n    Ideally, the minimizer should be able to reduce the leakage to a single byte. If more then a couple bytes leak, it typically indicates that the violation is non-deterministic, and it might be a good idea to re-run the program minimizer or to change the configuration to increase the number of attempts/increase the noise threshold. If *no* bytes leak, this is a certain sign that something went wrong; re-run the minimizer.\n\n## Step 3: Add Comments to Minimized Program\n\nRun the minimizer again with the `comment` pass enabled to annotate the minimized program with memory access information. This will help you map hardware traces to specific instructions in the program.\n\n```bash\nrvzr minimize -s base.json -c ./violation-0000-0000/minimize.yaml \\\n    -t min.asm -o commented.asm -i <num_inputs> \\\n    --enable-comment-pass 1\n```\n\n## Insert Speculation Fences\n\nTo isolate speculative behavior, add fences:\n\n```bash\nrvzr minimize -s base.json -c ./violation-0000-0000/minimize.yaml \\\n    -t commented.asm -o fenced.asm -i <num_inputs> \\\n    --enable-fence-pass 1\n```\n\nThis pass with attempt to insert an `LFENCE` after every instruction in the program and check if the violation still occurs.\n\nIn the resulting file (`fenced.asm`) the region *without* fences is the one that causes the violation. The remaining instructions are just setting up the data for the violation, and are likely irrelevant.\n\n!!! warning \"Unexpected Fence Insertion Results\"\n    If an `LFENCE` is inserted after *every* instruction in the test case and the violation still occurs, this is most likely due to a bug in the model or the executor. If you are using a custom model, consider checking the model for correctness. If you haven't made changes to the Revizor source code, please, open an issue in the [bug tracker](https://github.com/microsoft/side-channel-fuzzer/issues).\n\n## Map Hardware Traces to Minimized Program and Data\n\nWhen both program and its inputs are minimized, you should be able to identify which instructions caused the cache accesses in the hardware traces and which data was leaked.\n\nWhen we run the `reproduce` command with the minimized program and inputs, we will see the following hardware traces:\n\n```bash\nrvzr reproduce -s base.json -c ./violation-0000-0000/reproduce.yaml \\\n    -t commented.asm -i ./inputs/min_input*.bin\n\n...\n\n================================ Violations detected ==========================\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:1   | ID:11 |\n-----------------------------------------------------------------------------------\n^...............................................^............... | 420    | 0     |\n^............................................................... | 80     | 0     |\n^..............^................................................ | 0      | 500   |\n```\n\n!!! tip \"Input IDs\"\n    If in your case the input IDs have changed after minimization, you can either exclude some of the inputs from the arguments of the `reproduce` command, or re-run the minimizer with fewer passes.\n\nWe see that the hardware traces have been significantly simplified compared to the original violation, and now there are at most two accessed cache sets in each trace: 0 and 48 for input #1, and 0 and 15 for input #11. This is a good sign: the minimization was successful.\n\nWe can also tell that the only difference between the two traces is the accessed cache set 48 vs 15 . This is the cache set that is causing the violation, and we should be aiming to find the instruction that does the access.\n\nTo do so, let's look at the contents of the `commented.asm` file. This file contains the minimized program with comments that show which memory addresses or cache lines are accessed by each instruction.\n\n```assembly\n; ... skipped header ...\n1.  and rax, 0b1111111111111 # instrumentation\n2.  lfence\n3.  mov edx, dword ptr [r14 + rax]\n4.  # mem access: [1] 0x0 cl 0:0 | [11] 0x0 cl 0:0\n5.  or cx, 0b1000 # instrumentation\n6.  and cl, 0b11111000 # instrumentation\n7.  and dx, 0b11 #\n8.  and rsi, 0b1111111111111 #\n9.  add cl, 39 #\n10. mov rbx, 0b1111111111111 #\n11. bt si, dx\n12. jbe .bb_0.1\n13. jmp .exit_0\n14. .bb_0.1:\n15. mov ecx, edi\n16. and rcx, 0b1111111111000 # instrumentation\n17. mov byte ptr [r14 + rcx], 88\n; ... skipped footer ...\n```\n\nThis program contains only two memory accesses, at lines 3 and 17.\n\nThe [annotation](minimize.md#comment-pass-output) at line 4 tells us that the `mov` instruction accesses memory offset `0x0` when executed with input 1 (`[1]`) and the same cache set when executed with input 11 (`[11]`). The notation `0:0` stands for cache set `0` and cache line offset `0`.\n\nThis information lets us map this instruction to the first access in the hardware trace:\n\n```plaintext\n    ^...............................................^...............\n    |\n  This eviction maps to `mov edx, dword ptr [r14 + rax]` at line 3\n```\n\nThe second memory access (line 17) does not have an annotation, which implies that the contract model has not executed this instruction with the inputs provided. It does not, however, mean that the CPU has not executed this instruction, as there is a chance that this instruction was executed speculatively. This is a typical scenario in violations detected by Revizor.\n\nIf we look at the instructions prior to the memory access, we can see `jbe` instruction at line 12, which is a conditional jump - a common source of speculation, namely branch prediction. This type of speculation is not permitted by the target contract (CT-SEQ), so it could cause a violation. From this, we can make a hypothesis that the memory access at line 17 is speculative and is the one causing the second cache access:\n\n```plaintext\n Inputs [1]:\n              Hypothesis: This eviction maps to `mov` at line 17\n                                                  |\n  ^...............................................^...............\n\n Inputs [11]:\n  ^..............^................................................\n                 |\n           Hypothesis: This eviction maps to `mov` at line 17\n```\n\nTo check if our hypothesis is correct, let's cross-reference this information with the leaked bytes from the differential input minimizer:\n\n```plaintext\n; .. skip zero bytes\n0x00002000 .....^..\n0x00002040 ........ ........ ........ ........\n  > Result: Leaked 1 bytes\n  > Addresses: ['0x2028']\n```\n\nThis summary tells us that `rdi` has a differing value between inputs #1 and 11. At the same time, the first time `rdi` is used in the program is at line 15, where it is moved to `rcx`, and then later used as a part of the address in the memory access at line 17. This would make the speculative memory access at line 17 access different addresses with the two inputs, and would explain the difference between the hardware traces.\n\nAt this point, the hypothesis is more-or-less confirmed, and we can declare that the root cause of the leak was the misprediction of the `jbe` branch at line 12, which caused the speculative execution of the memory access at line 17, and which in turn leaked the value of `rdi`.\n\nIf we want to further increase our confidence, we can manually inspect the contents of the inputs at the address `0x2028` to see if the values correspond to the cache set ID that we observe in the hardware traces. This can be done by running the `hexdump` command on the input files:\n\n```bash\n$ hexdump -C ./inputs/min_input_0001.bin | grep 2020\n00002020  00 00 00 00 00 00 00 00  1e 1c 4a 00 1e 1c 4a 00  |..........J...J.|\n$ hexdump -C ./inputs/min_input_0011.bin | grep 2020\n00002020  00 00 00 00 00 00 00 00  c8 13 58 00 c8 13 58 00  |..........X...X.|\n```\n\nThe values are `0x4a1c1e004a1c1e` for input #1 and `0x5813c8005813c8` for input #11. These are masked with `0b1111111111000` by `and` at line 16 and become `7192` and `5064` respectively. If we translate these values to cache set IDs (`id = (addr % 0x1000) // 64`), we get `48` and `15`. These values match the cache set IDs that we observed in the hardware traces, which confirms our hypothesis.\n\nIf we want even more confidence, we can manually modify the input files (e.g, with `hexedit` tool) to see if the hardware traces change when we modify the value of `rdi` in the input files.\n\n\n---\n\n## Modify the Program\n\nIn many cases, the minimization process will not provide a clear result as in the example above and you will not be able to make a specific hypothesis about the root cause of the violation. In such cases, you can try to modify the program in various ways to see if the violation still occurs. There are no strict rules on which modifications to make and you will have to rely on your intuition and knowledge of the target microarchitecture, but here are some general guidelines:\n\n1. **Simplify Instructions**: Start by trying to manually replace instructions in `minimized.asm` with simpler ones. For example, replace complex instructions with memory operands with simple loads or stores.\n2. **Increase/Decrease Aliasing**: Try to change the addresses of memory accesses to match (or not match if they already do) the addresses of other instruction. Such aliasing often triggers speculation (e.g., in Speculative Store Bypass or MDS attacks).\n3. **Add/Remove Dependent Instructions**: If you have a hypothesis about which instruction triggers speculation, try adding or removing data-dependent instructions before it. This will change the size of the speculative window and might change hardware traces, which will give you more insight into the violation.\n4. **Change Memory Permissions**: If the violation is related to memory accesses, try changing the permissions of the memory regions that are accessed by the program. For example, if the memory is read-only, try changing it to read-write. If the violation disappears, it might indicate that the violation is related to the permission checks in the CPU.\n5. **Change Instruction Operands**: Try changing operands to add or remove data dependencies between instructions. For example, if you have a sequence of two moves `mov rax, [rax]; mov rbx, [rax]`, try changing the second move to `mov rbx, [rbx]` to see if the violation still occurs if there are no data dependencies between the instructions.\n\nAfter each modification, run the `reproduce` command to see if the violation still occurs:\n\n```bash\nrvzr reproduce -s base.json -c ./violation-<timestamp>/reproduce.yaml \\\n    -t modified.asm -i ./inputs/min_input*.bin\n```\n\n\n!!! tip \"Share Your Findings\"\n    If you find any other strategies that work well, please consider sharing them by opening a pull request to this documentation. We would love to hear about your experiences and learn from them.\n\n\n## See Also\n\n- [How to Interpret Violation Results](interpret-results.md) - Understanding and validating violations before root-cause analysis\n- [How to Minimize Test Cases](minimize.md) - Complete minimization workflow and pass descriptions\n- [Minimization Passes](../ref/minimization-passes.md) - Reference documentation for all minimization passes\n- [Configuration Options](../ref/config.md) - Configuration parameters for reproduction and minimization\n- [Command-Line Interface](../ref/cli.md) - Complete CLI reference for all execution modes\n- [Sandbox Memory Layout](../ref/sandbox.md) - Understanding input file structure and register initialization\n- [Trace Analysis and Violation Detection](../topics/trace-analysis.md) - How Revizor detects violations\n- [Contracts and Leakage Models](../topics/contracts.md) - Understanding contract semantics\n"
  },
  {
    "path": "docs/howto/use-macros.md",
    "content": "# How To Use Macros\n\nThis document explains the concept of macros in Revizor and describes how to create test cases that use macros.\n\nNote that macros are especially useful in the template-based mode of Revizor, so if you are not familiar, check out the [Template-Based Mode](../howto/use-templates.md) documentation as well.\n\n## What is a macro?\n\nMacros in Revizor are special pseudo-instructions that provide a flexible way to insert complex operations into test cases. They appear as labels of a special format in the assembly code but are dynamically expanded into actual implementations during execution by the model and the executor.\n\nMacros solve two key challenges, especially in the context of multi-domain testing:\n\n* Structuring: Enable insertion of pre-defined instruction sequences (like domain transitions or microarchitectural isolation primitives) within randomized test contexts\n* Unification: Allow the same test case template to be instantiated differently across executor and model stages, accommodating differences in ISA support.\n\n## Why use macros?\n\nMacros exist to provide extra flexibility and convenience when creating test case. There are certain operations that are cumbersome or impractical to express directly in assembly code, and macros serve to abstract away these complexities.\n\n\n## Macro Definition and Usage\n\n### Assembly Syntax\n\nMacros use standard assembly syntax of a label with the `.macro` prefix:\n\n```assembly\n.macro.macro_name.argument1.argument2.argument3.argument4:\n```\n\nA macro can take at most four arguments. The arguments are strictly static; Revizor does not support dynamic arguments in macros, such as registers or memory addresses.\n\n\n### Example Usage\n\nA user can create a test case program where only a subset of instruction is measured by using `measurement_start` and `measurement_end` macros:\n\n```asm\n.intel_syntax noprefix\n.section .data.main\n\n... ; non-measured code here\n\n.macro.measurement_start:\n\n... ; measured code here\n\n.macro.measurement_end:\n\n... ; non-measured code here\n\n.test_case_exit:\n```\n\nRevizor will automatically replace the macros with no-op operations of an ISA-dependent size, and record the location and the arguments of the macros in the test case metadata. When the executor and the model run the test case, they will recognize these macros and execute the corresponding logic. Note that the logic can be configurable, e.g., when the user has set `executor_mode: P+P` (prime+probe), the `measurement_start` macro will correspond the Prime stage of the measurement, and `measurement_end` will correspond to the Probe stage.\n\nSee [Implementation Overview](#implementation-overview) for details on how macros are implemented in the executor and model.\n\n## Implementation Overview\n\n### Internal Representation of Macros\n\nRevizor internally replaces all macros with a no-op placeholder of a fixed size (8 bytes for x86-64, 12 bytes for ARM64). This placeholder is used to maintain the original instruction flow while allowing the executor and model to recognize and handle macros dynamically. The macro location, type, and arguments are stored in the test case metadata, namely in the `SYMBOL TABLE` section of the [RCBF File Format](../ref/binary-formats.md), where `owner` is set to the actor ID of the actor that contains the macro, `offset` is the offset of the macro placeholder in the code section of the actor, `id` is the macro type (defined in [executor_km/include/macro_expansion.h](https://github.com/microsoft/side-channel-fuzzer/blob/main/src/x86/executor/include/macro_loader.h)), and `args` is a compressed representation of the macro arguments.\n\n### Macros in Executor\n\nEach actor's code section contains a dedicated memory region for macros, and the implementation is copied there during test case initialization. The executor copies the implementations of all macros into this section, and it replaces the macro placeholders with direct jumps to the corresponding implementations. The executor also inserts a return jump at the end of each macro implementation to return control flow back to the original instruction sequence.\n\nFor example, if we have a simple test case like this:\n\n```asm\n.macro.measurement_start:\n... ; some code here\n.macro.measurement_end:\n.test_case_exit:\n```\n\nThe executor with expand it as follows:\n\n```asm\njump measurement_start_impl\nlfence\n.l1:\n... ; some code here\njump measurement_end_impl\nlfence\n.l2:\n.test_case_exit:\n\n.macro_code_section:\nmeasurement_start_impl:\n... ; sequence of instructions that implements the macro\njump .l1  ; jump to the end of the macro section\n\nmeasurement_end_impl:\n... ; sequence of instructions that implements the macro\njump .l2  ; jump to the end of the macro section\n```\n\nNote that the executor also inserts LFENCE barriers after each macro jump. This is to ensure that the macro execution does not trigger straight-line speculation, which could interfere with the measurement process.\n\n\n### Macros in Model\n\nIn the model, macros are implemented as dynamic callbacks. The model executes a hook function on every instruction execution, checking if the current instruction matches an entry in the symbol table. If a match is found, the model invokes the corresponding callback function to emulate the macro behavior.\n"
  },
  {
    "path": "docs/howto/use-templates.md",
    "content": "# How to Use Templates\n\nTemplate-based mode (`tfuzz`) enables targeted testing of specific CPU scenarios by using predefined assembly templates that get expanded with random instructions. This mode narrows down the fuzzing space to focus on particular interaction patterns while maintaining randomization within those patterns.\n\n## Overview\n\nTemplate-based mode generates test cases from assembly templates containing macros that get dynamically expanded during generation. Templates define the structure and flow of test cases while allowing specific sections to be populated with random instructions based on configuration.\n\n## Command Line Usage\n\nTemplate-based mode is invoked using the `rvzr tfuzz` command. The invocation is almost identical to the normal `rvzr fuzz` mode, but it takes an additional `-t` or `--template` parameter to specify the assembly template file.\n\nInvocation example:\n\n```bash\nrvzr tfuzz -t template.asm -c config.yaml -s base.json -n 10 -i 100\n```\n\nwhere `template.asm` is the template file.\n\n\n## Template Structure\n\nTemplates are assembly files that combine:\n\n- Regular assembly instructions\n- Macros (special pseudo-instructions as described in [Macros](../ref/macros.md))\n\nExample template:\n\n```asm\n.intel_syntax noprefix\n.section .data.main\n\n.macro.random_instructions.10.0:  ; Replaced with 10 random instructions\ndiv rax, rbx                      ; rax and rbx may be set by random instructions\njmp .test_case_exit               ; Jump to exit point if no exception occurs\n\n.fault_handler:\n    .macro.random_instructions.10.1:  ; Generate 10 random instructions executed when a fault occurs\n\n.test_case_exit:\n```\n\nRevizor will take this template and replace the `.macro.random_instructions.N` with N random instructions from the instruction pool defined in the configuration file. A new test case will be generated this way in each fuzzing round, allowing for a wide variety of test cases while still adhering to the structure defined in the template. For example, if `-n 10` is specified, the generator will produce 10 test cases based on the template, each with different random instruction sequences.\n"
  },
  {
    "path": "docs/index.md",
    "content": "---\ntitle: \"Revizor\"\nhide:\n  - navigation\n  - toc\n---\n\n<style>\n.md-typeset h1,\n.md-content__button {\n    display: none;\n}\n\n.hero-section {\n    text-align: center;\n    max-width: 800px;\n    margin: 0 auto;\n}\n\n.hero-section img {\n    max-width: 320px;\n    height: auto;\n}\n\n.hero-section p {\n    margin: 0;\n}\n\n.hero-section .tagline {\n    color: var(--md-default-fg-color--light);\n    font-size: 1.5rem;\n    font-weight: 300;\n    margin-top: 1.0rem;\n    margin-bottom: 5rem;\n}\n\n.grid.cards > ul > li {\n    text-align: center;\n}\n\n.grid.cards > ul > li > p > strong{\n    font-size: 1.1rem;\n}\n\n.grid.cards > ul > li > p.text {\n    text-align: justify;\n    margin: 1rem;\n}\n\n.grid.cards > ul > li .md-button {\n    margin: 0.25rem;\n}\n\nh2 {\n    text-align: center;\n    margin-top: 3rem;\n    margin-bottom: 1rem;\n}\n\nh2 > strong {\n    font-weight: 700;\n    font-size: 1.8rem;\n}\n\n\n</style>\n\n<div class=\"hero-section\" markdown>\n\n<img src=\"./assets/logo.svg#only-light\" alt=\"Revizor Logo\" align=\"center\" width=\"320px\" />\n<img src=\"./assets/logo-light.svg#only-dark\" alt=\"Revizor Logo\" align=\"center\" width=\"320px\" />\n\n<p class=\"tagline\">Hardware fuzzing for the age of speculation</p>\n\n</div>\n\n<div class=\"grid cards\" markdown>\n\n-   __:fontawesome-solid-arrow-right: Get Started__\n\n    ---\n\n    <p class=\"text\">\n    Welcome to the Revizor documentation! Whether you're a new user looking to get started or a developer interested in contributing, you'll find all the information you need here.\n    </p>\n\n    [Start Here](intro/start-here.md){ .md-button .md-button--primary }\n    [Learn Revizor](intro/01-overview.md){ .md-button }\n    [Ask a Question](howto/ask-a-question.md){ .md-button }\n    [Cite Revizor](ref/papers.md){ .md-button }\n\n\n- __:fontawesome-solid-code: Source Code__\n\n    ---\n\n    <p class=\"text\">\n    The Revizor project lives on GitHub. Explore the source code, report issues, and contribute to the project.\n    </br></br></br>\n    </p>\n\n\n    [GitHub](https://github.com/microsoft/side-channel-fuzzer){ .md-button }\n    [Contributing](internals/contributing/overview.md){ .md-button }\n    [Bug Reports](https://github.com/microsoft/side-channel-fuzzer/issues){ .md-button }\n    [Explore Docs](structure.md){ .md-button }\n\n\n- __:fontawesome-solid-comments: Join the Community__\n\n    ---\n\n    <p class=\"text\">\n    Join the Revizor community to get help, discuss ideas, suggest features, and share your experiences.\n    </br></br></br>\n    </p>\n\n    [Zulip Community](https://rvzr.zulipchat.com/){ .md-button }\n    [GitHub Discussions](https://github.com/microsoft/side-channel-fuzzer/discussions){ .md-button }\n\n</div>\n\n---\n\n## __:fontawesome-solid-bug: Trophies__{ .trophies-header }\n\n#### Transient Scheduler Attack - L1 Cache (TSA-L1)\n\n=== \"Description\"\n    A speculative leak affecting AMD Family 19h processors where false completions in load instructions can leak data from the L1 data cache across security boundaries. The attack exploits the linear address-based microtag system used for L1 cache lookups - when a load finds a matching microtag entry but the L1 doesn't contain valid data, invalid data from the matching microtag entry is used in a false completion. This leak enables information disclosure between kernel/userspace, hypervisor/guest, across different applications or VMs, and from SEV-SNP VMs to the host.\n=== \"CVE\"\n    [CVE-2024-36357](https://nvd.nist.gov/vuln/detail/CVE-2024-36357)\n=== \"Links\"\n    * More details in: [Enter, Exit, Page Fault, Leak: Testing Isolation Boundaries for Microarchitectural Leaks](https://aka.ms/enter-exit-leak)\n    * AMD Security Advisory: [Advisory](https://www.amd.com/en/resources/product-security/bulletin/amd-sb-7029.html)\n\n\n#### Transient Scheduler Attack - Store Queue (TSA-SQ)\n\n=== \"Description\"\n    A speculative leak affecting AMD Family 19h processors where false completions in Store-To-Load Forwarding operations can leak data from previous store instructions. When a load matches an older store's address but the store data isn't yet available, a false completion occurs using invalid data from a previously executed store that occupied the same store queue entry. This effect enables information leakage from the OS kernel to user applications, hypervisor to guest, and to a lesser extent, between application.\n=== \"CVE\"\n    [CVE-2024-36350](https://cve.mitre.org/cgi-bin/cvename.cgi?name=2024-36350)\n=== \"Links\"\n    * More details in: [Enter, Exit, Page Fault, Leak: Testing Isolation Boundaries for Microarchitectural Leaks](https://aka.ms/enter-exit-leak)\n    * AMD Security Advisory: [Advisory](https://www.amd.com/en/resources/product-security/bulletin/amd-sb-7029.html)\n\n#### Control Register Speculation\n\n=== \"Description\"\n    A speculative leak affecting AMD processors where user processes can speculatively infer control register values even when User Mode Instruction Prevention (UMIP) is enabled. This bypasses intended security boundaries by allowing unprivileged code to access system-level configuration information through speculative channels.\n=== \"CVE\"\n    [CVE-2024-36348](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-36348)\n=== \"Links\"\n    * More details in: [Enter, Exit, Page Fault, Leak: Testing Isolation Boundaries for Microarchitectural Leaks](https://aka.ms/enter-exit-leak)\n    * AMD Security Advisory: [Advisory](https://www.amd.com/en/resources/product-security/bulletin/amd-sb-7029.html)\n\n#### TSC_AUX Speculation\n\n=== \"Description\"\n    A speculative leak affecting AMD processors affecting AMD processors that permits user processes to infer the Time Stamp Counter Auxiliary (TSC_AUX) register value even when direct reads are disabled.\n=== \"CVE\"\n    [CVE-2024-36349](https://nvd.nist.gov/vuln/detail/CVE-2024-36349)\n=== \"Links\"\n    * More details in: [Enter, Exit, Page Fault, Leak: Testing Isolation Boundaries for Microarchitectural Leaks](https://aka.ms/enter-exit-leak)\n    * AMD Security Advisory: [Advisory](https://www.amd.com/en/resources/product-security/bulletin/amd-sb-7029.html)\n\n\n#### Divider State Sampling (DSS)\n\n=== \"Description\"\n    A speculative leak where division-by-zero operations can transiently return values that depend on previous division operations. The leaked state persists across privilege boundaries. The discovery of the leak triggered a patch to the Linux kernel as well as other operating systems.\n=== \"CVE\"\n    [CVE-2023-20588](https://nvd.nist.gov/vuln/detail/CVE-2023-20588)\n=== \"Links\"\n    More details in: [Speculation at Fault](https://www.usenix.org/system/files/usenixsecurity23-hofmann.pdf)\n\n#### String Comparison Overrun (SCO)\n\n=== \"Description\"\n    Revizor discovered that string operations on Intel and AMD CPUs (in particular, string comparison and string scan) can speculatively bypass the bounds of their target strings, which permits the attacker to leak data from out-of-bounds memory locations.\n=== \"Links\"\n    More details in: [Hide & Seek with Spectres](https://www.microsoft.com/en-us/research/publication/hide-and-seek-with-spectres-efficient-discovery-of-speculative-information-leaks-with-random-testing/)\n\n#### Zero Dividend Injection (ZDI)\n\n=== \"Description\"\n    64-bit division operations on Intel CPUs can speculative ignore the upper bits of the divisor, thus producing an incorrect computational result. This speculation can potentially impact the security of cryptographic algorithms that use division to implement modulo operations.\n=== \"Links\"\n    More details in: [Hide & Seek with Spectres](https://www.microsoft.com/en-us/research/publication/hide-and-seek-with-spectres-efficient-discovery-of-speculative-information-leaks-with-random-testing/)\n\n#### Read-Modify-Write Speculation\n\n=== \"Description\"\n    A new variant of Microarchitectural Data Sampling (MDS) where a store operation to read-only memory triggers speculative behavior. When a read-modify-write instruction (like XADD) attempts to access read-only memory, it speculatively returns stale data from internal CPU buffers, even though the read itself would be permitted.\n=== \"Links\"\n    More details in: [Speculation at Fault](https://www.usenix.org/system/files/usenixsecurity23-hofmann.pdf)\n\n#### Non-canonical Store Forwarding\n\n=== \"Description\"\n    A speculative leak where stores to non-canonical addresses can be forwarded to subsequent loads from the canonical versions of those addresses. This means that even though a store operation fails due to an invalid address format, its data can still be transiently accessed by later instructions using a related valid address.\n=== \"Links\"\n    More details in: [Speculation at Fault](https://www.usenix.org/system/files/usenixsecurity23-hofmann.pdf)\n\n#### Variable-latency Spectre\n\n=== \"Description\"\n    A variant of Spectre vulnerability where the leakage is caused by the race condition that appears when a speculative memory access is data-dependent on a variable-latency instruction. This race condition can expose the operands of the variable-latency instruction.\n=== \"Links\"\n    More details in [the Revizor paper](https://www.microsoft.com/en-us/research/publication/revizor-testing-black-box-cpus-against-speculation-contracts/)\n\n#### Store-based Spectre V1\n\n=== \"Description\"\n    Several defense proposals (e.g., STT, KLEESpectre) assumed that stores do not modify the cache state until they retire. We used Revizor to validate this assumption, and discovered that is not true on recent Intel CPUs (e.g., CoffeeLake).\n=== \"Links\"\n    More details in [the Revizor paper](https://www.microsoft.com/en-us/research/publication/revizor-testing-black-box-cpus-against-speculation-contracts/)\n\n#### Speculative Store with Forwarding\n\n=== \"Description\"\n    Revizor discovered that two consecutive loads from the same address can speculatively return two different values if one of them receives a forwarded value from a store while the other load experiences a speculative store bypass. This combination exposes more information to the attacker compared to the original store bypass.\n=== \"Links\"\n    More details in [the appendix to the Revizor paper](https://www.microsoft.com/en-us/research/publication/revizor-testing-black-box-cpus-against-speculation-contracts/)\n\n<!--\n### Reproduced Vulnerabilities\n\n- [Spectre V1 (Bounds Check Bypass, BCB)](https://spectreattack.com/)\n- [Spectre V4 (Speculative Store Bypass, SSBP)](https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/speculative-store-bypass.html)\n- [Meltdown (SMAP variant)](https://meltdownattack.com/)\n- [Foreshadow (L1TF)](https://foreshadowattack.eu/)\n- [Microarchitectural Data Sampling (MDS)](https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/advisory-guidance/microarchitectural-data-sampling.html)\n- [Load Value Injection (LVI), including LVI-Null](https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/load-value-injection.html)\n -->\n"
  },
  {
    "path": "docs/internals/architecture/analysis.md",
    "content": "|                  |                    |\n| ---------------- | ------------------ |\n| Module           | `rvzr/analyser.py` |\n| Public interface | `Analyser`         |\n| Inputs           | `CTrace`, `HTrace` |\n| Outputs          | `Violation`        |\n\nThe Analyser compares contract traces with hardware traces to detect violations. The core principle: inputs with identical CTraces should produce equivalent HTraces. When they don't, a contract violation has occurred.\n\n```python\nFor all inputs i, j:\n    if CTrace(i) == CTrace(j) and HTrace(i) != HTrace(j):\n        → Violation detected\n```\n\nAnalyser implementations:\n\nDifferent analysers define \"equivalent HTrace\" differently:\n\n- `MergedBitmapAnalyser` (default) — Merges samples using bitwise OR, compares bitmaps. For cache-based channels.\n- `SetAnalyser` — Compares sets of unique samples.\n- `MWUAnalyser` — Uses Mann-Whitney U statistical test. For timing-based channels.\n- `ChiSquaredAnalyser` — Uses chi-squared test for distribution differences.\n"
  },
  {
    "path": "docs/internals/architecture/code.md",
    "content": "# Test Case Code Generation\n\n|                  |                          |\n| ---------------- | ------------------------ |\n| Module           | `rvzr/code_generator.py` |\n| Public interface | `CodeGenerator`          |\n| Inputs           | `InstructionSet`         |\n| Outputs          | `TestCaseProgram`        |\n\nThis module generates random assembly programs for testing. The generator creates programs designed to trigger speculative execution and expose microarchitectural leaks.\n\n### Generation process\n\n1. Create control flow graph — Generate a random Directed Acyclic Graph (DAG) of basic blocks. The DAG structure prevents infinite loops while allowing branches and mispredictions.\n\n2. Add jump instructions — Insert conditional and unconditional jumps at block boundaries to connect the blocks according to the DAG.\n\n3. Fill basic blocks — Populate blocks with random instructions from the tested instruction pool, respecting instruction frequencies and operand constraints.\n\n4. Instrument — (Optionally) Prevent faults by masking memory addresses, avoiding division by zero, and ensuring all accesses stay within the sandbox.\n\n5. Assemble — Convert to binary and extract metadata.\n\n6. Transform into RCBF — Serialize the test case into Revizor's custom binary format ([RCBF](../../ref/binary-formats.md)) for execution.\n\n### Test case representation\n\n```text\nTestCaseProgram\n  ├─ CodeSection (one per actor)\n  │    └─ Function\n  │         └─ BasicBlock\n  │              └─ InstructionNode\n  │                   └─ Instruction\n  │                        └─ Operand\n  └─ TestCaseBinary\n       └─ SymbolTable\n```\n\n### Variants\n\nArchitecture-specific implementations of the code generator exist for x86 and ARM64, named `X86Generator` and `ARM64Generator` in `rvzr/arch/*/code_generator.py`\n"
  },
  {
    "path": "docs/internals/architecture/data.md",
    "content": "# Test Case Data Generation\n\n|                  |                          |\n| ---------------- | ------------------------ |\n| Module           | `rvzr/data_generator.py` |\n| Public interface | `DataGenerator`          |\n| Inputs           | `Config`                 |\n| Outputs          | `InputData`              |\n\n`DataGenerator` generates input data that is used to initialize registers and memory before executing a test case, on both the model and the target hardware.\n\n\n## Generation modes\n\nTwo input generation modes are supported:\n\n### Standard generation\n\nInterface: `DataGenerator.generate(...)`\n\nThis method creates fully random inputs using a PRNG. Can optionally reduce entropy (to increase trace collisions) or inject special values (zeros, boundary values) to trigger edge cases.\n\n### Boosted generation\n\nInterface: `DataGenerator.generate_boosted(...)`\n\nBoosted generation solves the following challenge:\nTwo detect a violation via relational non-interference testing, we always need at least two inputs that produce identical contract traces (see [Trace Analysis](overview.md#6-trace-analysis)). Generating such contract-equivalent inputs through pure randomness is extremely inefficient because the entropy of contract traces is usually very high, and thus most random inputs produce unique traces.\n\nBoosted generation addresses this by leveraging dynamic taint analysis on the model side. It works as follows: Start by producing a set of random inputs using standard generation. Then, we execute the test case with each input in the model and perform backwards taint analysis to identify which input bytes affect the contract trace (tainted) and which don't (untainted). This produces a set of `InputTaint` objects that map input bytes to their taint status. These taint maps a fed back into the `generate_boosted()` method, which creates new inputs such that the tainted bytes remain fixed while the untainted bytes are randomized.\n\n```text\nOriginal InputData → Model → InputTaint → N contract-equivalent inputs\n```\n\nSuch \"boosted\" inputs are guaranteed to produce the same contract trace as the original input while still being mostly random.\n\n\n## Data Representation\n\nEach input is represented as an `InputData` object, which is a numpy structured array containing\n\n- Memory contents\n- General-purpose registers\n- SIMD registers\n- Flags and special registers\n\nfor each actor in the test case. This object can be serialized into Revizor's custom binary format ([RDBF](../../ref/binary-formats.md)) for consumption by the model and executor.\n"
  },
  {
    "path": "docs/internals/architecture/exec.md",
    "content": "# Hardware Tracing\n\n|                  |                                         |\n| ---------------- | --------------------------------------- |\n| Module           | `rvzr/executor.py`, `rvzr/executor_km/` |\n| Public interface | `Executor`                              |\n| Inputs           | `TestCaseProgram`, `InputData`          |\n| Outputs          | `HTrace`                                |\n\n## Executor\n\nThe Executor runs test cases on real hardware and collects hardware traces (HTraces) using side-channel measurements. It uses a two-layer architecture: Python code communicates with a kernel module that performs measurements in kernel space.\n\n```text\nPython (executor.py)\n  ├─ X86IntelExecutor\n  ├─ X86AMDExecutor\n  └─ ARM64Executor\n       │\n       │ /sys/rvzr_executor/ interface\n       ▼\nKernel Module (executor_km/)\n```\n\n## HTrace representation\n\nThe `HTrace` class (`rvzr/traces.py`) represents hardware traces collected during execution. The executor produces one `HTrace` object per program-input pair, meaning that for each `TestCaseProgram` execution with each `InputData` input, one `HTrace` is generated.\n\nEach `HTrace` encapsulates multiple measurements results (samples): This is because the executor typically repeats the execution several times and each execution produces one measurement sample. Such repeated measurements allow us to apply statistical methods when comparing noisy hardware traces (see [Trace Analysis](analysis.md) below).\n\nThe structure of an `HTrace` is as follows:\n\n```text\nHTrace\n  └─ Array[RawHTraceSample]\n       ├─ trace       Main measurement (cache bitmap, timestamp, or registers)\n       └─ pfc0-pfc4   Performance counter values\n```\n"
  },
  {
    "path": "docs/internals/architecture/fuzz.md",
    "content": "# Orchestration Module\n\n|                  |                                          |\n| ---------------- | ---------------------------------------- |\n| Module           | `rvzr/fuzzer.py`                         |\n| Public interface | `Fuzzer`                                 |\n| Inputs           | `Config`, `InstructionSet`, ASM Template |\n| Outputs          | Violation artifact, logs                 |\n\nThe `Fuzzer` class is the main coordinator. It manages the core components (`CodeGenerator`, `DataGenerator`, `Model`, `Executor`, and `Analyser`) and orchestrates the fuzzing loop.\n\n## Main workflow\n\n```text\nFuzzer.start()\n  └─> for each test case:\n        ├─> CodeGenerator.create_test_case() → TestCaseProgram\n        ├─> DataGenerator.generate() → List[InputData]\n        └─> Fuzzer.fuzzing_round(program, inputs)\n              ├─> Model.trace_test_case() → List[CTrace]\n              ├─> Executor.trace_test_case() → List[HTrace]\n              ├─> Analyser.filter_violations() → List[Violation]\n              └─> if violation: multi-stage filtering pipeline\n```\n\n\n## Multi-stage filtering\n\nWhen a potential violation is found, the Fuzzer runs it through several validation stages. Each stage modifies parameters and re-checks the violation to rule out false positives:\n\n1. `fast` — Initial fast detection using minimal speculative nesting on the model side and small sample size on the executor side\n2. `nesting` — Re-collect ctraces with the model using full speculative nesting. This rules out false positives caused by incomplete speculation modeling\n3. `taint_mistake` — Re-collect ctraces for the boosted inputs to rule out boosting-based generation mistakes\n4. `priming` — Perform a so-called \"priming test\" (swap the order of violating inputs) to rule out false positives caused by inconsistent microarchitectural state across executions\n5. `noise` — Increase sample size on the executor side to increase statistical confidence and rule out noise-induced violations\n6. `arch_mismatch` — Compare the architectural output (i.e., register/memory states) of the model and executor to rule out violations caused by functional mismatches (i.e., by bugs in the model or executor)\n\nIf a violation survives all stages, Revizor saves a reproduction package (called \"violation artifact\") containing the test case, inputs, configuration, and detailed report.\n\n## Fuzzer variants\n\nThe `Fuzzer` class is abstract. There are several variants modifying the baseline logic:\n\n- `X86Fuzzer` / `ARM64Fuzzer` — Architecture-specific implementations\n- `ArchitecturalFuzzer` — Validates model correctness (i.e., performs stage 6 `arch_mismatch` for all test cases, even non-violating ones)\n- `ArchDiffFuzzer` — Completely discards the model, and instead compares two hardware executions, one with a normal test case and one with a speculation fence added after every instruction. This variant is used to detect speculation-induced architectural bugs, like zenbleed.\n"
  },
  {
    "path": "docs/internals/architecture/isa.md",
    "content": "# Instruction Set Specification\n\n|                  |                    |\n| ---------------- | ------------------ |\n| Module           | `rvzr/isa_spec.py` |\n| Public interface | `InstructionSet`   |\n| Inputs           | `base.json`        |\n| Outputs          | `InstructionSet`   |\n\nThis module manages the instruction set available for fuzzing. It loads ISA definitions from a JSON file (`base.json`) and applies user-configured filters to create a pool of allowed instructions.\n\nEach instruction is represented by an `InstructionSpec` containing instruction name and category, operand specifications, and instruction properties.\n\nProcessing pipeline:\n\n1. Load ISA specification from JSON\n2. Apply filters (allowlist, blocklist, categories, register restrictions)\n3. Remove duplicates\n4. Categorize instructions by type (control flow, memory access, etc.)\n\n\n"
  },
  {
    "path": "docs/internals/architecture/logging.md",
    "content": "# Logging\n\n|                  |                               |\n| ---------------- | ----------------------------- |\n| Module           | `rvzr/logs.py`                |\n| Public interface | `FuzzLogger`, etc.            |\n| Inputs           | N/A                           |\n| Outputs          | Log messages (stdout, stderr) |\n\nRevizor uses a centralized logging system with configurable verbosity. The system uses the Borg pattern to share state across modules.\n\nAvailable logging modes:\n\n- info — General messages and progress\n- stat — Statistics\n- dbg_* — Debug modes for specific components\n\nLogging components:\n\n- Basic functions: `error()`, `warning()`, `inform()`, `dbg()`\n- Module-specific loggers: `FuzzLogger`, `GeneratorLogger`, `ISALogger`, `ExecutorLogger`, `AnalyserLogger`\n\n"
  },
  {
    "path": "docs/internals/architecture/mini.md",
    "content": "# Post-violation Analysis\n\n|                  |                                 |\n| ---------------- | ------------------------------- |\n| Module           | `rvzr/postprocessing/`          |\n| Public interface | `Minimizer`                     |\n| Inputs           | Violation artifact (.asm, .bin) |\n| Outputs          | Minimized test case and inputs  |\n\nAfter confirming a violation, users can run post-processing to simplify the test case and identify the root cause. The postprocessing module applies minimization passes that reduce complexity while preserving the violation.\n\nClass hierarchy:\n\n```text\nMinimizer\n  └─ Orchestrates passes, manages files\n\nBaseMinimizationPass\n  ├─ Instruction passes (modify code)\n  ├─ Data passes (modify inputs)\n  └─ Analysis passes (add annotations)\n```\n\nInstruction passes (operate on test case code):\n\n- `InstructionRemovalPass` — Remove instructions one at a time to find essential ones\n- `NopReplacementPass` — Replace with NOPs (preserves alignment)\n- `InstructionSimplificationPass` — Replace complex instructions with simpler ones\n- `ConstantSimplificationPass` — Simplify immediate values\n- `MaskSimplificationPass` — Simplify bitmasks\n- `LabelRemovalPass` — Remove unused labels\n- `FenceInsertionPass` — Insert fences to identify speculation boundaries\n\nData passes (operate on inputs):\n\n- `DifferentialInputMinimizerPass` — Use delta debugging to find minimal byte differences\n- `InputSequenceMinimizationPass` — Reduce number of inputs\n\nAnalysis passes (add annotations):\n\n- `AddViolationCommentsPass` — Annotate assembly with memory addresses from execution\n"
  },
  {
    "path": "docs/internals/architecture/model.md",
    "content": "# Contract Tracing\n\n|                  |                                |\n| ---------------- | ------------------------------ |\n| Module           | `rvzr/model.py`                |\n| Public interface | `Model`                        |\n| Inputs           | `TestCaseProgram`, `InputData` |\n| Outputs          | `CTrace`                       |\n\n## Model\n\nThe Model executes test cases according to a leakage contract and produces contract traces (CTraces). These represent the information expected to leak during execution, including speculative execution.\n\nRevizor supports two model backends:\n\n- **Unicorn**: This backend is based on the [Unicorn CPU emulator](https://www.unicorn-engine.org/). It implements the contract by hooking into instruction execution and memory access events. Documentation is provided in [Unicorn Backend](../model-backends/model-unicorn.md).\n- **DynamoRIO**: This backend uses [DynamoRIO](https://dynamorio.org/) for dynamic binary instrumentation. It instruments the test case to insert hooks for tracing and speculation simulation. Documentation is provided in [DynamoRIO Backend](../model-backends/model-dr.md).\n\nBoth implement the same interface defined by the abstract `Model` class.\n\n## Contract Trace Representation\n\nA `CTrace` is a sequence of typed observations representing leaked information:\n\n```text\nCTrace\n  └─ List[CTraceEntry]\n       ├─ mem    Memory address\n       ├─ pc     Program counter\n       ├─ val    Data value\n       ├─ reg    Register value\n       └─ ind    Indirect branch target\n```\n\nCTraces use `xxhash` for fast equality checking, enabling efficient grouping into equivalence classes.\n"
  },
  {
    "path": "docs/internals/architecture/overview.md",
    "content": "# Architecture Overview & Code Structure\n\nThis document introduces Revizor's architecture and key components. It is designed to provide an overview of how the codebase is organized and how the main pieces work together.\n\n!!! info \"Prerequisites\"\n    This document assumes familiarity with the concepts of side-channel attacks, speculative execution, and [Speculation Contracts and Model-based Relational Testing (MRT)](../../topics/contracts.md).\n\n## How Revizor Works\n\nRevizor detects CPU security vulnerabilities using Model-based Relational Testing (MRT). The core idea is to compare what a CPU should leak (according to a leakage model) with what it actually leaks during execution.\n\nBasic process:\n\n1. Generate random assembly programs\n2. Execute them on both a leakage model and real hardware\n3. Compare the observed hardware behavior with the model's predictions\n4. If they match, the CPU behaves as expected (discard the test)\n5. If they differ, a potential vulnerability has been found\n\nThe leakage model acts as a reference model of the expected CPU behavior. If the real CPU leaks more information than the model predicts (i.e., if it diverges from the reference), this indicates a potential security vulnerability. For details on how leakage models work, see [Speculation Contracts](../../topics/contracts.md).\n\nRevizor runs the following loop until it finds a violation or completes the configured number of test cases:\n\n![architecture](../../assets/fuzzing-flow.png)\n\n## 1. Initialization\n\nThis step runs once at startup. Revizor reads the fuzzing configuration, which specifies:\n\n- Target CPU architecture\n- ISA (instruction set) specification\n- Which instructions to test\n- Which side channels to monitor\n- Other fuzzing parameters\n\nThe `cli.py` module handles command-line arguments and creates the main objects: `InstructionSet` (from `isa_spec.py`), `Config` (from `config.py`), and `Fuzzer` (from `fuzzer.py`).\n\n## 2. Code Generation\n\nEach fuzzing round starts by generating a random test program. This is an assembly program with semi-random control flow, built from a pool of allowed instructions.\n\nThe code generator can be configured to control the shape of the control flow graph, which instructions to include, and how often each instruction appears. It also (optionally) instruments the program to prevent faults like division by zero.\n\nThe `Fuzzer` calls `CodeGenerator.create_test_case()` (in `code_generator.py`), which returns a `TestCaseProgram` object representing the generated assembly program.\n\n## 3. Data Generation\n\nNext, Revizor generates random inputs for the test program. Each input contains initial values for registers and memory. These values are pseudo-random but use fixed seeds for reproducibility.\n\nThe `DataGenerator` class (in `data_generator.py`) creates these inputs and returns them as `InputData` objects. See [binary formats](../../ref/binary-formats.md#revizor-data-binary-format-rdbf) for the structure of input data.\n\n## 3.5 Test Case Filtering (Optional)\n\nSome test cases are unlikely to reveal vulnerabilities, so Revizor can filter them out early to save time. This is optional and disabled by default.\n\nTwo filters are available:\n\n- Speculation filter: Uses performance counters to check if the test case triggers branch mispredictions. Without mispredictions, the test cannot expose speculative leaks.\n- Observation filter: Compares the original test case with a \"fenced\" version (with serialization instructions added). If both produce identical traces, speculation left no observable effects.\n\nThese filters are implemented in architecture-specific fuzzer classes (like `X86Fuzzer` in `rvzr/arch/x86/fuzzer.py`).\n\n## 4. Model Execution\n\nThe model executes the test program with each generated input and produces contract traces (CTraces). These traces represent what the model predicts should leak during execution.\n\nThe `Model` class (in `model.py`) provides two key methods:\n\n- `load_test_case()`: Loads the program into the model\n- `trace_test_case()`: Executes the program with each input and returns CTraces\n\nRevizor supports multiple model backends: [Unicorn](../model-backends/model-unicorn.md) (CPU emulator) and [DynamoRIO](../model-backends/model-dr.md) (dynamic instrumentation). Both implement the same interface.\n\n## 5. Hardware Execution\n\nThe executor runs the test program on the target hardware with each input and collects hardware traces (HTraces). A hardware trace is a set of observable microarchitectural effects (like cache state or timing) caused by the test case execution. Traces are typically collected using side-channel techniques (e.g., Prime+Probe, Flush+Reload) or by reading performance counters.\n\nTo ensure that the measurements reflect the test case execution (rather than noise), the executor creates a controlled measurement environment by disabling interrupts, flushing caches, and repeating executions multiple times.\n\nThe `Executor` class (in `executor.py`) works through a kernel module (`executor_km/`) that performs measurements in kernel space. It provides the same interface as the model: `load_test_case()` and `trace_test_case()`.\n\n## 6. Trace Analysis\n\nThe analyzer compares contract traces (what should leak) with hardware traces (what actually leaked) to detect violations. Instead of directly comparing traces, it uses an equivalence class approach.\n\nHow it works:\n\n1. Group by contract: Inputs with identical CTraces form a ContractEqClass. According to the model, these inputs should be indistinguishable.\n2. Group by hardware: Within each ContractEqClass, inputs with similar HTraces form HardwareEqClasses. These inputs are actually indistinguishable on real hardware.\n3. Detect violations: If a ContractEqClass splits into multiple HardwareEqClasses, a violation has occurred. The model says the inputs should look the same, but hardware reveals differences between them.\n\nThis approach focuses on information leakage rather than exact trace values, and it essentially implements a non-interference check (see [Theoretical Foundations](../../topics/contracts.md)).\n\nThe `Analyser` class (in `analyser.py`) implements this logic in its `filter_violations()` method.\n\n## 7. Post-violation Analysis\n\nWhen Revizor detects a potential violation, it runs additional tests to filter out false positives. These tests modify execution parameters and verify the violation still occurs. See [post-violation tests](mini.md) for details.\n\nIf the violation survives all filters, Revizor reports it to the user and saves reproduction artifacts. The user can then use [minimization tools](../../howto/minimize.md) to simplify the test case and identify the root cause.\n\nThe post-violation logic is implemented in `Fuzzer.fuzzing_round()`, and the `FuzzLogger` class handles reporting.\n"
  },
  {
    "path": "docs/internals/code-structure.md",
    "content": "# Code Structure\n\nThe Revizor codebase is organized into the following main directories:\n\n```text\nrvzr/                         Main source code directory containing core fuzzing logic\n  ├── *.py                    Core modules that implement main fuzzing components\n  ├── tc_components/          Test case representation objects (code and data)\n  ├── model_unicorn/          Unicorn-based leakage model\n  ├── model_dynamorio/        DynamoRIO-based leakage model\n  ├── executor_km/            Kernel module that implements the hardware executor\n  ├── postprocessing/         Minimization utilities for contract counterexamples\n  └── arch/                   Architecture-specific implementations (x86/ and arm64/)\ntests/                        Unit and integration tests\ndocs/                         Documentation files\n```\n\nThe main entry point is `rvzr/cli.py`, which parses command-line arguments and initializes the `Fuzzer` object.\n\n"
  },
  {
    "path": "docs/internals/contributing/code-style.md",
    "content": "# Code Style\n\nPlease follow these coding standards when writing code for inclusion in Revizor.\n\n## Python\n\n* Unless otherwise specified, follow PEP 8. But remember that PEP 8 is only a guide, so respect the style of the surrounding code as a primary goal.\n* An exception to PEP 8 is our rules on line lengths. Don’t limit lines of code to 79 characters if it means the code looks significantly uglier or is harder to read. We allow up to 100 characters.\n* All files should be formatted using the `flake8` auto-formatter. Use all default settings except for the line width (`--max-line-length 100`)\n* The Python and C files use 4 spaces for indentation, and YAML uses 2 spaces.\n* The project repository includes an .editorconfig file. We recommend using a text editor with EditorConfig support to avoid indentation and whitespace issues.\n* Use underscores, not camelCase, for variable, function and method names (i.e. poll.get_unique_voters(), not poll.getUniqueVoters()).\n* Use InitialCaps for class names (or for factory functions that return classes).\n* In docstrings, follow PEP 257.\n\n## C\n\n* All files should be formatted using the `clang-format`. The settings are included into the `.clang-format` files in the directories with C files. Just run the formatter with: `clang-format -i *.c`\n\n## Misc\n\n* Remove import statements that are no longer used when you change code. flake8 will identify these imports for you. If an unused import needs to remain for backwards-compatibility, mark the end of with `# NOQA` to silence the flake8 warning.\n* Systematically remove all trailing whitespaces from your code as those add unnecessary bytes, add visual clutter to the patches and can also occasionally cause unnecessary merge conflicts. Some IDE’s can be configured to automatically remove them and most VCS tools can be set to highlight them in diff outputs.\n"
  },
  {
    "path": "docs/internals/contributing/general.md",
    "content": "# General Development Guidelines\n\n## Testing\n\nTo run automated tests you will need to install a few more dependencies:\n\n* [Bash Automated Testing System](https://bats-core.readthedocs.io/en/latest/index.html)\n* [mypy](https://mypy.readthedocs.io/en/latest/getting_started.html#installing-and-running-mypy)\n* [flake8](https://flake8.pycqa.org/en/latest/index.html)\n\nWith the dependencies installed, you can run the tests with:\n\n```bash\n./tests/runtests.sh\n```\n\nNote that some of the acceptance tests are microarchitecture-dependent.\nThese tests are labeled \"Detection\" (e.g., `\"Detection [spectre-type] Spectre V1; load variant\"`), and they may fail if the CPU under test does not have a given vulnerability.\nGenerally, if a few of these tests fail, it is not a problem, but if all of them (or a significant portion) fail, it indicates an issue with the fuzzer.\n\n## Submitting Patches\n\nTo submit a patch, use the following procedure:\n\n* Fork Revizor on github:\n\n    [https://docs.github.com/en/github/getting-started-with-github/fork-a-repo](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo)\n\n* Create a topic branch:\n\n```bash\ngit checkout -b my_branch\n```\n\n* Make sure all tests pass (see [Testing](#testing))\n* Make sure your code follows the guidelines in [Code Style](code-style.md)\n* Push to your branch\n\n```bash\ngit push origin my_branch\n```\n\n* Initiate a pull request on github:\n\n    [https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request)\n\n* Wait for the PR to get reviewed and merged\n"
  },
  {
    "path": "docs/internals/contributing/git.md",
    "content": "# Git Workflow Guidelines\n\n## Git Messages\n\nWe practice the following conventions for commit messages:\n\n```\n<scope>: [<type>] <subject>\n```\n\nWhere:\n\n* `<scope>`: The scope of the change.\n* `<type>`: The type of the change.\n* `<subject>`: A short description of the change.\n\n\n### Scopes\n\nThe following scopes are typical:\n\n\n| Scope       | Description                                                      |\n| ----------- | ---------------------------------------------------------------- |\n| `all`       | Changes that affect the entire project (e.g., major refactoring) |\n| `root`      | Root directory changes (e.g., readme, git, author list)          |\n| `fuzz`      | Changes to the core fuzzer algorithm.                            |\n| `cli`       | Changes to the command-line interface.                           |\n| `exec`      | Changes to the executor.                                         |\n| `model`     | Changes to the model.                                            |\n| `analyser`  | Changes to the analyser.                                         |\n| `mini`      | Changes to the postprocessor (i.e., minimizer).                  |\n| `code_gen`  | Changes to the program generator                                 |\n| `data_gen`  | Changes to the input generator                                   |\n| `tests`     | Changes to the tests                                             |\n| `isa`       | Changes to the ISA loader or to `get_spec` files                 |\n\nIf a commit covers several scopes, use the most relevant one.\n\nIf a commit targets a specific architecture (e.g., x86), add the architecture to the scope (e.g., `fuzz/x86`).\n\n### Types\n\nUse one of the following types:\n\n| Type     | Description                                                                   |\n| -------- | ----------------------------------------------------------------------------- |\n| `feat`   | A new feature.                                                                |\n| `fix`    | A bug fix.                                                                    |\n| `docs`   | Documentation changes.                                                        |\n| `chore`  | Changes to the build process or auxiliary tools.                              |\n| `ft`     | Fault tolerance changes (e.g., adding error handling or recovery mechanisms). |\n| `refact` | Refactoring of the codebase. This includes code style change.                 |\n| `perf`   | Performance improvements.                                                     |\n| `revert` | Reverts a previous commit.                                                    |\n\nIf possible, try to use only these types.\nIf you need to use a different type, please discuss it with a maintainer.\n\n## Git Branches\n\nWe practice the [git workflow](https://git-scm.com/docs/gitworkflows), with a few modifications.\n\n![branching workflow](../../assets/branches.png)\n\nWe use the following branches for graduation:\n\n* `main`: The latest release. This branch should always be stable, and it is the last branch to receive changes.\n* `main-fixes`: Commits that go in the next maintenance release. This branch is created from the last release branch.\n* `dev`: The development branch. This branch is the first to receive changes.\n\nCommits should be merged upwards:\n\n* `dev` -> `pre-release` -> `main`\n* In case of hot fixes, `main-fixes` -> `main` AND `main-fixes` -> `pre-release`\n\nFor working on unstable code (e.g., progress on features or bug fixes), use either forks or feature branches.\nUse forks if you are the only one working on the feature, and use a pull request to merge the changes back into the main repository.\nUse a feature branch if multiple people are working on the feature, in which case name the branch `feature-<name>` or `bugfix-<name>`, and make sure to branch from the `dev` branch.\n\nThe only exception is the `gh-pages` branch, which is used for the project's website.\nThis branch is used by automated tools and should never be used for development.\n"
  },
  {
    "path": "docs/internals/contributing/overview.md",
    "content": "# Guide to Contributing\n\nThis document provides an overview of how to contribute to the Revizor project.\n\n## What can I contribute?\n\nRevizor is an open-source project, and we welcome contributions of all kinds. You don't have to be an expert in hardware security or fuzzing to contribute! Even small contributions are valuable.\n\nHere are some ways you can help:\n\n* :fontawesome-solid-bug: Report Issues: The easiest way to contribute is by reporting issues you encounter while using Revizor. Try following the introductory [guides and tutorials](../../intro/start-here.md), and if you find any issues, bugs, or unclear documentation, please report them on our [GitHub Issues page](https://github.com/microsoft/side-channel-fuzzer/issues).\n* :fontawesome-solid-pencil: Improve Documentation: You can also contribute by improving the documentation. If you find any gaps, outdated information -- even typos -- feel free to submit a pull request with your improvements.\n* :fontawesome-solid-code: Code Contributions: If you're interested in coding, you can contribute new features, fix bugs, or enhance existing functionality. Check out the [issue tracker](https://github.com/microsoft/side-channel-fuzzer/issues) for open issues and feature requests.\n* :fontawesome-solid-lightbulb: New Features: Finally, if you have expertise in hardware security, fuzzing, or related areas, consider contributing new features and enhancements to Revizor (see [ideas for contributions](#ideas-for-contributions) if you need inspiration).\n\n## Reporting Bugs and Issues\n\nTo report a bug or an issue, please use the [GitHub Issues page](https://github.com/microsoft/side-channel-fuzzer/issues).\n\nIf you're reporting a simple bug, it is sufficient to provide a small description of the problem and the environment in which it occurred (Revizor version, target architecture, OS, etc.).\n\nFor more complex issues, especially those related to the fuzzing process, also include the configuration file you've used and the command-line arguments.\n\nThe recommended report template is as follows:\n\n```\n## Description\nA clear and concise description of what the bug is.\n\n## To Reproduce\n1. Go to '...'\n2. Run '...'\n3. See error\n\n## Expected behavior\nA clear and concise description of what you expected to happen.\n\n**Environment**\n- Revizor version:\n- Architecture:\n- OS:\n...\n\n## Additional context\nAdd any other context about the problem here.\n\n## Attachments\n- Configuration file used:\n- Command-line arguments:\n- Logs or error messages:\n```\n\n## Submitting Patches\n\nTo submit a patch, be it to the code or to the documentation, use the following procedure:\n\n* [Fork Revizor on github](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo)\n* Create a topic branch (`git checkout -b my_branch`)\n* Make and commit your changes in the new branch\n* Make sure all tests pass (`./tests/runtests.sh <target_ISA>`) and that the code is formatted accordingly to the [Code Style](code-style.md) guidelines.\n* Push to your branch (`git push origin my_branch`)\n* [Initiate a pull request on github](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request)\n* Wait for the PR to get reviewed and merged\n\n#### Contributor License Agreement and Code of Conduct\n\nMost contributions require you to agree to a\nContributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us\nthe rights to use your contribution. For details, visit [https://cla.opensource.microsoft.com](https://cla.opensource.microsoft.com).\n\nWhen you submit a pull request, a CLA bot will automatically determine whether you need to provide\na CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions\nprovided by the bot. You will only need to do this once across all repos using our CLA.\n\nThis project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).\nFor more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or\ncontact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.\n\n## <a name=\"ideas-for-contributions\"></a> Ideas for Contributions\n\nIf you're looking for ideas and inspiration on how you can meaningfully extend and improve Revizor, here are some suggestions:\n\n---\n\n#### Add Support for New Instructions\n\nThere are quite many specialized instructions that Revizor does not yet fully support. Implementing support for these instructions can help improve the coverage and effectiveness of the fuzzer. As a bonus, you might discover new type of information leaks in the process.\n\nThese include, but are not limited to:\n\n* Floating-point instructions (either x87 or SSE/AVX)\n* Segment-based memory accesses or instructions that manipulate segment registers\n* Complex control-flow instructions (e.g., `call`, `ret`, indirect jumps)\n* MMX instructions\n\n---\n\n#### Make Generators Smarter\n\nBoth code and data generators can be improved in various ways to produce more effective test cases. The bar is fairly low here, as current generators are fully random.\n\nIdeas include:\n\n* Bias generators to produce values that are more likely to trigger edge cases (e.g., boundary values, special bit patterns)\n* Implement ability to control the frequency of certain instruction types in generated programs\n* Implement mutation-based generation strategies that modify existing test cases to explore new behaviors\n\nIf you decide to work on any of these or have your own ideas, please discuss them with us first by reaching out on [GitHub Discussions](https://github.com/microsoft/side-channel-fuzzer/discussions) or opening a draft pull request. This way we can ensure that your efforts align with the project's goals and avoid duplication of work.\n\n---\n\n#### Improve Reporting Tools\n\nThe current logging and debugging tools in Revizor are relatively basic. Enhancing these utilities for better readability and usability can significantly aid users in understanding fuzzing results and diagnosing issues.\n\nIdeas include:\n\n* Refactor the logging module to output a live dashboard, similar to what is seen in other fuzzers like AFL or libFuzzer\n* Improve the debugging output to improve readability when debugging models\n\n---\n\n#### Implement New Measurement Modes\n\nRevizor currently collects side-channel observations primarily through cache measurements or by recording the execution time of test programs. Implementing additional measurement modes can help uncover new types of leaks and improve the fuzzer's effectiveness.\n\nNew measurement modes could include:\n\n* Instruction cache measurements (e.g., using I-cache side channels)\n* Contention-based measurements (e.g., measuring resource contention on the memory bus)\n* Performance counter-based measurements (i.e., reading directly from CPU performance counters)\n\nBeyond that -- if you're brave enough -- you could attempt implementing concurrent measurement modes, for example, by running each actor in a test case on a different core or SMT thread. This is a complex task that requires significant changes to executor, and might require new techniques for dealing with non-determinism and imprecise synchronization. But if successful, it could open up new avenues for discovering cross-core or cross-thread leaks. You might even make a paper out of it.\n\n---\n\n#### Implement Coverage-Guided Fuzzing\n\nAnother interesting avenue for exploration is implementing proxy-based coverage metrics. Currently, Revizor runs in a fully random mode, without any feedback being collected in the process of fuzzing. Implementing coverage-guided fuzzing techniques could significantly improve the efficiency of the fuzzer.\n\nIdeas include:\n\n* Proxy-based coverage metrics, where an emulator or a simulator is used as a proxy for the CPU coverage. That is, the fuzzer would run test cases on an emulator, collect the software coverage information (which edges of the emulator code were executed), and use that to guide the generation of new test cases.\n* Specification-based coverage metrics, where a formal specification of the instructions (e.g., ARM Architecture Specification Language) is used to determine edge cases in the execution of instructions. The fuzzer would then aim to cover all possible behaviors defined in the specification.\n\n\n"
  },
  {
    "path": "docs/internals/index.md",
    "content": "# Developer Documentation\n\nThis section provides technical documentation for developers contributing to Revizor.\n\n## Development Guidelines\n\n- [Guide to Contributing](contributing/overview.md): Overview of the contribution process and resources\n- [General Guidelines](contributing/general.md): Development environment setup, testing procedures, contribution workflow\n- [Code Style](contributing/code-style.md): Formatting conventions for Python and C code, naming conventions\n- [Git Workflow](contributing/git.md): Branch management, commit message format, merge procedures\n\n## Architecture and Modules\n\n- [Code Structure](code-structure.md): Organization of the source code directory and key modules\n- [Overview](architecture/overview.md): High-level system architecture and component interaction\n    - [Orchestration](architecture/fuzz.md): Main fuzzing loop and coordination between components\n    - [ISA Specification](architecture/isa.md): Instruction set architecture definitions and JSON-based specification format\n    - [Test Case Code Generation](architecture/code.md): Program generation algorithm and relevant classes\n    - [Test Case Data Generation](architecture/data.md): Data generation algorithm and relevant classes\n    - [Hardware Tracing](architecture/exec.md): Execution of test cases on the target HW and hardware trace collection\n    - [Contract Tracing](architecture/model.md): Leakage modeling and contract trace generation (high-level overview; implementation details in backend-specific pages)\n    - [Trace Analysis](architecture/analysis.md): Comparison of contract and hardware traces to detect violations\n    - [Minimization](architecture/mini.md): Post-detection reduction of test cases to minimal reproducing examples\n    - [Logging](architecture/logging.md): Logging infrastructure and debugging facilities\n\n## Contract Modeling Backends\n\nRevizor supports two different backends for contract-based leakage modeling. They are documented in the following pages:\n\n- [Unicorn Backend](model-backends/model-unicorn.md): Backend based on the Unicorn CPU emulator\n- [DynamoRIO Backend](model-backends/model-dr.md): Backend based on the DynamoRIO dynamic binary instrumentation engine\n"
  },
  {
    "path": "docs/internals/model-backends/model-dr.md",
    "content": "# DynamoRIO-based Model Backend\n\nThis document describes the DynamoRIO-based model.\nAs any other model, this backend is responsible for collecting contract traces for generated test cases.\n\n## Design Overview\n\nThis backend is composed of several parts:\n\n* The Python adapter (`rvzr/model_dynamorio/model.py`) is responsible for receiving a test case from Revizor, transforming it into a format that can be executed by the backend, triggering the backend to execute the test case, and returning the collected contract traces to Revizor.\n* The Test Case Loader (`rvzr/model_dynamorio/adapter.c`) is a C program that loads a test case program and a batch of inputs into its memory, and executes the test case program with each input in a sequence.\n* The DynamoRIO components (`rvzr/model_dynamorio/backend`) are executed together with the test case loader, and they instrument the loader binary to collect contract traces.\n\nThese components can be roughly divided into the instrumentation-time components that are responsible for modifying the binary, and execution-time components that implement the model logic (i.e., the contract).\n\n[![DynamoRIO-based Model Backend](../../assets/dr-model.png)](../../assets/dr-model.png)\n\n## Python Adapter\n\nRevizor communicates with the backend through a Python adapter (`rvzr/model_dynamorio/model.py:DynamoRIOModel`).\n\nAt the beginning of the fuzzing process, Revizor configures the backend by calling `configure_clauses` method.\nThis configuration will be later passed down to the backend when the test case is executed.\n\nDuring the fuzzing process, Revizor sends test cases to the backend by calling `load_test_case` method, and then triggers the backend to execute the test case by calling `trace_test_case` method.\nInternally, `trace_test_case` will call the backend to execute the test case and collect the contract traces.\nThe adapter will then parse the traces and return them back to the caller.\n\nThe `trace_test_case` method implements the following algorithm:\n\n- Convert test case program and inputs into RCBF and RDBF files, respectively\n- For each input, call the test case loader with the RCBF and RDBF files. Attach the DynamoRIO backend to the call so that the binary instrumentation is performed:\n```shell\n~/.local/dynamorio/drrun -c ~/.local/dynamorio/libdr_model.so --tracer <observation-clause> -- ~/.local/dynamorio/adapter <rcbf> <rdbf>\n```\n- Parse contract traces from the backend and convert them into `CTrace` objects\n- Return the list of collected `CTrace` objects to the caller (usually, `fuzzer.py`)\n\n## Test Case Loader\n\nSince the test cases produced by Revizor are raw binaries, they cannot be directly executed (e.g., they don't have `libc` linked).\nThe test case loader (`rvzr/model_dynamorio/adapter.c`) is a simple C program that fixes this issue by providing a wrapper around the test case binary.\n\nThe loader implements the following algorithm:\n\n- Receive the test case binary and an input from the Python adapter via CLI arguments\n- Load the test case binary and the input into dedicated memory regions\n- Print the addresses of the test case and input memory regions (for trace normalization)\n- Initialize registers based on the input\n- Jump to the test case binary entry point\n- Return\n\n## DynamoRIO Tool\n\nThe DynamoRIO tool (`rvzr/model_dynamorio/backend`) is responsible for instrumenting the test case loader binary and collecting contract traces.\n\n### Implementation Overview\n\nAll instrumentation logic is implemented as a DynamoRIO client. In particular,\n`model.cpp` contains the event callbacks that are executed at instrumentation time,\nwhile `dispatcher.cpp` contains the body of the callbacks that are inserted\nby the DR client and are executed before every instruction at runtime. Finally, the `Dispatcher` object holds the state that is\nshared between instrumentation-time callbacks and execution-time callbacks.\n\nThe following figure provides an overview of the implementation.\n\n[![DynamoRIO Instrumentation Overview](../../assets/dr-instrumentation.png)](../../assets/dr-instrumentation.png)\n\n1. `dr_client_main()` is responsible of installing the initial instrumentation callbacks to hook all relevant DR events (`module_load`, `bb_translation`, exceptions and the `exit` event)\n2. `dr_client_main()` also sets the name of the function to instrument (passed by `cli.cpp`)\n3. on `module_load`, the instrumentation checks for the presence of the target function in the loaded module. If found, the callback adds a `drwarp` callback (`event_instrumentation_start`) which will be executed at the start of the target function\n4. once a call to the target function is found, the `event_instrumentation_start` will save the return address in a global object (`instrumented_func`) and call `start()` on the dispatcher\n5. from that moment on, every translated basic block is instrumented by our client, in particular:\n    - a `dispatch_callback()` is inserted before every instruction\n    - at the function exit point (i.e. the previously saved return addres) an `exit_callback` is inserted\n6. these callbacks are executed at runtime with the following effects:\n    - the `dispatch_callback()` implements the observation and execution clauses (see next section)\n    - the `exit_callback()` checks the current speculation state before exiting:\n        - speculative exits cause a rollback\n        - architectural exit causes the instrumentation to stop\n\nFinally, exceptions and the `exit` event are also forwarded to the Dispatcher:\n\n- Speculative **exceptions** will cause a rollback, while architectural ones are forwarded to the target program\n- The **exit** event stops instrumentation and flushes all logs (in case the exit callback has not been executed architecturally)\n\n### Instrumentation Components\n\nThe instrumentation components modify the binary of the test case loader by adding a call to the function `dispatch_callback` before every instruction in the binary (or more specifically, every instruction in the `test_case_entry` function of the loader).\n\nThe tool interacts with DynamoRIO through the `model.cpp` module.\nThis module registers an event for entering the `test_case_entry`, which triggers the flush of the internal DynamoRIO code fragment cache and the start of instrumentation.\nThe module also registers an event for every instruction in the `test_case_entry`, and the event in turn calls the `Dispatch::instrument_instruction()`. Finally, exceptions are hooked and passed to the dispatcher through `Dispatch::handle_exception()`, which can decide to either handle the signal (e.g. on speculative paths) or forward it to the test case (e.g. architectural exceptions).\n\nThe `Dispatch` class implements the actual instrumentation logic.\nWhen the `instrument_instruction()` method is called, it inserts a clean call to the `dispatch_callback` function before the instruction.\nThe call receives the PC and opcode of the instruction as arguments.\nDynamoRIO also automatically saves the complete register state before the call, thus making it available to `dispatch_callback`.\n\n### Execution-Time Components\n\nThe execution-time components are responsible for implementing the contract logic, and are triggered by the `dispatch_callback` function.\nAt the current state of the backend, the dispatch callback invokes only two classes, Tracer and Speculator, that implement the observation and execution clauses, respectively. Optionally, each component can log additional events, e.g.\nspeculation rollbacks or the current register state, through a shared `Logger` component.\n\nSubclasses of `TracerABC` record contract-relevant information via `observe_instruction` and `observe_mem_access` methods.\nE.g., `TracerCT` implements `CT` observation clause by recording the PC of instructions upon `observe_instruction` and the address of memory accesses upon `observe_mem_access`. Currently, `observe_exception` simply adds a special entry to the trace to indicate that the program ended due to an (architectural) exception.\n\nSubclasses of `SpeculatorABC` implement the contract speculation logic.\nE.g., `SpeculatorCond` implements `speculate_instruction`.\nWhen this method is called with a branch instruction, the class takes a checkpoint of the process state, flips the branch condition (i.e., modified `FLAGS` register), and continues the execution.\nDuring the simulated speculation, each call to `speculate_instruction` counts the number of executed instructions, and when the number reaches the limit (e.g., 256), the class restores the checkpoint and continues the execution from the original state. (Actually, the algorithm is more complex, but this is the general idea.)\n\nWhen the instrumentation ends (according to `model.cpp`), the tracer's `tracing_finalized` method is called, during which any remaining traces are flushed into the trace file, together with an \"End Of Trace\" entry.\nThe Python adapter will then read the trace file, decode it, and return the corresponding CTrace to Revizor.\n\n### Standalone Usage\n\nThe DR tool can be used as a standalone tool to collect the runtime trace of any program, independently from the rest of Revizor's infrastructure.\n\nA typical usage is for example:\n\n```shell\n~/.local/dynamorio/drrun -c ~/.local/dynamorio/libdr_model.so --tracer <observation-clause> --speculator <speculation-clause> -- ls /dev/null\n```\n\nBy default, this will instrument `ls` starting from `__libc_start_main` until the end of the program, run it with `/dev/null` as an argument, and generate a binary file called `rvzr_trace.dat` that contains the collected trace. Other flags can be printed using `~/.local/dynamorio/drrun -c ~/.local/dynamorio/libdr_model.so -h`\n\nThe trace file location can be changed by adding `--trace-output <PATH>`. Additionally, the tool can also dump the trace in human-readable format to STDOUT using the `--print-trace` flag.\n\nTo decode and analyze the trace file, downstream tools should always use the `TraceDecoder` class provided by `trace_decoder.py`. For internal usage, this module also provides a simple entrypoint for trace printing:\n\n```bash\npython3 trace_decoder.py rzvr_trace.dat\n```\n\n#### Debugging\n\nAttaching a debugger like GDB to the DR tool might not always be the best debugging option, as the program has three separate states:\n\n1. the state of the program being instrumented (e.g. `ls`)\n2. the state of the DR client (`libdr_model.so`) instrumentation\n3. the state of DynamoRIO itself (`drrun`)\n\nMore information about debugging DR clients can be found [here](https://dynamorio.org/page_debugging.html).\n\nFor our instrumentation, other (possibly simpler) options are available:\n\n1. **Inspecting Debug Traces**: the DR tool can optionally log extra information, e.g. the complete state of the register file before each instruction, each value being read and written to memory, and speculation events like checkpoints are rollbacks, in a separate debug trace:\n    - This option can be enabled using `--log-level <N>`\n    - By default, the tool will dump debug entries to `rzvr_dbg_trace.dat` in binary format; to change the path of the debug trace file use `--debug-output <PATH>`\n    - `--print-debug-trace` can be used to pretty-print debug entries to STDOUT during execution\n    - `trace_decoder.py` also provides a decoder for debug entries\n    - **WARNING:** debug traces can become very big, especially for nested speculation\n2. **Running DynamoRIO with logging**: DynamoRIO can also produce logs (see DR documentation):\n\n```\n~/.local/dynamorio/drrun -debug -loglevel 3 -c ~/.local/dynamorio/libdr_model.so --tracer <observation-clause> --speculator <speculation-clause> -- ls /dev/null\n```\n\n"
  },
  {
    "path": "docs/internals/model-backends/model-unicorn.md",
    "content": "# Unicorn Backend\n\nUnicorn backend architecture:\n\n```text\nUnicornModel (main orchestrator)\n  ├─ UnicornTracer            Records observations (PC, memory addresses, etc.)\n  ├─ UnicornSpeculator        Simulates speculative execution\n  ├─ UnicornTaintTracker      Tracks data flow for boosted input generation\n  ├─ ExtraInterpreter         Handles features Unicorn doesn't support\n  └─ InstructionCoverage      Tracks which instructions were tested\n```\n\nKey components:\n\n- `UnicornModel`:   Manages the emulator and coordinates components through hooks on instruction and memory events.\n- `UnicornTracer`:   Implements the observation clause of the contract. Different tracers record different information (program counters, memory addresses, data values).\n- `UnicornSpeculator`:   Implements the speculation clause using checkpoint-rollback mechanisms. When speculation triggers (branch misprediction, CPU exception), it saves state and executes speculatively up to a window limit (default 250 instructions). It rolls back on serializing instructions or window expiration.\n- `UnicornTaintTracker`:   Performs dynamic taint analysis to identify which input bytes affect the contract trace. Used for boosted input generation.\n\n"
  },
  {
    "path": "docs/intro/01-overview.md",
    "content": "# Revizor at a Glance\n\n## What is Revizor?\n\nRevizor is a security-oriented fuzzer that detects microarchitectural information leaks in CPUs—the vulnerabilities behind attacks like Spectre and Meltdown. It tests processors \"blindly,\" requiring no prior knowledge of specific flaws or hardware internals. Instead, it compares actual CPU behaviour against a [*leakage contract*](../glossary.md#speculation-contract-aka-leakage-contract): a specification defining known sources of information leakage. Any discrepancy reveals a potential vulnerability.\n\n## What Problems Does Revizor Solve?\n\nModern CPUs achieve their speed through speculative execution, out-of-order processing, complex caching, and other microarchitectural optimizations. These optimizations create side channels—timing variations, cache-state changes, buffer contentions—that can leak sensitive data. Such leaks are notoriously difficult to catch: they cause no crashes, depend on precise timing, and emerge only under specific conditions. Revizor automates the detection of these elusive side-channel leaks.\n\nSpecifically, Revizor addresses several key challenges:\n\n* **Automated discovery**: Finding side-channel attacks manually demands deep (often undocumented) microarchitectural knowledge and extensive trial-and-error. Revizor automates this process, systematically exploring the CPU's behaviour by probing the microarchitecture with lots of automatically generated test cases.\n* **Variant analysis**: Side-channel vulnerabilities spawn many variants. Revizor can search for new attack vectors that might bypass existing patches.\n* **Validation of mitigations**: Vendor patches meant to close side channels have sometimes proven incomplete. Revizor verifies whether fixes actually eliminate the leakage.\n\n## Quick Example: Detecting Spectre V1\n\nTo illustrate how Revizor works, consider a simple fuzzing campaign that will lead to a detection of a known vulnerability in most modern CPUs, namely Spectre V1.\n\n!!! info \"Prerequisites\"\n    Before running this example, ensure you have Revizor installed and set up correctly. Follow the [Installation Guide](02-install.md) if you haven't done so already.\n\nWe will use a configuration file in `demo/detect-v1.yaml`. This config file tells Revizor to test a small subset of x86-64 ISA (arithmetic instructions + conditional branches) against a contract that states that the CPU should not speculate and should only leak information about loads, stores, and the program counter. As most modern CPUs implement branch prediction, we expect to see a violation of this contract.\n\nRun the fuzzer with the following command:\n\n```bash\n$ rvzr fuzz -s base.json -n 1000 -i 100 -c demo/detect-v1.yaml -w ./\n```\n\nAfter a short while, you should see output similar to this:\n\n```\nINFO: [prog_gen] Setting program_generator_seed to random value: 562112\n\nINFO: [fuzzer] Starting at 14:00:51\n13    ( 2%)| Stats: Cls:100/100,In:200,R:9,SF:5,OF:6,Fst:2...\n\n================================ Violations detected ==========================\nViolation Details:\n\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:71  | ID:171|\n-----------------------------------------------------------------------------------\n^................^..^.....^.....................^.....^......... | 599    | 0     |\n^...................^.....^..................................... | 28     | 23    |\n^................^..^.....^.......^...^......................... | 0      | 604   |\n\n\n================================ Statistics ===================================\n\nTest Cases: 14\nInputs per test case: 200.0\nViolations: 1\nEffectiveness:\n  Total Cls: 100.0\n  Effective Cls: 100.0\nDiscarded Test Cases:\n  Speculation Filter: 5\n  Observation Filter: 6\n  Fast Path: 2\n  Max Nesting Check: 0\n  Tainting Check: 0\n  Early Priming Check: 0\n  Large Sample Check: 0\n  Priming Check: 0\n\nDuration: 52.1\nFinished at 14:01:43\n```\n\nThis message indicates that Revizor found a [violation](../glossary.md#violation) of the specified\ncontract, and the tool will store the corresponding\n[violation artifact](../glossary.md#violation-artifact-aka-contract-counterexample) in\n`./violation-<timestamp>/`.\n\nWhat happened here is that Revizor generated a series of random\n[test programs](../glossary.md#test-case-program), executed them on the target CPU and the\nreference model that implement the contract, collected the side-channel observations on both sides,\nand compared them. In this case, one of the generated test programs produced two different\n[hardware traces](../glossary.md#hardware-trace-htrace) for two different inputs while the model\n(contract) produced the same trace for both inputs. This discrepancy indicates that the CPU leaked\ninformation through microarchitectural side channels in a way that violates the specified contract.\n\nThe corresponding program and the [inputs](../glossary.md#test-case-data-aka-test-case-input) are\nstored in the violation artifact (`./violation-<timestamp>/`), and it will contain an assembly file\n`program.asm` that surfaced a violation, a sequence of inputs `input_*.bin` to this program, and\nsome details about the violation in `report.txt`.\n\nIf we inspect the assembly code in `program.asm` and do an analysis of the violation, we will most likely find that it is a gadget that implements a typical Spectre V1 pattern: a conditional branch and a speculative memory access that leaks data through the cache. (This is a most likely outcome because the pattern is statistically very common for the given configuration). For example, the program may look like this (simplified for illustration):\n\n```assembly\n.section .data.main\n...\njnp .bb_0.1  // conditional branch\njmp .exit_0\n.bb_0.1:\n    ...\n    or byte ptr [r14 + rcx], al  // data-dependent memory access\n    ...\n.exit_0:\n.test_case_exit:\n```\n\n!!! info \"On violation analysis\"\n    This example was intentionally chosen to have a straightforward output that directly corresponds to a known vulnerability pattern. In practice, analyzing violations can be more complex, especially for novel or less understood leaks. We won't go into the details of the analysis here as it is a relatively complex topic; refer to the [this guide](../howto/root-cause-a-violation.md) if you want to dive into the details.\n\nThe power of this approach is that Revizor doesn't need to know the specific vulnerability it's looking for. It simply tests whether the CPU matches the expected security specification. When it finds a discrepancy, that's a potential vulnerability worth investigating.\n\n## What's Next?\n\nNow that you understand what Revizor is and what it does, here are your next steps:\n\n* **Dive Deeper into Concepts**: For a more detailed explanation of the information flow analysis used in Revizor, the concepts of leakage contracts, and other related topics, see the [Core Concepts Guide](03-primer.md).\n* **Follow a Tutorial**: Our [step-by-step tutorial series](./04-tutorials.md) guides you through detecting your first vulnerability, understanding the results, and designing effective fuzzing campaigns.\n* **Explore the Glossary**: Familiarize yourself with key terms and definitions in the [Glossary](../glossary.md) to better understand Revizor's terminology (we have quite a few unique terms!).\n* **Get Help**: If you run into issues or have questions, visit our [FAQ](../faq/general.md) for common questions, or [ask a question](../howto/ask-a-question.md) to reach out to the community.\n"
  },
  {
    "path": "docs/intro/02-install.md",
    "content": "# Installation\n\n**Warning**:\nRevizor runs randomly-generated code in kernel space.\nThis means that a misconfiguration (or a bug) can crash the system and potentially lead to data loss.\nMake sure you're not running Revizor on a production machine, and that you have a backup of your data.\n\n### 1. Requirements\n\n**Hardware**: x86-64 or ARM64 CPU. Specifically:\n\n* All Intel and AMD x86-64 CPUs are supported.\n* Some ARM CPUs are also supported, namely Microsoft Cobalt and Raspberry Pi. Other ARM CPUs may work, but are not officially supported.\n\n**No virtualization**:\nYou will need a bare-metal OS installation.\nTesting from inside a VM is not supported.\n\n**OS**: The target machine has to be running Linux v4.15 or later.\n\n### 2. Python Package\n\nThe preferred installation method is using `pip` within a virtual environment.\nThe python version must be 3.9 or later.\n\n```bash\nsudo apt install python3.9 python3.9-venv\n/usr/bin/python3.9 -m pip install virtualenv\n/usr/bin/python3.9 -m virtualenv ~/venv-revizor\nsource ~/venv-revizor/bin/activate\npip install revizor-fuzzer\n```\n\n### 3. Executor\n\nIn addition to the Python package, you will need to build and install the executor, which is a kernel module.\n\n```bash\n# building a kernel module require kernel headers\nsudo apt-get install linux-headers-$(uname -r) linux-headers-generic\n\n# get the source code\ngit clone https://github.com/microsoft/side-channel-fuzzer.git\n\n# build executor\ncd side-channel-fuzzer/rvzr/executor_km\nmake uninstall  # the command will give an error message, but it's ok!\nmake clean\nmake\nmake install\n```\n\n### 4. (Optional) DynamoRIO Backend\n\nIf you want to use the DynamoRIO-based model, it has to be installed separately:\n\n```bash\n# install dependencies\nsudo apt-get install cmake g++ g++-multilib doxygen git zlib1g-dev libunwind-dev libsnappy-dev liblz4-dev\n\n# install DynamoRIO and the model\nmake -C rvzr/model_dynamorio\n\n# check installation\n~/.local/dynamorio/drrun -c ~/.local/dynamorio/libdr_model.so --list-tracers -- ls\n# expected output:\n#   ct\n#   ...\n#   /dev/null\n```\n\n### 5. Download ISA spec\n\n```bash\nrvzr download_spec -a x86-64 --extensions ALL_SUPPORTED --outfile base.json\n\n# Alternatively, use the following command to include system instructions;\n# however, mind that testing these instructions may crash the system if misconfigured!\n# rvzr download_spec -a x86-64 --extensions ALL_AND_UNSAFE --outfile base.json\n```\n\n### 6. Test the Installation\n\nTo make sure that the installation was successful, run the following command:\n\n```bash\n./tests/quick-test.sh\n\n# The expected output is:\nDetection: OK\nFiltering: OK\n```\n\nIf you see any other output, check if the previous steps were executed correctly.\nIf you still have issues, please [open an issue](https://github.com/microsoft/side-channel-fuzzer/issues).\n\n\n### 7. (Optional) System Configuration\n\nExternal processes can interfere with Revizor's measurements.\nTo minimize this interference, we recommend the following system configuration:\n\n* Disable Hyperthreading (BIOS option);\n* Disable Turbo Boost (BIOS option);\n* Boot the kernel on a single core (add `-maxcpus=1` to [Linux boot parameters]((https://wiki.ubuntu.com/Kernel/KernelBootParameters))).\n\nIf you skip these steps, Revizor may produce false positives, especially if you use a low value for [`executor_sample_sizes`](../ref/config.md#executor-configuration) for measurements.\nHowever, a large sample size (> 300-400) usually mitigates this issue.\n"
  },
  {
    "path": "docs/intro/03-primer.md",
    "content": "# Primer: Speculation Contracts and Model-Based Relational Testing\n\nBelow is a brief primer on the theoretical foundations of speculation contracts and model-based relational testing—concepts that underlie the Revizor tool. This primer provides a high-level overview of the topic, introducing the concepts of noninterference, speculation contracts, and model compliance.\n\nThis document is intended for those new to the topic, particularly people without a background in information-flow analysis. For a more detailed and technical explanation, refer to the [original contracts paper](https://arxiv.org/pdf/2006.03841).\n\n## Information-Flow Properties\n\nWe will start with the basics: the concepts of confidentiality and\n[noninterference](../glossary.md#noninterference), which are fundamental to understanding how\n[speculation contracts](../glossary.md#speculation-contract-aka-leakage-contract) work.\n\nTraditionally, security mechanisms like access control and encryption have focused on protecting\ndata at rest or in transit. However, these mechanisms do not address the problem of\n[**information flow**](../glossary.md#information-flow) within a system.\nFor example, consider a program that reads a secret input and then writes it to a public output, such as a web server that logs failed login attempts along with the username and masked password entered. Even if the program is secure in the sense that it does not allow unauthorized access to the secret data, it may still leak the secret through its public output, such as logging \"User admin failed login with password starting with 'P@ss'\" — revealing partial information about the secret password. This is where **information-flow security** comes into play.\n\nInformation-flow security is concerned with how data moves through a computation and how it can be observed by an attacker. The goal is to ensure that secret information does not leak to observers who are unauthorized to access it. An **end-to-end confidentiality policy** might be stated as: *“No secret input data can be inferred by an attacker through observations of system output.”* In other words, even if an adversary can see all public outputs of a computation, they should learn nothing about the secret inputs.\n\n**Information-flow properties** generally classify program variables or inputs/outputs into security levels (e.g., `Secret` and `Public`). The key property for confidentiality is that *no information flows from Secret to Public.* But how can information flow? There are two primary routes:\n\n- **Explicit flows:** These occur when confidential data is directly assigned or passed into a public variable or output. For example, in code, writing `public = secret` is an explicit flow from a secret variable to a public variable (an obvious violation of confidentiality). Any mechanism that directly transfers the bits of a secret into a publicly observable sink is an explicit flow. Such flows are usually straightforward to detect.\n\n- **Implicit flows:** These occur indirectly, through the control structure of the program. An implicit flow arises when the *control path* taken by a program (e.g., which branch of an `if` or how many loop iterations) depends on a secret, thereby implicitly leaking information.\n\n=== \"Example 1: Implicit Flow\"\n\n:   Consider this pseudocode example:\n\n    ```c\n    if (Sec == 0) {\n        Pub = 0;\n    } else {\n        Pub = 1;\n    }\n    ```\n\n    Here `Sec` is a secret input and `Pub` is a public output. There is no direct assignment of `Sec` to `Pub`. However, an observer of `Pub` can deduce information about `Sec`. In fact, this program sets `Pub` to 0 if `Sec` was 0; otherwise, it sets `Pub` to 1—effectively copying the one-bit information “is Sec zero?” into `Pub`. This is an implicit flow of information from `Sec` to `Pub` through the control structure (the `if` condition on `Sec`).\n\n\n## Noninterference: Definition and Examples\n\n**Noninterference** is a formal property that captures the idea of perfect confidentiality: changes in secret data have *no observable effect* on public outputs. This property can be formalized as: *\"a system is noninterferent if variations in Secret inputs cause no differences in Public outputs\"*. Equivalently, confidential inputs do not interfere with the publicly visible state of the system.\n\nTo make this more concrete, imagine we run a program twice with two different secret inputs but the same public inputs. If **no attacker can distinguish** the two runs by observing anything public, then the program satisfies the noninterference property. The “attacker” here is assumed to have complete access to all public outputs, which are formalized as a function `PublicOut`:\n\n```\noutput = PublicOut(Sec, Pub)\n```\n\nNoninterference essentially demands that for any two secrets `Sec1` and `Sec2` and any public input `Pub`, the program’s behavior from an attacker’s perspective is identical when run on `(Sec1, Pub)` versus `(Sec2, Pub)`:\n\n=== \"Definition 1: Noninterference\"\n:   A program `P` is noninterferent if, for all<br>public inputs `Pub` and all pairs of secret inputs `Sec1`, `Sec2` it holds that <br>`PublicOut(P, Sec1, Pub) = PublicOut(P, Sec2, Pub)`.\n\nHere are some examples to illustrate this principle:\n\n=== \"Example 2: Interfering program\"\n\n:   Suppose our program simply copies a secret to output:\n\n    ```c\n    void copy(int* sec, int* output) {\n        *output = *sec;\n    }\n    ```\n\n:    Running it with two different secrets clearly yields different public outputs (e.g., `output` becomes 5 in one run and 7 in another). An attacker would distinguish these runs, so the program is **not** noninterferent—it blatantly leaks information.\n\n---\n\n=== \"Example 3: Noninterfering program\"\n\n:   A trivial example of a noninterferent program is one that produces no output dependent on the secret. For instance:\n\n    ```c\n    void assign_zero(int* sec, int* output) {\n        *output = 0;\n    }\n    ```\n\n:    This program ignores secret `sec` entirely and always sets the public output `output` to 0. No matter what the secret input is, the public output is constant (0), so an attacker gains no information about `sec`. Indeed, any two runs are indistinguishable (both runs output 0). This satisfies noninterference (albeit by doing nothing useful with the secret).\n\n---\n\n=== \"Example 4: Allowed benign dependency\"\n\n:   It is possible for a program to use secret data internally yet still be noninterferent as long as the final public outputs don’t reveal those secrets. For instance:\n\n    ```c\n    void mask_secret(int* sec, int* output) {\n        int temp = *sec;\n        temp = temp * 0;   // multiply secret by 0\n        *output  = temp;\n    }\n    ```\n\n:    Here the program *did* read the secret (`sec`) and even manipulated it, but it “washed out” the secret by multiplying by 0. The value assigned to `output` is always 0. From an external view, this is just like the previous example—no dependence of `output` on `sec`. Noninterference is concerned only with *what can be observed by the attacker*, not with whether the program internally used the secret. As long as any use of the secret eventually has no effect on outputs, the policy holds.\n\n:   Naturally, this example is not useful either, as it does nothing with the secret. In practice, however, there are techniques to ensure noninterference while still making use of secret data for useful computations. We won't go into these techniques here as they are beyond the scope of this primer.\n\n---\n\nOne important insight is that noninterference is relative to a given specification of what is “observable.” If you consider only the functional outputs as observable, a program might be noninterferent in that model. But if in reality the attacker can observe more (e.g., the execution time of a program), then the program that was secure in theory might be insecure in practice. This leads us to examine how *side channels* break the assumptions of basic noninterference.\n\n## Beyond Direct Outputs: Side Channels\n\nThe original works on information-flow properties focused on direct outputs of a program (e.g., writing to a file or a network socket). However, in practice, attackers can extract information from more than just the “official” outputs of a program. For example, the attacker might observe how long a computation takes or measure the power consumption of a device. These additional sources of information are called **side channels**. Side channels are unintended channels through which secret data can be inferred by observing the system’s behavior, even if the direct outputs are secure.\n\nThese side channels can reveal information about the secret inputs, and so we must include them in the definition of noninterference. Similarly to how we defined `PublicOut(Sec, Pub)` as the observable output, we can define `Trace` as the observable side-channel information for a given program `P`.\n\n```\ntrace = Trace(P, Sec, Pub)\n```\n\nFor example, a trace might be the execution time of the program or its cache access pattern.\n\nNoninterference then requires that the traces of two runs with different secrets - `(Sec1, Pub)` versus `(Sec2, Pub)` - are indistinguishable to an attacker. This is a stronger requirement than just looking at the functional outputs.\n\n=== \"Definition 2: Side-Channel Noninterference\"\n:   Given a side channel that produces a trace `Trace`, a program `P` is noninterferent with respect to this side channel if, for all public inputs&nbsp;`Pub` and all pairs of secret inputs `Sec1`, `Sec2` it holds that <br>`Trace(P, Sec1, Pub) = Trace(P, Sec2, Pub)`.\n\nHere are some examples of side channels and how they can violate noninterference:\n\n=== \"Example 5A: Timing side channel\"\n\n:   Consider a program that reads a compares a password with a user’s input:\n\n    ```c\n    bool check_password(const char *attempt, const char *pswd) {\n        for (int i = 0; i < length(pswd); i++) {\n            if (attempt[i] != pswd[i]) {\n                return false;  // mismatch found, return early\n            }\n        }\n        return true; // all characters matched\n    }\n    ```\n\n:   If the attacker can measure how long the function takes to reject a guess, they can infer the password one character at a time. This leakage surfaces as a violation of the noninterference property with respect to timing observations.\n\n:   A counterexample to Definition 2 could be as follows: Let's say we use the same input on two different secrets:\n\n:   - `input1={attempt=\"aaa\", pswd=\"abc\"}`\n:   - `input2={attempt=\"aaa\", pswd=\"aab\"}`\n\n:   The traces of these inputs will be:\n\n:   - `trace1 = Trace(check_password, input1) = 1`\n:   - `trace2 = Trace(check_password, input2) = 2`\n\n:   These inputs constitute a violation of Definition 2, as `trace1 != trace2` even though the two inputs have the same public values.\n\n---\n\n=== \"Example 5B: Timing side channel - Password length\"\n\n:   Noninterference is able to model different kinds of secret-dependent leaks. Let's take for example a patched version of the previous program:\n\n    ```c\n    bool check_password(const char *attempt, const char *pswd) {\n        int len = min(length(attempt), length(pswd));\n        bool same = true;\n        for (int i = 0; i < len; i++) {\n            same = same && (attempt[i] == pswd[i]); // all the loop is executed\n        }\n        return same;\n    }\n    ```\n\n:   In this version there is no early-exit condition, yet the attacker is still able to infer the _length_ of the password through a side-channel. This is captured by the following counterexample:\n\n:   - `input1={attempt=\"aaaaaa\", pswd=\"b\"}`, `trace1 = 1`\n:   - `input2={attempt=\"aaaaaa\", pswd=\"bbb\"}`, `trace2 = 3`\n\n:   Which shows that the program still violates Definition 2.\n\n---\n\n=== \"Example 6: Cache side channel\"\n\n:   Consider a program that uses a secret value to index into an array, as in the following code:\n\n    ```c\n    int multiply(const char *array, int pub, int sec) {\n        char x = array[sec];\n        return x * pub;\n    }\n    ```\n\n:   A co-located attacker could observe the cache access pattern of the program by using Prime+Probe or Flush+Reload attack. Such traces can reveal the addresses accessed by the program and thus leak the secret value. This leakage would violate the noninterference property with respect to cache observations.\n\n:   A violation could be surfaced by two inputs:\n\n:   - `input1={array=0x10000, pub=1, sec=0x40}`\n:   - `input2={array=0x10000, pub=1, sec=0x80}`\n\n:   Let's assume that the cache line size is 64 bytes, and the cache is direct-mapped, meaning that the cache line ID is based on the memory access address `addr` as `line_id = (addr % 0x1000) // 0x40`. Since the array access in the first line of `multiply` will access two different addresses for the two inputs, they will also produce two different traces:\n\n:   - `trace1 = Trace(multiply, input1) = ((0x10000 + 0x40) % 0x1000) // 0x40 = 1`\n:   - `trace2 = Trace(multiply, input2) = ((0x10000 + 0x80) % 0x1000) // 0x40 = 2`\n\n:   Since we have two inputs that match on the secret value `sec` but differ on the cache trace, this constitutes a violation of Definition 2.\n\n## Challenges of Side-Channel Noninterference\n\nDespite its completeness, the above formalization of side-channel noninterference is too simplistic to faithfully capture the side effects of program execution on modern, highly optimized hardware, especially CPUs. There are two key challenges:\n\n- *Challenge 1 - Noisy and Non-Deterministic Traces*: The traces observed by the attacker over a side channel are typically noisy, non-deterministic, and depend on the microarchitectural state of the CPU. For example, cache access patterns can be influenced by other programs running on the machine, the operating system and its interrupts, and can depend on microarchitectural buffers like store buffers or branch history tables. This means that the `Trace` function is not a simple deterministic function of the program inputs, but a complex function of many factors, some of which affect the result concurrently and in a non-deterministic fashion.\n\n- *Challenge 2 - Unknown Side Channels*: Modern CPUs have a plethora of side channels, including cache timing, branch prediction, and many others. To ensure complete confidentiality, we need to check that the program does not leak information over *any* of them. This is a challenging task, as we do not know the full set of possible side channels when it comes to commercial hardware with proprietary microarchitectures. For example, a CPU might have an obscure microarchitectural optimization that vastly expands possibilities for information leaks, as was the case with Spectre and Meltdown vulnerabilities. Not including this optimization will undermine the noninterference analysis. Therefore, to test for noninterference comprehensively, we need a way to discover and reason about all possible side channels that could leak information.\n\nThe next two sections discuss how speculation contracts address these challenges.\n\n## Speculation Contracts: Dealing with the Complexity of Modern Hardware\n\nAs a solution to the first challenge, Guarnieri et al. (2021) introduced the concept of **speculation contracts**. A speculation contract is a simplified and deterministic model of the hardware, designed to capture the information that a given program *could* leak over side channels when executed with the given inputs. The key term here is \"could\"—the contract is not meant to exactly predict the side-channel traces, but instead, it errs on the side of caution, overestimating the possible leaks to achieve deterministic and noise-free traces.\n\nA speculation contract works by defining two key aspects for every instruction in the CPU's ISA:\n\n1. [**Observation Clause**](../glossary.md#observation-clause): For each instruction that may have\nan observable side effect, the contract declares an observation clause. It describes the data\nexposed by the instruction.\n\n2. [**Execution Clause**](../glossary.md#execution-clause): For each instruction whose semantics\nmay be affected by hardware optimizations (e.g., speculative execution), the contract declares an\nexecution clause. It describes the effect of such optimizations, but without specifying the exact\nmechanism of the optimization.\n\nAt a high level, a contract implements a function `ContractTrace` that maps a program `P` and its\ninputs `Sec, Pub` to a [contract trace](../glossary.md#contract-trace-ctrace) `ctrace`. It is\nessentially a conservative approximation of the `Trace` function.\n\n```\nctrace = ContractTrace(P, Sec, Pub)\n```\n\nThe contract trace is a sequence of all data that is exposed when a program is executed according\nto a contract. It captures the side-channel observations that *could be visible* if the CPU\nfollowed the speculation contract's rules for a given program execution.\n\nAccordingly, the noninterference property is redefined in terms of the contract trace:\n\n=== \"Definition 3: Contract Noninterference\"\n\n:   Given a contract that produces a contract trace&nbsp;`ContractTrace`, a program `P` is noninterferent with respect to this contract if,<br>for all public inputs&nbsp;`Pub` and all secret inputs `Sec1`, `Sec2`, it holds that <br>`ContractTrace(P, Sec1, Pub) = ContractTrace(P, Sec2, Pub)`.\n\nThe following examples illustrate how a contract can be used to model side-channel leaks on a CPU.\n\n=== \"Example 7: Memory Observation Contract, MEM-SEQ\"\n\n:   Let's imagine a CPU with a shared data cache and no other optimizations (i.e., no speculation). A co-located attacker can recover the addresses of loads/stores by observing which of the cache sets changed their state via a cache timing side-channel attack (e.g., Prime+Probe). We can encode these expectations in an observation clause for loads and stores by specifying that they expose their address. Since the CPU does not speculate, the execution clause for all instructions is empty. We call this contract MEM-SEQ (memory leakage with sequential execution), and it can be summarized as a table:\n\n    |       | Observation Clause | Execution Clause |\n    | ----- | ------------------ | ---------------- |\n    | Load  | Expose Address     | -                |\n    | Store | Expose Address     | -                |\n    | Other | -                  | -                |\n\n:   Note that MEM-SEQ intentionally overestimates the leaks by assuming that the attacker observes complete addresses loads/stores (in contrast to a subset of bits that are actually leaked in practice) and that *all* loads/stores are observable (in reality, they might be masked by noise or other factors). This overestimation is intentional to ensure that the contract is conservative and captures all possible corner cases.\n\n:   Let's now consider how we can produce a contract trace using MEM-SEQ. We will use a slightly modified version of the `multiply` function from Example 6:\n\n    ```c\n    int multiply(const char *array, int pub, int sec) {\n        char x = array[sec];   // MEM-SEQ exposes: &array[sec]\n        char y = array[pub];   // MEM-SEQ exposes: &array[pub]\n        return x * y;\n    }\n    ```\n\n:   The inputs are:\n\n:   - `input1 = {array=0x10000, pub=1, sec=2}`\n:   - `input2 = {array=0x10000, pub=1, sec=3}`\n\n:   The model collects a trace by executing the program line-by-line according to the rules in the table above (in practice, this is usually done using a modified CPU emulator). The first line has a load from memory, so the model records the address `&array[sec]` as exposed. The second line has another load, so the model records the address `&array[pub]` as exposed. The contract traces for this program would be:\n\n:   - `ctrace1 = ContractTrace(multiply, input1) = [0x10002, 0x10001]`\n:   - `ctrace2 = ContractTrace(multiply, input2) = [0x10003, 0x10001]`\n\n:   Finally, this model can be used to check for noninterference by comparing contract traces according to Definition 3. In this case, we have two inputs with matching public values and different secrets, and they produced different contract traces, `ctrace1 != ctrace2`. This constitutes a violation and means that the `multiply` function is not noninterferent with respect to MEM-SEQ.\n\n---\n\n=== \"Example 8: Branch Prediction Contract, MEM-COND\"\n\n:   Now let's consider a more complex scenario, with a CPU that implements branch prediction—a common form of speculative execution. In this case, the CPU may incorrectly predict branch targets and execute instructions that are not part of the correct control flow. We can model this behavior in a contract by introducing an execution clause for conditional jumps that specifies the mispredicted target. To make the example useful, we will assume that the CPU also has a data cache, so the observation clause for loads and stores remains the same as in MEM-SEQ. We call this contract MEM-COND (memory leakage with conditional branch misprediction).\n\n    |            | Observation Clause | Execution Clause  |\n    |------------|--------------------|-------------------|\n    | Load       | Expose Address     | -                 |\n    | Store      | Expose Address     | -                 |\n    | Cond. Jump | -                  | Mispredict Target |\n    | Other      | -                  | -                 |\n\n:   As a target program we will use the following function:\n\n    ```c\n    int conditional_multiply(char *array, int pub, int sec) {\n        int z = array[pub];   // MEM-COND exposes: &array[pub]\n        if (z < 10) {         // MEM-COND mispredicts (assume z = 10)\n            z *= array[sec];  // MEM-COND exposes: &array[sec]\n        }\n        return z;\n    }\n    ```\n\n:   and a pair of inputs with the same public value but different secrets:\n\n:   - `input1 = {array=0x10000, pub=1, secret=2}`\n:   - `input2 = {array=0x10000, pub=1, secret=3}`\n\n:   The first line of `conditional_multiply` has a load, so it exposes its address, `&array[pub]`. For the sake of this example, let's assume this load returns `10`, so the next branch is not supposed to be taken. However, according to MEM-COND, branches take the wrong target, so the model executes the third line anyway. This line is a load, so it exposes the address `&array[sec]`. After this, the program terminates, and the resulting traces are:\n\n:   - `ctrace1 = ContractTrace(conditional_multiply, input1) = [0x10002, 0x10001]`\n:   - `ctrace2 = ContractTrace(conditional_multiply, input2) = [0x10003, 0x10001]`\n\n:   Again, the traces are different, so the program violates noninterference with respect to MEM-COND.\n:   Notably, however, these two inputs would *not* violate noninterference with respect to MEM-SEQ, as the branch at line 2 would not be mispredicted, and the traces would be identical:\n\n:    `ctrace_mem_seq1 = ctrace_mem_seq2 = [0x10001]`\n\n## Building and Testing Speculation Contracts\n\nSpeculation contracts are typically built by hand, with the initial versions based on public knowledge of the CPU's microarchitecture and its side-channel vulnerabilities. However, in the case of commercial CPUs, the exact details of the microarchitecture are often proprietary and not publicly disclosed. In these cases, the contract could—and often will—be incomplete. This is where the testing of speculation contracts becomes crucial: the initial \"draft\" of a contract is tested against the real hardware to ensure that it captures all side-channel leaks that the CPU exhibits. If the contract misses something, it is refined based on the results of the testing, and the process is repeated until the contract is deemed safe to use.\n\nBut how do we test a speculation contract? A naive approach might be to directly compare the traces produced by the model with the traces collected from the real CPU for the same program and inputs. However, this approach is generally not feasible because the contract traces intentionally overestimate the hardware traces, so mismatches are expected. Moreover, the model might expose information differently than the real hardware (e.g., the model might expose load/store addresses, while the hardware exposes cache set indexes), meaning direct comparison is often impossible.\n\nInstead, a more precise approach is to compare *the information contained in the traces*. The idea is to check that the information exposed by the model is a strict superset of the information exposed by the real hardware. This is done by verifying that all inputs producing identical contract traces for a given program also produce identical hardware traces. If this property holds for all possible programs and inputs (ignore the complexity question for now), then any program that would be noninterferent with respect to the real hardware is guaranteed to be noninterferent with respect to the speculation contract. At this point, the model is safe to use as a proxy for real hardware when analyzing side-channel leaks.\n\nTo formalize this idea, let's introduce a new function `HardwareTrace` to denote the\n[hardware trace](../glossary.md#hardware-trace-htrace) collected from the real hardware, and it\nwill take an extra argument `Ctx` to capture the fact that real-world hardware traces depend on\nthe microarchitectural state (e.g., on the state of branch predictors or caches).\n\n=== \"Definition 4: [Contract Compliance](../glossary.md#contract-compliance)\"\n\n:   A CPU complies with a speculation contract if, for all programs `P`, all input pairs\n`(Sec1, Pub), (Sec2, Pub)`, and all initial microarchitectural states&nbsp;`Ctx`, if\n`ContractTrace(P, Sec1, Pub) = ContractTrace(P, Sec2, Pub)`, then\n`HardwareTrace(P, Sec1, Pub, Ctx) = HardwareTrace(P, Sec2, Pub, Ctx)`.\n\n\nand conversely\n\n=== \"Definition 5: [Contract Violation](../glossary.md#violation)\"\n\n:   A CPU violates a speculation contract if there exists a program&nbsp;`P`, a microarchitectural\nstate&nbsp;`Ctx`, and two inputs `(Sec1, Pub), (Sec2, Pub)` such that\n`ContractTrace(P, Sec1, Pub) = ContractTrace(P, Sec2, Pub)` and <br>\n`HardwareTrace(P, Sec1, Pub, Ctx) != HardwareTrace(P, Sec2, Pub, Ctx)`.\n\nWe call the tuple `(P, Ctx, Sec1, Sec2)` a\n[**contract counterexample**](../glossary.md#violation-artifact-aka-contract-counterexample). The\ncounterexample demonstrates that an adversary can learn more information from hardware traces than\nwhat the contract specifies. A counterexample indicates a potential microarchitectural leakage\nthat was not accounted for by the contract. The goal of Revizor is to find such counterexamples.\n\n## [Model-Based Relational Testing](../glossary.md#model-based-relational-testing-mrt) and Revizor\n\nRevizor applies the principles above, and provides a framework for building executable speculation\ncontracts together with a mechanism to test real hardware (currently only CPUs) against these\ncontracts by searching for contract counterexamples, as in Definition 5. However, there are\ncertain issues that appear when the theory from the previous section is applied in practice, which\nwe had to address in Revizor.\n\nThe first issue is the search space: testing all possible programs and inputs is literally\nimpossible. We mitigate this issue by relying on a sampling-based approach, similar to fuzzing,\nwhere we approximate the complete search space via random sampling. Specifically, Revizor generates\nsmall (50-100 instructions long) programs, creates random inputs for them, collects both the\ncontract and hardware traces for these inputs, and checks whether any of the traces constitute a\ncontract counterexample. This process is called\n[*Model-based Relational Testing*](../glossary.md#model-based-relational-testing-mrt), and it is\ndetailed further in the [Architecture Overview](../internals/architecture/overview.md).\n\nThis approach works well in practice because any given hardware optimization can typically be triggered by many different programs, and we need to find only one instance to detect a violation. Evidence of this is the [list of trophies](https://microsoft.github.io/side-channel-fuzzer/) that Revizor has already amassed.\n\nThe second issue we encountered is nondeterminism. As mentioned earlier, hardware traces can be non-deterministic due to various factors like interrupts or other programs running on the machine. To handle this, we use statistical methods: Revizor collects hardware traces for each program-input pair multiple times and then compares their distributions. If the distributions of the traces are statistically similar, Revizor considers the traces to be equivalent. This approach helps us account for noise in the hardware traces while still making reliable decisions about contract compliance.\n\nFor more details, see [Architecture Overview](../internals/architecture/overview.md).\n\n## Conclusion\n\nIn this primer, we have introduced the concepts of noninterference, side channels, and speculation contracts, which all underlie the design of Revizor:\n\n- The hardware fuzzer in Revizor uses speculation contracts and the concepts of noninterference (1) to detect unexpected side channels and dangerous microarchitectural optimizations in commercial CPUs, and (2) to aid in building sound leakage models for those CPUs.\n- The software fuzzer in Revizor (*NOTE: currently under construction*) uses the leakage models produced by the hardware fuzzer, and applies the principles of noninterference testing to detect side-channel vulnerabilities in real-world software.\n\nWith these two components, we aim to provide a comprehensive tool for discovering and mitigating side-channel vulnerabilities software that can handle even the most obscure and complex microarchitectural optimizations in modern hardware.\n\n---\n\n## Sources and Further Reading\n\n- A. Sabelfeld and A. C. Myers. *Language-Based Information-Flow Security*. IEEE Journal on Selected Areas in Communications, 21(1), 2003. (Survey of information-flow security, implicit/explicit flows, covert channels, etc.)\n- J. A. Goguen and J. Meseguer. *Security Policies and Security Models*. IEEE Symposium on Security and Privacy, 1982. (Origin of noninterference as a security policy formalism.)\n- J. B. Almeida et al. *Verifying Constant-Time Implementations*. USENIX Security Symposium, 2016. (Constant-time programming principles and the ct-verif tool for automated verification.)\n- M. Guarnieri, B. Köpf, J. Reineke, P. Vila. *Hardware-Software Contracts for Secure Speculation*. IEEE Symposium on Security and Privacy, 2021. (Original paper on speculation contracts.)\n- O. Oleksenko, C. Fetzer, B. Köpf, M. Silberstein. *Revizor: Testing Black-box CPUs against Speculation Contracts*. ACM International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS), 2022. (Paper describing Model-based Relational Testing and Revizor.)\n\n"
  },
  {
    "path": "docs/intro/04-tutorials.md",
    "content": "# Starting with Tutorials\n\nLet's learn by example.\n\nThis is a starting point for a tutorial series that will teach you how to use Revizor for testing CPUs, from the most basic cases, to detection of Spectre and Meltdown, to building custom campaigns for detecting new vulnerabilities, and up to building custom extensions for Revizor for the most advanced cases.\n\n!!! note \"Prerequisites\"\n    Before proceeding with this tutorial, ensure that you have completed the installation steps outlined in the [Installation Guide](02-install.md).\n\n!!! question \"Need Help?\"\n    - **Questions about the tutorial?** Check the [FAQ](../faq/general.md) or open a [GitHub discussion](https://github.com/microsoft/side-channel-fuzzer/discussions)\n    - **Found a bug?** Report it in [GitHub issues](https://github.com/microsoft/side-channel-fuzzer/issues)\n\n### Let's get started!\n\nReady to dive in?\n\n* [Tutorial 1](./tutorials/01-first-fuzz.md) - run your first fuzzing campaign with Revizor\n* [Tutorial 2](./tutorials/02-first-vuln.md) - find your first microarchitectural vulnerability with Revizor\n* [Tutorial 3](./tutorials/03-faults.md) - learn how to test faults and exceptions with Revizor\n* [Tutorial 4](./tutorials/04-isolation.md) - explore how to test domain isolation boundaries\n* [Tutorial 5](./tutorials/05-extending.md) - extend Revizor with custom features\n"
  },
  {
    "path": "docs/intro/start-here.md",
    "content": "# Getting started\n\nNew to Revizor? Or to side-channel testing in general? You came to the right\nplace: read this material to quickly get up and running.\n\n## Introductory Materials\n\n* [Revizor at a Glance](01-overview.md): Understand what Revizor is, what problems it solves, and see a quick example of violation detection.\n* [Installation Guide](02-install.md): Get Revizor installed on your system and verify your setup.\n* [Core Concepts](03-primer.md): Learn about contracts, traces, speculation, and other fundamental concepts needed to use Revizor effectively.\n* [Tutorial Series](04-tutorials.md): Follow a series of hands-on tutorials that walk you through running your first tests, detecting violations, and rump up all the way to root-cause analysis and design of custom campaigns.\n* [Glossary](../glossary.md): A quick reference for key terms used throughout the documentation.\n\n## Research\n\nInterested in the academic research behind Revizor? Check out the papers listed in the [Research Papers](../ref/papers.md) section.\n\n## Need Help?\n\n[Ask a Question](../howto/ask-a-question.md) about Revizor if you need assistance or have any questions.\n"
  },
  {
    "path": "docs/intro/tutorials/01-first-fuzz.md",
    "content": "# Tutorial 1: Your First Fuzz\n\nThis is the first part of the tutorial on the basic usage of Revizor.\n\n### Overview\n\nIn this first tutorial, we'll start with a baseline experiment to verify your Revizor installation and familiarize yourself with the basic workflow. This tutorial walks you through a simple fuzzing campaign that should find no violations.\n\nThe goal of this first campaign is verification, not vulnerability detection. We'll deliberately choose an instruction set that should not trigger speculation on Intel or AMD CPUs—specifically, simple arithmetic operations without any branches or memory speculation sources. Since there are no conditional branches to mispredict and no page faults to speculate around, we expect the CPU to execute sequentially without any speculative side effects.\n\nThis baseline is useful for two reasons. First, it confirms your installation is working correctly. If the fuzzer crashes or behaves unexpectedly, you'll know there's a setup issue rather than discovering problems later during more complex campaigns. Second, it establishes what \"no violations\" looks like, so you can recognize the difference when you do find a vulnerability in the next tutorial.\n\n### Create your first configuration file\n\nRevizor's behavior is controlled by a YAML configuration file that specifies which instructions to test and what contract to check against. Create a file named `config.yaml` with the following content:\n\n```yaml\n# tested instructions\ninstruction_categories:\n  - BASE-BINARY\n\n# prevent branch generation\nmax_bb_per_function: 1\nmin_bb_per_function: 1\n\n# contract\ncontract_observation_clause: loads+stores+pc\ncontract_execution_clause:\n  - no_speculation\n```\n\nLet's understand each section. The `instruction_categories` field tells Revizor which instructions to include in generated test cases. We're using `BASE-BINARY`, which includes only arithmetic and logical operations like `add`, `sub`, `and`, `xor`, and `mov`. These operations are data-processing instructions that don't involve control flow or special memory access patterns.\n\nThe `max_bb_per_function` and `min_bb_per_function` settings both set to 1 ensure that Revizor generates programs with exactly one basic block—meaning no branches at all. This simplifies our test cases to pure arithmetic sequences, eliminating any possibility of branch misprediction.\n\nThe contract configuration section is set to use the simplest contract, CT-SEQ. This contract assumes nothing about the target CPU except the presence of CPU caches, making it a zero-knowledge baseline for detecting unknown vulnerabilities. With CT-SEQ, Revizor reports any information leaks beyond the most trivial non-speculative cache accesses.\n\nFor a complete reference of all configuration options, see the [Configuration Reference](../../ref/config.md).\n\n\n### Run the Campaign\n\nLet's run the fuzzer with your baseline configuration:\n\n```bash\nrvzr fuzz -s base.json -c config.yaml -n 100 -i 50 -w .\n```\n\nThis command tells Revizor to execute 100 test cases (`-n 100`) with 50 inputs per test case (`-i 50`), using the ISA specification from `base.json` and your configuration file. The `-w .` flag specifies the working directory for saving any violations.\n\nYou'll see output similar to this:\n\n```\nINFO: [fuzzer] Starting at 14:32:18\n100   (100%)| Stats: Cls:50/50,In:100,R:5,SF:0,OF:0,Fst:0,CN:0,CT:0,P1:0,CS:0,P2:0,V:0\n================================ No Violations detected ===========================\n```\n\nThe campaign should complete in under a minute with no violations detected. This is exactly what we expect—our simple arithmetic instructions don't trigger speculation, so the hardware behaves according to the strict sequential contract.\n\n### Interpret the statistics\n\nLet's examine the statistics line to understand what Revizor is reporting:\n\n```\n100   (100%)| Stats: Cls:50/50,In:100,R:5,SF:0,OF:0,Fst:0,CN:0,CT:0,P1:0,CS:0,P2:0,V:0\n```\n\n#### `100 (100%)`\n\nThis part shows we completed all 100 test cases. This number was continuously updated while the fuzzer was running.\n\n#### `Cls:50/50`\n\nThese numbers indicate the number of [equivalence classes](../../glossary.md#contract-equivalence-class) formed by the inputs. The first number is the effective classes (> 1 input per class) and the second is the total number of classes.\n\nIf you don't understand what all of this means, that's ok. The only important factors are:\n\n- if both numbers are equal (or at least close), and they are also equal to the number of inputs that you've set via `-i` command-line argument: everything is going well.\n- if the numbers are different, it means either a misconfiguration or an issue with the input generator. Ensure that `input_per_class` config option is `> 1`.\n- if the numbers are equal, but they are both considerably lower than the number of inputs set via `-i`: You're using an overly simple fuzzing configuration, and you're unlikely to find anything with it.\n\nNone of the issues above should happen if you're using the config file from this tutorial. If they do, double-check your installation.\n\n#### `R:5`\n\nThis is an indirect indicator of the level of noise on the system. More concretely, it is the average sample size used by the executor. It is an adaptive number, which increases when the tool starts to encounter false positive caused by noise.\n\nThis number should be relatively small. If you see that it's going above 10-20 range, it is likely because something is polluting the measurements. Consider applying the suggestions [here](../02-install.md#7-optional-system-configuration).\n\n#### `SF:0,OF:0,Fst:0,CN:0,CT:0,P1:0,CS:0,P2:0`\n\nThese numbers are the statistics on the effectiveness of various optimizations used by Revizor, such as speculation and observation filtering.\n\nYou can ignore these numbers for now, as they are useful only when you're trying to optimize performance of the fuzzer. If you're still curious, though, see the [Fuzzing Statistics Reference](../../ref/runtime-statistic.md).\n\n### Understand what this means\n\nThe successful completion of this baseline campaign tells you several things. Your Revizor installation is working correctly—the fuzzer can generate test cases, execute them on your hardware, collect traces, and analyze the results. Your system is stable enough for fuzzing—there's no excessive noise preventing measurement. The kernel module loaded correctly and can execute test programs in the sandbox environment.\n\n!!! success \"Setup Verified\"\n    If you've successfully completed this baseline campaign with no violations, your Revizor installation is ready for real vulnerability detection. You can now proceed to Tutorial 2 with confidence.\n\n!!! warning \"Troubleshooting Common Issues\"\n    If the fuzzer crashes or produces errors, check these common problems:\n\n    **Module not loaded**: Ensure the kernel module is loaded with `lsmod | grep rvzr_executor`. If not, run `cd rvzr/executor_km && make && sudo make install`.\n\n    **Permission denied**: Revizor needs root privileges to access performance counters. Check that your user account on the system has `sudo` privileges.\n\n    **ISA specification missing**: If you see \"base.json not found\", run `rvzr download_spec` first to download the instruction set specification.\n\n### What's Next?\n\nYou've finished the first tutorial. Congrats!\n\nIf you're ready to go further and start detecting violations, proceed to [Tutorial 2](./02-first-vuln.md).\n\n"
  },
  {
    "path": "docs/intro/tutorials/02-first-vuln.md",
    "content": "# Tutorial 2: Detecting Your First Vulnerability\n\nThis tutorial is the first step into actual vulnerability detection. You'll learn how to set up a fuzzing campaign that tests conditional branches. And, most likely, it will end with a detection of Spectre V1.\n\n### Testing Workflow\n\nBefore we begin with actual testing, let's take a step back and consider how a typical testing workflow looks like.\n\nThe process of using Revizor normally constitutes of the following steps:\n\n1. **Design the campaign** by selecting which instructions to test and choosing an appropriate contract that defines what behavior we consider a violation.\n2. **Create a configuration file** that captures these decisions.\n3. **Run the fuzzer** to generate and execute random test cases.\n4. **Validate the violation** to ensure it's genuine and not a false positive.\n5. **Minimize the test case** to remove unnecessary complexity, making it easier to understand.\n6. **Analyze the minimized program** to identify the root cause of the vulnerability.\n\nIn the following, we will go step-by-step through this workflow.\n\n### Plan the campaign\n\nLet's imagine we have a new CPU and want to determine if conditional branches produce any information leakage on it. These instructions are infamous for causing Spectre V1, therefore it is always useful to start with them when testing a new CPU.\n\nThe first step is planning our fuzzing campaign strategically.\n\nFor effective testing, we'll focus on a minimal instruction subset rather than the entire ISA. Spectre V1 requires only two capabilities: conditional branches (to trigger misprediction) and memory accesses (to leak information through side channels). By limiting our instruction set to just arithmetic operations and conditional branches, we accomplish two goals. First, the fuzzer will find violations faster because there are fewer instruction combinations to explore. Second, when we do find a violation, it will be much easier to analyze because the test case will be simpler.\n\n!!! warning\n    Note that this focused approach is *not* representative of a real fuzzing campaign. This tutorial is intentionally simplified to help with understanding. In a real campaign, you'll need to find balance between having a broad scope (increases changes of finding unknown vulnerabilities) and having focus on specific CPU features (simplifies root-cause analysis). For more guidance on campaign design, see [How to Design a Fuzzing Campaign](../../howto/design-campaign.md).\n\nWe'll pair this minimal instruction set with the strictest possible contract—one that forbids any speculation whatsoever. This means Revizor will flag any speculative behavior as a violation. While this contract is more restrictive than what modern CPUs actually guarantee, it's perfect for our purposes. Since we're only testing conditional branches and simple arithmetic, any speculation we detect will almost certainly be Spectre V1.\n\nWith this campaign plan, we are trying to answer a specific question: \"Does this CPU leak information through conditional branches?\"\n\n### Create the configuration file\n\nNow that we've planned our campaign, let's translate it into a configuration file. Create a YAML file with the following content:\n\n```yaml\n# tested instructions\ninstruction_categories:\n  - BASE-BINARY\n  - BASE-COND_BR\n\n# contract\ncontract_observation_clause: loads+stores+pc\ncontract_execution_clause:\n  - no_speculation\n\n# enable perf. optimizations\nenable_speculation_filter: true\nenable_observation_filter: true\nenable_fast_path_model: true\n```\n\nThe `instruction_categories` section implements our decision to use a minimal instruction set. We're including `BASE-BINARY` for arithmetic operations like addition and comparison, and `BASE-COND_BR` for conditional branches like `jz` and `jne`. These two categories give the fuzzer everything it needs to express Spectre V1 patterns.\n\nThe contract configuration consists of two clauses. The `contract_observation_clause` tells Revizor what microarchitectural side effects to track. We're using `loads+stores+pc`, which observes memory access addresses and the program counter—exactly what an attacker would monitor through cache timing attacks. The `contract_execution_clause` defines what execution behavior is allowed. By setting it to `no_speculation`, we're telling Revizor that any speculative execution is a violation.\n\nThe performance optimization flags at the bottom significantly speed up fuzzing without affecting correctness. The `enable_speculation_filter` skips test cases that don't trigger speculation at all. The `enable_observation_filter` skips test cases that leave no observable traces. The `enable_fast_path_model` allows Revizor to reuse contract traces across similar inputs, reducing the model execution overhead.\n\nFor a complete reference of all configuration options, see the [Configuration Reference](../../ref/config.md).\n\n### Run the fuzzer\n\nNow we're ready to start fuzzing. Run Revizor with the following command:\n\n```\n./revizor.py fuzz -s base.json -c config.yaml -n 1000 -i 10 -w .\n```\n\nThis command tells Revizor to run 1000 test cases (`-n 1000`), with 10 inputs per test case (`-i 10`), using the ISA specification from `base.json` (`-s`) and our configuration file (`-c`). The `-w .` flag tells Revizor to save any violations it finds to the current directory.\n\nAs the fuzzer runs, you'll see a continuously updating progress line:\n\n```\n50    ( 5%)| Stats: Cls:10/10,In:20,R:7,SF:38,OF:6,Fst:6,CN:0,CT:0,P1:0,CS:0,P2:0,V:0\n```\n\n### View the detected violation\n\nAfter a minute or so, you should see a violation.\nIt will be reported in a format similar to this:\n\n```\n================================ Violations detected ==========================\nViolation Details:\n\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:4   | ID:14 |\n-----------------------------------------------------------------------------------\n^......^...^........^.................^...........^............. | 626    | 0     |\n^......^...^........^........................................... | 1      | 18    |\n^^.....^...^........^....^...................................... | 0      | 609   |\n\n```\n\nExcellent! We've successfully detected a contract violation. Let's understand what this violation report is telling us.\n\n\nThe report shows us the violation details in a table format. The header row displays the input IDs that triggered the violation—in this case, inputs 4 and 14:\n\n`| ID:4   | ID:14 |`\n\nThese are two inputs from our test case that the contract predicted would behave identically, but the hardware traces show they behaved differently.\n\nThe three rows below show the different hardware traces that were observed:\n\n```\n^......^...^........^.................^...........^.............\n^......^...^........^...........................................\n^^.....^...^........^....^......................................\n```\n\nEach row represents a distinct cache access pattern, visualized as a bitmap where `^` marks an accessed cache line and `.` marks an untouched cache line. We're using Prime+Probe cache side channel measurements (default), so each position in the bitmap corresponds to one of the 64 cache sets in the L1D cache. (A cache set is a group of cache lines that compete for the same position in the cache—when the CPU accesses memory at a particular address, the data goes into a specific cache set determined by the address bits.)\n\nFor example, the first trace reads like this:\n\n```\nCache Set 0 accessed\n|          Cache Set 11 accessed\n|          |                          Cache set 38 accessed\n|          |                          |\n^......^...^........^.................^...........^.............\n       |            |                             |\n       |            |                             Cache Set 50 accessed\n       |            Cache Set 20 accessed\n       Cache Set 7 accessed\n```\n\nFinally, the numbers in the columns tell us how often each trace appeared for each input:\n\n```\n... | 626    | 0     |\n... | 1      | 18    |\n... | 0      | 609   |\n```\n\nLooking at the first hardware trace we see it appeared 626 times for input 4 but never for input 14. The third trace shows the opposite pattern—0 times for input 4 but 609 times for input 14. This clear separation in the distributions confirms this is a genuine violation, not random noise.\n\nWhat we're seeing is a data-dependent cache access pattern. The test case accessed different cache lines depending on the input data, creating an observable side channel. We don't know yet what caused this channel, but we can already tell that it's likely to be caused by speculation; non-speculative cache accesses are permitted by our reference contract, so they wouldn't be reported as violations.\n\nFor more details on interpreting violation reports, see [How to Interpret Violation Results](../../howto/interpret-results.md).\n\n### Violation Artifact\n\nThe artifact for this violation is stored in a directory named `violation-<timestamp>`:\n\n```bash\n$ ls -l violation-251203-103338\ninput_0000.bin  input_0004.bin  input_0008.bin  input_0012.bin  input_0016.bin  minimize.yaml    reproduce.yaml\ninput_0001.bin  input_0005.bin  input_0009.bin  input_0013.bin  input_0017.bin  org-config.yaml\ninput_0002.bin  input_0006.bin  input_0010.bin  input_0014.bin  input_0018.bin  program.asm\ninput_0003.bin  input_0007.bin  input_0011.bin  input_0015.bin  input_0019.bin  report.txt\n```\n\nThe `program.asm` file holds the test case program that triggered the violation. The `input_*.bin` files contain the input sequence that exposed the leak. The `report.txt` file provides additional details including hardware and contract traces. The configuration files include `org-config.yaml` (the original configuration), `reproduce.yaml` (for reproducing the violation), and `minimize.yaml` (for test case minimization).\n\n### Validate the violation\n\nLet's verify this violation is genuine and reproducible. First, we'll move the violation artifacts to a simpler path:\n\n```bash\nmv violation-251203-103338 ./violation\n```\n\nNow we'll reproduce the violation using the saved artifacts:\n\n```bash\n./revizor.py reproduce -s base.json -c ./violation/reproduce.yaml -t ./violation/program.asm -i ./violation/input*.bin\n```\n\nIf the violation is genuine, we should see Revizor report it again:\n\n```\n================================ Violations detected ==========================\nViolation Details:\n\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:4   | ID:14 |\n-----------------------------------------------------------------------------------\n^......^...^........^.................^...........^............. | 626    | 0     |\n^......^...^........^........................................... | 1      | 20    |\n^^.....^...^........^....^...................................... | 0      | 607   |\n```\n\nPerfect! The hardware traces are roughly the same as before, confirming this is a stable, reproducible violation.\n\n!!! tip \"Dealing with False Positives\"\n    In most cases, violations are genuine. However, if you're on a high-noise system, you might occasionally see non-reproducible violations. If this happens, adjust the noise tolerance by increasing `analyser_stat_threshold` or `executor_sample_sizes` in your configuration file (see the [Configuration Reference](../../ref/config.md) for details), then rerun the fuzzer. Also, consider trying to mitigate the noise, for example by disabling hyperthreading or by turning prefetchers off.\n\n\n### Minimize the test case\n\nNow that we've confirmed the violation is real, let's simplify it for easier analysis. The minimizer will systematically remove unnecessary instructions while keeping the violation reproducible.\n\nUse the following command. We won't go into it's details now as they are irrelevant to this tutorial. If you're curious, check our [How to Minimize](../../howto/minimize.md) guide.\n\n```bash\n./revizor.py minimize -s base.json \\\n    -c ./violation/minimize.yaml -t ./violation/program.asm \\\n    -o ./violation/min.asm -i 10 --num-attempts 3 \\\n    --enable-instruction-pass 1 \\\n    --enable-simplification-pass 1 \\\n    --enable-nop-pass 1 \\\n    --enable-constant-pass 1 \\\n    --enable-mask-pass 1 \\\n    --enable-label-pass 1\n```\n\nWe'll see the minimization progress as it works through multiple passes:\n\n```\n[PASS 1] Reproducing the violation\n  > Violation reproduced. Proceeding with minimization\n  > Violating input IDs: [4, 14]\n[INFO] Minimization attempt 1/3\n[PASS 2] Instruction Removal Pass\n\n........---...--\n[PASS 3] Instruction Simplification Pass\n\n--..-\n[PASS 4] NOP Replacement Pass\n\n(and so on...)\n```\n\nThis process typically takes 5-10 minutes. Each `.` indicates a failed removal attempt (the violation disappeared), while each `-` shows a successful simplification (the violation persisted with fewer instructions). After it finishes, we'll find the minimized program in `./violation/min.asm`.\n\n``` asm\n.intel_syntax noprefix\n.section .data.main\n.function_0:\n.macro.measurement_start: nop qword ptr [rax + 0xff]\nadd al, -118 # instrumentation\nand rdi, 0b1111111111100 # instrumentation\nadc al, byte ptr [r14 + rdi]\nmov rax, -1332388169\nimul eax, eax, -75\nand rcx, 0b1111111111000 # instrumentation\nadd dword ptr [r14 + rcx], eax\nand rax, 0b1111111111000 # instrumentation\nimul qword ptr [r14 + rax]\nand rcx, 0b1111111000000 # instrumentation\nlock inc qword ptr [r14 + rcx]\nand rdi, 0b1111111111000 # instrumentation\nadd byte ptr [r14 + rdi], al\nsub dl, al\njp .bb_0.1\njmp .exit_0\n.bb_0.1:\nand rbx, 0b1111111111000 # instrumentation\ncmp dword ptr [r14 + rbx], eax\nand rdi, 0b1111111111000 # instrumentation\ncmp qword ptr [r14 + rdi], rbx\nand rbx, 0b1111111000000 # instrumentation\nlock sub word ptr [r14 + rbx], dx\nand rbx, 0b1111111111000 # instrumentation\ndec word ptr [r14 + rbx]\nand rsi, 0b1111111111000 # instrumentation\nneg qword ptr [r14 + rsi]\nand rbx, 0b1111111111000 # instrumentation\nadc ax, word ptr [r14 + rbx]\n.exit_0:\n.macro.measurement_end: nop qword ptr [rax + 0xff]\n.section .data.main\n.test_case_exit:nop\n```\n\nLet's verify the minimized program still triggers the violation:\n\n``` bash\n$ ./revizor.py reproduce -s base.json -c ./violation/reproduce.yaml -t ./violation/min.asm -i ./violation/input*.bin\n\nINFO: [prog_gen] Setting program_generator_seed to random value: 112509\n\nINFO: [fuzzer] Starting at 11:04:52\n> Entering slow path...> Priming  1             > Increasing sample size... to 50> Increasing sample size... to 100> Increasing sample size... to 500> Priming  1\n\n================================ Violations detected ==========================\nViolation Details:\n\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:5   | ID:15 |\n-----------------------------------------------------------------------------------\n^^................^..^.......................................... | 404    | 15    |\n^^.........^....^.^..^.......................................... | 223    | 0     |\n^^................^..^.......^....................^............. | 0      | 612   |\n```\n\nExcellent! The violation still reproduces with the minimized program. We've successfully reduced the test case while preserving the vulnerability.\n\nThe program is still fairly complex, though. Let's run input minimization to identify exactly which values are being leaked.\n\n### Analyze the leak through input minimization\n\n```bash\n$ revizor ./revizor.py minimize -s base.json -c ./violation/minimize.yaml -t ./violation/min.asm -o ./violation/min.asm -i 25  --input-outdir ./violation/min-inputs \\\n    --enable-input-diff-pass 1 \\\n    --enable-input-seq-pass 1 \\\n    --enable-instruction-pass false\n```\n\nAmong other information, the minimizer prints the leaked values:\n\n```\n  > Minimizing the difference between inputs 2 and 3\n\nAddress    +0x0     +0x40    +0x80    +0xc0    +0x100   +0x140   +0x180   +0x1c0\n0x00000000 ........ ....=... ........ ........ ........ ........ ........ ........\n0x00000200 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000400 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000600 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000800 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000a00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000c00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000e00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001000 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001200 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001400 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001600 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001800 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001a00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001c00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001e00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00002000 ....^...\n0x00002040 ........ ........ ........ ........\n  > Result: Leaked 1 bytes\n  > Addresses: ['0x2020']\n```\n\nThere are two bits of information that we learn from here:\n\n- Most of the input has been successfully zeroed-out (`.`). This means it is likely irrelevant to the leak.\n- The only non-zero byte is at address `0x2020` (marked with `^`). This is likely the leaked byte.\n\nTo understand how this address maps to the test case, we need to look at the layout of the input: [here](../../ref/artifact-file-formats.md). We can see that the leak is within the GPR region of actor 0 (the only actor in this test case). Specifically, 0x2020 - 0x2000 = 0x20, is the offset used to initialize RSI (GPRs are ordered as: `rax`, `rbx`, `rcx`, `rdx`, `rsi`, `rdi`, `flags`, `rsp`).\n\nNow we just need to find how the test case uses RSI (possibly speculatively), and we will have a good idea of the root-cause of the leak.\n\nLet's inspect the minimized program in `./violation/min.asm`:\n\n``` asm linenums=\"1\"\n.intel_syntax noprefix\n.section .data.main\n.function_0:\n.macro.measurement_start: nop qword ptr [rax + 0xff]\nadd al, -118\nand rdi, 0b1111111111100\nadc al, byte ptr [r14 + rdi]\n# mem access: [5] 0x1578 cl 21:56 | [15] 0x1578 cl 21:56\nmov rax, -1332388169\nimul eax, eax, -75\nand rcx, 0b1111111111000\nadd dword ptr [r14 + rcx], eax\n# mem access: [5] 0x2498-0x2498 cl 18:24 | [15] 0x2498-0x2498 cl 18:24\nand rax, 0b1111111111000\nimul qword ptr [r14 + rax]\n# mem access: [5] 0x1060 cl 1:32 | [15] 0x1060 cl 1:32\nand rcx, 0b1111111000000\nlock inc qword ptr [r14 + rcx]\n# mem access: [5] 0x2480-0x2480 cl 18:0 | [15] 0x2480-0x2480 cl 18:0\nand rdi, 0b1111111111000\nadd byte ptr [r14 + rdi], al\n# mem access: [5] 0x1578-0x1578 cl 21:56 | [15] 0x1578-0x1578 cl 21:56\nsub dl, al\njp .bb_0.1\njmp .exit_0\n.bb_0.1:\nand rbx, 0b1111111111000\ncmp dword ptr [r14 + rbx], eax\nand rdi, 0b1111111111000\ncmp qword ptr [r14 + rdi], rbx\nand rbx, 0b1111111000000\nlock sub word ptr [r14 + rbx], dx\nand rbx, 0b1111111111000\ndec word ptr [r14 + rbx]\nand rsi, 0b1111111111000\nneg qword ptr [r14 + rsi] # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< HERE: RSI is used here\nand rbx, 0b1111111111000\nadc ax, word ptr [r14 + rbx]\n.exit_0:\n.macro.measurement_end: nop qword ptr [rax + 0xff]\n.section .data.main\n.test_case_exit:nop\n```\n\nWe can see that RSI is used in the instruction at line 36:\n\n``` asm\nneg qword ptr [r14 + rsi]\n```\n\nThat already gives most of the information we need. We can see a clear Spectre V1 pattern here:\n\n1. There is a conditional branch at line 24 (`jp .bb_0.1`)\n2. And a load of a previously-unused value on a mispredicted path (line 36)\n\nTo verify that, let's inspect the actual value of RSI in the violating inputs (inputs 2 and 3 according to the minimizer output above). We can use `hexdump` for that:\n\n``` bash\n$ hexdump -C ./violation/min-inputs/min_input_0002.bin | grep 2020\n00002020  93 22 00 00 93 22 00 00  00 00 00 00 00 00 00 00  |.\"...\"..........|\n$ hexdump -C ./violation/min-inputs/min_input_0003.bin | grep 2020\n00002020  40 00 00 00 40 00 00 00  00 00 00 00 00 00 00 00  |@...@...........|\n```\n\nSo the value of RSI were:\n\n- Input 2: `rsi=0x0000229300002293`\n- Input 3: `rsi=0x0000004000000040`\n\nThese values were masked by the instruction at line 35:\n\n``` asm\nand rsi, 0b1111111111000 # instrumentation\n```\n\nWhich means that the values of RSI used in memory accesses at line 36 were:\n\n- Input 2: `0x0000229300002293 & 0b1111111111000 = 0x290`\n- Input 3: `0x0000004000000040 & 0b1111111111000 = 0x040`\n\nAll memory accesses within the test case are relative to `r14`, which is page-aligned and points to the base of the sandbox memory.\n\nTherefore, we can calculate the ID of the cache lines accessed by the instruction at line 36 as follows:\n\n- Input 2: cache line ID = `0x290 // 0x40 = 0xa = 10`\n- Input 3: cache line ID = `0x040 // 0x40 = 0x1 = 1`\n\nSo, if our hypothesis is correct, we should see that in the hardware trace of the violation, cache lines 10 and 1 were accessed when executing inputs 2 and 3. Let's verify it by running rvzr in the reproduce mode:\n\n```\n$ ./revizor.py reproduce -s base.json -c ./violation/reproduce.yaml -t ./violation/min.asm -i ./violation/min-inputs/min_input_*.bin\n\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:2   | ID:3  |\n-----------------------------------------------------------------------------------\n^^........^..................................................... | 626    | 0     |\n^^.............................................................. | 1      | 627   |\n\n```\n\nThe first hardware trace (dominant for input 2) is:\n\n```\n^^........^.....................................................\n||        |\n||        Cache set 10 accessed\n|Cache set 1\nCache set 0 accessed\n```\n\nThe second hardware trace (dominant for input 3) is:\n\n```\n^^..............................................................\n||\n| Cache set 1 accessed\nCache set 0 accessed\n```\n\nIndeed, we see that our hypothesis is correct! The instruction at line 36 accessed different cache lines depending on the value of RSI, which was influenced by speculative execution after the conditional branch at line 24.\n\nThis tells us that the root-cause of the leak was misprediction of a conditional branch that led to speculative leak of a value (RSI) through a data access.\n\n### Summary\n\nCongratulations! We've successfully detected and analyzed a Spectre V1 vulnerability from start to finish.\n\n!!! success \"What We've Learned\"\n    In this section, we've walked through the complete workflow for detecting speculative execution vulnerabilities:\n\n    - **Strategic planning**: Choosing a minimal instruction set and appropriate contract focused our search\n    - **Violation detection**: Revizor found the vulnerability automatically in under two minutes\n    - **Validation**: Reproduction confirmed the violation was genuine and stable\n    - **Minimization**: We reduced a complex test case to its essential components\n    - **Root-cause analysis**: By examining register values and cache access patterns, we identified the exact mechanism of the leak\n\n    This same workflow applies to discovering and analyzing any speculative execution vulnerability.\n\n### What's Next?\n\nProceed to [Tutorial 3](./03-faults.md) to see how the same principles can be applied to detect more complex vulnerabilities based on CPU exceptions and faults.\n"
  },
  {
    "path": "docs/intro/tutorials/03-faults.md",
    "content": "# Tutorial 3: Testing faults with Revizor\n\nHaving detected Spectre V1, let's now apply the same methodology to find a different vulnerability class. Meltdown-style vulnerabilities exploit speculative execution around exception handling rather than branch misprediction.\n\n!!! important\n    This tutorial relies on the knowledge about sandboxed execution and the memory layout of the sandbox. If you haven't read about it yet, please refer to the [Sandbox Reference](../../ref/sandbox.md) and the [Actors and Isolation Topic Guide](../../topics/actors.md) before proceeding.\n\n### Plan the campaign\n\nThe key difference in this campaign is the speculation source. Instead of conditional branches, we'll test page faults. Meltdown and related vulnerabilities occur when a CPU speculatively executes instructions that follow a faulting memory access, potentially leaking data from inaccessible memory regions.\n\nFrom the practical standpoint, the key difference that we will need to configure the [sandbox](../../ref/sandbox.md) to make it possible for the test case to trigger page faults. Namely, we will make one of the pages accessible by the test cases non-readable.\n\n### Create the configuration file\n\nOur configuration for this campaign makes three important changes from the Spectre V1 setup. First, we remove `BASE-COND_BR` from the instruction categories since we already know conditional branches cause Spectre V1 violations. This focuses our testing on other speculation sources.\n\nSecond, we add an `actors` section with `data_properties` to configure the sandbox memory layout. Revizor's sandbox allocates each actor two 4KB memory regions: a main area with normal read-write permissions and a faulty area where we can configure special permissions. By setting `present: false` in the data properties, we mark the faulty area as non-present in the page tables. When the test case attempts to access this region, the CPU will raise a page fault, giving us the exception-based speculation source we want to test.\n\nThird, we change the contract execution clause to `delayed-exception-handling`. Modern CPUs implement out-of-order execution, so data-independent instructions after a fault may execute before the exception is recognized. This is expected behavior and would cause trivial violations under the strict `no_speculation` contract. The `delayed-exception-handling` clause accommodates this expected speculation, allowing Revizor to focus on more interesting leaks. For more details on contract selection, see [How to Choose a Contract](../../howto/choose-contract.md).\n\n```yaml\n# contract\ncontract_observation_clause: loads+stores+pc\ncontract_execution_clause:\n  - delayed-exception-handling\n\n# tested instructions\ninstruction_categories:\n  - BASE-BINARY\n  # - BASE-COND_BR\n\nactors:\n  - main:\n    - data_properties:\n      - present: false\n\nenable_speculation_filter: true\nenable_observation_filter: true\nenable_fast_path_model: true\n```\n\n\n### Run the fuzzer\n\nWith the configuration ready, let's run the fuzzer.\n\n```\n$ ./revizor.py fuzz -s base.json -c dbg/tut/2.yaml -n 1000 -i 20 -w .\n\nINFO: [fuzzer] Starting at 12:05:26\n66    ( 7%)| Stats: Cls:19/19,In:40,R:19,SF:0,OF:0,Fst:6,CN:60,CT:0,P1:0,CS:0,P2:0,V:0\n```\n\nNotice in the statistics that `SF:0,OF:0`—unlike the Spectre V1 campaign, none of our test cases are filtered by the speculation or observation filters since every test case with a page fault exhibits speculation.\n\nEventually (after a few minutes), Revizor detects a violation:\n\n```\n================================ Violations detected ==========================\nViolation Details:\n\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:3   | ID:23 |\n-----------------------------------------------------------------------------------\n^^.^.......^.........^..^.........................^............^ | 627    | 0     |\n^^.^...^...^............^.........................^............^ | 0      | 627   |\n\n```\n\nThe output is similar to what we saw in the Spectre V1 campaign, so we won't go into the details of reading the violation report again. The key takeaway is that we've successfully detected a contract violation, and the hardware traces show different cache access patterns for the two inputs.\n\n### Validate the violation\n\nAs before, we validate the violation by reproducing it:\n\n```\n$ ./revizor.py reproduce -s base.json -c ./violation/reproduce.yaml -t ./violation/program.asm -i ./violation/input*.bin\n```\n\nThe output should be similar to the original:\n\n```\n================================ Violations detected ==========================\nViolation Details:\n\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:3   | ID:23 |\n-----------------------------------------------------------------------------------\n^^.^.......^.........^..^.........................^............^ | 627    | 0     |\n^^.^...^...^............^.........................^............^ | 0      | 627   |\n```\n\nGreat! The violation reproduces successfully, confirming it's genuine.\n\n\n### Minimize the test case\n\nNow we minimize the test case to make it easier to analyze:\n\n```\n./revizor.py minimize -s base.json -c ./violation/minimize.yaml -t ./violation/program.asm  -o ./violation/min.asm -i 10 --num-attempts 3 \\\n    --enable-instruction-pass 1 \\\n    --enable-simplification-pass 1 \\\n    --enable-nop-pass 1 \\\n    --enable-constant-pass 1 \\\n    --enable-mask-pass 1 \\\n    --enable-label-pass 1\n```\n\nAfter the minimization completes, verify that the minimized program still reproduces the violation:\n\n```\n./revizor.py reproduce -s base.json -c ./violation/reproduce.yaml -t ./violation/min.asm -i ./violation/input*.bin\n\nINFO: [prog_gen] Setting program_generator_seed to random value: 578824\n\nINFO: [fuzzer] Starting at 12:14:08\n> Entering slow path...> Priming  6             > Increasing sample size... to 50> Increasing sample size... to 100> Increasing sample size... to 500> Priming  6\n\n================================ Violations detected ==========================\nViolation Details:\n\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:11  | ID:31 |\n-----------------------------------------------------------------------------------\n^^.^.......^...^........^.........................^...^........^ | 627    | 0     |\n^^.^.......^...^........^.........................^............^ | 0      | 627   |\n```\n\n### Identify the leaked value\n\nNext, we minimize the inputs to identify which specific values are being leaked:\n\n```\n./revizor.py minimize -s base.json -c ./violation/minimize.yaml -t ./violation/min.asm -o ./violation/min.asm -i 10 --input-outdir ./violation/min-inputs \\\n    --enable-input-diff-pass 1 \\\n    --enable-input-seq-pass 1 \\\n    --enable-comment-pass 1 \\\n    --enable-instruction-pass false\n\n(skipping output for brevity)\n  > Minimizing the difference between inputs 0 and 1\n\nAddress    +0x0     +0x40    +0x80    +0xc0    +0x100   +0x140   +0x180   +0x1c0\n0x00000000 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000200 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000400 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000600 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000800 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000a00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000c00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00000e00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001000 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001200 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001400 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001600 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001800 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001a00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001c00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00001e00 ........ ........ ........ ........ ........ ........ ........ ........\n0x00002000 ..=.=^..\n0x00002040 ........ ........ ........ ........\n  > Result: Leaked 1 bytes\n  > Addresses: ['0x2028']\n  > Saving new inputs in '/home/t-oleksenkoo/revizor/violation/min-inputs'\n  > Violating input IDs: [5, 15]\n```\n\nKey takeaways:\n\n- The leaked value originates from address `0x2028` in the input, which corresponds to offset `0x28` in the GPR initialization region of the sandbox memory, used to initialize the `RDI` register.\n- Two other values in the input were not zeroed out, which indicates they are somehow relevant to triggering the violation. Namely, those are offsets `0x10` and `0x20`, which correspond to `RCX` and `RSI`.\n\n### Perform root-cause analysis\n\nWith the minimized program and inputs, we can now investigate the root cause. The minimized program is as follows:\n\n``` asm linenums=\"1\"\n.intel_syntax noprefix\n.section .data.main\n.function_0:\n.macro.measurement_start: nop qword ptr [rax + 0xff]\nand rsi, 0b1111111111000 # instrumentation\nadd rdi, qword ptr [r14 + rsi]\nadd cl, dl\nand rcx, 0b1111111111000 # instrumentation\nadd qword ptr [r14 + rcx], rbx\nand rbx, 0b1111111111000 # instrumentation\nadd dword ptr [r14 + rbx], ecx\nand rax, 0b1111111111000 # instrumentation\ncmp dword ptr [r14 + rax], ecx\nand rdi, 0b1111111111000 # instrumentation\nor byte ptr [r14 + rdi], 1 # instrumentation  # <<<<<<<<<<<<<<< HERE: RDI is used here\nmov ax, 1 # instrumentation\ndiv byte ptr [r14 + rdi]                      # <<<<<<<<<<<<<<< HERE: RDI is used here\nand rsi, 0b1111111111000 # instrumentation\nsub byte ptr [r14 + rsi], bl\nand rcx, 0b1111111111000 # instrumentation\nsub al, byte ptr [r14 + rcx]\nand rcx, 0b1111111111000 # instrumentation\nmul qword ptr [r14 + rcx]\nand rax, 0b1111111000000 # instrumentation\nlock sub word ptr [r14 + rax], -128\n.macro.measurement_end: nop qword ptr [rax + 0xff]\n.section .data.main\n.test_case_exit:nop\n```\n\nRDI is used in two places:\n\n1. Line 15: `or byte ptr [r14 + rdi], 1` (a write)\n2. Line 17: `div byte ptr [r14 + rdi]` (a read)\n\nThis is a clear data-dependent pattern, which explains why RDI is being leaked. But normally, these patterns should not be reported as violations of CT-DEH (our selected contract), since the contract permits cache-based leakage. So if the violation was reported, it means these instructions were not executed in the model. Let's investigate why.\n\nWe will inspect how the model executes this program. To this end, we will add a debug flag to the config file:\n\n```yaml\nlogging_modes:\n    - dbg_model\n```\n\nThen, we will reproduce the violation again, now with a verbose log of test case execution on the model:\n\n```\n./revizor.py reproduce -s base.json -c ./violation/reproduce.yaml -t ./violation/min.asm -i ./violation/min-inputs/min_input_0000.bin\n\n                     ##### Input 0 #####\n0x0 : macro .measurement_start, .noarg\n  rax=0x0000000000000000 rbx=0x0000000000000000 rcx=0x0000d04a0000d04a rdx=0x0000000000000000\n  rsi=0x0000d0510000d051 rdi=0x000056b8000056b8 flags=0b000000000010\n  xmm0=0x00000000000000000000000000000000 xmm1=0x00000000000000000000000000000000\n  xmm2=0x00000000000000000000000000000000 xmm3=0x00000000000000000000000000000000\n  xmm4=0x00000000000000000000000000000000 xmm5=0x00000000000000000000000000000000\n  xmm6=0x00000000000000000000000000000000 xmm7=0x00000000000000000000000000000000\n\n0x8 : and rsi, 0b1111111111000\n  rax=0x0000000000000000 rbx=0x0000000000000000 rcx=0x0000d04a0000d04a rdx=0x0000000000000000\n  rsi=0x0000d0510000d051 rdi=0x000056b8000056b8 flags=0b000000000010\n  xmm0=0x00000000000000000000000000000000 xmm1=0x00000000000000000000000000000000\n  xmm2=0x00000000000000000000000000000000 xmm3=0x00000000000000000000000000000000\n  xmm4=0x00000000000000000000000000000000 xmm5=0x00000000000000000000000000000000\n  xmm6=0x00000000000000000000000000000000 xmm7=0x00000000000000000000000000000000\n\n0xf : add rdi, [r14 +rsi]\n  rax=0x0000000000000000 rbx=0x0000000000000000 rcx=0x0000d04a0000d04a rdx=0x0000000000000000\n  rsi=0x0000000000001050 rdi=0x000056b8000056b8 flags=0b000000000110\n  xmm0=0x00000000000000000000000000000000 xmm1=0x00000000000000000000000000000000\n  xmm2=0x00000000000000000000000000000000 xmm3=0x00000000000000000000000000000000\n  xmm4=0x00000000000000000000000000000000 xmm5=0x00000000000000000000000000000000\n  xmm6=0x00000000000000000000000000000000 xmm7=0x00000000000000000000000000000000\n\n    > load from +0x2050 value 0x0\nEXCEPTION #13: Read from non-readable memory (UC_ERR_READ_PROT)\n0x13: [transient, nesting = 1] add cl, dl\n  rax=0x0000000000000000 rbx=0x0000000000000000 rcx=0x0000d04a0000d04a rdx=0x0000000000000000\n  rsi=0x0000000000001050 rdi=0x000056b8000056b8 flags=0b000000000110\n  xmm0=0x00000000000000000000000000000000 xmm1=0x00000000000000000000000000000000\n  xmm2=0x00000000000000000000000000000000 xmm3=0x00000000000000000000000000000000\n  xmm4=0x00000000000000000000000000000000 xmm5=0x00000000000000000000000000000000\n  xmm6=0x00000000000000000000000000000000 xmm7=0x00000000000000000000000000000000\n\n0x15: [transient, nesting = 1] and rcx, 0b1111111111000\n  rax=0x0000000000000000 rbx=0x0000000000000000 rcx=0x0000d04a0000d04a rdx=0x0000000000000000\n  rsi=0x0000000000001050 rdi=0x000056b8000056b8 flags=0b000000000010\n  xmm0=0x00000000000000000000000000000000 xmm1=0x00000000000000000000000000000000\n  xmm2=0x00000000000000000000000000000000 xmm3=0x00000000000000000000000000000000\n  xmm4=0x00000000000000000000000000000000 xmm5=0x00000000000000000000000000000000\n  xmm6=0x00000000000000000000000000000000 xmm7=0x00000000000000000000000000000000\n\n0x1c: [transient, nesting = 1] add [r14 +rcx], rbx\n  rax=0x0000000000000000 rbx=0x0000000000000000 rcx=0x0000000000001048 rdx=0x0000000000000000\n  rsi=0x0000000000001050 rdi=0x000056b8000056b8 flags=0b000000000110\n  xmm0=0x00000000000000000000000000000000 xmm1=0x00000000000000000000000000000000\n  xmm2=0x00000000000000000000000000000000 xmm3=0x00000000000000000000000000000000\n  xmm4=0x00000000000000000000000000000000 xmm5=0x00000000000000000000000000000000\n  xmm6=0x00000000000000000000000000000000 xmm7=0x00000000000000000000000000000000\n\n    > load from +0x2048 value 0x0\nEXCEPTION #13: Read from non-readable memory (UC_ERR_READ_PROT)\nROLLBACK to 0x7f\n```\n\nThis log shows in detail which instructions from the test case were executed by the model, whether they were transient or non-transient, and the register/memory state before each instruction.\n\nWe can see that, early in the execution of the test case, a page fault occurs when trying to read from memory at address `0x2050`. This is because of the configuration we're using, where the second page of the sandbox memory (the faulty page) is set as non-readable.\n\nAccordingly, since we're using `delayed-exception-handling` execution clause, the model will not execute any instructions that are data-dependent on this faulting load. This includes the two instructions that use RDI (lines 15 and 17), since RDI was computed based on the value loaded from address `0x2050`.\n\nFrom this, we can conclude that the CPU implements some sort of speculation on page faults: The RDI-dependent instructions were not supposed to be executed, but we see leakage of RDI in cache traces nonetheless.\n\nTo understand what specific value is returned speculatively, we can manually modify the test case, and replace the instructions after the faulting load with a gadget that will specifically leak RDI:\n\n``` asm linenums=\"1\"\n.intel_syntax noprefix\n.section .data.main\n\n.macro.measurement_start: nop qword ptr [rax + 0xff]\nand rsi, 0b1111111111000 # instrumentation\nmov rdi, qword ptr [r14 + rsi]\n\nand rdi, 0b111111111111  # mask the value of RDI\nmov rdi, qword ptr [r14 + rdi]\n.macro.measurement_end: nop qword ptr [rax + 0xff]\n\n.test_case_exit:\n```\n\nWill will also enable another debug mode to see the hardware traces even when no violation is detected:\n\n```yaml\nlogging_modes:\n    # - dbg_model\n    - dbg_dump_htraces\n```\n\nThen, we can run the modified test case:\n\n```\n$ ./revizor.py reproduce -s base.json -c ./violation/reproduce.yaml \\\n    -t ./violation/min.asm -i ./violation/min-inputs/min_input_0000.bin\n\n================================ Collected Traces =============================\n- Input 0:\n  HTr:\n    ^^.^.......^............^.........................^............^ [10]\n\n  Feedback: (816, 685, 64, 0, 0)\n```\n\nWe see that multiple cache lines were accesses, so it is hard to pinpoint the exact one that belongs to the speculative leak. (We likely have all these evictions due to the page walk triggered by the page fault.)\n\nWe can identify the specific cache line by further modifying the test case to add an hard offset to the speculative memory access, e.g., changing:\n\n``` asm\nmov rdi, qword ptr [r14 + rdi + 0x100]\n```\n\nThen, we can run it again and see how the hardware trace changes:\n\n```\n./revizor.py reproduce -s base.json -c ./violation/reproduce.yaml -t ./violation/min.asm -i ./violation/min-inputs/min_input_0000.bin\n\n================================ Collected Traces =============================\n- Input 0:\n  HTr:\n    ^^.^^......^............^.........................^............^ [10]\n\n  Feedback: (816, 685, 71, 0, 0)\n```\n\nLet's compare it side-by-side with the previous trace:\n\n```\nBefore: ^^.^.......^............^.........................^............^\nAfter:  ^^.^^......^............^.........................^............^\n            |\n            + Added cache set access due to +0x100 offset\n              (cache set ID 4)\n```\n\nThis shows that the speculative access used cache set ID 4. From this, we can do a simple calculation to deduce the value of RDI that was used for the memory access:\n\n```\nCache ID = 4\nCache Line Size = 0x40\nHardcoded Offset = 0x100\nSpeculative Address = (Cache ID * Cache Line Size) = rdi + Hardcoded Offset // ignore r14\n=>\nrdi_masked = (Cache ID * Cache Line Size) - Hardcoded Offset = (4 * 0x40) - 0x100 = 0x0\n```\n\nNow we know that the masked value of RDI used in the speculative access was `0x0`. The remaining part is to figure out what was the original value of RDI before masking. For that, we can shift the pre-mask value of RDI by 12 bits (since the mask is `0b111111111111` = 0xfff = 12 bits) and repeat the procedure. We'll do 6 times to reveal the whole value.\n\nThe resulting traces are as follows:\n\n```\nno shift: ^^.^.......^............^.........................^............^\n12 bits:  ^^.^.......^............^.........................^............^\n24 bits:  ^^.^.......^............^.........................^............^\n36 bits:  ^^.^.......^............^.........................^............^\n48 bits:  ^^.^.......^............^.........................^............^\n60 bits:  ^^.^.......^............^.........................^............^\n```\n\nWe can see that in all cases, the cache set accessed is 0, which means that the masked value of RDI was always 0, regardless of how much we shifted it.\n\nThis tells us that the faulting load returned 0 speculatively, which reveals to us the root cause of the violation. This is an instance of a previously-discovered vulnerability called LVI-Null, which we have successfully and independently rediscovered using Revizor!\n\n!!! success \"What We've Learned\"\n    In this section, we applied the same systematic workflow to a different vulnerability class:\n\n    - **Flexible configuration**: By changing just a few configuration options (removing branches, adding page faults, adjusting the contract), we refocused our search entirely\n    - **Contract selection matters**: The `delayed-exception-handling` contract helped filter out trivial violations while exposing genuine leaks\n    - **Deep analysis techniques**: We manually modified test cases and used offset manipulation to precisely identify what value the CPU returned speculatively\n\n    The same workflow—plan, configure, fuzz, validate, minimize, analyze—works across all speculative execution vulnerability classes.\n\n### What's Next?\n\nProceed to [Tutorial 4](./04-isolation.md) to see how we can go even further and start testing high-level isolation properties.\n"
  },
  {
    "path": "docs/intro/tutorials/04-isolation.md",
    "content": "# Tutorial 4: Testing Security Domain Isolation with Revizor\n\nIn the previous tutorials, we used random test generation to find Spectre V1 and LVI-Null by testing against contracts. While contract violations are interesting, the most critical security issues often arise from failures in isolation between different security domains—such as user vs kernel mode, or different virtual machines.\n\nIn this tutorial, we'll explore how to use Revizor's template-based fuzzing and multi-actor testing features to evaluate isolation guarantees. Specifically, we'll test whether privileged kernel code can leak information to unprivileged user code through speculative execution.\n\n### Preliminaries\n\nThrough this tutorial, you should become familiar with three concepts: actors, templates, and actor non-interference. These concepts are covered in detail in the [Topic Guide: Actors](../../topics/actors.md) and [Howto: Use Templates](../../howto/use-templates.md), but we'll provide a brief overview here.\n\n**Actors** are an abstraction that separates a test case into multiple components, each with its own code, execution context, privilege level, and memory space. This allows us to model scenarios where different parts of the test case run under different security domains. For example, we can define a `kernel` actor that runs in kernel mode and a `user` actor that runs in user mode. While they will have separate memory spaces and are isolated through privilege separation by the CPU, information could still leak from the kernel actor to the user actor through side channels; Revizor helps us detect such leaks.\n\n**Templates** are assembly files that define the high-level structure of test cases. They allow us to specify hard-coded parts of the test case and its actors, while still leaving room for random instruction generation.\n\nTemplates are essential for testing isolation because they define how different actors interact. For example, a template can specify that the user actor calls into the kernel actor, which processes secret data, and then returns control to the user actor for observation. This structure is unlikely to be generated through pure randomness, so templates enable targeted testing of specific attack patterns.\n\n**Actor Non-Interference Contract** is a specialized contract that checks whether one actor's execution can influence another actor's observations. In our case, we want to ensure that the kernel actor's processing of secret data does not affect what the user actor can observe through side channels. If the user actor's hardware trace differs based on the kernel actor's secret data, that's a non-interference violation, indicating a potential isolation failure.\n\n### Plan the campaign\n\nLet's imagine we want to test whether a CPU properly enforces isolation between kernel and user mode. Specifically, we want to check if privileged kernel code can leak information to unprivileged user code through speculative execution side channels—attacks like Meltdown exploit exactly this type of isolation failure.\n\nFor this campaign, we'll use a two-actor setup: a kernel actor (the victim) that processes secret data, and a user actor (the attacker) that attempts to observe those secrets through side channels. Rather than relying on pure random generation, we'll use a template that explicitly encodes the interaction pattern: the kernel processes data, then transfers control to user mode, where observation code runs. This template-based approach ensures we're testing the specific isolation boundary we care about.\n\nWe'll pair this multi-actor test structure with the Actor Non-Interference contract. This contract checks whether the user actor's hardware traces (cache state, timing, etc.) differ based on the kernel actor's input data. If they do, it means information crossed the privilege boundary—a clear isolation failure. Unlike model-based contracts that compare hardware against an idealized model, non-interference testing directly verifies that one actor cannot observe another actor's secrets, which is precisely the security property we want to enforce.\n\nWith this campaign plan, we are trying to answer a specific question: \"Can unprivileged code observe secrets from privileged code through speculative side channels?\"\n\n\n### Create the configuration file\n\n```yaml\n# contract for isolation testing\ncontract_observation_clause: ct-ni\n\n# instruction categories\ninstruction_categories:\n  - BASE-BINARY\n\n# actor configuration\nactors:\n  - main:\n      - privilege_level: \"kernel\"\n      - observer: false\n  - user:\n      - privilege_level: \"user\"\n      - observer: true\n\n# filters\nenable_speculation_filter: true\nenable_observation_filter: true\nenable_fast_path_model: true\n```\n\nThis configuration introduces several important concepts. The `contract_observation_clause` is set to `ct-ni`, which tells Revizor to use the Actor Non-Interference check instead of the standard model-based testing.\n\nThe `actors` section defines two execution contexts. The `main` actor runs in kernel mode (`mode: kernel`) and has `observer: false`, meaning it's the victim whose secrets might leak. The `user` actor runs in user mode (`mode: user`) and has `observer: true`, meaning it's the attacker trying to observe kernel secrets through side channels.\n\nFor more details on actor configuration, see [Topic Guide: Actors](../../topics/actors.md).\n\n### Create the template\n\nNow we need a template that exercises the kernel-user boundary. Create `template.asm`:\n\n``` asm\n.intel_syntax noprefix\n\n# ----------------------------- Kernel-mode Actor (Victim) -------------------\n.section .data.main\n.function_main_1:\n    # random code of the victim\n    .macro.random_instructions.16.8.main_1:\n\n    # switch to user actor to observe\n    .macro.set_k2u_target.user.function_user_1:\n    .macro.set_u2k_target.main.function_main_2:\n    .macro.switch_k2u.user.1:\n\n.macro.fault_handler:\n    # one more call to the user to complete the measurement in case of a fault\n    .macro.set_k2u_target.user.function_user_2:\n    .macro.set_u2k_target.main.function_main_3:\n    .macro.switch_k2u.user.2:\n\n# return point for the user actor\n.function_main_2:\n    .macro.landing_u2k.main_2:\n\n# exit\n.function_main_3:\n    .macro.landing_u2k.main_3:\n    nop\n\n# ----------------------------- User-mode Actor ------------------------------\n.section .data.user\n.function_user_1:\n    # reset registers to ensure we're not observing leftover state\n    .macro.landing_k2u.user_1:\n    xor rax, rax  # noremove\n    mov rax, qword ptr [r14 + 0x2000] # noremove\n    mov rbx, qword ptr [r14 + 0x2008] # noremove\n    mov rcx, qword ptr [r14 + 0x2010] # noremove\n    mov rdx, qword ptr [r14 + 0x2018] # noremove\n    mov rsi, qword ptr [r14 + 0x2020] # noremove\n    mov rdi, qword ptr [r14 + 0x2028] # noremove\n    lfence\n\n    # attacker code to observe side effects\n    .macro.measurement_start:\n    .macro.random_instructions.16.8.user_1:\n    .macro.measurement_end.1:\n\n    # switch back to kernel actor\n    .macro.switch_u2k.main.1:\n\n# second measurement call; for the cases when the first one was bypassed by a fault\n.function_user_2:\n    .macro.landing_k2u.user_2:\n    .macro.measurement_end.2:\n    .macro.switch_u2k.main.2:\n    lfence\n\n# ----------------------------- Exit -----------------------------------------\n.section .data.main\n.test_case_exit:\n```\n\nLet's break down this template block by block to understand how it orchestrates the kernel-user isolation test:\n\n**Kernel Actor - Initial Execution (`function_main_1`)**\n\n```asm\n.section .data.main\n.function_main_1:\n    .macro.random_instructions.16.8.main_1:\n```\n\nThe template begins in the kernel actor's code space (`.section .data.main`). The `.macro.random_instructions.16.8.main_1` macro generates 16 random instructions with an average of 8 memory accesses. This randomized kernel code represents the victim's execution.\n\n**Transition Setup - Kernel to User**\n\n```asm\n    .macro.set_k2u_target.user.function_user_1:\n    .macro.set_u2k_target.main.function_main_2:\n    .macro.switch_k2u.user.1:\n```\n\nThese macros configure and execute a privilege level transition. The `set_k2u_target` macro specifies that when dropping to user mode, execution should begin at `function_user_1` in the `user` actor. The `set_u2k_target` macro specifies that when returning to kernel mode, execution should resume at `function_main_2` in the `main` actor. Finally, `switch_k2u` performs the actual privilege drop, transferring control to user mode. The `.1` suffix is a unique label for this transition.\n\n**Kernel Actor - Return Point (`function_main_2`)**\n\n```asm\n.function_main_2:\n    .macro.landing_u2k.main_2:\n    .macro.fault_handler:\n```\n\nThis is where the kernel resumes after the user actor returns control. The `landing_u2k` macro handles the privilege escalation transition, restoring the kernel execution context. The `fault_handler` macro designates this location as the exception handler—if any faults occur during execution (in either actor), control transfers here.\n\n**Second Transition - Kernel to User Again**\n\n```asm\n    .macro.set_k2u_target.user.function_user_2:\n    .macro.set_u2k_target.main.function_main_3:\n    .macro.switch_k2u.user.2:\n```\n\nThe kernel performs another transition to user mode, this time to `function_user_2`. This is necessary because, if the random code in the user actor triggers a fault, the `measurement_end` may never be reached, and the hardware trace would be corrupted. By splitting the measurement into two parts, we ensure that even if a fault occurs during the first measurement, we can still capture whatever trace was collected up to that point.\n\n**Kernel Actor - Exit (`function_main_3`)**\n\n```asm\n.function_main_3:\n    .macro.landing_u2k.main_3:\n    nop\n```\n\nThe final kernel return point. After the second user-mode measurement completes, execution returns here and falls through to the test case exit.\n\n**User Actor - First Observation (`function_user_1`)**\n\n```asm\n.section .data.user\n.function_user_1:\n    .macro.landing_k2u.user_1:\n    xor rax, rax  # noremove\n    mov rax, qword ptr [r14 + 0x2000] # noremove\n    mov rbx, qword ptr [r14 + 0x2008] # noremove\n    ...\n    lfence\n```\n\nThis is where the attacker code executes. The `landing_k2u` macro handles the privilege drop transition, setting up the user execution context. The explicit register initialization loads fresh values from memory (via `r14`, which points to the sandbox memory). The `# noremove` comments prevent Revizor's minimization passes from removing these instructions—they're essential for resetting architectural state. The `lfence` ensures these loads complete before observation begins, preventing them from affecting the measurement.\n\n**User Actor - Measurement**\n\n```asm\n    .macro.measurement_start:\n    .macro.random_instructions.16.8.user_1:\n    .macro.measurement_end.1:\n```\n\nThe `measurement_start` macro marks where hardware trace collection begins. Only code between `measurement_start` and `measurement_end` contributes to the observed side-channel trace. The random instructions here represent attacker code that might be sensitive to cache state, timing variations, or other microarchitectural side effects left by the kernel's execution. The `.1` suffix distinguishes this measurement from the second one.\n\n**User Actor - Return to Kernel**\n\n```asm\n    .macro.switch_u2k.main.1:\n```\n\nThe `switch_u2k` macro performs a privilege escalation, returning control to the kernel actor. This transition was pre-configured earlier by the `set_u2k_target` macro.\n\n**User Actor - Second Observation (`function_user_2`)**\n\n```asm\n.function_user_2:\n    .macro.landing_k2u.user_2:\n    .macro.measurement_end.2:\n    .macro.switch_u2k.main.2:\n    lfence\n```\n\nThe second user-mode entry point completes the measurement that was started in `function_user_1`.\n\n### Run the isolation test\n\nExecute the multi-actor fuzzing campaign:\n\n```bash\n./revizor.py tfuzz -s base.json -c config.yaml -t template.asm -n 1000 -i 10 -w .\n```\n\nWe're running 1000 test cases with 10 inputs each. Multi-actor testing often requires more iterations to find violations because we're looking for interactions between actors, which adds complexity.\n\nThe fuzzer will run and search for isolation violations. On most systems, you will not find a violation; isolation mechanisms are generally robust. We will need to try harder to find issues.\n\n```\nDuration: 60.5\nFinished at 08:44:40\n```\n\n### Adding Faults\n\nNow let's add a little more complexity to the experiment. We will make the attacker \"active\" by allowing the user actor to try and access the memory of the kernel actor. This simulates an attacker that attempts to read privileged memory, which should be blocked by the CPU's privilege separation.\n\nTo do this, we will use a generator pass that is specifically designed for this purpose. The `user-to-kernel-access` pass randomly selects a memory access from the user actor's code and modifies it to access the kernel actor's memory space. This creates a faulting access that the CPU should prevent.\n\nUpdate the configuration file to include this generator pass:\n\n```yaml\nfaults_allowlist:\n  - user-to-kernel-access\n\n# actor configuration\nactors:\n  - main:\n      - privilege_level: \"kernel\"\n      - observer: false\n      - fault_blocklist:\n        - user-to-kernel-access\n  - user:\n      - privilege_level: \"user\"\n      - observer: true\n```\n\nNote that we also added a `fault_blocklist` to the kernel actor. This is done to prevent redundant work on the generator side; there is no point in making kernel access its own memory.\n\n### Run the fuzzer with faults enabled\n\nRun the fuzzer again with the updated configuration:\n\n```bash\n./revizor.py tfuzz -s base.json -c config.yaml -t template.asm -n 5000 -i 10 -w .\n```\n\nThis time, with the user actor actively trying to access kernel memory, we have a higher chance of provoking isolation violations.\n\nIf you're testing a system vulnerable to Meltdown, you should see a violation reported:\n\n```\n================================ Violations detected ==========================\nViolation Details:\n\n-----------------------------------------------------------------------------------\n                             HTrace                              | ID:3   | ID:13 |\n-----------------------------------------------------------------------------------\n^^.^.......^.........^..^.........................^............^ | 627    | 0     |\n^^.^...^...^............^.........................^............^ | 0      | 627   |\n```\n\nValidate and minimize the violation, as we've done in the previous tutorials.\n\nAs a result, you should obtain a minimized test case that contains a typical Meltdown pattern: the user actor attempts to read kernel memory, which causes a fault, but speculative execution allows some of the kernel data to leak through side channels, and thus impact the user's hardware traces.\n\n!!! success \"What We've Learned\"\n    In this tutorial, we've progressed from random fuzzing to structured testing:\n\n    - **Templates provide structure**: When testing specific attack scenarios, templates let us encode the essential pattern while still benefiting from randomization\n    - **Macros control generation**: The macro system gives fine-grained control over what code gets generated and where\n    - **Multi-actor testing**: Revizor can test isolation between different privilege levels or security domains using the actor system\n    - **Noninterference contract**: This specialized contract detects when one actor's data influences another actor's observations\n\n### What's Next?\n\nThis concludes our tutorials on using Revizor for security testing. Note that all examples in the tutorials were simplified for clarity. If you wish to explore more realistic scenarios, refer to our guide on [Design a Campaign](../../howto/design-campaign.md) or check an advanced tutorial on [Detecting TSA-SQ](./tsa-sq.md).\n\nProceed to [Tutorial 5](./05-extending.md) to learn how you can extend various components of Revizor to fit your research needs.\n"
  },
  {
    "path": "docs/intro/tutorials/05-extending.md",
    "content": "# Tutorial 5: Extending Revizor\n\nIn this tutorial, we will switch gears: instead of using Revizor's existing components, we will extend Revizor by adding custom functionality to some of its core modules.\n\n## Workflow\n\nThe general workflow for extending any part of Revizor is as follows:\n\n- Subclass the exiting module or interface you want to extend. For a list of all interfaces, refer to the [Architecture Overview](../../internals/architecture/overview.md) document.\n- Implement your custom logic by overwriting the necessary methods.\n- Register your new class in the factory so that Revizor can access the new implementation. It will also enable the user to select the new implementation via a config file.\n- Add new configuration options if your extension requires additional parameters.\n\n## Changing Data Generation Algorithm\n\nAs our first example, we will modify the data (input) generation algorithm used by Revizor. By default, Revizor generates random input data for each test case. However, in some scenarios, it may be beneficial to generate inputs that contain extreme values (e.g., minimum or maximum integers) to test edge cases in the microarchitecture. We will implement this feature.\n\nThe data generation logic is defined by the `DataGenerator` interface, with its default implementation located in `rvzr/data_generator.py`. We will create a new subclass of `DataGenerator` that generates minimum or maximum integer values with a configurable probability.\n\nImplement the new generation algorithm by overwriting the generation logic in the default `DataGenerator` class.\n\n``` python\n# rvzr/data_generator.py\nclass MinMaxIntGenerator(DataGenerator):\n    \"\"\"\n    A variant of DataGenerator that generates minimum or maximum integer\n    values with a configurable probability.\n    \"\"\"\n    int_sizes: Final[List[int]] = [8, 16, 32, 64]\n\n    def __init__(self, seed: int):\n        super().__init__(seed)\n        self._probability_of_max = CONF.input_gen_probability_of_minmax\n\n    def _generate_one(self, state: int, n_actors: int) -> Tuple[InputData, int]:\n        input_ = InputData(n_actors)\n        input_.seed = state\n\n        per_actor_data_size = input_.itemsize // 8\n\n        rng = np.random.default_rng(seed=state)\n        for i in range(n_actors):\n            # generate random data\n            data = rng.integers(\n                self.max_input_value, size=per_actor_data_size, dtype=np.uint64)  # type: ignore\n\n            # if the probability of max is 0, we're done\n            if self._probability_of_max == 0:\n                input_.set_actor_data(i, data)\n                continue\n\n            # otherwise, with a given probability, set some values to min or max int\n            for val_id in range(per_actor_data_size):\n                roll = rng.random()\n                if roll > self._probability_of_max:\n                    continue\n                int_size = random.choice(self.int_sizes)\n                int_sign = random.choice([True, False])\n                value = (2 ** (int_size - 1))\n                if not int_sign:\n                    value = -value\n                data[val_id] = np.uint64(value & 0xFFFFFFFFFFFFFFFF)\n\n            input_.set_actor_data(i, data)\n\n        return input_, state + 1\n```\n\nWe now need to let Revizor know about the existence of this new class. This is achieved by via the factory module `rvzr/factory.py`:\n\n``` python\n# rvzr/factory.py\n_DATA_GENERATORS: Dict[str, Type[data_generator.DataGenerator]] = {\n    'random': data_generator.DataGenerator,\n    'minmax': data_generator.MinMaxIntGenerator,  # <<<<<<<<<<<<<<<< ADDED LINE\n}\n```\n\nFinally, our implementation used a new config option (`input_gen_probability_of_minmax`) to control the probability of generating extreme values. We need to register this new option in the configuration module `rvzr/config.py`:\n\n``` python\n# rvzr/config.py\nclass Config:\n    ...\n    input_gen_probability_of_minmax: float = 0.5  # <<<<<<<<<<<<<<<< ADDED LINE\n```\n\nThat's it. That's all it takes to change the data generation algorithm in Revizor.\n\nNow, let's test the implementation:\n\n``` yaml\n# config.yaml\ndata_generator: minmax\ninput_gen_probability_of_minmax: 0.7\n```\n\nRun Revizor with the new configuration:\n\n``` shell\n./revizor.py generate -s base.json -c config.yaml -w ./ -n 1 -i 1\n```\n\nSee that the new generator was applied:\n\n```\n$ hexdump -C ./tc0/input0.bin| head -10\n00000000  80 ff ff ff ff ff ff ff  00 00 00 00 00 00 00 80  |................|\n00000010  00 80 ff ff ff ff ff ff  2a 35 00 00 00 00 00 00  |........*5......|\n00000020  80 00 00 00 00 00 00 00  00 80 00 00 00 00 00 00  |................|\n00000030  c4 83 00 00 00 00 00 00  37 26 00 00 00 00 00 00  |........7&......|\n00000040  36 d5 00 00 00 00 00 00  00 80 ff ff ff ff ff ff  |6...............|\n00000050  41 27 00 00 00 00 00 00  00 80 00 00 00 00 00 00  |A'..............|\n00000060  32 69 00 00 00 00 00 00  64 b0 00 00 00 00 00 00  |2i......d.......|\n00000070  00 80 ff ff ff ff ff ff  7c d7 00 00 00 00 00 00  |........|.......|\n00000080  00 00 00 00 00 00 00 80  00 80 00 00 00 00 00 00  |................|\n00000090  31 86 00 00 00 00 00 00  f9 f4 00 00 00 00 00 00  |1...............|\n```\n\nSuccess! We can see large and small integer values in the generated input data (`ff ff ff ...`),\nmeaning that our new data generator is working as expected.\n\n## Adding a Code Generation Pass\n\nWe will now explore the other part of the test case generation pipeline - generation of test case programs (code). In this example, we will add a new code generation pass that replaces all registers in the test case with a fixed register (`RAX`).\n\n!!! note\n    Frankly, it is not a very useful generation pass, but it serves the purpose of demonstration. The same principles apply to more complex generation passes.\n\nWe will follow the same steps as before. The code pass interface is located in `rvzr/code_generator.py` as the `Pass` class. We will create a new subclass of it, and, since we are creating an ISA-specific pass, we will place it into `rvzr/arch/x86/generator.py`.\n\n``` python\n# rvzr/arch/x86/generator.py\nclass _X86RaxPass(Pass):\n    \"\"\"\n    Demonstration-only pass that replaces all register operands with RAX.\n    \"\"\"\n\n    def run_on_test_case(self, test_case: TestCaseProgram) -> None:\n        for bb in test_case.iter_basic_blocks():\n            for node in bb.iter_nodes():\n                inst = node.instruction\n                for op in inst.operands:\n                    if isinstance(op, RegisterOp):\n                        op.value = \"rax\"\n```\n\nRegister the new class with the generator:\n\n``` python\n# rvzr/arch/x86/generator.py\nclass X86Generator(CodeGenerator):\n    ...\n        self._passes = [\n            _X86PatchUndefinedFlagsPass(self._instruction_set, self),\n            _X86SandboxPass(self._target_desc, self._faults),\n            _X86PatchUndefinedResultPass(),\n            _X86RaxPass(),  # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ADDED LINE\n        ]\n```\n\nThat's it. Now, let's test our new code generation pass by running Revizor again:\n\n```\n./revizor.py generate -w . -n 1 -i 1 -s base.json\n```\n\nCheck the generated program:\n\n```\n$ cat tc0/program.asm | head -10\n\n.intel_syntax noprefix\n.section .data.main\n.function_0:\n.bb_0.0:\n.macro.measurement_start: nop qword ptr [rax + 0xff]\nand rax, 0b1111111111000 # instrumentation\nlock add byte ptr [r14 + rdi], rax\ncmp rax, 106\nor rax, 0b1000000000000000000000000000000 # instrumentation\nbsr rax, rax\n```\n\nAs we can see, all register operands have been replaced with `RAX`, confirming that our new code generation pass is functioning correctly.\n\n\n"
  },
  {
    "path": "docs/intro/tutorials/tsa-sq.md",
    "content": "# Tutorial: Detecting TSA-SQ with Revizor\n\nThis tutorial demonstrates how we used Revizor to detect TSA-SQ (Transient Scheduler Attack - Store Queue), a microarchitectural vulnerability discovered in AMD Zen4 processors. We'll walk through the design rationale behind the fuzzing campaign configuration and template, explaining how each component contributes to successful vulnerability detection.\n\nYou can reproduce this campaign using the provided configuration and template files, which are available in the Revizor repository under `demo/tsa-sq/`.\n\n!!! info \"Prerequisites\"\n    To follow this tutorial, you should have:\n\n    - Non-virtualized access to an AMD Zen4 processor for testing\n    - A working installation of Revizor. See [installation guide](../02-install.md) for setup instructions.\n    - Basic understanding of Revizor's fuzzing framework, in particular the concepts of [model-based relational testing](../03-primer.md), [actors](../../topics/actors.md), [templates](../../howto/use-templates.md), [macros](../../ref/macros.md).\n    - Familiarity with microarchitectural vulnerabilities and side-channel attacks\n\n\n## Background: Understanding TSA-SQ\n\nBefore diving into the Revizor configuration, let's briefly understand what TSA-SQ is. According to the [AMD security bulletin](https://www.amd.com/content/dam/amd/en/documents/resources/bulletin/technical-guidance-for-mitigating-transient-scheduler-attacks.pdf), TSA-SQ exploits timing variations in the CPU's store queue during \"false completion\" events. When a load instruction matches the address of an older store whose data isn't yet available, it may complete falsely using stale data from a previous store that occupied the same store queue entry. This creates timing differences that an attacker can observe to infer information about previous stores, even from different privilege levels.\n\nThe key insight is that an unprivileged user process can potentially observe timing variations that depend on data from kernel stores, creating a kernel-to-user information leak channel.\n\n## Design Rationale\n\nWhen this campaign was designed, we were not yet aware of the TSA-SQ vulnerability (in fact, the vulnerability was discovered as *result* of this campaign). Therefore, the campaign design is not specifically tailored to detect TSA-SQ, but rather to stress-test the general isolation between kernel and user modes in a way that could reveal microarchitectural vulnerabilities.\n\n## Threat Model and Actor Configuration\n\nOur fuzzing campaign targets a common and high-impact threat model: a malicious user process attempting to extract sensitive data from the kernel. This scenario is particularly relevant for privilege escalation attacks where an attacker seeks to leak kernel secrets.\n\nThe actor section of `config.yaml` reflects this threat model:\n\n```yaml\nactors:\n  - main:\n    - mode: \"host\"\n    - privilege_level: \"kernel\"\n    ...\n  - user:\n    - observer: true\n    - mode: \"host\"\n    - privilege_level: \"user\"\n```\n\nThe `main` actor represents the victim kernel, while the `user` actor represents the attacker. The `observer: true` flag designates the user actor as the attacker attempting to extract information. This configuration, in combination with the noninterference contract, tells Revizor that any information leakage from `main` to `user` should be flagged as a violation.\n\n## Template Design: Simulating Attack Patterns\n\nThe template structure follows the typical flow of a microarchitectural side-channel attack, specifically implementing a Flush+Reload pattern across privilege transitions.\n\n![tsa-sq-template.png](../../assets/tsa-sq-template.png)\n\nYou can find the complete template in [`template.asm`](https://github.com/microsoft/side-channel-fuzzer/blob/main/demo/tsa-sq/template.asm).\n\nLet's examine each phase:\n\n**Phase 1: Setup and Flush (function_main_0 and function_user_0)**\n\nThe first stage represent the attacker preparing the microarchitectural state for measurements. The first action in the template is in the `function_user_0`, where the `user` actor initializes the microarchitectural state by flushing the cache lines that will be used for measurements. This is done using the `measurement_start` macro, which is translated into a Flush stage of Flush+Reload attack. Revizor does this translation automatically based on the `executor_mode: F+R` setting in the configuration file.\n\nNote that the template does not actually start from the `function_user_0` actor function. Instead, it starts with the `function_main_0`, which is a function belonging to the `main` actor. This is because Revizor requires that the entry point to the test case must be within the `main` actor's code.\n\n**Phase 2: Secret Injection (function_main_1)**\n\nAfter the initial setup, the attacker transitions to the victim and let's it do some computations on the victim's secret data. The victim actor execute a sequence of random instructions in the `function_main_1` macro, which simulates the kernel performing operations on sensitive data. Here, \"random instructions\" means a sequence of instructions that is randomly generated in each fuzzing round (i.e., each generated test case will have a different sequence of instructions in `function_main_1`).\n\nThis randomness is crucial because it allows us to test a wide range of ways how secret data can impact microarchitectural state, without knowing a priori what specific instruction sequences might trigger a leak. This was one of the key factors that allowed us to discover TSA vulnerabilities without knowing about them beforehand.\n\n**Phase 3: Secret Extraction (function_user_1)**\n\nBack in user mode, we first clear the architectural state to eliminate any architectural information flow between actors. This is necessary to prevent any architectural information flows between the actors, which could otherwise lead to false positives in the analysis because Revizor is unable to distinguish between architectural and microarchitectural information flows (to be precise, Revizor would be able to distinguish them with a more subtle contract, but re-initializing the registers is a simpler solution).\n\n```assembly\nxor rax, rax  # noremove\nmov rax, qword ptr [r14 + 0x2000] # noremove\nmov rbx, qword ptr [r14 + 0x2008] # noremove\n# ... more register initialization\n```\n\nAfter that, the attacker execute another sequence of random instructions, which simulates the user process attempting to access the sensitive data that was just processed by the kernel. Note that this sequence may include an attempt to access kernel memory from the user mode (see the `user-to-kernel-access` fault allowlist in the configuration). As we found out post-factum, this is not strictly necessary for TSA-SQ, but it helps to create complex microarchitectural conditions that can trigger the leak.\n\nDepending on whether random instruction sequence triggers the fault, the user actor will either switch to the kernel mode explicitly (using the `switch_u2k.main.user_1` macro) or the CPU will transfer control to the fault handler (`fault_handler` macro in the `function_main_2`). In this experiment, we were not particularly interested in fault handling, so both paths lead to the same point in the template.\n\n**Phase 4: State Measurement (function_user_2)**\n\nFinally, the \"Reload\" stage in `function_user_2` measures which cache lines were accessed by the random code in the previous stage. If the accessed cache lines were somehow influenced by the kernel's secret data, this will lead to a discrepancy in the \"Reload\" measurements, leading to diverging hardware traces for different inputs, and ultimately to Revizor detecting a violation.\n\n## Configuration Overview\n\nBeyond the actor configuration, `config.yaml` contains several other important settings that guide the fuzzing campaign, as described next:\n\n* **Contract**: The contract configuration specifies what information leakage we consider acceptable\n\n```yaml\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - noninterference\n```\n\nThe `noninterference` execution clause implements the security property that observer actors cannot learn information about non-observer actors through microarchitectural channels. Combined with the `ct` (constant-time) observation clause, this allows the observer to see memory access patterns and control flow but prohibits leakage of raw data values.\n\n* **Exceptions**: The configuration includes `user-to-kernel-access` in the fault allowlist, which enables testing for Meltdown-type vulnerabilities. This was part of our original experimental design when we didn't yet know about TSA's existence. Revizor's program generator will randomly select memory accesses in the user actor and modify them to target kernel memory, triggering page faults.\n\nInterestingly, this exception-based approach helped discover TSA-SQ because the false completion events in the store queue can lead to timing differences in subsequent instructions, and the faults provide a constant-time reference point for the timing differences to get transformed into persistent cache state. Namely, when a variable-latency instruction is executed concurrently with a faulting instruction, it creates a race condition, where the cache impact of the variable-latency instruction can be influenced by whether the faulting instruction completes before or after it.\n\nNote the fault configuration quirk: we enable `user-to-kernel-access` globally but block it specifically for the main actor using `fault_blocklist`. This is the only way to enable a fault for a specific actor, because Revizor does not allow faults to be allow-listed for a specific actor.\n\n* **Statistical Analysis**: The statistical analysis parameters balance sensitivity with noise tolerance:\n\n```yaml\nanalyser_stat_threshold: 0.05\nexecutor_sample_sizes: [15, 40, 160, 320]\n```\n\nThe low threshold of 0.05 makes the analysis sensitive to subtle timing differences, while the adaptive sample sizes allow Revizor to start with quick tests and increase precision when potential violations are detected.\n\n* **Instruction Set**: The instruction set is defined as `x86-64` because we are targeting AMD CPUs, and the instruction categories include all base instructions, which allows for a wide range of microarchitectural interactions in the randomly generated code. Ideally, we would include even more categories, such as SIMD extensions and other advanced instructions, but Revizor does not yet support them (coming up soon, though!).\n\n## Running the Campaign\n\nWith the configuration and template in place, we can run the detection campaign using Revizor's `tfuzz` command. This command generates test cases based on the provided template and configuration, executes them, and analyzes the results for violations.\n\n```shell\n./revizor.py tfuzz -s base.json --save-violations t -w ./results/ \\\n    -c config.yaml -t template.asm -n 100000 -i 25\n```\n\nThis runs 100,000 test cases with 25 inputs each. The `--save-violations` flag preserves any detected violations for later analysis. When TSA-SQ is present, you'll eventually see output similar to:\n\n```\n================================ Violations detected ==========================\nContract trace:\n 14140085380608124960 (hash)\nHardware traces:\n  Input group 1: [11]\n  Input group 2: [36]\n  ^^^.........^.................................^^................ [287    | 36    ]\n  ^^^.........^.................................^................. [31     | 284   ]\n```\n\nThe different hardware trace patterns for inputs 11 and 36, despite having the same contract trace hash, indicate that the CPU is leaking information not predicted by the noninterference contract.\n\nOn our machine, the campaign typically takes about 5 hours to detect a leak, but your mileage may vary depending on the CPU model and due to the inherent randomness of the process.\n\n## Verifying Genuine Violations\n\nTo confirm that a detected violation is genuine, reproduce it using:\n\n```bash\n./revizor.py reproduce -s base.json -c ./results/violation-*/reproduce.yaml \\\n    -t ./results/violation-*/program.asm -i ./results/violation-*/input_*.bin\n```\n\nA genuine violation will reproduce consistently across multiple runs with the same statistical pattern, confirming that the timing differences represent a real microarchitectural information leak.\n\nThe next step is to do root-cause analysis of the violation, which is beyond the scope of this tutorial. See [Root-Causing a Violation Detected by Revizor](../../howto/root-cause-a-violation.md) for details on this process.\n\n"
  },
  {
    "path": "docs/ref/artifact-file-formats.md",
    "content": "# Artifact File Formats\n\nThis document describes the structure of violations artifact files stored by Revizor when it detects a contract violation.\n\n## Program Artifact Format\n\nThe program artifact is stored as an assembly file named `program.asm` in the violation directory (e.g., `violation-<timestamp>/program.asm`).\n\nThe file uses Intel syntax and is structured around actors, with each actor's code placed in a separate section.\n\nThe program artifact is structured as follows:\n\n```asm\n.intel_syntax noprefix         # Required: Use Intel syntax\n.test_case_enter:              # Required: marks the beginning of the test case\n\n.section .data.main            # Start of \"main\" actor section\n...                            # Instructions for main actor,\n                               # including possible control transfers to other actors\n\n.test_case_exit:               # Required: marks the end of the test case;\n                               # Must be within the \"main\" actor section\n\n.section .data.actor2          # Start of \"actor2\" actor section\n...                            # Instructions for actor2\n```\n\n\n## Input Data Artifact Format\n\nThe inputs to the program are stored as binary files in the violation directory, named according to their order in the input sequence (e.g., `violation-<timestamp>/input_004.bin`).\n\nThe format mimics the layout of the [sandbox memory](sandbox.md), with the only exception that some of the sections are removed as they are irrelevant for input data (e.g., the MACRO STACK and the padding areas).\n\nThe layout of the input data files is as follows:\n\n| Offset | Actor ID | Section Name | Size, B |\n| ------ | -------- | ------------ | ------- |\n| 0x0    | ACTOR 0  | MAIN AREA    | 0x1000  |\n| 0x1000 |          | FAULTY AREA  | 0x1000  |\n| 0x2000 |          | GPR AREA     | 0x40    |\n| 0x2040 |          | SIMD AREA    | 0x100   |\n| 0x2140 |          | (unused)     | 0xec0   |\n| 0x0    | ACTOR 1  | MAIN AREA    | 0x1000  |\n| 0x1000 |          | FAULTY AREA  | 0x1000  |\n| 0x2000 |          | GPR AREA     | 0x40    |\n| 0x2040 |          | SIMD AREA    | 0x100   |\n| 0x2140 |          | (unused)     | 0xec0   |\n| ...    | ...      | ...          | ...     |\n\n\n\n"
  },
  {
    "path": "docs/ref/binary-formats.md",
    "content": "# Binary Formats in Revizor\n\n!!! info \"Advanced Topic\"\n    This is an advanced topic describing internal implementation details of Revizor. You are unlikely to need this information unless you are extending or modifying Revizor's core components.\n\nThis document describes the structure of the custom binary formats used by Revizor to transfer test cases and their data between different components, specifically for transferring generated test cases and their inputs to the executor kernel module and to the DynamoRIO-based model backend.\n\nSuch custom formats are necessary because the components are implemented in different programming languages and different technologies, so passing objects directly is not possible. Using one of the standard formats (e.g., ELF) is also not an option because test cases in Revizor have special structure (e.g., multiple actors in different execution modes, some instructions are macros, etc.) and this structure is not supported by the standard formats.\n\nThe formats are designed to as simple as possible to minimize the overhead of serialization and deserialization.\n\n## Revizor Code Binary Format (RCBF)\n\nRCBF is a structured representation of the complete test case binary, together with its metadata.\nThe structure is as follows:\n\n``` yaml title=\"RCBF Structure\" linenums=\"1\"\nHEADER (16 bytes total)\n  n_actors:                8 bytes  # Number of Actors in the test case (also equals the number of code sections)\n  n_symbols:               8 bytes  # Number of symbols in the test case\n\nACTOR TABLE (48 x n_actors bytes)\n  actor_entry:             # (repeated n_actors times)\n    id:                    8 bytes  # Unique identifier for the actor\n    mode:                  8 bytes  # Execution mode of the actor\n    pl:                    8 bytes  # Protection level\n    data_permissions:      8 bytes  # Data access permissions\n    data_ept_permissions:  8 bytes  # EPT (Extended Page Table) data permissions\n    code_permissions:      8 bytes  # Code execution permissions\n\nSYMBOL TABLE (32 x n_symbols bytes)\n  symbol_entry:            # (repeated n_symbols times)\n    owner:                 8 bytes  # ID of the actor that owns this symbol\n    offset:                8 bytes  # (Offset of the symbol within its section\n    id:                    8 bytes  # (Symbol's unique identifier\n    args:                  8 bytes  # (Number of arguments the symbol takes (relevant for macros)\n\nMETADATA (24 x n_actors bytes)\n  metadata_entry:\n    owner:                 8 bytes  # (ID of the actor that owns this section\n    size:                  8 bytes  # (Size of the code section in bytes\n    reserved:              8 bytes  # (Reserved for future use\n\nDATA (8 kB x n_actors bytes)\n  code_section:            # (repeated n_actors times)\n    code:                  8 kB     # (Actual assembled binary code for the section\n```\n\nThe file begins with a header containing the number of actors (it is also the number of sections) and the number of symbols in the test case.\nThe term \"symbol\" in this context refers to any location in the test case that can be referenced.\nTwo common types of symbols are functions (specifically, function entry points) and macros.\n\nNext, the file contains the actor table, which is an array of actor metadata entries, one for each actor in the test case.\nThe actor metadata entry contains the actor's ID, execution mode, protection level, data permissions, EPT data permissions, and code permissions.\n\nAfter the actor table, the file contains the symbol table, which is an array of symbol entries, one for each symbol in the test case.\nThe symbol entry contains the ID the section to which the symbol belongs, the offset of the symbol within the section, the symbol's ID, and the number of arguments the symbol takes (if it is a macro).\n\nThe file continues with the table of metadata for each section in the test case.\nEach metadata entry contains the ID of the actor that owns the section and the size of the section.\n\nFinally, the file contains a sequence of code sections, one for each actor in the test case.\nThese sections contain the actual assembled binary for each of the sections in the test case.\n\n## Revizor Data Binary Format (RDBF)\n\nRDBF is a structured representation of the data used to initialize sandbox memory and registers before executing the test case.\n\nNote that this format combines multiple inputs into a single file. This is done because typically, a single test case program is executed multiple times with different inputs, and so it is more efficient to send a batch of inputs at once.\n\n\n``` yaml title=\"RDBF Structure\" linenums=\"1\"\nHEADER (16 bytes)\n  n_actors:               8 bytes  # Number of Actors in the test case (also equals the number of data sections)\n  n_inputs:               8 bytes  # Number of inputs in the batch\n\nMETADATA (16 x n_actors bytes)\n  metadata_entry:         # (repeated n_actors x n_inputs times)\n    section_size:         8 bytes  # Size of the data section\n    reserved:             8 bytes  # Reserved for future use\n\nDATA (12 x n_actors x n_inputs KB)\n  input:                  # (repeated n_inputs times)\n    data_section:         # (repeated n_actors times)\n      main_area:          4 KB  # Main data area\n      faulty_area:        4 KB  # Faulty page area\n      reg_init_region:    4 KB  # Register initialization area\n```\n\nThe file begins with a section containing the number of actors (equal to the number of sections) and the number of inputs in the batch.\n\nNext, the file contains the table of metadata for each data section, which only contains the size of the section.\n\nFinally, the file contains a sequence of data sections, one for each actor in the test case and each input in the batch. The data sections are arranged to mirror the data layout in the sandbox memory (see the [sandbox memory layout](sandbox.md) document for more information).\n\n"
  },
  {
    "path": "docs/ref/cli.md",
    "content": "# Command-Line Interface\n\nThis document provides a complete reference for all command-line options accepted by the `rvzr` command (or `./revizor.py` if running directly from the source tree).\n\n!!! note \"CLI vs Configuration Files\"\n    Revizor is controlled via two interfaces: command line arguments and a configuration file.\n    Command line arguments specify the mode of operation and set high-level parameters (e.g., file paths, number of fuzzing rounds), while the configuration file specifies details of the fuzzing campaign (e.g., the target contract, generation parameters, etc). This document focuses on the former; for information on configuration files, see the [configuration documentation](config.md).\n\n\n## General Syntax\n\nThe general syntax of the command line is:\n\n```\nrvzr MODE [OPTIONS]\n\n# Where MODE can be:\n#   fuzz            fuzzing mode\n#   tfuzz           template fuzzing mode\n#   reproduce       reproduce mode\n#   minimize        test case minimization mode\n#   analyse         stand-alone trace analysis mode\n#   generate        stand-alone generation mode\n#   download_spec   call the script that downloads the instruction set specification\n```\n\nThe available options depend on the selected mode. See [Execution Modes](modes.md) for descriptions of each mode's purpose and behavior.\n\nFor example, a typical way to run Revizor is in fuzzing mode with a command like this:\n\n```bash\nrvzr fuzz -s base.json -n 100 -i 10  -c config.yaml -w ./violations\n```\n\nThis command will run the fuzzer for 100 iterations (i.e., 100 test cases), with 10 inputs per test case.\nThe fuzzer will use the ISA spec stored in the `base.json` file, and will read the configuration from `config.yaml`. If the fuzzer finds a violation, it will be stored in the `./violations` directory.\n\n\n## Fuzzing Mode\n\nCommand-line arguments supported in `fuzz` mode:\n\n```\n  -h, --help            show this help message and exit\n  -c CONFIG, --config CONFIG\n                        Path to the configuration file (YAML) that will be used during fuzzing.\n  -I INCLUDE_DIR, --include-dir INCLUDE_DIR\n                        Path to the directory containing configuration files that included by the main configuration file (received via --config).\n  -s INSTRUCTION_SET, --instruction-set INSTRUCTION_SET\n                        Path to the instruction set specification (JSON) file.\n  -n NUM_TEST_CASES, --num-test-cases NUM_TEST_CASES\n                        Number of test cases.\n  -i NUM_INPUTS, --num-inputs NUM_INPUTS\n                        Number of inputs per test case.\n  -w WORKING_DIRECTORY, --working-directory WORKING_DIRECTORY\n  -t TESTCASE, --testcase TESTCASE\n                        Use an existing test case [DEPRECATED - see reproduce]\n  --timeout TIMEOUT     Run fuzzing with a time limit [seconds]. No timeout when set to zero.\n  --nonstop             Don't stop after detecting an unexpected result\n  --save-violations SAVE_VIOLATIONS\n                        If set, store all detected violations in working directory.\n```\n\n## Template Fuzzing Mode\n\nCommand-line arguments supported in `tfuzz` mode:\n\n```\n  -h, --help            show this help message and exit\n  -c CONFIG, --config CONFIG\n                        Path to the configuration file (YAML) that will be used during fuzzing.\n  -I INCLUDE_DIR, --include-dir INCLUDE_DIR\n                        Path to the directory containing configuration files that included by the main configuration file (received\n                        via --config).\n  -s INSTRUCTION_SET, --instruction-set INSTRUCTION_SET\n                        Path to the instruction set specification (JSON) file.\n  -n NUM_TEST_CASES, --num-test-cases NUM_TEST_CASES\n                        Number of test cases.\n  -i NUM_INPUTS, --num-inputs NUM_INPUTS\n                        Number of inputs per test case.\n  -w WORKING_DIRECTORY, --working-directory WORKING_DIRECTORY\n  -t TEMPLATE, --template TEMPLATE\n                        The template to use for generating test cases\n  --timeout TIMEOUT     Run fuzzing with a time limit [seconds]. No timeout when set to zero.\n  --nonstop             Don't stop after detecting an unexpected result\n  --save-violations SAVE_VIOLATIONS\n                        If set, store all detected violations in working directory.\n```\n\n## Reproduce Mode\n\nCommand-line arguments supported in `reproduce` mode:\n\n```\n  -h, --help            show this help message and exit\n  -c CONFIG, --config CONFIG\n                        Path to the configuration file (YAML) that will be used during fuzzing.\n  -I INCLUDE_DIR, --include-dir INCLUDE_DIR\n                        Path to the directory containing configuration files that included by the main configuration file (received\n                        via --config).\n  -s INSTRUCTION_SET, --instruction-set INSTRUCTION_SET\n                        Path to the instruction set specification (JSON) file.\n  -t TESTCASE, --testcase TESTCASE\n                        Path to the test case\n  -i [INPUTS ...], --inputs [INPUTS ...]\n                        Path to the directory with inputs\n  -n NUM_INPUTS, --num-inputs NUM_INPUTS\n                        Number of inputs per test case. [IGNORED if --input-dir is set]\n```\n\n## Minimize Mode\n\nCommand-line arguments supported in `minimize` mode:\n\n```\n  -h, --help            show this help message and exit\n  -c CONFIG, --config CONFIG\n                        Path to the configuration file (YAML) that will be used during fuzzing.\n  -I INCLUDE_DIR, --include-dir INCLUDE_DIR\n                        Path to the directory containing configuration files that included by the main configuration file (received\n                        via --config).\n  -s INSTRUCTION_SET, --instruction-set INSTRUCTION_SET\n                        Path to the instruction set specification (JSON) file.\n  --testcase TESTCASE, -t TESTCASE\n                        Path to the test case program that needs to be minimized.\n  -i NUM_INPUTS, --num-inputs NUM_INPUTS\n                        Number of inputs to the program that will be used during minimization.\n  --testcase-outfile TESTCASE_OUTFILE, -o TESTCASE_OUTFILE\n                        Output path for the minimized test case program.\n  --input-outdir INPUT_OUTDIR\n                        Output directory for storing minimized inputs.\n  --num-attempts NUM_ATTEMPTS\n                        Number of attempts to minimize the test case.\n  --enable-<pass>       Enable a specific pass during minimization.\n```\n\nSee also the [minimization documentation](minimization-passes.md) for a list of available minimization passes.\n\n## Stand-alone Trace Analysis\n\nCommand-line arguments supported in `analyse` mode:\n\n```\n  -h, --help            show this help message and exit\n  -c CONFIG, --config CONFIG\n                        Path to the configuration file (YAML) that will be used during fuzzing.\n  -I INCLUDE_DIR, --include-dir INCLUDE_DIR\n                        Path to the directory containing configuration files that included by the main configuration file (received\n                        via --config).\n  -s INSTRUCTION_SET, --instruction-set INSTRUCTION_SET\n                        Path to the instruction set specification (JSON) file.\n  --ctraces CTRACES\n  --htraces HTRACES\n```\n\n## Stand-alone Generation\n\nCommand-line arguments supported in `generate` mode:\n\n```\n  -h, --help            show this help message and exit\n  -c CONFIG, --config CONFIG\n                        Path to the configuration file (YAML) that will be used during fuzzing.\n  -I INCLUDE_DIR, --include-dir INCLUDE_DIR\n                        Path to the directory containing configuration files that included by the main configuration file (received\n                        via --config).\n  -s INSTRUCTION_SET, --instruction-set INSTRUCTION_SET\n                        Path to the instruction set specification (JSON) file.\n  -r SEED, --seed SEED  Add seed to generate test case.\n  -n NUM_TEST_CASES, --num-test-cases NUM_TEST_CASES\n                        Number of test cases.\n  -i NUM_INPUTS, --num-inputs NUM_INPUTS\n                        Number of inputs per test case.\n  -w WORKING_DIRECTORY, --working-directory WORKING_DIRECTORY\n  --permit-overwrite    Permit overwriting existing files.\n```\n\n### Download Instruction Set Specification\n\nThe following command-line arguments are supported in `download_spec` mode:\n\n```\n  -h, --help            show this help message and exit\n  -a ARCHITECTURE, --architecture ARCHITECTURE   The ISA to download the specification for (e.g., x86-64)\n  --outfile OUTFILE, -o OUTFILE   The destination file to save the downloaded specification.\n  --extensions [EXTENSIONS ...]   List of ISA extensions to include in the specification (e.g., SSE, VTX)\n```\n"
  },
  {
    "path": "docs/ref/config.md",
    "content": "# Configuration Options\n\nBelow is a list of the available configuration options for Revizor, which are passed down to Revizor via a config file.\n\nFor an example of how to write the config file, see [demo/big-fuzz.yaml](https://github.com/microsoft/side-channel-fuzzer/tree/main/demo/big-fuzz.yaml).\n\n\n## <a name=\"fuzzer\"></a> Fuzzing Configuration\n\n#### `fuzzer`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water:`basic`</span> Select the variant of a fuzzer to be used.\n\n    === \"Syntax\"\n        ```yaml\n        fuzzer: <mode>\n        ```\n    === \"Available Options\"\n        `basic` | `architectural` | `archdiff`\n    === \"Options Explained\"\n        * `basic` - normal model-based fuzzing. A violation in this mode indicates that the CPU\n        exposes more information than predicted by the contract. This option should be used in most\n        testing campaigns.\n        * `architectural` - self-fuzzing for architectural mismatches between the model and the executor.\n        This option should be used for testing the fuzzer itself, i.e., a violation in this\n        mode indicates a bug in the fuzzer rather then a bug in the CPU. This is useful when running\n        the fuzzer with a previously-untested instruction set, or when a new contract is implemented.\n        * `archdiff` - fuzzing for architectural invariants. This is a special mode targeted for\n        for semi-microarchitectural violations, similar to ZenBleed. This mode is experimental and\n        should be used with caution.\n\n#### `enable_priming`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `True`</span> This option enables or disables priming. It should be set to True in most cases, as priming is crucial for eliminating false positives.\n\n:    **What is priming?**: Priming solves the following problem: Revizor collects hardware traces for inputs in a sequence,\n    and the microarchitectural state is not reset between the inputs. This means that the microarchitectural\n    state for the input at, for example, position 100 is different from the state for the input at position 200.\n    Accordingly, the hardware traces for these inputs may differ because the measurements are taken in different\n    microarchitectural contexts.\n\n:    To address this issue, we use priming, which swaps the inputs in the sequence and re-runs the tests.\n    For example, if the original sequence is `(i1 . . . i99,i100,i101 . . . i199,i200)`, the priming\n    sequence will be `(i1 . . . i99,i200,i101 . . . i199,i100)`. If the violation persists in this\n    sequence, it is a true positive. If the violation disappears, it is a false positive, and it\n    will be discarded.\n\n    === \"Syntax\"\n        ```yaml\n        enable_priming: <True|False>\n        ```\n\n#### `enable_speculation_filter`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `False`</span> If enabled, Revizor will not consider test cases that do not trigger speculation.\n\n:    This option is useful for improving the throughput of the fuzzer, but it can discard potential violations if the leakage is not caused by speculation.\n\n    === \"Syntax\"\n        ```yaml\n        enable_speculation_filter: <True|False>\n        ```\n\n#### `enable_observation_filter`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `False`</span> If enabled, Revizor will not consider test cases that do not leave speculative traces.\n    This is achieved by pre-filtering: For each test case, Revizor adds an `LFENCE` after each instruction in the test case, and compares the resulting hardware traces with the original. If the traces are identical, the test case is discarded without further processing.\n\n:   This option is useful for improving the throughput of the fuzzer, but it can discard potential violations if the leakage is not caused by speculation.\n\n    === \"Syntax\"\n        ```yaml\n        enable_observation_filter: <True|False>\n        ```\n\n#### `enable_fast_path_model`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `True`</span> If enabled, the fuzzer will assume that all boosted inputs produce the same contract trace, and thus it will re-use the contract trace of the original input for all its boosted variants. This is normally a valid assumption to make if the taint tracker in the model does not contain bugs.\n\n:   This option is a pure performance optimization. It only impacts the speed of fuzzing, and not its correctness.\n\n    === \"Syntax\"\n        ```yaml\n        enable_fast_path_model: <True|False>\n        ```\n\n#### `color`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `False`</span> If enabled, the output will be colored.\nThis option is helps a lot with readability, but may produce corrupted output when redirected to a file.\n\n    === \"Syntax\"\n        ```yaml\n        color: <True|False>\n        ```\n\n#### `logging_modes`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `['info', 'stat']`</span> Control the information logged by Revizor.\n\n    === \"Syntax\"\n        ```yaml\n        logging_modes:\n          - <mode1>\n          - <mode2>\n          ...\n        ```\n    === \"Available Options\"\n        `info` | `stat` | `dbg_timestamp` | `dbg_violation` | `dbg_dump_htraces` | `dbg_dump_ctraces` | `dbg_dump_traces_unlimited` | `dbg_executor_raw` | `dbg_model` | `dbg_coverage` | `dbg_generator` | `dbg_priming` | `dbg_isa_filter`\n    === \"Options Explained\"\n        * `info` - general information about the progress of fuzzing;\n        * `stat` - statistics the end of the fuzzing campaign;\n        * `dbg_timestamp` - every 1000 test cases print the timestamp during the fuzzing process;\n        * `dbg_violation` - upon detecting a violation, print detailed information about it;\n        * `dbg_dump_htraces` - print the first 100 hardware traces for every test case;\n        * `dbg_dump_ctraces` - print the first 100 contract traces for every test case;\n        * `dbg_dump_traces_unlimited` - print ALL traces (use carefully, produces LOTS of text);\n        * `dbg_executor_raw` - prints hardware traces for every stage of the fuzzing process;\n        this differs from `dbg_dump_htraces` in that it prints the traces collected by\n        speculation/observation filters as well as at every iteration of multi-sample collection;\n        * `dbg_model` - print a detailed info about EVERY instruction executed on the model (use carefully, produces LOTS of text);\n        * `dbg_coverage` - stores instruction coverage information;\n        * `dbg_generator` - prints a list of instructions used to generate test cases;\n        * `dbg_priming` - prints information about the priming process; only useful for debugging the priming mechanism itself.\n        * `dbg_isa_filter` - when rvzr loads information about the instruction set (normally, from `base.json`), it filters out some of the instructions, either because of the config options provided by the user, or because some instructions are known to cause issues in the model or executor. This debug option prints the list of instructions that were filtered out, along with the reason for filtering them out.\n\n#### `multiline_output`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `False`</span> If enabled, each output message will be printed on a separate line. Otherwise, the fuzzing progress will be continuously overwriting the same line (works only in the terminal).\n\n    === \"Syntax\"\n        ```yaml\n        enable_priming: <True|False>\n        ```\n\n## <a name=\"code-generator\"></a> Program Generator Configuration\n\n#### `generator`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `random`</span> Select the type of program generator to be used.\n\n    === \"Syntax\"\n        ```yaml\n        generator: <type>\n        ```\n    === \"Available Options\"\n        `random`\n    === \"Options Explained\"\n        * `random` - generate random assembly programs. This is the only supported option at the moment.\n\n#### `instruction_set`\n\n:   <span class=\"inline-box\" title=\"Default Value is Chosen Automatically Based on the Target CPU\">:octicons-cpu-24:</span> The instruction set under test.\n\n    === \"Syntax\"\n        ```yaml\n        instruction_set: <isa>\n        ```\n    === \"Available Options\"\n        `x86-64` | `arm64`\n\n#### `instruction_categories`\n\n:   <span class=\"inline-box\" title=\"Default Value is Chosen Automatically Based on the Target CPU\">:octicons-cpu-24:</span> Select a list of instruction categories to be used when generating programs. This list effectively filters out instructions from the ISA descriptor file (e.g., `base.json`) passed via the command line (`-s`).\n\n    !!! info \"Priority\"\n        This list has higher priority than `instruction_blocklist` but lower than `instruction_allowlist`.\n\n        The resulting instruction pool is:\n        `all from(instruction_categories) - instruction_blocklist + instruction_allowlist`\n\n    === \"Syntax\"\n        ```yaml\n        instruction_categories:\n          - <category1>\n          - <category2>\n          ...\n        ```\n    === \"Available Options\"\n        Any category in the ISA descriptor file (`base.json`).\n\n#### `instruction_blocklist`\n\n:   <span class=\"inline-box\" title=\"Default Value is Chosen Automatically Based on the Target CPU\">:octicons-cpu-24:</span> A list of instructions that will **not** be used for generating programs. This list filters out instructions from `instruction_categories`, but not from `instruction_allowlist`.\n\n    !!! info \"Priority\"\n        This list has lower priority than `instruction_allowlist`.\n\n        The resulting instruction pool is:\n        `all from(instruction_categories) - instruction_blocklist + instruction_allowlist`\n\n    !!! warning \"Danger Zone\"\n        This option has a somewhat sensible default value for each supported architecture, selected to avoid known-bad instructions. Thus, setting this option explicitly is unadvisable. Prefer using `instruction_blocklist_append` to add more instructions to the default blocklist.\n\n    === \"Syntax\"\n        ```yaml\n        instruction_blocklist:\n          - <instruction1>\n          - <instruction2>\n          ...\n        ```\n    === \"Available Options\"\n        Any instruction in the ISA descriptor file (`base.json`).\n\n#### `instruction_blocklist_append`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `[]`</span> A list of instructions that will be appended to the default blocklist for the target ISA. This option is identical to `instruction_blocklist`, but the list is added to the default instead of replacing it.\n\n    !!! info \"Priority\"\n        This list has lower priority than `instruction_allowlist`.\n\n        The resulting instruction pool is:\n        `all from(instruction_categories) - instruction_blocklist + instruction_allowlist`\n\n    === \"Syntax\"\n        ```yaml\n        instruction_blocklist_append:\n          - <instruction1>\n          - <instruction2>\n          ...\n        ```\n    === \"Available Options\"\n        Any instruction in the ISA descriptor file (`base.json`).\n\n#### `instruction_allowlist`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `[]`</span> A list of instructions to use for generating programs.\n\n    !!! info \"Priority\"\n        This list has priority over `instruction_categories` and over `instruction_blocklist`, thus adding instructions on top of the categories.\n\n        The resulting instruction pool is:\n        `all from(instruction_categories) - instruction_blocklist + instruction_allowlist`\n\n    === \"Syntax\"\n        ```yaml\n        instruction_allowlist:\n          - <instruction1>\n          - <instruction2>\n          ...\n        ```\n    === \"Available Options\"\n        Any instruction in the ISA descriptor file (`base.json`).\n\n#### `program_generator_seed`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `0`</span> Seed of the program generator (aka code generator). If set to zero, a random seed will be used for each run.\n\n    === \"Syntax\"\n        ```yaml\n        program_generator_seed: <seed>\n        ```\n\n#### `program_size`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `24`</span> Number of instructions in the test case programs to be produced by the code generator. Note that the actual size might be larger because of the instrumentation.\n\n    === \"Syntax\"\n        ```yaml\n        program_size: <size>\n        ```\n\n#### `avg_mem_accesses`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `12`</span> Average number of memory accesses in the test case programs to be produced by the code generator. The actual number will be random, but the average over all programs will be close to this value.\n\n    === \"Syntax\"\n        ```yaml\n        avg_mem_accesses: <count>\n        ```\n\n#### `min_bb_per_function`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `1`</span> Minimal number of basic blocks per function in generated programs.\n\n    === \"Syntax\"\n        ```yaml\n        min_bb_per_function: <count>\n        ```\n\n#### `max_bb_per_function`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `2`</span> Maximal number of basic blocks per function in generated programs.\n\n    === \"Syntax\"\n        ```yaml\n        max_bb_per_function: <count>\n        ```\n\n#### `min_successors_per_bb`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `2`</span> Minimal number of successors for each basic block in generated programs.\n\n    !!! note \"Hint, not a rule\"\n        This option is a *hint*; it could be overwritten\n\n        * if the instruction set does not have the necessary instructions to satisfy it\n        * if a certain number of successor is required for correctness.\n        * if min_successors_per_bb > max_successors_per_bb, the value is overwritten with max_successors_per_bb\n\n    === \"Syntax\"\n        ```yaml\n        min_successors_per_bb: <count>\n        ```\n\n#### `max_successors_per_bb`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `2`</span> Maximal number of successors for each basic block in generated programs.\n\n    !!! note \"Hint, not a rule\"\n        This option is a *hint*; it could be overwritten\n\n        *  if the instruction set does not have the necessary instructions to satisfy it\n        *  if a certain number of successor is required for correctness\n\n    === \"Syntax\"\n        ```yaml\n        max_successors_per_bb: <count>\n        ```\n\n#### `register_allowlist`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `[]`</span> A list of registers that **can** be used for generating programs.\n\n    !!! info \"Priority\"\n        This list has higher priority than `register_blocklist`. The resulting list is: `(all registers - register_blocklist) + register_allowlist`.\n\n    === \"Syntax\"\n        ```yaml\n        register_allowlist:\n          - <register1>\n          - <register2>\n          ...\n        ```\n    === \"Available Options\"\n        Any register supported by the target CPU.\n\n#### `register_blocklist`\n\n:   <span class=\"inline-box\" title=\"Default Value is Chosen Automatically Based on the Target CPU\">:octicons-cpu-24:</span> A list of registers that will **not** be used for generating programs.\n\n    !!! info \"Priority\"\n        This list has lower priority than `register_allowlist`. The resulting list is: `(all registers - register_blocklist) + register_allowlist`.\n\n    !!! warning \"Danger Zone\"\n        The default value of this option includes registers that reserved for internal use by the executor, and thus should be avoided. Modifying this option may lead to a full system crash.\n\n    === \"Syntax\"\n        ```yaml\n        register_blocklist:\n          - <register1>\n          - <register2>\n          ...\n        ```\n    === \"Available Options\"\n        Any register supported by the target CPU.\n\n#### `faults_allowlist`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `[]`</span> By default, the generator will produce programs that never trigger exceptions. This option modifies this behavior by permitting the generator to produce 'unsafe' instruction sequences that could potentially trigger an exception. The model and executor will also be configured to handle these exceptions gracefully.\n\n    === \"Syntax\"\n        ```yaml\n        faults_allowlist:\n          - <fault1>\n          - <fault2>\n          ...\n        ```\n    === \"Available Options\"\n        `div-by-zero` | `div-overflow` | `opcode-undefined` | `breakpoint` | `debug-register` | `non-canonical-access` | `user-to-kernel-access`\n    === \"Options Explained\"\n        * `div-by-zero` - generate divisions with unmasked divisor, which can cause a division by zero exception.\n        * `div-overflow` - generate divisions with unmasked dividend, which can cause an overflow exception.\n        * `opcode-undefined` - generate undefined opcodes, which can cause an undefined opcode exception.\n        * `breakpoint` - generate breakpoints, which can cause INT3 exceptions.\n        * `debug-register` - generate instructions that cause INT1 exceptions.\n        * `non-canonical-access` - randomly select a memory access in a generated program and instrument it to access a non-canonical address.\n        * `user-to-kernel-access` - randomly select memory access instructions in user-privilege actors and instrument them to access the kernel actor's (actor 0) memory. This creates cross-privilege-level memory access patterns useful for detecting CPU vulnerabilities like Meltdown. Requires at least one actor with `privilege_level: user`. The instrumentation modifies both the memory operands and the sandboxing masks to ensure accesses target the kernel's FAULTY data area.\n\n\n## <a name=\"actor\"></a> Actor Configuration\n\nAll actors are defined in the `actors` list, with the following syntax:\n\n```yaml\nactors:\n  - <actor1_name>:\n      <actor_option>: <value>\n      <actor_option>:\n        <sub_option1>: <value1>\n        <sub_option2>: <value2>\n      ...\n  - <actor2_name>:\n      ...\n  ...\n```\n\nThe following options are available for each actor:\n\n#### `mode`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `host`</span> The execution mode of the actor.\n\n    === \"Syntax\"\n        ```yaml\n        actors:\n          - <actor_name>:\n              mode: <mode>\n        ```\n    === \"Available Options\"\n        `host` | `guest`\n    === \"Options Explained\"\n        * `host` - the actor runs in the normal, non-virtualized mode.\n        * `guest` - the actor runs in a VM (one VM per actor).\n\n#### `privilege_level`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `kernel`</span> The privilege level of the actor.\n\n    === \"Syntax\"\n        ```yaml\n        actors:\n          - <actor_name>:\n              privilege_level: <level>\n        ```\n    === \"Available Options\"\n        `user` | `kernel`\n    === \"Options Explained\"\n        * `user` - the actor runs in user mode (CPL=3).\n        * `kernel` - the actor runs in kernel mode (CPL=0).\n\n#### `data_properties`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: (see below)</span> The properties of the data memory used by the actor. These properties are applied only to the faulty page of the actor's data region (see [sandbox](../ref/sandbox.md) for details).\n\n:   Note that the above properties are set in the host page tables for actors with `mode: host`, and in the guest page tables for actors with `mode: guest`.\n\n    === \"Syntax\"\n        ```yaml\n        actors:\n          - <actor_name>:\n              data_properties:\n                present: <True|False>\n                writable: <True|False>\n                user: <True|False>\n                accessed: <True|False>\n                dirty: <True|False>\n                executable: <True|False>\n                reserved_bit: <True|False>\n                randomized: <True|False>\n        ```\n    === \"Available Options\"\n        `present` | `writable` | `user` | `accessed` | `dirty` | `executable` | `reserved_bit` | `randomized`\n    === \"Options Explained\"\n        * `present` [default: True] - the value of the Present bit in the page table entry.\n        * `writable` [default: True] - the value of the Writable bit in the page table entry.\n        * `user` [default: False] - the value of the User/Supervisor bit in the page table entry.\n        * `accessed` [default: True] - the value of the Accessed bit in the page table entry.\n        * `dirty` [default: True] - the value of the Dirty bit in the page table entry.\n        * `executable` [default: False] - the value of the Executable bit in the page table entry.\n        * `reserved_bit` [default: False] - the value of the Reserved bit in the page table entry.\n        * `randomized` [default: False] - if true, the values of the above properties will be randomized for each test case.\n\n#### `data_ept_properties`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `(see below)`</span> The properties of the EPT entry used by the actor (on Intel) or the NPT entry (on AMD). These properties are applied only to the faulty page of the actor's data region (see [sandbox](../ref/sandbox.md) for details).\n\n:   This property has no effect on actors with `mode: host`.\n\n    === \"Syntax\"\n        ```yaml\n        actors:\n          - <actor_name>:\n              data_ept_properties:\n                present: <True|False>\n                writable: <True|False>\n                executable: <True|False>\n                accessed: <True|False>\n                dirty: <True|False>\n                user: <True|False>\n                reserved_bit: <True|False>\n                randomized: <True|False>\n        ```\n    === \"Available Options\"\n        `present` | `writable` | `executable` | `accessed` | `dirty` | `user` | `reserved_bit` | `randomized`\n    === \"Options Explained\"\n        * `present` [default: True] - the value of the Present bit in the EPT/NPT entry.\n        * `writable` [default: True] - the value of the Writable bit in the EPT/NPT entry.\n        * `executable` [default: False] - the value of the Executable bit in the EPT/NPT entry.\n        * `accessed` [default: True] - the value of the Accessed bit in the EPT/NPT entry.\n        * `dirty` [default: True] - the value of the Dirty bit in the EPT/NPT entry.\n        * `user` [default: False] - the value of the User/Supervisor bit in the EPT/NPT entry.\n        * `reserved_bit` [default: False] - the value of the Reserved bit in the EPT/NPT entry.\n        * `randomized` [default: False] - if true, the values of the above properties will be randomized for each test case.\n\n#### `observer`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `False`</span> If enabled, the actor will be an observer actor, hence modelling an attacker. This option is only used if the contract is `noninterference`, and it is ignored otherwise.\n\n    === \"Syntax\"\n        ```yaml\n        actors:\n          - <actor_name>:\n              observer: <True|False>\n        ```\n\n#### `instruction_blocklist`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `[]`</span> Actor-specific instruction blocklist.\n\n:   This option is useful when writing a test case template that uses multiple actors, and some actors should use a different set of instructions than the others. For example, if privileged instructions should be blocked for low-privilege actors.\n\n    !!! info \"Priority\"\n        This list has priority over the global `instruction_blocklist` and modifies the instruction pool for the specific actor.\n\n    === \"Syntax\"\n        ```yaml\n        actors:\n          - <actor_name>:\n              instruction_blocklist:\n                - <instruction1>\n                - <instruction2>\n                ...\n        ```\n\n#### `fault_blocklist`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `[]`</span> Actor-specific fault blocklist.\n\n:   For example, when using `user-to-kernel-access`, you typically want to add it to the kernel actor's `fault_blocklist` to prevent the kernel from accessing its own memory (which would not be a cross-privilege access).\n\n    !!! info \"Priority\"\n        This list has priority over the global `faults_allowlist` and modifies the fault-inducing instrumentation for the specific actor.\n\n    === \"Syntax\"\n        ```yaml\n        actors:\n          - <actor_name>:\n              fault_blocklist:\n                - <fault1>\n                - <fault2>\n                ...\n        ```\n    === \"Available Options\"\n        See [`faults_allowlist`](#faults_allowlist) for the list of available faults.\n\n\n## <a name=\"data-generator\"></a> Data Generator Configuration\n\n#### `data_generator`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `random`</span> Select the method of test case data generation.\n\n    === \"Syntax\"\n        ```yaml\n        data_generator: <type>\n        ```\n    === \"Available Options\"\n        `random`\n    === \"Options Explained\"\n        * `random` - generate random input data for the test cases. This is the only supported option at the moment.\n\n#### `data_generator_seed`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `10`</span> Seed of the test case data generator. If set to zero, a random seed will be used for each run.\n\n    === \"Syntax\"\n        ```yaml\n        data_generator_seed: <seed>\n        ```\n\n#### `data_generator_entropy_bits`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `31`</span> Entropy of the random values created by the data generator.\n\n    === \"Syntax\"\n        ```yaml\n        data_generator_entropy_bits: <bits>\n        ```\n    === \"Allowed Values\"\n        Integer in the range `[1, 31]`\n\n#### `input_gen_probability_of_special_value`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `0.05`</span> When set to a non-zero value, the data generator will occasionally produce special values (such as zero or MAX_INT) alongside random values, with the frequency controlled by this probability. These special values help exercise fast-path optimizations in the microarchitecture.\n\n    === \"Syntax\"\n        ```yaml\n        input_gen_probability_of_special_value: <probability>\n        ```\n    === \"Allowed Values\"\n        Float in the range `[0.0, 1.0]`\n\n#### `inputs_per_class`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `2`</span> Number of inputs generated for each input class via input boosting (aka Contract-Driven Input Generation). For the explanation of the input classes and the generation algorithm, see [this paper](https://arxiv.org/pdf/2301.07642), Section 4.D. Contract-driven Input Generator.\n\n    === \"Syntax\"\n        ```yaml\n        inputs_per_class: <count>\n        ```\n\n\n## <a name=\"contract\"></a> Contract Configuration\n\n#### `contract_execution_clause`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `['seq']`</span> The execution clause of the contract. Multiple clauses can be combined to form a more permissive contract.\n\n    === \"Syntax\"\n        ```yaml\n        contract_execution_clause:\n          - <clause>\n        ```\n    === \"Available Options\"\n        `seq` | `no_speculation` | `seq-assist` | `cond` | `conditional_br_misprediction` | `bpas` | `nullinj-fault` | `nullinj-assist` | `delayed-exception-handling` | `div-zero` | `div-overflow` | `meltdown` | `fault-skip` | `noncanonical` | `vspec-ops-div` | `vspec-ops-memory-faults` | `vspec-ops-memory-assists` | `vspec-ops-gp` | `vspec-all-div` | `vspec-all-memory-faults` | `vspec-all-memory-assists`\n    === \"Options Explained\"\n        * `seq` - sequential execution.\n        * `no_speculation` - sequential execution. Synonym for `seq`.\n        * `seq-assist` - sequential execution with possible microcode assists.\n        * `cond` - permitted misprediction of conditional branches.\n        * `conditional_br_misprediction` - permitted misprediction of conditional branches. Synonym for `cond`.\n        * `bpas` - permitted speculative store bypass\n        * `nullinj-fault` - page faults are permitted to speculatively return zero.\n        * `nullinj-assist` - microcode assists are permitted to speculatively return zero.\n        * `delayed-exception-handling` - upon an exception or a fault, data-independent instructions that follow the exception are allowed to execute speculatively.\n        * `meltdown` - permission-based page faults are permitted to speculatively return the value in the memory.\n        * `fault-skip` - upon a fault, the faulting instruction is speculatively skipped.\n        * `noncanonical` - permitted speculative non-canonical memory accesses.\n        * `vspec*` - experimental contracts for value speculation. See [this paper](https://www.usenix.org/system/files/usenixsecurity23-hofmann.pdf) for details.\n        * `div-zero` - experimental contract; do not use.\n        * `div-overflow` - experimental contract; do not use.\n\n#### `contract_observation_clause`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `ct`</span> The observation clause of the contract. In most cases, the default value should be used.\n\n    === \"Syntax\"\n        ```yaml\n        contract_observation_clause: <clause>\n        ```\n    === \"Available Options\"\n        `none` | `l1d` | `memory` | `pc` | `ct` | `loads+stores+pc` | `ct-nonspecstore` | `ctr` | `arch` | `tct` | `tcto` | `ct-ni`\n    === \"Options Explained\"\n        * `none` - the model observes nothing. Useful for testing the fuzzer.\n        * `l1d` - the model observes the addresses of data accesses, adjusted to imitate the L1D cache trace. Has very few real applications, and should be generally avoided.\n        * `memory` - the model observes the addresses of data accesses.\n        * `ct` (constant time tracer) - the model observes the addresses of data accesses and the control flow.\n        * `loads+stores+pc` - the model observes the addresses of data accesses and the control flow. Synonym for `ct`.\n        * `ct-nonspecstore` - the model observes the addresses of data accesses and the control flow, but does not observe the addresses of stores during speculation.\n        * `ctr` - the model observes the addresses of data accesses and the control flow, as well as the values of the general-purpose registers.\n        * `arch` - the model observes the addresses of data accesses and the control flow, as well as the values loaded from memory. This clause imitates the security guarantees provided by secure speculation mechanisms like STT.\n        * `tct` (truncated constant time tracer) - the model observes address of the memory access and of the program counter at cache line granularity.\n        * `tcto` (truncated constant time tracer with overflows) - the model address of the memory access and of the program counter at cache line granularity + observe cache line overflows.\n        * `ct-ni` - (only available in multi-actor context) when executing actors with `observer: false`, the model observes the same data as as with `ct`. When executing actors with `observer: true`, the model observes complete memory of the actor as well as their register values.\n\n#### `model_backend`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `unicorn`</span> The backend used to implement the contract model.\n\n    === \"Syntax\"\n        ```yaml\n        model_backend: <backend>\n        ```\n    === \"Available Options\"\n        `dummy` | `unicorn` | `dynamorio`\n    === \"Options Explained\"\n        * `unicorn` - use Unicorn-based implementation of the model. This backend is more mature and feature-rich, but it supports a considerably smaller set of instruction than DynamoRIO (essentially, only the base x86 or ARM instruction sets, without any extensions).\n        * `dynamorio` - use DynamoRIO-based implementation of the model. This backend is less mature and supports fewer contracts and features than Unicorn, but it can handle a much larger set of instructions, including complex extensions like AVX-512 on x86-64. It is also generally faster than Unicorn, especially when testing large test case or running with many inputs per test case.\n        * `dummy` - use a dummy model. This model always returns the same (empty) contract trace, and as such will not produce meaningful results. This option is useful, however, when root-causing violations, because it allows to collect hardware traces without running the model, hence allowing to trace instructions that are not supported by any of the backends.\n\n#### `model_min_nesting`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `1`</span> Minimum number of nested mispredictions in the model. This value is used to generate the contract traces on the fast path of the fuzzer. Chose a small value when speculation is rare, and a larger value when speculation is common.\n\n:   This option is a pure performance optimization. It only impacts the speed of fuzzing, and not its correctness.\n\n    === \"Syntax\"\n        ```yaml\n        model_min_nesting: <depth>\n        ```\n\n#### `model_max_nesting`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `30`</span> Maximum number of nested mispredictions in the model. This value is used to generate the contract traces on the slow path of the fuzzer, i.e., when a potential violation is detected and the fuzzer tries to check if it is a true positive.\n\n:   In contrast to `model_min_nesting`, this option could cause false positives if set too low. Thus, it is advisable to set it to a sufficiently high value to cover all possible nested mispredictions in the test cases. Leave the default unless you are sure that a lower value is sufficient.\n\n    === \"Syntax\"\n        ```yaml\n        model_max_nesting: <depth>\n        ```\n\n#### `model_max_spec_window`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `250`</span> Size of the speculation window in the model.\n\n:    This option sets a trade-off between accuracy and performance. A larger speculation window avoids potential false positives due to inaccurate modelling of the speculation, but it also slows down the model execution. Leave the default unless you are sure that a different value is needed.\n\n    === \"Syntax\"\n        ```yaml\n        model_max_spec_window: <size>\n        ```\n\n## <a name=\"executor\"></a> Executor Configuration\n\n#### `executor`\n\n:   <span class=\"inline-box\" title=\"Default Value is Chosen Automatically Based on the Target CPU\">:octicons-cpu-24:</span> ISA-specific version of the executor to use. The default value is auto-detected based on `cpuinfo`. Should be changed only if the auto-detection fails.\n\n    === \"Syntax\"\n        ```yaml\n        executor: <type>\n        ```\n    === \"Available Options\"\n        `x86-64-intel` | `x86-64-amd` | `arm64`\n\n#### `executor_mode`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `P+P`</span> Method of collecting hardware traces in the executor. The method determines the contents of hardware traces.\n\n    === \"Syntax\"\n        ```yaml\n        executor_mode: <mode>\n        ```\n    === \"Available Options\"\n        `P+P` | `F+R` | `E+R` | `PP+P` | `TSC`\n    === \"Options Explained\"\n        * `P+P` - prime and probe side-channel attack. The hardware traces contain the cache sets that were accessed during the execution of the test case.\n        * `F+R` - flush and reload side-channel attack. The hardware traces contain the memory addresses that were accessed during the execution of the test case.\n        * `E+R` - evict and reload side-channel attack. The hardware traces contain the cache sets that were accessed during the execution of the test case.\n        * `PP+P` - partial prime and probe (i.e., leave a subset of cache lines unprimed). The hardware traces contain the cache sets that were accessed during the execution of the test case.\n        * `TSC` - use `RDTSCP` instruction to measure the execution time of test cases. The hardware traces contain the execution time, in cycles.\n\n#### `executor_warmups`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `5`</span> Number of warmup rounds executed before starting to collect hardware traces.\n\n    === \"Syntax\"\n        ```yaml\n        executor_warmups: <count>\n        ```\n\n#### `executor_sample_sizes`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `[10, 50, 100, 500]`</span> A list of sample sizes to be used during the measurements.\n\n    !!! info \"Clarification\"\n        Executor normally performs measurements multiple times for each test case in order to collect a sample of hardware traces. This allows Revizor to tolerate noise and non-determinism in the measurements by applying statistical methods for comparing the traces.\n\n        For performance reasons, Revizor does not immediately use a large sample size. Instead, it starts with a small sample, collects the traces, and checks if a violation is detected. If no violation is detected, the executor assumes that the test case is safe, and moves on to the next one. If a violation is detected, however, the executor tries to reproduce it with larger sample sizes.\n\n        This option defines the list of sample sizes through which Revizor will iterate in this process. To make it sensible, the list should be sorted in ascending order with a reasonable gap between the sizes.\n\n    === \"Syntax\"\n        ```yaml\n        executor_sample_sizes:\n          - <sample_size1>\n          - <sample_size2>\n          ...\n        ```\n\n#### `executor_filtering_repetitions`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `10`</span> The sample size to be used by the speculation and observation filters.\n\n    === \"Syntax\"\n        ```yaml\n        executor_filtering_repetitions: <count>\n        ```\n\n#### `executor_taskset`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `0`</span> The CPU core ID which the executor will use for running test cases. That is, the executor process will be pinned to this core.\n\n    === \"Syntax\"\n        ```yaml\n        executor_taskset: <core_id>\n        ```\n\n#### `enable_pre_run_flush`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `True`</span> If enabled, the executor will do its best to flush the microarchitectural state before running test cases.\n\n    === \"Syntax\"\n        ```yaml\n        enable_pre_run_flush: <True|False>\n        ```\n\n## <a name=\"analyser\"></a> Analyser Configuration\n\n#### `analyser`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `chi2`</span> The type of the analyser that is used to compare hardware traces against contract traces.\n\n    === \"Syntax\"\n        ```yaml\n        analyser: <type>\n        ```\n    === \"Available Options\"\n        `chi2` | `mwu` | `sets` | `bitmaps`\n    === \"Options Explained\"\n        * `sets` - combine the hardware traces for each input into a set. A violation is reported if two inputs in the same contract-equivalence class have different sets of hardware traces.\n        * `bitmaps` - combine the hardware traces for each input into a bitmap. A violation is reported if two inputs in the same contract-equivalence class have different bitmaps of hardware traces.\n        * `chi2` - use the chi-squared homogeneity test to compare the hardware traces of inputs in the same contract-equivalence class. This test effectively checks if the hardware traces from two different inputs come from the same distribution. A violation is reported if the test fails.\n        * `mwu` - [experimental; both false positives and negatives are possible] use the Mann-Whitney U test to compare the hardware traces of inputs in the same contract-equivalence class. This test effectively checks if the hardware traces from two different inputs come from the same distribution. A violation is reported if the test fails.\n\n#### `analyser_subsets_is_violation`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `False`</span> This option is relevant only for the `sets` and `bitmaps` analysers. If enabled, the analyser will not label hardware traces as mismatching if they form a subset relation.\n\n    === \"Syntax\"\n        ```yaml\n        analyser_subsets_is_violation: <True|False>\n        ```\n\n#### `analyser_outliers_threshold`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `0.1`</span> This option is relevant only for the `sets` and `bitmaps` analysers. The analyser will ignore the hardware traces that appear in less than this percentage of the sampled traces.\n\n    === \"Syntax\"\n        ```yaml\n        analyser_outliers_threshold: <threshold>\n        ```\n\n#### `analyser_stat_threshold`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `0.5`</span> This option is relevant only for the `chi2` and `mwu` analysers. The threshold for the statistical tests. If a pair of hardware traces has the (normalized) statistics below the threshold, then the traces are considered equivalent.\n\n:   For the chi2 test, the threshold is applied to the `statistics / (len(htrace1) + len(htrace2))`.\n\n:   For the mwu test, the threshold is applied to the p-value.\n\n    === \"Syntax\"\n        ```yaml\n        analyser_stat_threshold: <threshold>\n        ```\n\n## <a name=\"misc\"></a> Miscellaneous Configuration\n\n#### `coverage_type`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `none`</span> The type of coverage tracking.\n\n    === \"Syntax\"\n        ```yaml\n        coverage_type: <type>\n        ```\n    === \"Available Options\"\n        `none` | `model_instructions`\n    === \"Options Explained\"\n        * `none` - disable coverage tracking.\n        * `model_instructions` - track how many times the model executed each instruction in the target ISA.\n\n#### `minimizer_retries`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `1`</span> Number of minimization retries. When the minimizer performs a check to reduce a test case, each check is attempted this number of times and it succeeds if at least one check is successful.\n\n    === \"Syntax\"\n        ```yaml\n        minimizer_retries: <count>\n        ```\n\n## Unique x86-64 Options\n\n\n#### `x86_executor_enable_ssbp_patch`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `True`</span> Enable a microcode patch against Speculative Store Bypass, if available.\n\n    === \"Syntax\"\n        ```yaml\n        x86_executor_enable_ssbp_patch: <True|False>\n        ```\n\n#### `x86_executor_enable_prefetcher`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `False`</span> Enable all prefetchers, if the software controls are available.\n\n    === \"Syntax\"\n        ```yaml\n        x86_executor_enable_prefetcher: <True|False>\n        ```\n\n#### `x86_disable_div64`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `True`</span> Do not generate 64-bit division instructions. Useful for avoiding certain types of speculation that are specific to 64-bit division.\n\n    === \"Syntax\"\n        ```yaml\n        x86_disable_div64: <True|False>\n        ```\n\n#### `x86_enable_hpa_gpa_collisions`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `False`</span> When a test case contains at least one guest actor, allocate its memory in the guest physical address space to match the corresponding host physical addresses of the main actor. Useful for testing Foreshadow-like leaks.\n\n    === \"Syntax\"\n        ```yaml\n        x86_enable_hpa_gpa_collisions: <True|False>\n        ```\n\n#### `x86_generator_align_locks`\n\n:   <span class=\"inline-box\" title=\"Default Value\">:material-water: `True`</span> When generating memory accesses with locks, apply instrumentation to align the locks to 8 bytes. Useful for avoiding faults on unaligned accesses.\n\n    === \"Syntax\"\n        ```yaml\n        x86_generator_align_locks: <True|False>\n        ```\n\n---\n\n## What's Next?\n\n- [Command Line Interface](cli.md) - CLI options and modes\n- [demo/big-fuzz.yaml](https://github.com/microsoft/side-channel-fuzzer/tree/main/demo/big-fuzz.yaml) - Comprehensive example configuration\n- [demo/](https://github.com/microsoft/side-channel-fuzzer/tree/main/demo/) - Example configurations for various scenarios\n"
  },
  {
    "path": "docs/ref/index.md",
    "content": "# Reference Documentation\n\nComplete technical reference for all Revizor components, commands, configuration options, and formats.\n\n## User-Facing Components\n\n* [Command Line Interface](cli.md)\nComplete reference for all `rvzr` command-line options and arguments. Covers common options and mode-specific parameters.\n* [Execution Modes](modes.md)\nDetailed specifications for all execution modes: fuzzing, template fuzzing, reproduce, minimize, analyse, generate, and download_spec.\n\n* [Configuration Options](config.md)\nComplete reference for all configuration file parameters organized by component: fuzzer, generator, executor, model, analyser, and actors.\n\n* [Macros Reference](macros.md)\nComplete reference for all template macros including measurement control, fault handling, code generation, and actor transitions.\n\n* [Minimization Passes](minimization-passes.md)\nComplete list of available minimization passes for reducing test case complexity while preserving violations.\n\n## Architecture & Internals\n\nLow-level technical references for Revizor's internal components.\n\n* [Binary Formats](binary-formats.md)\nSpecifications for Revizor's binary file formats: RCBF (Revizor Contract Binary Format) and RDBF (Revizor DynamoRIO Binary Format).\n\n* [Registers](registers.md)\nRegister specifications and conventions for x86-64 and ARM64 architectures.\n\n* [Sandbox](sandbox.md)\nMemory layout and sandboxing mechanisms used during test execution.\n"
  },
  {
    "path": "docs/ref/macros.md",
    "content": "# Macros\n\nThis document provides a complete reference for all macros available in Revizor.\n\n!!! note \"Related Documentation\"\n    This document is intended as a reference; if you're looking for a practical guide on how to use the `macros`, please refer to [How-To: Use Macros](../howto/use-macros.md).\n\n## Overview\n\nMacros are special pseudo-instructions in assembly test cases that appear as labels with the `.macro` prefix. They are dynamically expanded into actual implementations during execution by the model and executor. Macros enable complex operations like domain transitions, measurement control, and random code generation within test cases.\n\nMacros accept up to four static arguments. Arguments are strictly static (either a constant integer or a string); dynamic values (registers, memory addresses) are not supported.\n\n=== \"Syntax\"\n\n    ```assembly\n    .macro.<macro_name>.<argument1>.<argument2>.<argument3>.<argument4>:\n    ```\n\n=== \"Example\"\n\n    ```assembly\n    ; Macro to switch execution to\n    ; a function called `main` that belongs to the actor `actor_2`\n    .macro.switch.user.function_user_0:\n    ```\n\n## Measurement Macros\n\nControl the start and end of hardware and contract trace collection.\n\n#### <a name=\"measurement_start\"></a> `measurement_start`\n\n:   Begins hardware and contract trace collection. Instructions before this macro are executed but not included in the contract/hardware traces.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.measurement_start:\n\n        ; alternative\n        .macro.measurement_start.<label>:\n        ```\n\n    === \"Arguments\"\n        1. `label` (optional): Unique identifier if the macro is used multiple times\n\n\n#### <a name=\"measurement_end\"></a> `measurement_end`\n\n:   Ends hardware and contract trace collection. Instructions after this macro are executed but not included in the contract/hardware traces.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.measurement_end:\n\n        ; alternative\n        .macro.measurement_end.<label>:\n        ```\n\n    === \"Arguments\"\n        1. `label` (optional): Unique identifier if the macro is used multiple times\n\n\n## Transition Macros\n\nSwitch between different actors and privilege levels, including kernel-user and host-guest transitions.\n\n#### <a name=\"set_h2g_target\"></a> `set_h2g_target`\n\n:   Sets the VM entry point for host-to-guest transitions.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.set_h2g_target.<actor_name>.<function_name>:\n        ```\n\n    === \"Arguments\"\n        - `actor_name`: Target guest actor identifier\n        - `function_name`: Entry point function in guest actor\n\n#### <a name=\"set_g2h_target\"></a> `set_g2h_target`\n\n:   Sets the VM exit point for guest-to-host transitions.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.set_g2h_target.<actor_name>.<function_name>:\n        ```\n\n    === \"Arguments\"\n        - `actor_name`: Target host actor identifier\n        - `function_name`: Landing point function in host actor\n\n#### <a name=\"switch_h2g\"></a> `switch_h2g`\n\n:   Performs host-to-guest transition (VM entry). The entry and exit point must be set beforehand using `set_h2g_target` and `set_g2h_target` macros.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.switch_h2g.<actor_name>.<label>:\n        ```\n\n    === \"Arguments\"\n        - `actor_name`: Target guest actor identifier\n        - `label` (optional): Unique identifier if the macro is used multiple times\n\n\n#### <a name=\"landing_h2g\"></a> `landing_h2g`\n\n:   Marks the guest landing point after host-to-guest transition. This macro works together with `switch_h2g` to ensure complete restoration of the execution context.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.landing_h2g.<label>:\n        ```\n\n    === \"Arguments\"\n        - `label` (optional): Unique identifier if the macro is used multiple times\n\n\n#### <a name=\"switch_g2h\"></a> `switch_g2h`\n\n:   Performs guest-to-host transition (VM exit). The entry and exit point must be set beforehand using `set_h2g_target` and `set_g2h_target` macros.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.switch_g2h.<actor_name>.<label>:\n        ```\n\n    === \"Arguments\"\n        - `actor_name`: Target host actor identifier\n        - `label` (optional): Unique identifier if the macro is used multiple times\n\n\n#### <a name=\"landing_g2h\"></a> `landing_g2h`\n\n:   Marks the host landing point after guest-to-host transition. This macro works together with `switch_g2h` to ensure complete restoration of the execution context.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.landing_g2h.<label>:\n        ```\n\n    === \"Arguments\"\n        - `label` (optional): Unique identifier if the macro is used multiple times\n\n\n#### <a name=\"set_k2u_target\"></a> `set_k2u_target`\n\n:   Sets the user mode entry point for kernel-to-user transitions.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.set_k2u_target.<actor_name>.<function_name>:\n        ```\n\n    === \"Arguments\"\n        - `actor_name`: Target user-mode actor identifier\n        - `function_name`: Entry point function in user actor\n\n\n#### <a name=\"set_u2k_target\"></a> `set_u2k_target`\n\n:   Sets the kernel mode entry point for user-to-kernel transitions.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.set_u2k_target.<actor_name>.<function_name>:\n        ```\n\n    === \"Arguments\"\n        - `actor_name`: Target kernel-mode actor identifier\n        - `function_name`: Entry point function in kernel actor\n\n\n#### <a name=\"switch_k2u\"></a> `switch_k2u`\n\n:   Performs kernel-to-user transition (privilege level drop). The entry and exit point must be set beforehand using `set_k2u_target` and `set_u2k_target` macros.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.switch_k2u.<actor_name>.<label>:\n        ```\n\n    === \"Arguments\"\n        - `actor_name`: Target user actor identifier\n        - `label` (optional): Unique identifier if the macro is used multiple times\n\n\n#### <a name=\"switch_u2k\"></a> `switch_u2k`\n\n:   Performs user-to-kernel transition (privilege level escalation). The entry and exit point must be set beforehand using `set_k2u_target` and `set_u2k_target` macros.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.switch_u2k.<actor_name>.<label>:\n        ```\n\n    === \"Arguments\"\n        - `actor_name`: Target kernel actor identifier\n        - `label` (optional): Unique identifier if the macro is used multiple times\n\n\n#### <a name=\"landing_k2u\"></a> `landing_k2u`\n\n:   Marks the user-mode landing point after kernel-to-user transition.  This macro works together with `switch_k2u` to ensure complete restoration of the execution context.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.landing_k2u.<label>:\n        ```\n\n    === \"Arguments\"\n        - `label` (optional): Unique identifier if the macro is used multiple times\n\n\n#### <a name=\"landing_u2k\"></a> `landing_u2k`\n\n:   Marks the kernel-mode landing point after user-to-kernel transition.  This macro works together with `switch_u2k` to ensure complete restoration of the execution context.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.landing_u2k.<label>:\n        ```\n\n    === \"Arguments\"\n        - `label` (optional): Unique identifier if the macro is used multiple times\n\n## Fault Handling Macros\n\nDefine exception and interrupt handlers within test cases.\n\n#### <a name=\"fault_handler\"></a> `fault_handler`\n\n:   Specifies the control flow target for exceptions and interrupts. When an exception occurs, control transfers to this location. If not defined, the executor uses a default handler that jumps to the test case exit point.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.fault_handler:\n        ```\n\n    === \"Arguments\"\n        - None\n\n\n## Environment Configuration Macros\n\nChange the execution environment from within a test case.\n\n#### <a name=\"set_data_permissions\"></a> set_data_permissions\n\n:   Configures data permission on the faulty page of the current actor by modifying the page table entry (PTE) permissions.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.set_data_permissions.<set_mask>.<clear_mask>:\n        ```\n    === \"Arguments\"\n        - `set_mask`: 16-bit bitmask specifying which permission bits to set (ORed with the faulty page's PTE)\n        - `clear_mask`: 16-bit bitmask specifying which permission bits to clear (ANDed with the faulty page's PTE)\n\n\n## Generation Macros\n\nDefine automatically-generated points within a template. Available only in the [Template Fuzzing Mode](modes.md#tfuzz)\n\nIn contrast to the rest of the macros, generation macros are used by the generator instead of the executor or model. By the point the executor/model run the test case, these macros are expected to have been already expanded into actual code.\n\n#### <a name=\"random_instructions\"></a> `random_instructions`\n\n:   Generates N random instructions during template expansion. Used in template fuzzing mode to insert randomized code sequences.\n\n    === \"Syntax\"\n        ```assembly\n        .macro.random_instructions.<num_instructions>.<avg_mem_accesses>.<label>:\n        ```\n\n    === \"Arguments\"\n        - `num_instructions`: Number of random instructions to generate\n        - `avg_mem_accesses`: Average number of memory accesses. Average means that when a large-enough number of test cases are generated, the mean number of memory accesses per expansion of this macro will approximate this value.\n        - `label` (optional): Unique identifier if the macro is used multiple times\n\n---\n\n## What's Next?\n\n- [How to Use Macros](../howto/use-macros.md) - Detailed usage guide and implementation details\n- [How to Use Templates](../howto/use-templates.md) - Template-based testing\n- [Actors](../topics/actors.md) - Multi-domain testing concepts\n\n**Examples:**\n\n- [demo/tsa-l1d/template.asm](https://github.com/microsoft/side-channel-fuzzer/tree/main/demo/tsa-l1d/template.asm) - TSA-L1D attack template with actor transitions\n- [demo/tsa-sq/template.asm](https://github.com/microsoft/side-channel-fuzzer/tree/main/demo/tsa-sq/template.asm) - TSA-SQ attack template with actor transitions\n"
  },
  {
    "path": "docs/ref/minimization-passes.md",
    "content": "# Minimization Passes\n\nThis document provides a detailed list of all available minimization features (passes) supported in the `minimize` execution mode of Revizor. These passes are used to simplify randomly generated violation artifacts to enable human analysis.\n\n!!! note \"Related Documentation\"\n    This document is intended as a reference; if you're looking for a practical guide on how to use the `minimize` mode, please refer to [How-To: Minimize Violation](../howto/minimize.md).\n\n## Types of Passes\n\n`minimize` mode supports three types of passes:\n\n* program passes modify the program\n* input passes modify the input sequence\n* analysis passes provide additional information about the violation, usually by adding comments to the program.\n\n## Program Passes\n\n#### <a name=\"enable-instruction-pass\"></a>`--enable-instruction-pass`\n\n:   Enables the instruction minimization pass that iteratively removes instructions from the program while preserving the violation.\n\n#### <a name=\"enable-simplification-pass\"></a>`--enable-simplification-pass`\n\n:   Enables the instruction simplification pass that replaces complex instructions with simpler ones while preserving the violation.\n\n#### <a name=\"enable-nop-pass\"></a>`--enable-nop-pass`\n\n:   Enables the NOP replacement pass that iteratively replaces instructions with NOPs of the same size while preserving the violation.\n\n#### <a name=\"enable-constant-pass\"></a>`--enable-constant-pass`\n\n:   Enables the constant simplification pass that replaces immediate arguments of instructions with 0s while preserving the violation.\n\n#### <a name=\"enable-mask-pass\"></a>`--enable-mask-pass`\n\n:   Enables the mask simplification pass that reduces the size of the instrumentation masks while preserving the violation.\n\n#### <a name=\"enable-label-pass\"></a>`--enable-label-pass`\n\n:   Enables the label removal pass that removes unused labels from the assembly file.\n\n#### <a name=\"enable-fence-pass\"></a>`--enable-fence-pass`\n\n:   Enables the fence insertion pass that adds LFENCEs after instructions while preserving the violation.\n\n## Input Passes\n\n#### <a name=\"enable-input-seq-pass\"></a>`--enable-input-seq-pass`\n\n:   Enables the input sequence minimization pass that removes inputs from the original generated sequence while preserving the violation.\n\n#### <a name=\"enable-input-diff-pass\"></a>`--enable-input-diff-pass`\n\n:   Enables the violating input difference minimization pass that operates on the pair of (contract-equivalent) inputs that triggered the violation and attempts to minimize the difference between the two inputs.\nIt does so by iterating over all bytes in the inputs, and (1) attempting to replace each byte with zero, and if it fails, (2) copying the byte from the first input to the second input.\n\n## Analysis Passes\n\n#### <a name=\"enable-source-analysis\"></a>`--enable-source-analysis`\n\n:   Enables the speculation source identification pass that analyzes the program to identify suspected sources of speculation, and adds the corresponding comments to the assembly file.\nNote that the analysis is not guaranteed to be correct, and it may produce false results.\n\n#### <a name=\"enable-comment-pass\"></a>`--enable-comment-pass`\n\n:   Enables the violation comment pass that adds comments to the assembly file with details about the violation.\n"
  },
  {
    "path": "docs/ref/modes.md",
    "content": "# Execution Modes\n\nRevizor supports several modes of operation, each targeting a different use cases.\nThe selection of the mode is described in the [CLI documentation](cli.md).\nBelow is a brief description of each mode.\n\n## Overview\n\n| Mode             | CLI Key       | Use Case                      | Description                                                                                                |\n| ---------------- | ------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------- |\n| Fuzzing          | fuzz          | General Testing               | Test a CPU against a contract model. Test cases generated randomly                                         |\n| Template Fuzzing | tfuzz         | Targeted Testing              | Test a CPU against a contract model. Test cases generated based on a template                              |\n| Reproduce        | reproduce     | Reproducing a Violation       | Reproduce a violation found by fuzzing OR run a manually-written test case                                 |\n| Minimization     | minimize      | Simplification of a Violation | Simplify a test case by applying a series of simplification passes to the test case program and its inputs |\n| Trace Analysis   | analyse       | External Integration          | Analyze pre-recorded traces for violations                                                                 |\n| Generation       | generate      | External Integration          | Only generate test cases, without testing them                                                             |\n| ISA Spec Install | download_spec | Tool Installation             | Call a script that downloads the instruction set specification                                             |\n\n\n## <a name=\"fuzz\"></a> `fuzz`\n\n=== \"Syntax\"\n```bash\n$ rvzr fuzz [OPTIONS]\n```\n\n:   Main fuzzing mode of Revizor.\nIn this mode, Revizor randomly generates test cases and executes them on the target CPU and the model, records the corresponding traces, and checks if the hardware traces contain the same (or less) information as the contract traces. That is, it implements [Model-Based Relational Testing](../intro/03-primer.md#model-based-relational-testing-and-revizor) approach.\n\n:   **Use case:** Broad testing of CPU behavior against contract specifications.\n\n\n\n## <a name=\"tfuzz\"></a> `tfuzz`\n\n=== \"Syntax\"\n```bash\n$ rvzr tfuzz [OPTIONS]\n```\n\n:   Similar to the fuzzing mode, but test cases are generated based on a template. For details on templates, see the [template fuzzing how-to guide](../howto/use-templates.md).\n\n:   **Use case:** Targeted testing of specific scenarios, microarchitectural patches, or actor interactions.\n\n\n## <a name=\"reproduce\"></a> `reproduce`\n\n=== \"Syntax\"\n```bash\n$ rvzr reproduce [OPTIONS]\n```\n:   In this mode, Revizor loads and executes a specific test case data and inputs from files. Performs single fuzzing round with the provided test case and inputs, and reports the results.\n\n:   Test cases can be violations from previous fuzzing runs or manually-written test programs.\n\n:   **Use cases:**\n\n    - Checking reproducibility: Testing if a violation artifact can be consistently reproduced on other CPUs or configurations.\n    - Verification of a violation: Confirming that a violation is genuine and not a false positive.\n    - Manual testing: Executing a custom test case written by the user.\n    - Root-causing: Checking the impact of manual modifications to a test case.\n\n\n\n## <a name=\"minimize\"></a> `minimize`\n\n=== \"Syntax\"\n```bash\n$ rvzr minimize [OPTIONS]\n```\n\n:   In this mode, Revizor applies simplification passes to a violation test case, reducing program and input complexity while preserving the violation behavior.\n\n:   **Use case:** Simplify violations for root cause analysis.\n\n\n\n## <a name=\"analyse\"></a> `analyse`\n\n=== \"Syntax\"\n```bash\n$ rvzr analyse [OPTIONS]\n```\n\n:   In this mode, Revizor analyzes pre-recorded contract and hardware traces for violations without executing test cases.\nAccepts trace files as input and applies the configured analyser to detect contract violations.\n\n:   **Use case:** Integration with external tools that perform trace collection separately from Revizor.\n\n\n## <a name=\"generate\"></a> `generate`\n\n=== \"Syntax\"\n```bash\n$ rvzr generate [OPTIONS]\n```\n:   Generates test cases without execution. Outputs test programs and inputs to them.\n\n:   **Use case:** Integration with external tools that use Revizor's test case generation capabilities.\n\n\n\n## <a name=\"download_spec\"></a> `download_spec`\n\n=== \"Syntax\"\n```bash\n$ rvzr download_spec [OPTIONS]\n```\n:   This mode is only used when Revizor is being set up. Downloads, parses, and stores instruction set specifications in JSON format.\n\n:   **Use case:** Tool installation and ISA specification management.\n\n\n## What's Next?\n\n* [Command Line Interface](cli.md) - How to run Revizor in different modes\n* [Minimization Passes](minimization-passes.md) - Available passes for the `minimize` mode\n"
  },
  {
    "path": "docs/ref/papers.md",
    "content": "# Research Papers\n\nRevizor is a result of extensive academic research in the field of hardware security and microarchitectural side-channel analysis. Below is a list of key research papers related to Revizor, its underlying concepts, and methodologies:\n\n\n=== \"Main Papers\"\n\n    If you use Revizor in your research or work, please consider citing some of the following papers:\n\n    * Original paper that introduced the concept of Model-based Relation Testing as well as the Revizor tool:\n\n        > Oleksii Oleksenko, Christof Fetzer, Boris Köpf, Mark Silberstein. \"[Revizor: Testing Black-box CPUs against Speculation Contracts](https://www.microsoft.com/en-us/research/publication/revizor-testing-black-box-cpus-against-speculation-contracts/)\" in Proceedings of the 27th ACM International Conference on Architectural Support for Programming Languages and Operating Systems (ASPLOS), 2022.\n\n    * Theoretical foundations of leakage contract:\n\n        > Marco Guarnieri, Boris Köpf, Jan Reineke, and Pepe Vila. \"[Hardware-software contracts for secure speculation](https://www.microsoft.com/en-us/research/publication/hardware-software-contracts-for-secure-speculation/)\" in Proceedings of the 2021 IEEE Symposium on Security and Privacy (S&P), 2021.\n\n=== \"Extensions to Revizor\"\n\n    The following papers present significant extensions and improvements to Revizor:\n\n     * Paper that introduced speculation filtering, observation filtering, and contract-based input generation:\n\n        > Oleksii Oleksenko, Marco Guarnieri, Boris Köpf, and Mark Silberstein. \"[Hide and Seek with Spectres: Efficient discovery of speculative information leaks with random testing](https://www.microsoft.com/en-us/research/publication/hide-and-seek-with-spectres-efficient-discovery-of-speculative-information-leaks-with-random-testing/)\" in Proceedings of the 2023 IEEE Symposium on Security and Privacy (SP), 2022.\n\n    * Paper that introduced exception-based testing (i.e., focus on Meltdown, Foreshadow) into Revizor:\n\n        > Jana Hofmann, Emanuele Vannacci, Cédric Fournet, Boris Köpf, and Oleksii Oleksenko. \"[Speculation at Fault: Modeling and Testing Microarchitectural Leakage of CPU Exceptions.](https://www.usenix.org/conference/usenixsecurity23/presentation/hofmann)\" in Proceedings of 32nd USENIX Security Symposium (USENIX Security), 2023.\n\n    * Paper that introduced testing of cross-VM and user-kernel leaks in Revizor, as well as presented TSA attacks on AMD CPUs:\n\n        > Oleksii Oleksenko, Flavien Solt, Cédric Fournet, Jana Hofmann, Boris Köpf, Stavros Volos. \"[Enter, Exit, Page Fault, Leak: Testing Isolation Boundaries for Microarchitectural Leaks](https://www.microsoft.com/en-us/research/wp-content/uploads/2025/07/Enter-Exit-SP26.pdf)\" in Proceedings of the 2026 IEEE Symposium on Security and Privacy (SP), 2026.\n\n=== \"Using Revizor\"\n\n    The following papers present case studies and practical applications of (parts of) Revizor:\n\n    * **AMuLet, 2025**: Ported Revizor to test Gem5 models of secure speculation mechanisms\n\n        > Bo Fu, Leo Tenenbaum, David Adler, Assaf Klein, Arpit Gogia, Alaa R. Alameldeen, Marco Guarnieri, Mark Silberstein, Oleksii Oleksenko, and Gururaj Saileshwar. \"[AMuLeT: Automated Design-Time Testing of Secure Speculation Countermeasures](https://arxiv.org/pdf/2503.00145)\". In Proceedings of the 30th ACM International Conference on Architectural Support for Programming Languages and Operating Systems, Volume 2 (ASPLOS '25). Association for Computing Machinery, New York, NY, USA, 32–47. https://doi.org/10.1145/3676641.3716247\n\n    * **LmTest, 2024**: Used a modified version of Revizor's leakage model to test cryptographic libraries against speculation contracts\n\n        > Gilles Barthe, Marcel Böhme, Sunjay Cauligi, Chitchanok Chuengsatiansup, Daniel Genkin, Marco Guarnieri, David Mateos Romero, Peter Schwabe, David Wu, and Yuval Yarom. 2024. Testing Side-channel Security of Cryptographic Implementations against Future Microarchitectures. In Proceedings of the 2024 on ACM SIGSAC Conference on Computer and Communications Security (CCS '24). Association for Computing Machinery, New York, NY, USA, 1076–1090. https://doi.org/10.1145/3658644.3670319\n\n\n\n"
  },
  {
    "path": "docs/ref/registers.md",
    "content": "# Register Allocation\n\nThe test cases are executed in a sandboxed environment, where some of the registers are reserved for internal use, and some are available for use in the test cases.\nBelow is a list of registers and their purpose.\n\n!!! info \"Advanced Topic\"\n    This is an advanced topic describing internal implementation details of Revizor. You are unlikely to need this information unless you are extending or modifying Revizor's core components.\n\n####`R15`\n\nContains the base address of the UTILITY area in the [sandbox](sandbox.md).\n\nIf the test case does not enter a VM, the register value remains constant during the execution of the test cases.\nOtherwise, the register value is updated to point to the UTILITY area of the currently active VM when the `switch_h2g` macro is called, and it is restored to the original value when the `switch_g2h` macro is called.\n\nThe register is used by internal functions, such as the implementation of Prime+Probe.\n\n####`R14`\n\nContains the base address of the current actor's [sandbox](sandbox.md) (namely, it points to the base of the actor's MAIN area).\n\nAt the beginning of the test case execution, the register is set to the base address of the MAIN area of the first actor (actor `main`). The register value is updated to point to the MAIN area of the currently active actor when a macro from the `landing_*` group of macros is called. It is also updated by the `fault_handler` macro.\n\nThe register is used in test cases as a part of the sandboxing mechanism.\nFor example, all generated memory accesses are relative to the value stored in `R14`, and have the form of `[R14 + offset]`.\n\n\n####`R13` (`HTRACE_REGISTER` constant in the kernel module)\n\nContains either intermediate or final result of the hardware trace measurements.\n\nBefore entering the test case, the register is set to 0.\nWhen a `measurement_start` macro is executed, the register is (optionally) set to the starting value,\nsuch a initial reading of time stamp counter when the `TSC` mode is used.\nWhen a `measurement_end` macro is executed, the register is updated with the final value of the measurement and contains the resulting hardware trace.\n\n####`R12` (`STATUS_REGISTER` constant in the kernel module)\n\nContains a compressed status of the test case execution:\n\nBits[0:7] contain a measurement status.\nAt the beginning of the test case execution, the bits are set to 0.\nWhen `measurement_start` macro is executed, the bits are set to 1.\nWhen `measurement_end` macro is executed, the bits are set to 2.\nIf the measurement status is not 2 at the end of the test case execution, the kernel module will report an error.\n\nBits[8:31] are unused.\n\nBits[32:63] contain a counter of SMI (System Management Interrupt) events.\nThe counter is set automatically before entering the test case (`READ_SMI_START`), and updated when the test case finishes (`READ_SMI_END`).\nIf the difference between the readings is not 0, the kernel module will report an error.\n\n####`R11`\n\nThe register is used as a temporary buffer by some of the macros.\n\nBefore entering the test case, the register is set to 0.\nWhen certain macros are executed (e.g., `set_k2u_target`), the register will contain temporary values.\nThe register should not be used in the test case, as the temporary value may be consumed by latter macros.\n\n####`R10, R9, R8`\n\nStores the values of performance counters.\n`R10` stores the value of performance counter #1, `R9` stores the value of performance counter #2, and `R8` stores the value of performance counter #3.\n\nBefore entering the test case, the registers are set to 0.\nWhen a `measurement_start` macro is executed, the registers are (optionally) set to the starting values.\nWhen a `measurement_end` macro is executed, the registers are updated with the final values of the measurements.\n\n\n####Other General Purpose Registers\n\nThe remaining registers (`rax`, `rcx`, `rdx`, `rsi`, `rdi`, `rflags`) are available for use in the test cases and can be modified freely.\nA special case are `rsp` and `rbp`, which can be used in the test cases, but their values must always remain within the sandbox (see [Sandbox](sandbox.md)).\n\n####Vector Registers\n\nVector registers (`xmm0`-`xmm15`) are also available for use in the test cases.\nHowever, only `xmm0-xmm7` are initialized with input-based values, and the remaining registers are always zero-initialized.\n\nLarge-size vector registers (`ymm` and `zmm`) are not supported.\n"
  },
  {
    "path": "docs/ref/runtime-statistic.md",
    "content": "# Fuzzing Statistics\n\nThis document provides a complete reference on how to interpret the runtime statistics output of Revizor. These statistics are generated during fuzzing campaigns and provide insights into the performance and behavior of the fuzzer.\n\nThe runtime statistics are essentially printed twice: once during the fuzzing campaign, in a form of a continuously-updated progress log, and once at the end of the campaign, in a summarized report. The statistics in both places have the same meaning, but the final report includes cumulative totals for the entire campaign.\n\n## Runtime Statistics Fields\n\nA typical runtime statistics output looks like this:\n\n```\n17    ( 2%)| Stats: Cls:100/100,In:200,R:7,SF:10,OF:7,Fst:0,CN:0,CT:0,P1:0,CS:0,P2:0,V:0> Priming  27\n```\n\nThis line is continuously updated on each iteration of the fuzzer (after each test case is executed).\n\nThe fields are as follows:\n\n* `17    ( 2%)` - The current test case number and progress towards the total number of test cases.\n* `Cls:100/100` - The average number of unique equivalence classes per test case. The left number is number of \"effective\" classes (those that have at least two hardware inputs), while the right number is total classes observed. In a well-functioning campaign, these numbers should be equal. See [contract equivalence class](../glossary.md#contract-equivalence-class) in the glossary.\n* `In:200` - The number of inputs per test case. Normally, this number is equal to `-i` parameter passed times `inputs_per_class` configuration option.\n* `R:7` - Average number of hardware tracing samples per input. See [Trace Analysis - Statistical Comparison](../topics/trace-analysis.md) for more details.\n* `SF:10,OF:7,Fst:0,CN:0,CT:0,P1:0,CS:0,P2:0` - The number of test cases that have been filtered by each stage of the false-positive elimination pipeline.\n    * `SF` - Number of test cases filtered by the speculation filter.\n    * `OF` - Number of test cases filtered by the observation filter.\n    * `Fst` - Number of test cases filtered after fast-path execution.\n    * `CN` - Number of test cases filtered out when model nesting was increased from 1 (fast path) to `max_model_nesting`.\n    * `CT` - Number of test cases that had taint mistakes.\n    * `P1` - Number of test cases filtered out by priming stage with the minimal sample size.\n    * `CS` - Number of test cases filtered out when the sample size was increased to a non-minimal value.\n    * `P2` - Number of test cases filtered out by priming stage with the non-minimal sample size.\n* `V:0` - The number of detected violations so far (can be non-zero when running with `--nonstop` flag).\n* `Priming  27             ` - Current stage of the false-positive elimination pipeline.\n\n## Final Summary Report\n\nA typical final summary report looks like this:\n\n```\n================================ Statistics ===================================\n\nTest Cases: 18\nInputs per test case: 200.0\nViolations: 1\nEffectiveness:\n  Total Cls: 98.0\n  Effective Cls: 98.0\nDiscarded Test Cases:\n  Speculation Filter: 10\n  Observation Filter: 7\n  Fast Path: 0\n  Max Nesting Check: 0\n  Tainting Check: 0\n  Early Priming Check: 0\n  Large Sample Check: 0\n  Priming Check: 0\n\nDuration: 40.5\nFinished at 15:40:23\n```\n\nThis section summarizes overall statistics from the fuzzing campaign. The fields are similar to those explained in the runtime output section above:\n\n* `Test Cases` - Total number of test cases executed during the campaign.\n* `Inputs per test case` - Average number of inputs executed per test case.\n* `Violations` - Total number of violations detected during the campaign (may >1 when running with `--nonstop` flag).\n* `Effectiveness` - The average number of unique equivalence classes per test case. `Total Cls` is number of \"effective\" classes (those that have at least two hardware inputs), while `Effective Cls` is total classes observed. In a well-functioning campaign, these numbers should be equal. See [contract equivalence class](../glossary.md#contract-equivalence-class) in the glossary.\n* `Discarded Test Cases` - The number of test cases that have been filtered by each stage of the false-positive elimination pipeline.\n* `Duration` - Total duration of the fuzzing campaign in seconds.\n* `Finished at` - Timestamp when the fuzzing campaign completed.\n"
  },
  {
    "path": "docs/ref/sandbox.md",
    "content": "# Test Case Sandbox\n\nThis document describes the isolated environment for executing test cases, which is referred to as the *sandbox*. The sandbox contains the test case code and data, and the test case code is confined to access memory only within the sandbox.\n\nThe sandbox is implemented by all modules that execute test cases, including the executor (kernel module) and all model backends (Unicorn, DynamoRIO).\nTo ensure that the executions are consistent across all modules, the sandbox is structured in the same way in all the modules.\n\nThis document describes the memory layout of the sandbox, the initialization of the sandbox memory, and the fault isolation mechanism.\n\n## Memory Layout\n\nThe sandbox memory is divided into two main areas: the data sandbox and the code sandbox.\nEach actor in the test case has its own sub-area for its data and code, and the layout of these areas is the same for all actors.\n\n### Data Layout\n\nThe data area of a test case in a sandbox is organized as follows:\n\n\n| Offset | Actor ID | Area Name     | Size, B |\n| ------ | -------- | ------------- | ------- |\n| 0x0    | ACTOR 0  | Macro Stack   | 0x40    |\n| 0x40   |          | Underflow Pad | 0xfc0   |\n| 0x1000 |          | Main Area     | 0x1000  |\n| 0x2000 |          | Faulty Area   | 0x1000  |\n| 0x3000 |          | GPR Area      | 0x40    |\n| 0x3040 |          | SIMD Area     | 0x100   |\n| 0x3140 |          | Overflow Pad  | 0xec0   |\n| 0x4000 | ACTOR 1  | Macro Stack   | 0x40    |\n| 0x4040 |          | Underflow Pad | 0xfc0   |\n| 0x5000 |          | Main Area     | 0x1000  |\n| 0x6000 |          | Faulty Area   | 0x1000  |\n| 0x7000 |          | GPR Area      | 0x40    |\n| 0x7040 |          | SIMD Area     | 0x100   |\n| 0x7140 |          | Overflow Pad  | 0xec0   |\n| ...    | ...      | ...           | ...     |\n\n\nThe data area is divided into the following regions:\n\n* **Main and Faulty Areas**: These are the two regions of memory that are accessible by the test case code.\n  This is enforced by the test case generator, which instruments all memory accesses to ensure that they fall within these regions (see [code-generation](../internals/architecture/code.md) for more details).\n  Both areas are initialized with the input data from the [RBDF](binary-formats.md).\n  The main area always has default permissions (RW), while the faulty area has permissions can be configured to cause a fault when accessed.\n  This configuration originates from the [config file](config.md).\n* **GPR and SIMD Areas**: These regions store the values that will be used by the modules to initialize the general-purpose registers (GPR) and SIMD registers before executing the test case and when switching between actors. Both areas are initialized with the input data from the [RBDF](binary-formats.md).\n  The order of registers is: `rax`, `rbx`, `rcx`, `rdx`, `rsi`, `rdi`, `flags`, `rsp` for GPRs, and `xmm0` to `xmm7` for SIMD registers.\n* **Over- and Underflow Pads**: These two zero-initialized regions surround the actors' data areas, and their purpose is to determinize the hardware traces on the executor.\n  Namely, they are needed for the cases when the CPU speculatively bypasses the sandboxing instrumentation inserted by the test case generator, and the bypass leads to an out-of-bounds memory access.\n  As the pads are zero-initialized, the bypassed memory accesses will produce deterministic results.\n* **Macro stack**: This region is used to implement complex macros (e.g., VMENTER) that need to save and restore data on the stack with a guarantee that this data won't be corrupted by the following (randomly-generated) instructions (see [macros](macros.md) for more details.)\n\n### Code Layout\n\nThe code area of a test case in a sandbox is organized as follows:\n\n| Offset | Actor ID | Area Name       | Size, B |\n| ------ | -------- | --------------- | ------- |\n| 0x0    | ACTOR 0  | Main Code Area  | 0x2000  |\n| 0x2000 |          | Macro Code Area | 0x1000  |\n| 0x3000 | ACTOR 1  | Main Code Area  | 0x2000  |\n| 0x5000 |          | Macro Code Area | 0x1000  |\n| ...    | ...      | ...             | ...     |\n\nThe code area is divided into two regions:\n\n* **Main Code Area**: This region contains the binary of the actor's code.\n  The code comes from the [RCDF](binary-formats.md) file.\n  The first instruction in the code area of actor 0 is the entry point of the test case, and the last instruction of actor 0 is the exit point of the test case.\n* **Macro Code Area**: This region contains code of the expanded macros for each actor.\n  (see [macros](macros.md) for more details on the macro expansion process.)\n\n### References\n\n* Executor: [rvzr/executor_km/include/sandbox_manager.h](https://github.com/microsoft/side-channel-fuzzer/tree/main/rvzr/executor_km/include/sandbox_manager.h)\n* Unicorn backend: [rvzr/sandbox.py](https://github.com/microsoft/side-channel-fuzzer/tree/main/rvzr/sandbox.py)\n\n## Sandbox Initialization\n\nThe sandbox is initialized based on the test case code (normally in RCBD format) and the input data (normally in RDBF format).\nThe following diagram shows the mapping between the RCBF/RDBF files and the sandbox memory layout:\n\n```plaintext\n                                        |--------------------|\n                   zero initialized ->  | MACRO STACK        |\n                                        |--------------------|\n                   zero initialized ->  | UNDERFLOW PAD      |\n                                        |--------------------|\n      RDBF.data[actor_id].main_area ->  | MAIN AREA          |\n                                        |--------------------|\n    RDBF.data[actor_id].faulty_area ->  | FAULTY AREA        |\n                                        |--------------------|\nRDBF.data[actor_id].reg_init_region ->  | GPR AREA           |\n                                        |--------------------|\nRDBF.data[actor_id].reg_init_region ->  | SIMD AREA          |\n                                        |--------------------|\n                   zero initialized ->  | OVERFLOW PAD       |\n                                        |--------------------|\n\n\n     RCBF.tc_section[actor_id].code ->  | MAIN CODE AREA     |\n                                        |--------------------|\n     expanded macro code (executor) ->  | MACRO CODE AREA    |\n```\n\n## Fault Isolation\n\nUNDER CONSTRUCTION\n"
  },
  {
    "path": "docs/structure.md",
    "content": "# Revizor Documentation\n\nEverything you need to know about using, understanding, and contributing to Revizor.\n\n## First Steps\n\nAre you new to Revizor? Start here:\n\n* [Revizor at a Glance](intro/01-overview.md): Understand what Revizor is, what problems it solves, and see a quick example of violation detection.\n* [Installation Guide](intro/02-install.md): Get Revizor installed on your system and verify your setup.\n* [Your First Fuzzing Campaign](intro/tutorials/01-first-fuzz.md): Follow a hands-on tutorial that walks you through running your first test, detecting a violation, and understanding the results.\n* [Core Concepts](intro/03-primer.md): Learn about contracts, traces, speculation, and other fundamental concepts needed to use Revizor effectively.\n* [Glossary](glossary.md): A quick reference for key terms used throughout the documentation.\n\n---\n\n## Getting Help\n\nStuck? Need clarification? Here's where to get help.\n\n* [FAQ](faq/general.md) - What is Revizor? How does it work? What's a contract?\n* [GitHub Discussions](https://github.com/microsoft/side-channel-fuzzer/discussions) - Ask questions, share experiences, discuss ideas\n* [GitHub Issues](https://github.com/microsoft/side-channel-fuzzer/issues) - Report bugs or request features\n* [Contributing Guide](https://github.com/microsoft/side-channel-fuzzer/blob/main/CONTRIBUTING.md) - Help improve Revizor\n* [Zulip Chat](https://rvzr.zulipchat.com/) - Real-time community support\n\n---\n\n## How the Documentation is Organized\n\nRevizor's documentation is organized into five distinct categories based on your needs:\n\n### Learning-Oriented: Tutorials\n\nTutorials take you by the hand through a series of steps to complete a project. They are designed for newcomers who want to get started with Revizor. Start here if you're learning.\n\n* [Main Tutorial Series](intro/04-tutorials.md): Follow a series of hands-on tutorials that walk you through running your first tests, detecting violations, and rump up all the way to root-cause analysis and design of custom campaigns.\n* [How TSA-SQ Was Detected](intro/tutorials/tsa-sq.md): A practical case study showing how Revizor was used to discover the TSA-SQ vulnerability. For those interested in how Revizor is used in the real world.\n\n---\n\n### Task-Oriented: How-To Guides\n\nHow-to guides are recipes that guide you through steps to solve specific problems. They assume you have basic knowledge and want to accomplish something particular.\n\n* [How to Choose a Contract](howto/choose-contract.md) - Select appropriate reference model\n* [How to Design a Fuzzing Campaign](howto/design-campaign.md) - Plan effective testing strategies\n* [How to Interpret Results](howto/interpret-results.md) - Understand what the outputs mean\n* [How to Minimize Violations](howto/minimize.md) - Reduce test cases to essentials\n* [How to Root-Cause a Violation](howto/root-cause-a-violation.md) - Analyze and understand detected leaks\n* [How to Use Macros](howto/use-macros.md) - Leverage macros for customizing test cases\n* [How to Use Templates](howto/use-templates.md) - Create structured test cases with templates\n\n---\n\n### Understanding-Oriented: Topic Guides\n\nTopic guides provide background and explanation to help you understand how Revizor works. They don't contain step-by-step instructions but explain key concepts in depth.\n\n* [Leakage Contracts](topics/contracts.md) - Understanding security specifications\n* [Actors and Isolation](topics/actors.md) - Multi-domain testing concepts\n* [Leakage Models](topics/models.md) - How the model predicts CPU behavior\n* [Test Case Generation](topics/test-case-generation.md) - Code and data generation explained\n* [Trace Analysis](topics/trace-analysis.md) - How violations are detected\n\n---\n\n### Information-Oriented: Reference\n\nReference guides contain technical descriptions of Revizor's components. They're like a dictionary—useful when you know what you're looking for.\n\n* [Command Line Interface](ref/cli.md) - Complete CLI reference\n* [Configuration Options](ref/config.md) - All configuration parameters\n* [Execution Modes](ref/modes.md) - Fuzz, reproduce, analyze, minimize\n* [Macros Reference](ref/macros.md) - Template macro system\n* [Minimization Passes](ref/minimization-passes.md) - Available minimization techniques\n* [Runtime Statistics](ref/runtime-statistic.md) - Runtime metrics printed during execution\n* [Binary Format](ref/binary-formats.md) - (advanced) Revizor's custom binary format\n* [Allocated Registers](ref/registers.md) - (advanced) Register allocation details\n* [Sandbox](ref/sandbox.md) - (advanced) Sandbox for executing test cases\n\n---\n\n### Contributor-Oriented: Development Guides\n\nDevelopment guides help contributors understand the codebase, architecture, and development practices.\n\n* [Developer Index](internals/index.md)\n* [Architecture Overview](internals/architecture/overview.md)\n* [Code Style Guidelines](internals/contributing/code-style.md)\n* [Git Conventions](internals/contributing/git.md)\n\n---\n\n## Research and Background\n\nRevizor is built on peer-reviewed research in hardware security and formal methods. All papers related to Revizor are listed in the [Research Papers](ref/papers.md) section.\n\n---\n\n## Documentation Feedback\n\nIf you find errors, confusing explanations, or missing information in the documentation, please let us know:\n\n* Open an issue with the \"documentation\" label\n* Suggest improvements via pull request\n* Discuss on GitHub Discussions\n"
  },
  {
    "path": "docs/stylesheets/extra.css",
    "content": "/* Custom styles for mkdocs-material */\n\n/* Inline box for default values and highlighted inline content */\n.inline-box {\n    background-color: var(--md-code-bg-color);\n    border: 1px solid var(--md-default-fg-color--lightest);\n    padding: 2px 6px;\n    border-radius: 2px;\n    display: inline-block;\n}\n\n"
  },
  {
    "path": "docs/topics/actors.md",
    "content": "# Actors\n\nActors represent distinct security domains within a test case. They could be thought as sub-test-cases, each with its own code, data, execution context, and privilege level.\n\nThe main use case for actors is to test interactions and isolation boundaries between security domains. A typical example would be testing kernel-to-user isolation by defining a two-actor test case: one actor runs in kernel mode (the \"main\" actor), and the other runs in user mode (the \"user\" actor). The user actor attempts to observe information about the main actor's execution, simulating an attacker trying to leak sensitive kernel data.\n\nBy using this mechanism, Revizor can stress-test isolation boundaries by executing lots of randomly generated code on both sides of the boundary and checking for secret-dependent observations on the attacker side.\nThis mechanism discovered several critical vulnerabilities in production CPUs, most notably Transient Scheduler Attacks in AMD processors, and enables testing of mitigations for Meltdown, Foreshadow, and MDS.\n\n## What is an Actor?\n\nAn actor consists of three components:\n\n- Code region associated with a specific execution context\n- Private data memory with configurable permissions and properties\n- Execution context defined by CPU mode (host/guest), privilege level (kernel/user), and system configuration\n\nEvery test case starts with a default actor called `main` that runs in host kernel mode. This actor can transition to other actors using dedicated `switch_*` macros.\n\n## Actor Configuration\n\nActors are defined in the configuration file under the `actors` section:\n\n```yaml\nactors:\n  - main:                       # Default main actor\n    - mode: \"host\"              # Always host for main;\n                                # changing to \"guest\" will produce an error\n    - privilege_level: \"kernel\" # Always kernel for main;\n                                # changing to \"user\" will produce an error\n\n  - user:                       # Example user-mode actor\n    - mode: \"host\"\n    - privilege_level: \"user\"\n    - data_properties:          # Custom page table properties of the faulty page\n      - writable: false         # Faulty page of the user actor is read-only\n```\n\n\n!!! note \"Related Documentation\"\n    See the [configuration documentation](../ref/config.md#actor) for a full list of available options.\n\n## Actor Templates\n\nMulti-actor execution requires template-based mode. Templates define actors along with their code and data sections.\n\nTransitions between actors use dedicated macros for setting entry and exit points, switching contexts, and defining landing locations. Macros are available for kernel-user transitions (`.set_k2u_target`, `.switch_k2u`, etc.) and host-guest transitions (`.set_h2g_target`, `.switch_h2g`, etc.).\n\n!!! note \"Related Documentation\"\n    See [Macro Reference](../ref/macros.md#transition-macros) for detailed descriptions of all transition macros.\n\n\n## Actor Non-Interference Contract\n\nRevizor uses the Actor Non-Interference contract to verify isolation between security domains. The contract designates one or more actors as observers (attackers) and verifies that observer execution does not depend on data from victim actors.\n\nThe contract permits leakage of victim memory access addresses and control flow, but prohibits leakage of data values. This design filters cache-based leakage typically considered benign in modern systems while detecting unexpected microarchitectural leaks. Victim actors follow the ct-seq contract, while observers can expose all their own data.\n\nA violation occurs when observer traces depend on victim data beyond permitted address and control-flow information.\n\n!!! note \"Additional Reading\"\n    The Actor Non-Interference contract is explained in detail in the paper called [Enter, Exit, Page Fault, Leak: Testing Isolation Boundaries for Microarchitectural Leaks](https://www.microsoft.com/en-us/research/wp-content/uploads/2025/07/Enter-Exit-SP26.pdf).\n\n\n## Example Usage\n\nThe following example demonstrates kernel-to-user isolation testing with the Actor Non-Interference contract.\n\nTemplate with kernel and user actors:\n\n```asm\n.intel_syntax noprefix\n\n# ---------------- Main (Kernel) Actor ---------\n.section .data.main\n.function_main_0:\n    # Set up user transition\n    .macro.set_k2u_target.user.function_user_0:\n    .macro.set_u2k_target.main.function_main_1:\n\n    # Generate random kernel code\n    .macro.random_instructions.32.0:\n\n    # Transition to user mode\n    .macro.switch_k2u.user.0:\n\n.function_main_1:\n    .macro.landing_u2k.main_1:\n    # Back in kernel, clean up and exit\n    nop\n\n.test_case_exit:\n\n# ---------------- User Actor -----------------\n.section .data.user\n.function_user_0:\n    .macro.landing_k2u.user_0:\n\n    # Start measurement in user mode\n    .macro.measurement_start:\n\n    # Generate random user code\n    .macro.random_instructions.16.1:\n\n    # End measurement\n    .macro.measurement_end:\n\n    # Return to kernel\n    .macro.switch_u2k.main.0:\n\n```\n\nConfiguration file:\n\n```yaml\nactors:\n  - main:\n      mode: host\n      privilege_level: kernel\n  - user:\n      mode: host\n      privilege_level: user\n      observer: true              # User is the attacker\n      data_properties:\n        writable: false           # Trigger page faults on writes\n\ncontract_observation_clause: load+store+pc\ncontract_execution_clause: noninterference\n```\n\nIn this configuration, the user actor attempts to observe information from the kernel (main actor). The contract specifies that the user can observe memory addresses and control flow (load+store+pc) but not data values. Any leakage beyond this triggers a violation.\n\n"
  },
  {
    "path": "docs/topics/contracts.md",
    "content": "# Contracts\n\nA speculation contract is a formal specification of known microarchitectural leakage in CPUs.\nA contract serves to provide a precise and unambiguous documentation of all known sources of\nside-channel leaks on a given CPU (or a family of CPUs). For example, if a contract targets\na modern Intel or AMD CPU, it will typically include a specification of the leaks caused by\ncache side channels and by various speculative vulnerabilities such as Spectre and Meltdown.\n\nContracts emerged as a solution to a fundamental problem: modern CPUs have complex\nmicroarchitectural optimizations that create side channels, but these mechanisms are often\nproprietary and poorly documented. Contracts provide a systematic way to reason about these leaks\nwithout requiring complete knowledge of the underlying hardware.\n\nIn the context of Revizor, contracts serve as a reference model against which the actual CPU\nbehavior is compared; any deviation from the contract indicates a previously-unknown\nmicroarchitectural behavior, which may represent a security vulnerability.\n\n## Contract Structure\n\nA speculation contract consists of two types of clauses that together describe the information a\nprogram exposes during execution.\n\nThe *observation clause* specifies what data becomes observable for each instruction. For example, a\ncontract might declare that load and store instructions expose their target addresses. This models\nthe information an attacker could learn by monitoring a cache-based side channel such as\nPrime+Probe. The observation clause captures side effects without specifying the attack mechanism.\n\nThe *execution clause* specifies how hardware optimizations affect program execution. For\nspeculative execution, the clause describes which instructions execute transiently even when they\nshould not execute architecturally. For instance, the clause might specify that conditional\nbranches temporarily take the wrong target. The execution clause models optimization behavior\nwithout describing the implementation details.\n\nContracts intentionally overestimate leakage. Rather than precisely modeling what leaks occur,\ncontracts capture everything that could potentially leak given the specified hardware behaviors.\nThis conservative approach ensures that contracts remain valid even when the exact timing or\nconditions of leaks are unknown.\n\n## Example Contracts\n\nThe `CT-SEQ` contract models a CPU with caching but no speculation. It represents a baseline level\nof leakage present in any cached architecture where memory operations leave observable traces but\ninstructions execute in program order.\n\nBelow is a pseudo-code representation of the `CT-SEQ` contract:\n\n``` yaml\nCT-SEQ:\n  observation_clause:\n    load(address)  -> expose(address)\n    store(address) -> expose(address)\n    * -> none  # all other instructions expose no information\n  execution_clause:\n    * -> none  # no optimizations; all instructions execute in program order\n```\n\nThe `CT-COND` contract extends `CT-SEQ` by adding speculative execution of branches. The observation\nclause remains the same, but the execution clause permits conditional jumps to mispredict their\ntargets and speculatively execute wrong-path instructions. This contract models Spectre-style\nvulnerabilities where misprediction causes transient execution that leaves observable cache\nfootprints.\n\n``` yaml\nCT-COND:\n  observation_clause:\n    load(address)  -> expose(address)\n    store(address) -> expose(address)\n    * -> none\n  execution_clause:\n    jump.cond(target) ->  # emulate branch misprediction\n        jump.inverted_cond(target)\n    * -> none\n```\n\nMore complex contracts can model other optimizations. A contract for exception handling might allow\nfaulting user-to-kernel loads to transiently return privileged values before the fault\nis architecturally recognized, this modelling Meltdown-style vulnerabilities:\n\n``` yaml\nCT-MELTDOWN:\n  observation_clause:\n    load(address)  -> expose(address)\n    store(address) -> expose(address)\n    * -> none\n  execution_clause:\n    jump.cond(target) ->\n        jump.inverted_cond(target)\n    load(address) ->  # transiently return kernel data thus emulating Meltdown\n        if (in_user_mode() && is_kernel_address(address)) {\n            return load_privileged(address)\n        }\n    * -> none\n```\n\n## Contract Traces\n\nWhen a program executes according to a contract, it produces a contract trace. The trace is a\nsequence of all observations specified by the observation clause during the execution path\ndetermined by the execution clause. For `CT-SEQ`, the trace contains load and store addresses in\nprogram order. For `CT-COND`, the trace includes addresses from speculatively executed instructions\non mispredicted paths.\n\nContract traces are deterministic and noise-free, unlike actual hardware measurements. This\nproperty makes them suitable as a reference for comparison. A program executed repeatedly with the\nsame inputs always produces the same contract trace, even though real hardware traces may vary due\nto timing effects and concurrent activity.\n\nFor example, consider the following program:\n\n``` asm  linenums=\"1\"\n# addr1 = 0x100; addr2 = 0x200;\n# *addr1 = 1;    *addr2 = 2\nload rax, [addr1]  # expose(0x100)\ncmp rax, 0         # 1 != 0\nje label_zero      # speculatively mispredicted under CT-COND\n    load rbx, [addr2]  # expose(0x200) under CT-COND (but not under CT-SEQ)\nlabel_zero:\n```\n\nWhen this program is executed under `CT-SEQ`, only one load occurs (line 3), producing the trace:\n\n```\nctrace_seq = [ mem:0x100 ]\n```\n\nHowever, under `CT-COND`, the mispredicted branch causes the second load (line 6) to execute,\nthus producing a trace with two observations:\n\n```\nctrace_cond = [ mem:0x100, mem:0x200 ]\n```\n\n\n## Contract Compliance\n\nA CPU complies with a contract when the information it leaks never exceeds what the contract\npermits. More formally, compliance means that whenever two inputs produce identical contract\ntraces, they must also produce indistinguishable hardware traces given the same initial\nmicroarchitectural state. This definition ensures that an attacker observing hardware cannot learn\nmore than the contract allows.\n\nCompliance does not require that hardware traces match contract traces exactly. The contract might\nexpose complete addresses while hardware only leaks cache set indices. The contract might include\ndata from speculative paths that hardware does not actually execute. These differences are\nacceptable as long as the information content of hardware traces does not exceed contract traces.\n\nA violation occurs when two inputs produce identical contract traces but distinguishable hardware\ntraces. This indicates that hardware leaks information not captured by the contract, revealing an\nunexpected microarchitectural behavior. The violating program serves as evidence of a potential\nsecurity vulnerability.\n\n\n## Contract Evolution\n\nContracts are not static specifications. When Revizor discovers a violation, the user is free to\nupdate the contract to reflect the newly observed behavior. This way a contract serves as a \"filter\"\nthat allows us to automatically distinguish between the leaks that we already know about (and thus\naren't interested in detecting) versus the leaks that are genuinely new and that we may want to\ninvestigate further.\n\nMoreover, the process may go both ways: if the hardware behavior is determined to be a bug,\nand the vendor issues a patch, the contract may be updated to remove the previously-allowed leakage,\nwhich in turn will allow Revizor to detect regressions if the patch is later undone or\nincompletely applied.\n\nThis iterative process gradually refines contracts to match actual hardware behavior. Initial\ncontracts are based on public documentation and known vulnerabilities. Testing reveals gaps where\nhardware leaks more than expected. After investigation, either the contract expands or the\nhardware receives a patch. Over time, the contract converges toward a complete specification of\nthe CPU's microarchitectural leakage.\n\nThe contract framework also enables testing of proposed mitigations. Before deploying a patch,\nvendors can verify its effectiveness by running Revizor with the updated configuration. If\nviolations persist, the mitigation is incomplete. This proactive approach helps prevent the\ndeployment of ineffective patches that provide false security.\n\n## What's Next?\n\n* See the [primer](../intro/03-primer.md) for a deeper dive into non-interference and contract-based testing.\n* See the [model documentation](models.md) for details on how Revizor implements contracts.\n"
  },
  {
    "path": "docs/topics/models.md",
    "content": "# Leakage Models\n\nA leakage model is an executable implementation of a speculation contract. The model takes a\nprogram and inputs, executes them according to contract rules, and produces contract traces that\nrepresent the information expected to leak. Models enable automated testing by providing a\nreference against which real hardware behavior can be compared.\n\n```mermaid\nflowchart LR\n    P[Program]\n    I[Input 1 ... N]\n    M[Model]\n    CT[CTrace 1 ... N]\n\n    P --> M\n    I --> M\n    M --> CT\n\n    style M fill:#e1f5ff,stroke:#333,stroke-width:2px\n    style P fill:#ffe1e1,stroke:#333,stroke-width:1px\n    style I fill:#ffe1e1,stroke:#333,stroke-width:1px\n    style CT fill:#e1ffe1,stroke:#333,stroke-width:1px\n```\n\nModels solve a practical problem in security testing. Contracts specify what should leak in\nabstract terms, but to test hardware, we need concrete predictions for specific programs. The\nmodel bridges this gap by simulating program execution under contract assumptions and recording\nobservable effects as they occur.\n\n\n## Implementing Contracts\n\nA model implements contracts through two specialized components: the Tracer and the Speculator.\nThese components monitor instruction execution through hook functions and modify behavior according\nto contract rules.\n\n```mermaid\nflowchart LR\n    A0[Start</br>Program</br>Execution] --> A\n    A[Next</br>Instruction] -->B{Select Hook}\n    B -->|Instruction has</br>observation clause?| D[Tracer]\n    B -->|Instruction has</br>execution clause?| E[Speculator]\n    B -->|Model in</br>speculative mode?| F[Speculator]\n    D -->G[Record</br>observations] --> Z\n    E -->H1[Checkpoint]-->H[Emulate</br>speculation] --> I[Enter</br>speculative</br>mode] --> Z\n    F -->J{Termination</br>condition</br>met?}\n    J -->|Yes| X[Rollback] --> Z\n    J -->|No| Z\n    Z[Execute</br>Instruction] -->A\n\n    style D fill:#e1ffe1,stroke:#333\n    style G fill:#e1ffe1,stroke:#333\n    style E fill:#ffe1e1,stroke:#333\n    style F fill:#ffe1e1,stroke:#333\n    style H1 fill:#ffe1e1,stroke:#333\n    style H fill:#ffe1e1,stroke:#333\n    style I fill:#ffe1e1,stroke:#333\n    style X fill:#fff4e1,stroke:#333\n    style Z fill:#e1f5ff,stroke:#333\n```\n\nThe Tracer implements the observation clause. It monitors execution of each instruction and records\ncontract-relevant information. When an instruction with a non-trivial observation clause executes,\nthe model invokes the corresponding Tracer hook. For example, when a load instruction executes\nunder a contract that exposes memory addresses, the Tracer hook records the target address. The\nTracer accumulates these observations into a contract trace that represents all information exposed\nduring execution.\n\nThe Speculator implements the execution clause. It modifies the behavior of instructions with\nnon-trivial execution clauses to simulate microarchitectural optimizations. When such an\ninstruction executes, the model invokes the Speculator hook, which takes a checkpoint of the\nprogram state and modifies the instruction's behavior. This puts the model into speculative mode.\nFor example, when a conditional branch executes under a contract that permits misprediction, the\nSpeculator checkpoints the state, flips the branch condition, and continues on the wrong path.\n\nWhile in speculative mode, the model checks for termination conditions after each instruction. When\na condition is met, such as the speculation window expiring or encountering a serializing\ninstruction, the model exits speculative mode and rolls back to the most recent checkpoint. This\nrestores architectural state and resumes correct execution. Speculation can be nested, with one\nspeculative region triggering another before the first completes. The Speculator manages a stack of\ncheckpoints to handle nested speculation correctly.\n\nThe hook-based architecture allows models to implement contracts without modifying the core\nexecution engine. Whenever the model executes an instruction, it checks whether the instruction has\nnon-trivial observation or execution clauses. If observation clauses are present, Tracer hooks are\ninvoked. If execution clauses are present, Speculator hooks are invoked. The model also calls hooks\nto check speculation termination conditions when in speculative mode. This separation of concerns\nmakes it straightforward to implement different contracts by providing different Tracer and\nSpeculator implementations.\n\n## Model Backends\n\nRevizor supports two model backends that implement contracts using different techniques. Both\nprovide the same interface and produce equivalent contract traces, but they differ in\nimplementation approach and performance characteristics.\n\nThe Unicorn backend uses CPU emulation to execute programs. Unicorn is a CPU emulator derived from\nQEMU that supports multiple architectures. The model extends Unicorn with hooks that intercept\ninstruction execution and memory accesses. When an instruction executes, the hook checks whether\nthe contract requires recording an observation or triggering speculation. For speculation, the\nmodel uses Unicorn's snapshot and restore capabilities to implement checkpoint-rollback.\n\nThe DynamoRIO backend uses dynamic binary instrumentation to execute programs. DynamoRIO inserts\ninstrumentation code directly into the program at runtime. Before each instruction, the model\ninjects a callback that checks contract rules. For speculation, the model manipulates register\nstate to simulate wrong-path execution and uses checkpoints to restore architectural state when\nspeculation ends. Because the program runs natively on the host CPU, execution is faster than\nemulation.\n\nThe choice between backends involves trade-offs. The DynamoRIO backend is generally preferable as\nit offers better performance and it inherently supports all instructions that can be executed on\nthe host CPU. However, this backend is more recent and may not support all contract features and\nplatforms as robustly as the Unicorn backend. Therefore, the Unicorn backend remains available for\nuse cases where the DynamoRIO backend is not yet suitable.\n\n## Trace Representation\n\nContract traces are sequences of typed observations. Each observation records one piece of\ninformation that leaked during execution. The trace preserves the order in which observations\noccurred, capturing temporal aspects of information flow (although this could be overridden by\nthe Tracer if the contract prescribes so).\n\nAn observation has a type and a value. Types include memory addresses, branch targets, load/store\nvalues, register contents, or any other observable information. The contract determines which types\nappear in traces. CT-SEQ traces contain only memory addresses and branch targets. A contract\nmodeling MDS might include data values. The type system allows precise specification of what\ninformation the contract permits.\n\n## Accuracy and Limitations\n\nModels approximate real hardware behavior, and this approximation introduces both capabilities and\nlimitations. Understanding these boundaries is important for interpreting test results.\n\nModels implement contracts conservatively but not precisely. The contract specifies bounds on\nleakage, and the model respects these bounds while making implementation choices. For branch\nspeculation, the contract might say branches can mispredict, but not specify when or how often. A\nmodel that speculatively executes every branch overapproximates reality but remains consistent\nwith the contract. This conservatism means models may predict leakage that never occurs, but they\nshould not miss leakage that does occur.\n\nInstruction support varies between backends and real hardware. Some instructions are complex or\npoorly documented, making them difficult to emulate correctly. Unicorn covers most common\ninstructions but may have gaps or emulation bugs. When the model encounters unsupported\ninstructions, it typically halts with an error. This conservative behavior prevents incorrect\npredictions but limits which test cases can be executed.\n\nModels do not aim to capture all microarchitectural details. Real CPUs have dozens of optimizations\nincluding out-of-order execution, store buffers, prefetchers, and speculative memory\ndisambiguation. Contracts and models abstract away most of these details and focus instead on\nthe information observable through side channels, rather than trying to describe the mechanisms\nthat produce the leakage. This abstraction simplifies model implementation and focuses testing on\nthe most relevant aspects.\n\n## Performance Considerations\n\nModel performance directly affects testing throughput. Revizor must execute thousands or millions\nof test cases to achieve good coverage, and model execution dominates the time budget. Faster\nmodels enable more comprehensive testing within a given time frame.\n\nThe highest impact on performance comes from the complexity of the execution clause in the contract.\nSimple contracts with minimal speculation (e.g., `CT-SEQ`) execute quickly, while contracts with\nextensive speculation incur significant overhead due to increase in the number of executed\ninstructions.\n\nBackend choice also impacts performance. DynamoRIO typically outperforms Unicorn, especially for\nlarger test cases or when executing with many inputs per test case (> 100s). However, DynamoRIO\nhas a higher startup overhead, making it less efficient for very small test cases or when\nexecuting with few inputs per test case (< 10s). The performance crossover point depends on\nthe specific fuzzing scenario.\n\n## What's Next?\n\n* See the [contracts documentation](contracts.md) for details on contract specifications.\n* See the [internals documentation](../internals/architecture/model.md) for implementation details.\n* See [Unicorn backend](../internals/model-backends/model-unicorn.md) and [DynamoRIO backend](../internals/model-backends/model-dr.md) for backend-specific information.\n"
  },
  {
    "path": "docs/topics/test-case-generation.md",
    "content": "# Test Case Generation\n\n[Test case](../glossary.md#test-case-program) generation is the process of creating executable programs that probe CPU microarchitectural behavior. Revizor generates test cases either randomly or from user-defined templates, then instruments them to prevent unwanted faults, and finally compiles them into binaries suitable for execution. The test cases serve as inputs to both the leakage model and the hardware executor, enabling comparison of expected and observed microarchitectural behavior.\n\nThe generator must balance two competing requirements. First, it needs to produce diverse test cases that explore many different microarchitectural conditions and instruction sequences. This diversity is essential for thorough coverage of the CPU's behavior space. Second, when testing specific scenarios like domain transitions or mitigation effectiveness, it must generate programs with precise structures while still varying the surrounding context. This balance distinguishes Revizor's approach from simple random testing.\n\n## Generation Modes\n\nRevizor supports two distinct modes of test case generation, each suited to different testing scenarios.\n\nRandom generation creates test cases from scratch without any predefined structure. The generator builds a program by selecting instructions randomly from a configured pool, creating control flow by inserting conditional and unconditional branches, and allocating memory accesses to random addresses. This mode maximizes exploration of the instruction space and frequently discovers unexpected interactions between instructions. Random generation excels at finding vulnerabilities in single execution domains where no specific instruction sequence is required. However, it struggles with scenarios requiring precise setup, such as triggering specific page faults or transitioning between security domains.\n\nTemplate-based generation starts from a user-written assembly file that defines the overall structure of the test case. The template specifies where random code should be inserted, which domain transitions to perform, and how [actors](../glossary.md#actor) should interact. The generator parses the template, expands special macros that mark randomization points, fills those points with random instructions, and instruments the result. Templates enable testing of specific scenarios while maintaining randomization where it matters. For example, a template can ensure that a kernel-to-user transition occurs at a specific point while randomizing the instructions executed in each domain.\n\nThe distinction matters because microarchitectural vulnerabilities often depend on precise conditions. Consider testing whether a CPU leaks kernel data to user space. Random generation might occasionally produce programs that transition to user mode, but the probability is low and the surrounding context may not trigger the vulnerability. A [template](../glossary.md#template) guarantees the transition occurs and controls the operations performed before and after it, dramatically increasing the likelihood of discovering leakage.\n\n## Instrumentation Passes\n\nGenerated test cases, whether random or template-based, are not immediately ready for execution. They may contain instruction sequences that trigger unwanted faults, use undefined register values, or violate architectural constraints. Instrumentation passes transform test cases into safe, executable forms while preserving the properties being tested.\n\nInstrumentation operates on the [structured representation of test cases](#test-case-structure). Each pass implements a specific transformation by walking the instruction hierarchy and modifying it according to its rules. Passes run in sequence, with each pass seeing the output of previous passes. This pipeline architecture allows complex transformations to be built from simple components.\n\nOne critical example of a pass is the sandboxing pass, which instruments memory accesses to ensure they target valid addresses within the test case's allocated memory. Accesses to unmapped addresses cause page faults. While some test cases intentionally trigger faults, most do not, and random generation can easily produce invalid addresses. The pass analyzes memory operands, identifies potentially invalid accesses, and masks the instruction operands to bound them within the sandbox.\n\nAnother example is a pass that prevents division by zero. Divisions by zero and division overflows trigger exceptions on x86-64, which may be undesirable in a fuzzing campaign that does not focus on this specific type of exception. Yet random generator will commonly trigger them, especially the division overflows. This pass mitigates this issue by scanning the instruction stream for division instructions, and it instruments the division operands to ensure they are non-zero and within safe ranges.\n\nThe passes are designed to introduce minimal microarchitectural side effects, to avoid interfering with the properties being tested. For example, the instrumentation code primarily uses arithmetic and logical operations that do not have any known speculative effects, and the instructions operate primarily on registers rather than memory. This careful design ensures that the instrumentation does not inadvertently create or mask vulnerabilities (or at least minimizes the chances of doing so).\n\n## <a name=\"test-case-structure\"></a> Test Case Structure\n\nA test case is represented internally as a hierarchy of nested components. The following diagram illustrates this hierarchical structure:\n\n```mermaid\ngraph TD\n    TC[TestCaseProgram]\n    TC --> CS1[CodeSection: main]\n    TC --> CS2[CodeSection: user]\n\n    CS1 --> F1[Function: .function_0]\n    CS2 --> F2[Function: .function_user_0]\n\n    F1 --> BB1[BasicBlock: entry]\n    F1 --> BB2[BasicBlock: .label_1]\n    F1 --> BB3[BasicBlock: exit]\n\n    BB1 --> I1[Instruction: mov rax, rbx]\n    BB1 --> I2[Instruction: add rax, 1]\n    BB1 --> T1[Terminator: jmp .label_1]\n\n    BB2 --> I3[Instruction: ...]\n    BB2 --> T2[Terminator: ret]\n\n    F2 --> BB4[BasicBlock: entry]\n    BB4 --> I4[Instruction: ...]\n\n    style TC fill:#e1f5ff\n    style CS1 fill:#ffe1e1\n    style CS2 fill:#ffe1e1\n    style F1 fill:#fff4e1\n    style F2 fill:#fff4e1\n    style BB1 fill:#e1ffe1\n    style BB2 fill:#e1ffe1\n    style BB3 fill:#e1ffe1\n    style BB4 fill:#e1ffe1\n```\n\nAt the top level, a TestCaseProgram contains one or more CodeSections. Each CodeSection belongs to a single Actor and holds the code that executes in that actor's context. In single-actor fuzzing, only one section exists. In multi-actor testing, each actor gets its own section with its own code and data.\n\nWithin each CodeSection, code is organized into Functions. A Function consists of multiple BasicBlocks connected by control flow. The first block is the entry point, and execution proceeds through the blocks following branches and jumps. This structure mirrors conventional compiler intermediate representations, making it straightforward to apply standard analysis and transformation techniques.\n\nEach BasicBlock contains a sequence of Instructions terminated by zero or more control flow instructions. Regular instructions execute sequentially, while terminators (branches, jumps, returns) determine which block executes next. Instructions themselves are high-level representations that capture the operation, operands, and dependencies. They are not raw bytes but structured objects that can be analyzed and modified by instrumentation passes.\n\nThis hierarchical design serves several purposes. It allows instrumentation passes to operate at different levels of granularity, from modifying individual instructions to restructuring entire functions. It makes the structure explicit, eliminating the need to repeatedly parse assembly text. It enables efficient copying and modification when expanding templates. Most importantly, it provides a common representation used throughout the fuzzing pipeline, from generation through model execution to hardware measurement.\n\n\n\n\n## Macro Placeholders\n\n[Macro placeholders](../glossary.md#macro) are the key mechanism for combining fixed structure with randomization. They appear in templates as special labels that get expanded during test case generation. A macro looks like an assembly label but carries semantic meaning. For example, `.macro.random_instructions.64:` tells the generator to insert 64 randomly chosen instructions at that point.\n\n```mermaid\ngraph LR\n    subgraph \"Template (Before Expansion)\"\n        T1[\".section .main<br/>.function_0:\"]\n        T2[\".macro.random_instructions.3:\"]\n        T3[\"mov rax, [rbx]\"]\n    end\n\n    subgraph \"Expanded Test Case\"\n        E1[\".section .main<br/>.function_0:\"]\n        E2[\"add rax, rbx\"]\n        E3[\"mov [rdi], rcx\"]\n        E4[\"cmp rax, 0\"]\n        E5[\"mov rax, [rbx]\"]\n    end\n\n    T1 --> E1\n    T2 -.expands to.-> E2\n    T2 -.expands to.-> E3\n    T2 -.expands to.-> E4\n    T3 --> E5\n\n    style T2 fill:#ffe1e1\n    style E2 fill:#e1ffe1\n    style E3 fill:#e1ffe1\n    style E4 fill:#e1ffe1\n```\n\nThe critical insight is that macros defer decisions. A template author specifies where randomization should occur without specifying the exact instructions. This preserves the ability to test many different instruction sequences while maintaining the overall structure.\n\nMacros also enable progressive refinement of test cases. An initial template might use a single random macro to generate a large instruction sequence. If that discovers a [violation](../glossary.md#violation), the user can refine the template to add more structure around the violation, narrowing the search space. The macro system makes this iteration efficient because templates remain concise and readable.\n\n!!! note \"Related Documentation\"\n    For a complete list of available macros and their specifications, refer to the [Macro Reference](../ref/macros.md).\n\n    For a how-to guide on using macros, see [How-To: Use Macros](../howto/use-macros.md).\n\n## Template Structure and Expansion\n\n```mermaid\nflowchart LR\n    T[Template File] --> P[Parse Template]\n    C[Config File] --> P\n    P --> R[Fill Random<br/>Instructions]\n    R --> IP[Instrumentation<br/>Passes]\n    IP --> PR[Print to<br/>Assembly]\n    PR --> AS[Assemble]\n    AS --> B[Binary<br/>Test Case]\n\n    style T fill:#e1f5ff\n    style C fill:#e1f5ff\n    style R fill:#ffe1e1\n    style IP fill:#fff4e1\n    style B fill:#e1ffe1\n```\n\nTemplates use standard assembly syntax with macro extensions. This design choice means templates are valid assembly files that can be processed by conventional tools. Comments, labels, directives, and instructions follow normal assembly conventions. Only the macro pseudo-instructions are specific to Revizor.\n\nTemplate expansion proceeds in several phases. First, the generator parses the template using a standard assembly parser extended to recognize macros. Parsing produces the hierarchical test case structure described earlier, with macros represented as special instruction types. Next, the generator walks the structure and expands the `random_instructions` macro placeholders to random instruction sequences. Finally, instrumentation passes run, the test case is printed back to assembly, and the assembler produces an object file.\n\n\n!!! note \"Related Documentation\"\n    For detailed instructions on writing and using templates, refer to [How-To: Use Templates](../howto/use-templates.md).\n\n## Generation Performance\n\nGeneration performance affects overall fuzzing throughput, although it usually has a low impact on the overall process compared to the executor and the model. Generation may dominate the execution time only in the rare case where the other components are extremely fast, such as when testing very small test cases with a small (<10) number of inputs.\n\nBoth random and template-based generation modes have similar performance as the template is re-used across all generation requests after the first expansion.\n\n\n## What's Next?\n\n- [Configuration Reference](../ref/config.md): Generator configuration options\n- [Actors](actors.md): Security domains represented in test cases\n- [Binary Formats](../ref/binary-formats.md): File formats for test case binaries\n- [How-To: Use Macros](../howto/use-macros.md): Guide on using macros in templates\n- [How-To: Use Templates](../howto/use-templates.md): Guide on writing and using templates\n- [Code Generation](../internals/architecture/code.md): Implementation details of code generation and instrumentation\n"
  },
  {
    "path": "docs/topics/trace-analysis.md",
    "content": "# Trace Analysis\n\nThis document describes Revizor's trace analysis techniques for detecting microarchitectural\ncontract violations by comparing contract traces with hardware traces.\n\nTrace analysis is the core mechanism of Model-Based Relational Testing. It compares\n[contract traces](../glossary.md#contract-trace) (predicted leakage from the model) with\n[hardware traces](../glossary.md#hardware-trace) (observed leakage from real CPU) to detect\nmicroarchitectural [contract violations](../glossary.md#violation).\n\n## Contract Compliance Property\n\nThe fundamental property being tested is [contract compliance](../glossary.md#contract-compliance):\nif two inputs produce the same contract trace, they must produce the same hardware trace for all\nmicroarchitectural states.\n\nFormally, a CPU complies with a speculation contract if, for all possible programs P, all input pairs (I₁, I₂), and all initial microarchitectural states Ctx:\n\n```\nContractTrace(P, I₁) = ContractTrace(P, I₂)\n  ⟹\nHardwareTrace(P, I₁, Ctx) = HardwareTrace(P, I₂, Ctx)\n```\n\nIf this property is violated, the CPU is leaking information beyond what the contract predicts.\nA violation indicates that the contract is incomplete or that the CPU has an unexpected side\nchannel. See the [primer](../intro/03-primer.md#building-and-testing-speculation-contracts) for\ntheoretical foundations.\n\nRevizor approximates this property by randomly sampling the space of programs, inputs, and microarchitectural states, and checking for violations based on collected traces.\nThe following sections describe how this check is implemented in practice.\n\n## Problem Statement\n\nThe trace analysis task boils down to the following problem:\n\n=== \"Given\"\n\n:   - A test program P\n    - A sequence of inputs I₁ … Iₙ\n    - A sequence of contract traces CTrace₁ … CTraceₙ (one per input, produced by the model)\n    - A sequence of hardware traces HTrace₁ … HTraceₙ (one per input, produced by the executor)\n\n<div></div> <!-- empty line for spacing -->\n\n=== \"Objective\"\n\n:    Detect if there exist any input pairs (Iᵢ, Iⱼ) such that\n    ```\n    CTraceᵢ = CTraceⱼ but HTraceᵢ ≠ HTraceⱼ\n    ```\n\n## Deterministic Trace Comparison\n\nTo detect contract violations, we need to check the above property for all tested inputs.\nChecking each input pair separately would be extremely inefficient (O(N²) complexity). Instead,\nwe use an equivalence-class-based algorithm that groups inputs by their contract traces and\nchecks hardware trace consistency within each group:\n\n1. **Bundle measurements**: Create tuples `(Input ID, Input, CTrace, HTrace)` for each execution\n2. **Group by contract trace**: Partition measurements into contract equivalence classes where\n   all measurements share the same `CTrace`\n3. **Check hardware traces**: Within each contract equivalence class, verify all hardware traces\n   are identical\n4. **Report violations**: If a contract equivalence class contains different hardware traces,\n   report a violation\n\n```mermaid\nflowchart LR\n    B[Bundle measurements]\n    B --> C[Group by CTrace<br/>]\n    C --> D{For each EqClass}\n    D --> E[Compare hardware traces]\n    E --> F{All identical?}\n    F -->|Yes| G[Mark class OK]\n    F -->|No| H[Record violation]\n    G --> I{More classes?}\n    H --> I\n    I -->|Yes| D\n    I -->|No| J[End]\n\n    classDef good fill:#c3f3d9,stroke:#2b7b4b,color:#000;\n    classDef bad fill:#f9d5d5,stroke:#b61b1b,color:#000;\n    class G good\n    class H bad\n\n```\n\n=== \"Example\"\n\n    Suppose we test 4 inputs and get:\n\n    ```\n    I0: CTrace=A, HTrace=X\n    I1: CTrace=A, HTrace=X\n    I2: CTrace=B, HTrace=Y\n    I3: CTrace=A, HTrace=Z\n    ```\n\n    Contract equivalence classes:\n\n    ```\n    - Class [CTrace=A]: I0, I1, I3\n    - Class [CTrace=B]: I2\n    ```\n\n    Analysis:\n\n    - Class `[CTrace=A]` contains three measurements with HTraces `{X, X, Z}`\n    - Since X ≠ Z, this is a **violation**\n    - Inputs `I0` and `I1` behaved identically (`HTrace=X`) but input `I3` behaved differently (`HTrace=Z`),\n    despite having the same contract trace=A\n    - Accordingly, we found a violation: `I0` and `I3` have the same contract trace (`A = A`) while\n    the hardware traces differ (`X ≠ Z`)\n\n## Statistical Trace Comparison\n\n### The Problem of Noise\n\nReal hardware measurements are noisy. Even if we execute the same program with the same input\nrepeatedly, we might get different hardware traces each time, due to factors like timing variations\n in microcode execution, cache state changes from uncontrollable sources, non-controlled\n hardware optimizations, etc.\n\nThese discrepancies may cause false positives if we directly use the deterministic algorithm,\nbecause the differences between the hardware traces may be caused by noise, not by genuine\ninformation leakage.\n\n=== \"Example\"\n    Suppose we test only two of the inputs from the previous example, but the second\n    measurement produces a slightly different hardware trace due to noise:\n\n    ```\n    I0: CTrace=A, HTrace=X\n    I1: CTrace=A, HTrace=X'\n    ```\n\n    The resulting analysis will produce a false violation:\n\n    - Contract equivalence class `[CTrace=A]: I0, I1`\n    - Hardware traces: `{X, X'}`\n    - Since `X ≠ X'`, we incorrectly report a violation, even though both inputs are actually safe\n    and the difference is just noise.\n\n### Solution: Sampling and Statistical Analysis\n\nTo address this, we treat hardware traces as samples from a distribution rather than single\ndeterministic values. Instead of comparing individual hardware traces, we compare the distributions\nof hardware traces produced by each input. This replaces the deterministic equality check\n`HardwareTrace(P, I₁, Ctx) = HardwareTrace(P, I₂, Ctx)` with a statistical test that\ndetermines if two samples are drawn from the same distribution.\n\nTo implement this, we modify the measurement process: instead of collecting one hardware trace\nper input, Revizor collects **multiple samples** per input. Each `HTrace` object contains an\narray of measurement samples. For example, with sample size N=10:\n\n```\nI0: CTrace=A, HTrace=[X, X, X', X, X, X', X, X, X, X]\nI1: CTrace=A, HTrace=[X, X', X, X, X, X, X', X, X, X]\n```\n\nNow we must compare **distributions** rather than individual values, and the question becomes: \"Are these two samples drawn from the same distribution?\"\n\n### Chi-Squared Test for Categorical Data\n\nHardware traces are categorical data (no natural ordering), so Revizor uses Pearson's χ²\nhomogeneity test.\n\nGiven two samples `t₁` and `t₂` of hardware traces, the test computes:\n\n```\nχ² = Σ (obs₁(t) - expected(t))² / expected(t)\n   + Σ (obs₂(t) - expected(t))² / expected(t)\n```\n\nwhere:\n\n- `obs₁(t)` = count of trace `t` in sample 1\n- `obs₂(t)` = count of trace `t` in sample 2\n- `expected(t) = (obs₁(t) + obs₂(t)) / 2` = average count\n\nThe χ² statistic is normalized by total sample size to make it comparable across different\nsample sizes.\n\n**Decision rule**: If `χ²/N < threshold`, accept that the samples come from the same\ndistribution (no violation).\n\nThe threshold is configurable (default 0.05) and can be tuned based on expected noise levels via [`analyser_stat_threshold`](../ref/config.md#analyser_stat_threshold) config parameter.\n\nThe following examples illustrate how the χ² test distinguishes between noise and real violations.\n\n=== \"No violation\"\n\n    ```\n    I0: CTrace=A, HTrace=[X(8 times), X'(2 times)]\n    I1: CTrace=A, HTrace=[X(7 times), X'(3 times)]\n    ```\n\n    The distributions are similar (mostly X with some noise X'). χ² test will show they're\n    equivalent:\n\n    ```\n    obs₁(X) = 8, obs₁(X') = 2\n    obs₂(X) = 7, obs₂(X') = 3\n    expected(X) = (8 + 7) / 2 = 7.5\n    expected(X') = (2 + 3) / 2 = 2.5\n    χ² = (8 - 7.5)²/7.5 + (2 - 2.5)²/2.5 + (7 - 7.5)²/7.5 + (3 - 2.5)²/2.5\n       = 0.2666\n    χ²/N = 0.2666/10 = 0.02666\n    χ²/N < 0.05 → accept equivalence (no violation)\n    ```\n\n\n=== \"Real violation\"\n\n    ```\n    I0: CTrace=A, HTrace=[X(5 times), X'(1 time), Y(4 times)]\n    I1: CTrace=A, HTrace=[X(5 times), X'(1 time), Z(4 times)]\n    ```\n\n    The distributions are clearly different (X vs Y). χ² test will reject equivalence → violation:\n    ```\n    obs₁(X) = 5, obs₁(X') = 1, obs₁(Y) = 4\n    obs₂(X) = 5, obs₂(X') = 1, obs₂(Z) = 4\n    expected(X) = (5 + 5) / 2 = 5\n    expected(X') = (1 + 1) / 2 = 1\n    expected(Y) = (4 + 0) / 2 = 2\n    expected(Z) = (0 + 4) / 2 = 2\n    χ² = (5 - 5)²/5 + (1 - 1)²/1 + (4 - 2)²/2 + (0 - 2)²/2\n       + (5 - 5)²/5 + (1 - 1)²/1 + (0 - 2)²/2 + (4 - 2)²/2\n       = 8\n    χ²/N = 8/10 = 0.8\n    χ²/N > 0.05 → reject equivalence (violation)\n    ```\n\n### Adaptive Sample Sizing\n\nFor performance reasons, Revizor does not immediately use a large sample size. Instead, it\nstarts with a small sample, collects the traces, and checks if a violation is detected. If no\nviolation is detected, the executor assumes that the test case is safe, and moves on to the next\none. If a violation is detected, however, the executor tries to reproduce it with larger sample\nsizes.\n\nExample adaptive strategy:\n\n``` python\nfor N in [15, 40, 160, 320]:\n    collect N samples per input\n    if violation detected:\n        continue\n    else:\n        return \"no violation\"\nreturn \"violation detected\"\n```\n\nThe exact sample sizes and thresholds are configurable via [`executor_sample_sizes`](../ref/config.md#executor_sample_sizes) config parameter.\n\n## What's Next?\n\n- [Configuration Options](../ref/config.md) - Configure analyzer parameters\n- [Model-Based Relational Testing](../intro/03-primer.md) - Theoretical foundations\n- [Analyser Architecture](../internals/architecture/analysis.md) - Implementation\n  details\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "---\nsite_name: Revizor\nsite_url: https://github.com/microsoft/side-channel-fuzzer\nsite_author: Microsoft\nsite_description: A security-oriented tool for testing CPUs and detecting microarchitectural leaks like Spectre and Meltdown\ncopyright: 'Copyright &copy; 2025 Microsoft'\n\nrepo_name: microsoft/side-channel-fuzzer\nrepo_url: https://github.com/microsoft/side-channel-fuzzer\n\ndocs_dir: docs\n\ntheme:\n  name: material\n  palette:\n    - media: \"(prefers-color-scheme: light)\"\n      scheme: default\n      primary: indigo\n      accent: indigo\n      toggle:\n        icon: material/brightness-7\n        name: Switch to dark mode\n    - media: \"(prefers-color-scheme: dark)\"\n      scheme: slate\n      primary: indigo\n      accent: indigo\n      toggle:\n        icon: material/brightness-4\n        name: Switch to light mode\n  font:\n    text: Roboto\n    code: Roboto Mono\n  features:\n    - navigation.instant\n    - navigation.tracking\n    - navigation.indexes\n    - navigation.tabs\n    - toc.integrate\n    - search.suggest\n    - search.highlight\n  logo: assets/icon.svg\n  favicon: assets/favicon.svg\n\nextra_css:\n  - stylesheets/extra.css\n\nplugins:\n  - search\n\nmarkdown_extensions:\n  - toc:\n      toc_depth: 2\n\n  # diagrams\n  - pymdownx.superfences:\n      custom_fences:\n        - name: mermaid\n          class: mermaid\n          format: !!python/name:pymdownx.superfences.fence_code_format\n\n  # Better typography and formatting\n  - abbr\n  - admonition\n  - attr_list\n  - md_in_html\n  - def_list\n  - footnotes\n  - meta\n  - tables\n  - pymdownx.caret\n  - pymdownx.keys\n  - pymdownx.mark\n  - pymdownx.details\n\n  # Code highlighting (must be before tabbed)\n  - pymdownx.highlight:\n      anchor_linenums: true\n\n  - pymdownx.snippets\n\n  # Tabbed content blocks\n  - pymdownx.tabbed:\n      alternate_style: true\n      combine_header_slug: true\n\n  # Other extensions\n  - pymdownx.pathconverter\n  - pymdownx.tasklist:\n      custom_checkbox: true\n\n  # Enhanced formatting\n  - pymdownx.betterem:\n      smart_enable: all\n  - pymdownx.tilde\n  - pymdownx.smartsymbols\n\n  - pymdownx.emoji:\n      emoji_index: !!python/name:material.extensions.emoji.twemoji\n      emoji_generator: !!python/name:material.extensions.emoji.to_svg\n\n\nnav:\n  - Start Here: intro/start-here.md\n  - Documentation:\n    - Documentation Structure: structure.md\n    - Getting Started:\n      - Revizor at a Glance: intro/01-overview.md\n      - Installation: intro/02-install.md\n      - Deep Dive into Concepts: intro/03-primer.md\n      - Overview of Tutorials: intro/04-tutorials.md\n      - Tutorial 1 - First Campaign: intro/tutorials/01-first-fuzz.md\n      - Tutorial 2 - First Vulnerability: intro/tutorials/02-first-vuln.md\n      - Tutorial 3 - Testing Faults: intro/tutorials/03-faults.md\n      - Tutorial 4 - Testing Isolation: intro/tutorials/04-isolation.md\n      - Tutorial 5 - Extending Revizor: intro/tutorials/05-extending.md\n      - Extra Tutorial - Detecting TSA-SQ: intro/tutorials/tsa-sq.md\n    - How-To Guides:\n      - Ask a Question: howto/ask-a-question.md\n      - Choose a Contract: howto/choose-contract.md\n      - Design a Campaign: howto/design-campaign.md\n      - Interpret Output: howto/interpret-results.md\n      - Minimize Violations: howto/minimize.md\n      - Root-Cause a Violation: howto/root-cause-a-violation.md\n      - Use Templates: howto/use-templates.md\n      - Use Macros: howto/use-macros.md\n    - Topics:\n      - Contracts: topics/contracts.md\n      - Leakage Models: topics/models.md\n      - Trace Analysis: topics/trace-analysis.md\n      - Test Case Generation: topics/test-case-generation.md\n      - Actors: topics/actors.md\n    - Reference:\n      - Modes of Operation: ref/modes.md\n      - Command Line Interface: ref/cli.md\n      - Configuration Options: ref/config.md\n      - Runtime Statistics: ref/runtime-statistic.md\n      - Minimization Passes: ref/minimization-passes.md\n      - Macros: ref/macros.md\n      - Artifact File Formats: ref/artifact-file-formats.md\n      - Internal Binary Formats: ref/binary-formats.md\n      - Registers: ref/registers.md\n      - Sandbox: ref/sandbox.md\n    - Glossary: glossary.md\n    - Academic Research: ref/papers.md\n  - FAQ:\n    - General Questions: faq/general.md\n  - Contribute:\n    - Index: internals/index.md\n    - Guide to Contributing: internals/contributing/overview.md\n    - Guidelines:\n      - General Guidelines: internals/contributing/general.md\n      - Code Style: internals/contributing/code-style.md\n      - Git Workflow: internals/contributing/git.md\n    - Code Structure: internals/code-structure.md\n    - Architecture:\n      - Overview: internals/architecture/overview.md\n      - Orchestration: internals/architecture/fuzz.md\n      - ISA Specification: internals/architecture/isa.md\n      - Test Case Code Generation: internals/architecture/code.md\n      - Test Case Data Generation: internals/architecture/data.md\n      - Hardware Tracing: internals/architecture/exec.md\n      - Contract Tracing: internals/architecture/model.md\n      - Trace Analysis: internals/architecture/analysis.md\n      - Minimization: internals/architecture/mini.md\n      - Logging: internals/architecture/logging.md\n    - Model Backends:\n      - Unicorn Backend: internals/model-backends/model-unicorn.md\n      - DynamoRIO Backend: internals/model-backends/model-dr.md\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"revizor-fuzzer\"\nversion = \"2.0.0\"\ndescription = \"A fuzzer to search for microarchitectural leaks in CPUs\"\nreadme = \"README.md\"\nrequires-python = \">=3.9\"\nclassifiers = [\n    \"Programming Language :: Python :: 3\",\n    \"Programming Language :: Python :: 3 :: Only\",\n    \"License :: OSI Approved :: MIT License\",\n    \"Operating System :: POSIX :: Linux\",\n    \"Typing :: Typed\",\n]\ndependencies = [\n    \"unicorn==1.0.3; platform_machine == 'x86_64'\",\n    \"unicorn; platform_machine == 'aarch64'\",\n    \"pyyaml\",\n    \"types-pyyaml\",\n    \"numpy\",\n    \"pyelftools\",\n    \"xxhash\",\n    \"scipy\",\n    \"mypy\",\n    \"pylint\",\n    \"flake8\",\n    \"setuptools<80\",  # To avoid deprecation warnings cased by Unicorn\n    \"cffi\",\n    \"types-cffi\",\n]\nmaintainers = [{name = \"Oleksii Oleksenko\", email = \"\"}]\n\n[project.urls]\n\"Homepage\" = \"https://microsoft.github.io/side-channel-fuzzer/\"\n\"Source code\" = \"https://github.com/microsoft/side-channel-fuzzer\"\n\"Bug Tracker\" = \"https://github.com/microsoft/side-channel-fuzzer/issues\"\n\"Changelog\" = \"https://github.com/microsoft/side-channel-fuzzer/releases\"\n\n[tool.hatch.build]\nexclude = [\n  \"demo/\",\n  \"docs/\",\n  \"mkdocs-overrides/\",\n  \"rvzr/executor_km/\",\n  \"tests/\",\n  \"tests/x86_tests/\",\n  \"tests/x86_tests/\",\n  \"base.json\",\n  \"site/\",\n  \"dbg/\",\n  \"revizor.code-workspace\",\n]\n\n[tool.hatch.build.targets.wheel]\npackages = [\"rvzr\"]\n\n[project.scripts]\nrvzr = \"rvzr.cli:main\"\n"
  },
  {
    "path": "revizor.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nFile: Command Line Interface to Revizor\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n\nfrom rvzr.cli import main\n\nif __name__ == '__main__':\n    exit_code = main()\n    exit(exit_code)\n"
  },
  {
    "path": "rvzr/__init__.py",
    "content": "# flake8: noqa\n# pylint: skip-file\n\nfrom .isa_spec import *\nfrom .executor import *\nfrom .analyser import *\nfrom .data_generator import *\nfrom .code_generator import *\nfrom .cli import *\nfrom .logs import *\n\nfrom .model import *\nfrom .fuzzer import *\nfrom .factory import *\nfrom .config import *\n\nfrom .asm_parser import *\n\nfrom .arch.x86 import *\nfrom .model_unicorn import *\nfrom .postprocessing import *\n\n__version__ = \"2.0.0\"\n"
  },
  {
    "path": "rvzr/analyser.py",
    "content": "\"\"\"\nFile: various ways to compare collected ctraces with htraces\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom collections import Counter\nfrom typing import List, Dict, TYPE_CHECKING, Union, Final\nfrom abc import ABC, abstractmethod\nfrom scipy import stats  # type: ignore\n\nimport numpy.typing as npt\nimport numpy as np\n\nfrom .traces import HTrace, CTrace, TraceBundle, Violation, ContractEqClass, HardwareEqClass\nfrom .config import CONF, ConfigException\nfrom .stats import FuzzingStats\nfrom .logs import warning, error\n\nif TYPE_CHECKING:\n    from .tc_components.test_case_data import InputData\n    from .tc_components.test_case_code import TestCaseProgram\n\nIntArrayLike = Union[List[int], npt.NDArray[np.uint64]]\n\nSTAT = FuzzingStats()\n\n\n# ==================================================================================================\n# Abstract Analyser Interface\n# ==================================================================================================\nclass Analyser(ABC):\n    \"\"\" Interface definition for all analysers \"\"\"\n\n    @abstractmethod\n    def filter_violations(self,\n                          ctraces: List[CTrace],\n                          htraces: List[HTrace],\n                          test_case_code: TestCaseProgram,\n                          inputs: List[InputData],\n                          stats_: bool = False) -> List[Violation]:\n        \"\"\"\n        Compare the provided contract and hardware traces and return a list of contract\n        violations, if any are found.\n        :param ctraces: a list of contract traces to check\n        :param htraces: a list of hardware traces to check\n        :param test_case_code: the program under test\n        :param inputs: a list of inputs under test (one per trace)\n        :param stats_: whether to update the global fuzzing statistics based on the results\n        :return: a list of violations, if any are found\n        \"\"\"\n\n    @abstractmethod\n    def htraces_are_equivalent(self, htrace1: HTrace, htrace2: HTrace) -> bool:\n        \"\"\" Compare two hardware traces according to the current analyser's rules.\n\n        :param htrace1: first hardware trace\n        :param htrace2: second hardware trace\n        :return: True if the traces are equivalent, False otherwise\n        \"\"\"\n\n\n# ==================================================================================================\n# Equivalence class-based Analysers\n# ==================================================================================================\nclass EquivalenceAnalyserCommon(Analyser):\n    \"\"\"\n    Abstract class implementing the algorithm that compares contract and hardware traces\n    by using the concept of equivalence classes.\n\n    The algorithm check if any of the traces fits the definition of a contract violation,\n     which is:\n       For two pairs of traces (ctrace1, htrace1) and (ctrace2, htrace2),\n       where ctrace1 is a contract trace for input1\n       and htrace1 is a hardware trace for input1 (and so on),\n       the traces violate the contract if\n            ctrace1 == ctrace2 and\n            htrace1 NOT EQUIVALENT htrace2\n\n    The definition of EQUIVALENT is specific to the concrete implementation of the analyser\n    (see subclasses).\n    \"\"\"\n\n    def filter_violations(self,\n                          ctraces: List[CTrace],\n                          htraces: List[HTrace],\n                          test_case_code: TestCaseProgram,\n                          inputs: List[InputData],\n                          stats_: bool = False) -> List[Violation]:\n        # --------\n        # NOTE: This implementation is a common algorithm of checking for contract violations in\n        # all equivalence class-based analysers. The subclasses modulate the implementation by\n        # defining the htraces_are_equivalent method.\n        #\n        # The algorithm works by first grouping the measurements by their contract trace (ctrace),\n        # and then checking if all hardware traces (htraces) in the same group are equivalent\n        # according to the htraces_are_equivalent method. If not, a Violation object is created\n        # based on the violating traces, and added to the list of violations.\n        #\n        # Note that the algorithm also filters out measurements with corrupted/ignored htraces.\n        # The filtering is done by skipping the measurements with empty or corrupted htraces.\n        # This is done to avoid false positives in the analysis.\n        # --------\n\n        # Skip if there are no htraces\n        if not htraces:\n            return []\n\n        # Package all the measurements into TraceBundles\n        # and filter out the measurements with corrupted/ignored htraces\n        measurements = []\n        for i, htrace in enumerate(htraces):\n            if htrace.is_empty() or htrace.is_corrupted_or_ignored():\n                continue\n            measurements.append(TraceBundle(i, inputs[i], ctraces[i], htrace))\n        if not measurements:\n            return []\n\n        # Build a list of equivalence classes:\n        all_classes = ContractEqClass.build_contract_classes(measurements)\n\n        # Filter out ineffective equivalence classes\n        effective_classes = [eq_cls for eq_cls in all_classes if len(eq_cls.measurements) >= 2]\n\n        # Sort the classes by ctrace\n        effective_classes.sort(key=lambda x: x.ctrace)\n\n        # Compute hardware equivalence classes\n        for eq_cls in effective_classes:\n            hw_classes = HardwareEqClass.build_hw_classes(\n                eq_cls.measurements, equivalence_function=self.htraces_are_equivalent)\n            eq_cls.set_hw_classes(hw_classes)\n\n        # Check if any of the equivalence classes is a contract counterexample\n        violations: List[Violation] = []\n        for eq_cls in effective_classes:\n            hw_classes = eq_cls.get_hw_classes()\n            if len(hw_classes) >= 2:\n                v = Violation.from_contract_eq_class(eq_cls, inputs, test_case_code)\n                violations.append(v)\n\n        # Update statistics\n        if stats_:\n            STAT.eff_classes += len(effective_classes)\n            STAT.single_entry_classes += len(all_classes) - len(effective_classes)\n            STAT.analysed_test_cases += 1\n\n        return violations\n\n\nclass MergedBitmapAnalyser(EquivalenceAnalyserCommon):\n    \"\"\" A variant of the analyser that compares the htraces as merged bitmaps. I.e., it merges\n    the htrace lists into bitmaps and compares the results.\n\n    It also applies filtering of outliers according to CONF.analyser_outliers_threshold\n    \"\"\"\n\n    _bitmap_cache: Final[Dict[int, int]]\n    _MASK: Final[int]\n\n    def __init__(self) -> None:\n        super().__init__()\n        self._bitmap_cache = {}\n        self._MASK = pow(2, 64) - 1\n\n    def htraces_are_equivalent(self, htrace1: HTrace, htrace2: HTrace) -> bool:\n        bitmaps = [0, 0]\n\n        sample_size = htrace1.sample_size()\n        assert sample_size == htrace2.sample_size(), \"htraces have different sizes\"\n        threshold = CONF.analyser_outliers_threshold * sample_size\n\n        for i, htrace in enumerate([htrace1, htrace2]):\n            hash_ = hash(htrace)\n            raw = htrace.get_raw_traces()\n\n            # check if cached\n            if hash_ in self._bitmap_cache:\n                bitmaps[i] = self._bitmap_cache[hash_]\n                continue\n\n            # remove outliers\n            counter = Counter(raw)\n            filtered = [x for x in raw if counter[x] >= threshold]\n\n            # merge into bitmap\n            for t in filtered:\n                bitmaps[i] |= int(t)\n\n            # cache\n            self._bitmap_cache[hash_] = bitmaps[i]\n\n        if CONF.analyser_subsets_is_violation:\n            return bitmaps[0] == bitmaps[1]\n\n        # check if the bitmaps are disjoint\n        inverse = [~bitmaps[0] & self._MASK, ~bitmaps[1] & self._MASK]\n        return bool(((bitmaps[0] & inverse[1]) == 0) or ((bitmaps[1] & inverse[0]) == 0))\n\n\nclass SetAnalyser(EquivalenceAnalyserCommon):\n    \"\"\" A variant of the analyser that compares the htraces as sets. I.e., it squashes\n    the htrace lists into sets and compares the results.\n\n    It also applies filtering of outliers according to CONF.analyser_outliers_threshold\n    \"\"\"\n\n    def htraces_are_equivalent(self, htrace1: HTrace, htrace2: HTrace) -> bool:\n        \"\"\" Squash the htrace lists into sets and compare the results \"\"\"\n        sample_size = htrace1.sample_size()\n        assert sample_size == htrace2.sample_size(), \"htraces have different sizes\"\n        threshold = CONF.analyser_outliers_threshold * sample_size\n        filtered1 = [x for x in htrace1.get_raw_traces() if x >= threshold]\n        filtered2 = [x for x in htrace2.get_raw_traces() if x >= threshold]\n\n        trace_set1 = set(filtered1)\n        trace_set2 = set(filtered2)\n\n        if CONF.analyser_subsets_is_violation:\n            return trace_set1 == trace_set2\n\n        return trace_set1.issubset(trace_set2) or trace_set2.issubset(trace_set1)\n\n\nclass MWUAnalyser(EquivalenceAnalyserCommon):\n    \"\"\"\n    A variant of the analyser that uses the Mann-Withney U test to compare htraces.\n\n    WARNING: this is an experimental analyser and it may not work well for all cases.\n    \"\"\"\n\n    def __init__(self) -> None:\n        super().__init__()\n        warning(\"analyser\",\n                \"MWUAnalyser is an experimental analyser and may not work well for all cases. \")\n\n        a = [1] * CONF.executor_sample_sizes[0]\n        b = [2] * CONF.executor_sample_sizes[0]\n        _, p_value = stats.mannwhitneyu(a, b)\n        if CONF.analyser_stat_threshold < p_value:\n            raise ConfigException(\"analyser_stat_threshold is too high for the given sample size\")\n\n    def htraces_are_equivalent(self, htrace1: HTrace, htrace2: HTrace) -> bool:\n        \"\"\" Use the Mann-Withney U test to compare htraces \"\"\"\n        _, p_value = stats.mannwhitneyu(htrace1.get_raw_traces(), htrace2.get_raw_traces())\n        return bool(p_value > CONF.analyser_stat_threshold)\n\n\nclass ChiSquaredAnalyser(EquivalenceAnalyserCommon):\n    \"\"\"\n    A variant of the analyser that uses the chi-squared test to compare htraces.\n    \"\"\"\n\n    def __init__(self) -> None:\n        super().__init__()\n        a = [1] * CONF.executor_sample_sizes[0]\n        b = [2] * CONF.executor_sample_sizes[0]\n        stat = self.homogeneity_test(a, b)\n        if CONF.analyser_stat_threshold > stat:\n            error(\"analyser_stat_threshold is too low for the given sample size\")\n\n    def homogeneity_test(self, x: IntArrayLike, y: IntArrayLike) -> float:\n        \"\"\" Use the chi-squared test to compare htraces \"\"\"\n        assert len(x) == len(y)\n        counter1 = Counter(x)\n        counter2 = Counter(y)\n        keys = set(counter1.keys()) | set(counter2.keys())\n        observed = [counter1[k] for k in keys] + [counter2[k] for k in keys]\n        expected = [(counter1[k] + counter2[k]) / 2 for k in keys] * 2\n        ddof = len(keys) - 1\n        stat: float\n        stat, _ = stats.chisquare(observed, expected, ddof=ddof)\n        stat /= len(x) + len(y)\n        return stat\n\n    def htraces_are_equivalent(self, htrace1: HTrace, htrace2: HTrace) -> bool:\n        stat = self.homogeneity_test(htrace1.get_raw_traces(), htrace2.get_raw_traces())\n        return stat < CONF.analyser_stat_threshold\n"
  },
  {
    "path": "rvzr/arch/__init__.py",
    "content": ""
  },
  {
    "path": "rvzr/arch/arm64/__init__.py",
    "content": ""
  },
  {
    "path": "rvzr/arch/arm64/asm_parser.py",
    "content": "\"\"\"\nFile: Parsing of assembly files into our internal representation (TestCaseCode).\n      This file contains arm64-specific code.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# FIXME: this implementation is quite brittle; rewrite it using a proper parser (keystone ?)\n\nfrom __future__ import annotations\nimport re\nfrom typing import TYPE_CHECKING, List\n\nfrom rvzr.asm_parser import AsmParser, AsmLineParser, AsmParserError\nfrom rvzr.instruction_spec import OT, InstructionSpec\n\nfrom .target_desc import ARM64TargetDesc\n\nif TYPE_CHECKING:\n    from rvzr.isa_spec import InstructionSet\n    from rvzr.target_desc import TargetDesc\n\n\n# ==================================================================================================\n# Private: Parser of assembly lines in ARM64 syntax\n# ==================================================================================================\nclass _ARM646LineParser(AsmLineParser):\n    \"\"\" Parser of assembly lines in ARM64 syntax \"\"\"\n    _target_desc: ARM64TargetDesc\n    _curr_ln: int\n\n    def __init__(self, isa_spec: InstructionSet, target_desc: ARM64TargetDesc) -> None:\n        super().__init__(isa_spec, target_desc)\n        self._comment_char = \"//\"\n        self._re_tokenize = re.compile(\n            r\"^([^ .]+\\.?)([^ ]+)? ([^ ,]+)(,[^,]+)?(,[^,]+)?(,[^,]+)?( //.*)?\")\n        self._re_tokenize_nops = re.compile(r\"^([^ .]+\\.?)([^ ]+)?\")\n        self._condition_code = list(target_desc.branch_conditions.keys())\n\n    # ----------------------------------------------------------------------------------------------\n    # Implementation of ISA-specific hooks\n    def _tokenize(self, line: str) -> List[str]:\n        matches = self._re_tokenize.findall(line)\n        if matches == []:\n            matches = self._re_tokenize_nops.findall(line)\n        if not matches:\n            raise AsmParserError(self._curr_ln, \"Could not tokenize the line\")\n        tokens = [t.removeprefix(\",\") for t in matches[0] if t]\n        # print(tokens)\n\n        # the regex above splits memory address operands into multiple tokens\n        # we need to merge them back\n        tokens_merged = []\n        mem_started = False\n        mem_token = \"\"\n        for token in tokens:\n            if not token:\n                continue\n            if token[0] == \"[\" and token[-1] == \"]\":\n                tokens_merged.append(token)\n                continue\n            if token[0] == \"[\":\n                mem_started = True\n                mem_token = token\n                continue\n            if token[-1] == \"]\":\n                tokens_merged.append(mem_token + \",\" + token)\n                mem_started = False\n                mem_token = \"\"\n                continue\n            if mem_started:\n                mem_token += \",\" + token\n                continue\n            tokens_merged.append(token)\n\n        # print(tokens_merged)\n        return tokens_merged\n\n    def _get_instruction_name(self, line: str, tokens: List[str]) -> str:\n        return tokens[0]\n\n    def _get_instruction_operands(self, _: str, __: str, tokens: List[str]) -> List[str]:\n        \"\"\" Get the list of operand strings from the tokens \"\"\"\n        return tokens[1:]\n\n    def _get_initial_candidate_specs(self, _: str, name: str) -> List[InstructionSpec]:\n        \"\"\" Get the list of candidate specs for an instruction with the given name  \"\"\"\n        return self._instruction_map.get(name, [])\n\n    def _check_if_spec_matches(self, spec: InstructionSpec, operands_raw: List[str]) -> bool:\n        \"\"\" Check if the given spec matches the given list of operand strings \"\"\"\n        # pylint: disable=too-many-return-statements  # justified for selectors\n        # pylint: disable=too-many-branches  # justified for selectors\n        # print(spec.name, operands_raw, spec.operands)\n\n        if len(spec.operands) != len(operands_raw):\n            return False\n\n        for op_id, op_raw in enumerate(operands_raw):\n            op_spec = spec.operands[op_id]\n\n            # match condition\n            if op_spec.type == OT.COND:\n                if op_raw not in self._condition_code:\n                    return False\n                continue\n\n            # match label\n            if op_raw[0] == \".\":\n                if op_spec.type != OT.LABEL:\n                    return False\n                continue\n\n            # match address\n            if \"[\" in op_raw:\n                if op_spec.type not in [OT.AGEN, OT.MEM]:\n                    return False\n                continue\n\n            # match immediate value\n            if op_raw[0] == \"#\":\n                if op_spec.type != OT.IMM:\n                    return False\n                continue\n\n            # match register\n            if op_spec.type == OT.REG:\n                if op_raw not in op_spec.values:\n                    return False\n                continue\n\n            # match keyword immediate\n            if op_spec.type == OT.IMM:\n                if op_raw not in op_spec.values:\n                    return False\n                continue\n\n            # no match\n            return False\n        return True\n\n\n# ==================================================================================================\n# Public Interface: Parser of X86 assembly files\n# ==================================================================================================\nclass ARM64AsmParser(AsmParser):\n    \"\"\" Implementation of the AsmParser interface for X86 assembly files \"\"\"\n\n    def __init__(self, isa_spec: InstructionSet, target_desc: TargetDesc) -> None:\n        super().__init__(isa_spec, target_desc)\n        assert isinstance(target_desc, ARM64TargetDesc)\n        self._line_parser = _ARM646LineParser(isa_spec, target_desc)\n        self._asm_patcher.set_macro_placeholder(\"nop; nop; nop\")\n"
  },
  {
    "path": "rvzr/arch/arm64/config.py",
    "content": "\"\"\"\nFile: arm64-specific Configuration Options\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom typing import List, Dict\n\n_option_values = {\n    'actor': [\n        'name',\n        'mode',\n        'privilege_level',\n        'data_properties',\n        'data_ept_properties',\n        'observer',\n        'instruction_blocklist',\n        'fault_blocklist',\n    ],\n    \"actor_mode\": ['host',],\n    \"actor_privilege_level\": ['kernel',],\n    \"actor_data_properties\": [\n        'present',\n        'writable',\n        'user',\n        'accessed',\n        'dirty',\n        'executable',\n        'reserved_bit',\n        'randomized',\n    ],\n    \"actor_data_ept_properties\": [\n        \"present\",\n        \"writable\",\n        \"executable\",\n        \"accessed\",\n        \"dirty\",\n        'reserved_bit',\n        'randomized',\n    ],\n    'unicorn_instruction_categories': [\n        \"general-arithmetic\",\n        \"general-barrier\",\n        \"general-bitwise\",\n        \"general-uncond_branch\",\n        \"general-cond_branch\",\n        \"general-comparison\",\n        \"general-condsel\",\n        \"general-dataxfer\",\n        \"general-misc\",\n    ],\n    \"dr_instruction_categories\": [\n        # DynamoRIO backend is not yet supported on ARM\n    ],\n}\n\n# in contrast to x86, on ARM64, we handle all fault types by default\n_handled_faults: List[str] = [\"PF\", \"DE\", \"DB\", \"BP\", \"BR\", \"UD\", \"PF\", \"GP\"]\n\ninstruction_categories: List[str] = [\"general-arithmetic\", \"general-dataxfer\"]\n\"\"\" instruction_categories: a default list of tested instruction categories \"\"\"\n\n_buggy_instructions: List[str] = []\n\ninstruction_blocklist: List[str] = [\n]  # yapf: disable\ninstruction_blocklist.extend(_buggy_instructions)\n\n\nregister_blocklist: List[str] = [\n    # free - x0 .. x5\n    'x6', 'x7', 'x8', 'x9', 'x10', 'x11', 'x12', 'x13', 'x14', 'x15',\n    'x16', 'x17', 'x18', 'x19', 'x20', 'x21', 'x22', 'x23',\n    'x24', 'x25', 'x26', 'x27', 'x28', 'x29', 'x30', 'x31',\n    'sp',\n    'w6', 'w7', 'w8', 'w9', 'w10', 'w11', 'w12', 'w13', 'w14', 'w15',\n    'w16', 'w17', 'w18', 'w19', 'w20', 'w21', 'w22', 'w23',\n    'w24', 'w25', 'w26', 'w27', 'w28', 'w29', 'w30', 'w31',\n    'wsp', 'wpc',\n    'xzr', 'wzr',\n]  # yapf: disable\n\n\n# FIXME: this is copied from x86, needs to be adapted for ARM64\n_generator_fault_to_fault_name: Dict[str, str] = {\n    'div-by-zero': \"DE\",\n    'div-overflow': \"DE\",\n    'opcode-undefined': \"UD\",\n    'breakpoint': \"BP\",\n    'debug-register': \"DB\",\n    'non-canonical-access': \"GP\",\n    'user-to-kernel-access': \"PF\",\n}\n\n_actor_default = {\n    'name': \"main\",\n    'mode': \"host\",\n    'privilege_level': \"kernel\",\n    'observer': False,\n    'data_properties': {\n        'present': True,\n        'writable': True,\n        'user': False,\n        'accessed': True,\n        'executable': False,\n        'randomized': False,\n    },\n    'data_ept_properties': {\n        'present': True,\n        'writable': True,\n        'executable': False,\n        'accessed': True,\n        'user': False,\n        'randomized': False,\n    },\n    'instruction_blocklist': set(),\n    'fault_blocklist': set(),\n}\n"
  },
  {
    "path": "rvzr/arch/arm64/executor.py",
    "content": "\"\"\"\nFile: Implementation of executor for arm64 architecture\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n\nfrom rvzr.executor import Executor\nfrom rvzr.config import ConfigException\nfrom rvzr.target_desc import TargetDesc\n\n\nclass ARM64Executor(Executor):\n    \"\"\" ARM-specific implementation of the executor \"\"\"\n\n    def __init__(self, enable_mismatch_check_mode: bool = False):\n        super().__init__(enable_mismatch_check_mode)\n        self._vendor = TargetDesc.get_vendor()\n        if self._vendor != \"ARM\":\n            raise ConfigException(\n                \"Attempting to run ARM64Executor executor on a non-ARM CPUs!\\n\"\n                \"Change the `executor` configuration option to the appropriate vendor value.\")\n\n    def _set_vendor_specific_features(self) -> None:\n        pass\n"
  },
  {
    "path": "rvzr/arch/arm64/fuzzer.py",
    "content": "\"\"\"\nFile: arm64 implementation of the test case generator\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, List, Generator\nfrom contextlib import contextmanager\nimport tempfile\nimport os\n\nfrom rvzr.fuzzer import Fuzzer, ArchitecturalFuzzer, ArchDiffFuzzer\nfrom rvzr.traces import HTrace\nfrom rvzr.tc_components.test_case_data import InputData\nfrom rvzr.tc_components.test_case_code import TestCaseProgram\nfrom rvzr.stats import FuzzingStats\nfrom rvzr.config import CONF\nfrom .executor import ARM64Executor\n\nif TYPE_CHECKING:\n    from rvzr.asm_parser import AsmParser\n    from rvzr.elf_parser import ELFParser\n    from rvzr.code_generator import CodeGenerator\n    from rvzr.executor import Executor\n\nSTAT = FuzzingStats()\n\n\n# ==================================================================================================\n# ARM64-specific Implementation of the Fuzzer\n# ==================================================================================================\nclass ARM64Fuzzer(Fuzzer):\n    \"\"\"\n    Implementation of the standard fuzzing mode for the arm64 architecture.\n\n    Extends the generic Fuzzer class with:\n    1. Checking of the instruction set for compatibility with the required faults\n    2. Filtering of non-useful test cases with a Speculation Filter and an Observation Filter\n    \"\"\"\n\n    executor: ARM64Executor\n\n    # ----------------------------------------------------------------------------------------------\n    # Private Methods\n    def _filter(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        \"\"\"\n        This function implements a multi-stage algorithm that gradually filters out\n        uninteresting test cases\n\n        :param test_case: the target test case\n        :param inputs: list of inputs to be tested\n        :return: True if the test case should be filtered out; False otherwise\n        \"\"\"\n        # Exit if all filters are disabled\n        if not CONF.enable_observation_filter:\n            return False\n\n        # Number of repetitions for each input\n        reps = CONF.executor_filtering_repetitions\n\n        with _quick_and_dirty_mode(self.executor):  # Speed up the execution by disabling checks\n            # Collect hardware traces for the test case\n            try:\n                self.executor.load_test_case(test_case)\n                org_htraces = self.executor.trace_test_case(inputs, reps)\n            except IOError:\n                return True\n\n            if self._observation_filter(test_case, inputs, reps, org_htraces):\n                return True\n\n            return False\n\n    def _observation_filter(self, test_case: TestCaseProgram, inputs: List[InputData], reps: int,\n                            org_htraces: List[HTrace]) -> bool:\n        \"\"\"\n        Check if any of the htraces contain a speculative cache eviction\n        for this create a fenced version of the test case and collect traces for it\n        :param test_case: the target test case\n        :param inputs: list of inputs to be tested\n        :param reps: number of repetitions for each input\n        :param org_htraces: list of HTrace objects collected while executing the test case\n        :return: True if the test case should be filtered out; False otherwise\n        \"\"\"\n        if not CONF.enable_observation_filter:\n            return False\n\n        with tempfile.NamedTemporaryFile(delete=False) as fenced:\n            fenced_name = fenced.name\n        fenced_test_case = _create_fenced_test_case(test_case.asm_path(), fenced_name,\n                                                    self.asm_parser, self.code_gen, self.elf_parser)\n        try:\n            self.executor.load_test_case(fenced_test_case)\n            fenced_htraces = self.executor.trace_test_case(inputs, reps)\n        except IOError:\n            return True  # skip the test case if there is an error\n        os.remove(fenced.name)\n\n        traces_match = True\n        for i, _ in enumerate(inputs):\n            if not self.analyser.htraces_are_equivalent(fenced_htraces[i], org_htraces[i]):\n                traces_match = False\n                break\n        if traces_match:\n            STAT.observ_filter += 1\n            return True\n\n        return False\n\n\n# ==================================================================================================\n# Non-standard Fuzzers\n# ==================================================================================================\nclass ARM64ArchitecturalFuzzer(ArchitecturalFuzzer):\n    \"\"\"\n    ARM64-specific implementation of the ArchitecturalFuzzer.\n    \"\"\"\n    # No ARM64-specific implementation is needed\n\n\nclass ARM64ArchDiffFuzzer(ArchDiffFuzzer):\n    \"\"\"\n    ARM64-specific implementation of the ArchDiffFuzzer.\n    \"\"\"\n\n    @staticmethod\n    def _create_fenced_test_case(original_asm: str, fenced_asm: str, asm_parser: AsmParser,\n                                 generator: CodeGenerator,\n                                 elf_parser: ELFParser) -> TestCaseProgram:\n        return _create_fenced_test_case(original_asm, fenced_asm, asm_parser, generator, elf_parser)\n\n\n# ==================================================================================================\n# Helper functions\n# ==================================================================================================\n@contextmanager\ndef _quick_and_dirty_mode(executor: Executor) -> Generator[None, None, None]:\n    \"\"\"\n    Context manager that enables us to use quick and dirty mode in the form of `with` statement\n    \"\"\"\n    try:\n        executor.set_quick_and_dirty(True)\n        yield\n    finally:\n        executor.set_quick_and_dirty(False)\n\n\ndef _create_fenced_test_case(original_asm: str, fenced_asm: str, asm_parser: AsmParser,\n                             generator: CodeGenerator, elf_parser: ELFParser) -> TestCaseProgram:\n    \"\"\" Add fences to all instructions in the test case \"\"\"\n    with open(original_asm, 'r') as f:\n        with open(fenced_asm, 'w') as fenced_file:\n            for line in f:\n                fenced_file.write(line)\n                line = line.strip().lower()\n                if line and line[0] not in [\"/\", \".\", \"b\"] \\\n                        and \"macro\" not in line:\n                    fenced_file.write('dsb SY\\n isb\\n')\n    fenced_test_case = asm_parser.parse_file(fenced_asm, generator, elf_parser)\n    return fenced_test_case\n"
  },
  {
    "path": "rvzr/arch/arm64/generator.py",
    "content": "\"\"\"\nFile: arm64 implementation of the test case generator\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nimport math\nimport random\nfrom typing import List, Dict, TYPE_CHECKING, Tuple, Callable, Literal\n\nfrom rvzr.code_generator import CodeGenerator, Pass, Printer\nfrom rvzr.sandbox import SandboxLayout, DataArea\nfrom rvzr.instruction_spec import InstructionSpec\nfrom rvzr.tc_components.instruction import Instruction, Operand, RegisterOp, FlagsOp, \\\n    MemoryOp, ImmediateOp, AgenOp, CondOp\nfrom rvzr.tc_components.test_case_code import TestCaseProgram, BasicBlock, InstructionNode\n\nfrom .target_desc import ARM64TargetDesc\n\nif TYPE_CHECKING:\n    from rvzr.elf_parser import ELFParser\n    from rvzr.asm_parser import AsmParser\n    from rvzr.isa_spec import InstructionSet\n    from rvzr.target_desc import TargetDesc\n\n\n# ==================================================================================================\n# Private: Assembly Printing\n# ==================================================================================================\nclass _ARM64Printer(Printer):\n\n    def __init__(self, target_desc: ARM64TargetDesc) -> None:\n        super().__init__(target_desc)\n        self.prologue_template = [\"\"]\n        self.epilogue_template = [\n            \".section .data.main\\n\",\n            \".test_case_exit:nop\\n\",\n        ]\n\n    def _instruction_to_str(self, inst: Instruction) -> str:\n        \"\"\"\n        Override to handle ARM64 conditional operands.\n        \"\"\"\n        if inst.name == \"macro\":\n            return self._macro_to_str(inst)\n\n        # Handle conditional operands specially for ARM64\n        cond_op_str = \"\"\n        operands = list(inst.operands)\n        if operands and isinstance(operands[0], CondOp):\n            cond_op_str = operands[0].value\n            operands = operands[1:]\n\n        operands_str = \", \".join([self._operand_to_str(op) for op in operands])\n        if inst.is_instrumentation:\n            comment = \"// instrumentation\"\n        elif inst.is_noremove:\n            comment = \"// noremove\"\n        else:\n            comment = \"\"\n        return f\"{inst.name}{cond_op_str} {operands_str} {comment}\"\n\n    def _operand_to_str(self, op: Operand) -> str:\n        if isinstance(op, (MemoryOp, AgenOp)):\n            return f\"[{op.value}]\"\n        if isinstance(op, ImmediateOp):\n            if self._is_digit_extended(op.value):\n                return f\"#{op.value}\"\n            return f\"{op.value}\"\n\n        return op.value\n\n    def _macro_to_str(self, inst: Instruction) -> str:\n        macro_placeholder = \"nop; nop; nop\"\n        if inst.operands[1].value.lower() == \".noarg\":\n            return f\".macro{inst.operands[0].value}: {macro_placeholder}\"\n        return f\".macro{inst.operands[0].value}{inst.operands[1].value}: {macro_placeholder}\"\n\n    @staticmethod\n    def _is_digit_extended(s: str) -> bool:\n        \"\"\"\n        An extended version of the is_digit function. The difference is that is_digit\n        handles only decimal numbers, while this function can handle hex and binary\n        numbers as well.\n        \"\"\"\n        try:\n            base = 10\n            if s.startswith(\"0x\"):\n                base = 16\n            if s.startswith(\"0b\"):\n                base = 2\n            int(s, base)\n            return True\n        except ValueError:\n            return False\n\n\n# ==================================================================================================\n# Private: Collection of Instrumentation Passes\n# ==================================================================================================\n\n_DispatcherKey = Literal[\"memory\"]\n_SandboxDispatcher = Dict[_DispatcherKey, Tuple[List[InstructionNode],\n                                                Callable[[InstructionNode, BasicBlock], None]]]\n\n\nclass _ARM64SandboxPass(Pass):\n    \"\"\"\n    A pass that instruments the test case to prevent certain types of faults,\n    including:\n    - out-of-sandbox memory accesses\n    - ... (more to be added in the future)\n\n    NOTE: in contrast to x86, arm64 does not fault on div by zero, so no need to\n    sandbox division instructions\n    \"\"\"\n\n    # pylint: disable=R0801\n    # NOTE: there's an overlap between this class and it's equivalent in x86/generator.py\n    # This is acceptable for now as functions are different enough so that deduplication\n    # would hurt readability\n\n    def __init__(self, target_desc: TargetDesc) -> None:\n        super().__init__()\n        self.target_desc = target_desc\n\n        size_of_directly_accessible_memory = SandboxLayout.data_area_size(DataArea.MAIN) \\\n            + SandboxLayout.data_area_size(DataArea.FAULTY)\n        mask_width = int(math.log(size_of_directly_accessible_memory, 2))\n        self.sandbox_address_mask = \"#0b\" + \"1\" * mask_width\n\n    def run_on_test_case(self, test_case: TestCaseProgram) -> None:\n        dispatcher: _SandboxDispatcher = {\n            \"memory\": ([], self._sandbox_memory_access),\n        }\n\n        for bb in test_case.iter_basic_blocks():\n            dispatcher[\"memory\"][0].clear()\n\n            # collect all instructions that require sandboxing\n            for node in bb.iter_nodes():\n                inst = node.instruction\n                if inst.is_instrumentation or inst.is_from_template:\n                    continue\n\n                if inst.has_mem_operand(True):\n                    dispatcher[\"memory\"][0].append(node)\n\n            # sandbox them\n            for _, (nodes, sandbox_func) in dispatcher.items():\n                for node in nodes:\n                    sandbox_func(node, bb)\n\n    def _sandbox_memory_access(self, node: InstructionNode, parent: BasicBlock) -> None:\n        \"\"\" Force the memory accesses into the page starting from x20 \"\"\"\n\n        instr = node.instruction\n\n        # if implicit_mem_operands:\n        #     raise GeneratorException(\"Implicit memory accesses are not supported\")\n\n        # raise GeneratorException(\"Attempt to sandbox an instruction without memory operands\")\n\n        mem_operands = instr.get_mem_operands(True)\n        implicit_mem_operands = \\\n            instr.get_mem_operands(include_explicit=False, include_implicit=True)\n        mask = self.sandbox_address_mask\n\n        if mem_operands and not implicit_mem_operands:\n            assert len(mem_operands) == 1, \\\n                f\"Instructions with multiple memory accesses are not yet supported: {instr.name}\"\n            mem_operand = mem_operands[0]\n            address_reg = mem_operand.value\n            imm_width = mem_operand.width if mem_operand.width <= 32 else 32\n            apply_mask = Instruction(\"and\", is_instrumentation=True) \\\n                .add_op(RegisterOp(address_reg, mem_operand.width, False, True)) \\\n                .add_op(RegisterOp(address_reg, mem_operand.width, True, False)) \\\n                .add_op(ImmediateOp(mask, imm_width)) \\\n                .add_op(FlagsOp((\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n            parent.insert_before(node, apply_mask)\n            add_base = Instruction(\"add\", is_instrumentation=True) \\\n                .add_op(RegisterOp(address_reg, mem_operand.width, False, True)) \\\n                .add_op(RegisterOp(address_reg, mem_operand.width, True, False)) \\\n                .add_op(RegisterOp(\"x20\", 64, True, False)) \\\n                .add_op(FlagsOp((\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n            parent.insert_before(node, add_base)\n            return\n\n        raise NotImplementedError(\"Implicit memory accesses are not yet supported\")\n\n    @staticmethod\n    def requires_sandbox(inst: InstructionSpec) -> bool:\n        \"\"\" Check if the instruction requires instrumentation to prevent faults \"\"\"\n        if inst.has_mem_operand:\n            return True\n        return False\n\n\nclass _ARM64PatchUndefinedLoadsPass(Pass):\n\n    def __init__(self, target_desc: TargetDesc) -> None:\n        super().__init__()\n        self.target_desc = target_desc\n\n    def run_on_test_case(self, test_case: TestCaseProgram) -> None:\n        for bb in test_case.iter_basic_blocks():\n            to_patch: List[Instruction] = []\n\n            for node in bb.iter_nodes():\n                inst = node.instruction\n\n                if inst.is_instrumentation or inst.is_from_template:\n                    continue\n\n                # check if it's a load with post-index\n                if self._is_post_index(inst):\n                    to_patch.append(inst)\n\n            # fix operands\n            for inst in to_patch:\n                org_dest = inst.operands[0]\n                assert isinstance(org_dest, RegisterOp)\n                assert org_dest.width in self.target_desc.registers_by_size\n                options = self.target_desc.registers_by_size[org_dest.width]\n                options = [i for i in options if i != org_dest.value]\n                new_value = random.choice(options)\n                inst.operands[0].value = new_value\n\n    def _is_post_index(self, inst: Instruction) -> bool:\n        if \"ldr\" not in inst.name and \"str\" not in inst.name:\n            return False\n        if inst.get_imm_operands() == []:\n            return False\n\n        ops = inst.operands\n        assert isinstance(ops[0], RegisterOp)\n        assert isinstance(ops[1], MemoryOp)\n        normalized_dest = self.target_desc.reg_normalized[ops[0].value]\n        normalized_dest = self.target_desc.reg_denormalized[normalized_dest][64]\n        if normalized_dest in ops[1].value:\n            return True\n        return False\n\n\n# ==================================================================================================\n# Public Interface\n# ==================================================================================================\nclass ARM64Generator(CodeGenerator):\n    \"\"\" arm64-specific implementation of the test case program generator \"\"\"\n\n    def __init__(self, seed: int, instruction_set: InstructionSet, target_desc: TargetDesc,\n                 asm_parser: AsmParser, elf_parser: ELFParser) -> None:\n        super().__init__(seed, instruction_set, target_desc, asm_parser, elf_parser)\n        assert isinstance(self._target_desc, ARM64TargetDesc)\n\n        # configure instrumentation passes\n        self._passes = [\n            _ARM64SandboxPass(self._target_desc),\n            _ARM64PatchUndefinedLoadsPass(self._target_desc),\n        ]\n        self._printer = _ARM64Printer(self._target_desc)\n"
  },
  {
    "path": "rvzr/arch/arm64/get_spec.py",
    "content": "\"\"\"\nFile: A script that downloads the ARM64 instruction set\n      and parses it into a JSON file that can be used by the generator.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom typing import List\nimport shutil\n\nfrom rvzr.logs import warning, inform\n\n\nclass Downloader:\n    \"\"\" A class that downloads the ARM64 instruction set and converts it to JSON \"\"\"\n\n    def __init__(self, extensions: List[str], out_file: str) -> None:\n        self._extensions = extensions\n        self._out_file = out_file\n        warning(\n            \"downloader\", \"The ARM64 spec retrieval is not implemented yet, \\n\"\n            \"and this script will just copy a spec file from tests/arm64/min_arm64.json\")\n\n    def run(self) -> None:\n        \"\"\" Run the downloader \"\"\"\n        shutil.copy(\"tests/arm64/min_arm64.json\", self._out_file)\n        inform(\"downloader\", f\"ARM64 spec is copied to {self._out_file}\")\n"
  },
  {
    "path": "rvzr/arch/arm64/target_desc.py",
    "content": "\"\"\"\nFile: arm64-specific constants and lists\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom typing import List\nimport re\nimport unicorn.arm64_const as ucc  # type: ignore\n\nfrom rvzr.tc_components.instruction import Instruction\nfrom rvzr.target_desc import TargetDesc, CPUDesc, UnicornTargetDesc\n\n\nclass ARM64TargetDesc(TargetDesc):\n    \"\"\" Target description for arm64 architecture. \"\"\"\n\n    register_sizes = {\n        \"w0\": 32, \"w1\": 32, \"w2\": 32, \"w3\": 32, \"w4\": 32, \"w5\": 32, \"w6\": 32, \"w7\": 32,\n        \"wsp\": 32, \"wzr\": 32,\n        \"x0\": 64, \"x1\": 64, \"x2\": 64, \"x3\": 64, \"x4\": 64, \"x5\": 64, \"x6\": 64, \"x7\": 64,\n        \"sp\": 64, \"xsp\": 64, \"xzr\": 64,\n    }  # yapf: disable\n\n    registers_by_size = {\n        32: [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\"],\n        64: [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\"],\n    }  # yapf: disable\n\n    reg_normalized = {\n        \"w0\": \"R0\", \"x0\": \"R0\",\n        \"w1\": \"R1\", \"x1\": \"R1\",\n        \"w2\": \"R2\", \"x2\": \"R2\",\n        \"w3\": \"R3\", \"x3\": \"R3\",\n        \"w4\": \"R4\", \"x4\": \"R4\",\n        \"w5\": \"R5\", \"x5\": \"R5\",\n        \"w6\": \"R6\", \"x6\": \"R6\",\n        \"w7\": \"R7\", \"x7\": \"R7\",\n        \"w8\": \"R8\", \"x8\": \"R8\",\n        \"w9\": \"R9\", \"x9\": \"R9\",\n        \"w10\": \"R10\", \"x10\": \"R10\",\n        \"w20\": \"R20\", \"x20\": \"R20\",\n        \"w30\": \"R30\", \"x30\": \"R30\",\n        \"CF\": \"CF\", \"ZF\": \"ZF\", \"SF\": \"SF\", \"OF\": \"OF\",\n        \"pc\": \"RIP\",\n        \"sp\": \"RSP\", \"wsp\": \"RSP\", \"xsp\": \"RSP\",\n    }  # yapf: disable\n\n    reg_denormalized = {\n        \"R0\": {64: \"x0\", 32: \"w0\"},\n        \"R1\": {64: \"x1\", 32: \"w1\"},\n        \"R2\": {64: \"x2\", 32: \"w2\"},\n        \"R3\": {64: \"x3\", 32: \"w3\"},\n        \"R4\": {64: \"x4\", 32: \"w4\"},\n        \"R5\": {64: \"x5\", 32: \"w5\"},\n        \"R6\": {64: \"x6\", 32: \"w6\"},\n        \"R7\": {64: \"x7\", 32: \"w7\"},\n        \"R20\": {64: \"x20\", 32: \"w20\"},\n        \"R30\": {64: \"x30\", 32: \"w30\"},\n        \"RIP\": {64: \"pc\"},\n        \"RSP\": {64: \"sp\", 32: \"wsp\"},\n    }  # yapf: disable\n\n    mem_index_registers = [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\"]\n\n    page_property_to_pte_bit_name = {\n        \"present\": (\"valid\", False),\n        \"writable\": (\"non_writable\", True),\n        \"user\": (\"user\", False),\n        \"accessed\": (\"accessed\", False),\n        \"executable\": (\"non_executable\", True),\n    }\n\n    pte_bits = {\n        \"valid\": (0, True),\n        \"user\": (6, False),\n        \"non_writable\": (7, False),\n        \"accessed\": (10, True),\n        \"non_executable\": (53, True),\n    }\n\n    # FIXME: EPTE is not yet supported on ARM64; this is a placeholder\n    page_property_to_vm_pte_bit_name = {\n        \"present\": (\"valid\", False),\n        \"writable\": (\"non_writable\", True),\n        \"user\": (\"user\", False),\n        \"accessed\": (\"accessed\", False),\n        \"executable\": (\"non_executable\", True),\n    }\n\n    # FIXME: EPTE is not yet supported on ARM64; this is a placeholder\n    vm_pte_bits = {\n        \"valid\": (0, True),\n        \"user\": (0, False),\n        \"non_writable\": (0, False),\n        \"accessed\": (0, True),\n        \"non_executable\": (0, True),\n    }\n\n    branch_conditions = {\n        \"eq\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"],\n        \"ne\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"],\n        \"cs\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"],\n        \"cc\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"],\n        \"mi\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"],\n        \"pl\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"],\n        \"vs\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"],\n        \"vc\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"],\n        \"hi\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"],\n        \"ls\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"],\n        \"ge\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"],\n        \"lt\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"],\n        \"gt\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"],\n        \"le\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"],\n        \"al\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"]\n    }\n\n    def __init__(self) -> None:\n        super().__init__()\n\n        # modify/set target parameters based on the CPU under test and the configuration\n        self.registers_by_size = self._filter_blocked_registers()\n        self.cpu_desc = self._build_cpu_desc()\n\n        # connect Unicorn TD\n        self.uc_target_desc = ARM64UnicornTargetDesc()\n\n    @staticmethod\n    def is_unconditional_branch(inst: Instruction) -> bool:\n        return inst.name == \"b\"\n\n    @staticmethod\n    def is_call(inst: Instruction) -> bool:\n        return inst.name == \"bl\"\n\n    def _build_cpu_desc(self) -> CPUDesc:\n        vendor = self.get_vendor()\n\n        with open(\"/proc/cpuinfo\") as f:\n            cpuinfo = f.read()\n\n            family_match = re.search(r\"CPU architecture\\s*:\\s+(.*)\", cpuinfo)\n            assert family_match, \"Failed to find family in /proc/cpuinfo\"\n            family = int(family_match.group(1), 16)\n\n            model_match = re.search(r\"CPU variant\\s+:\\s+(.*)\", cpuinfo)\n            assert model_match, \"Failed to find model name in /proc/cpuinfo\"\n            model = int(model_match.group(1), 16)\n\n            stepping_match = re.search(r\"CPU part\\s+:\\s+(.*)\", cpuinfo)\n            assert stepping_match, \"Failed to find stepping in /proc/cpuinfo\"\n            stepping = int(stepping_match.group(1), 16)\n\n        return CPUDesc(vendor, model, family, stepping)\n\n\nclass ARM64UnicornTargetDesc(UnicornTargetDesc):  # pylint: disable=too-few-public-methods\n    \"\"\" arm64 target description in the context of a Unicorn-based model. \"\"\"\n\n    usable_registers: List[int] = [\n        ucc.UC_ARM64_REG_X0, ucc.UC_ARM64_REG_X1, ucc.UC_ARM64_REG_X2, ucc.UC_ARM64_REG_X3,\n        ucc.UC_ARM64_REG_X4, ucc.UC_ARM64_REG_X5, ucc.UC_ARM64_REG_NZCV, ucc.UC_ARM64_REG_SP\n    ]\n\n    usable_simd128_registers: List[int] = []\n\n    reg_str_to_constant = {\n        \"x0\": ucc.UC_ARM64_REG_X0,\n        \"x1\": ucc.UC_ARM64_REG_X1,\n        \"x2\": ucc.UC_ARM64_REG_X2,\n        \"x3\": ucc.UC_ARM64_REG_X3,\n        \"x4\": ucc.UC_ARM64_REG_X4,\n        \"x5\": ucc.UC_ARM64_REG_X5,\n        \"x6\": ucc.UC_ARM64_REG_X6,\n        \"x7\": ucc.UC_ARM64_REG_X7,\n        \"x8\": ucc.UC_ARM64_REG_X8,\n        \"x9\": ucc.UC_ARM64_REG_X9,\n        \"x10\": ucc.UC_ARM64_REG_X10,\n        \"x11\": ucc.UC_ARM64_REG_X11,\n        \"x12\": ucc.UC_ARM64_REG_X12,\n        \"x13\": ucc.UC_ARM64_REG_X13,\n        \"x14\": ucc.UC_ARM64_REG_X14,\n        \"x15\": ucc.UC_ARM64_REG_X15,\n        \"x16\": ucc.UC_ARM64_REG_X16,\n        \"x17\": ucc.UC_ARM64_REG_X17,\n        \"x18\": ucc.UC_ARM64_REG_X18,\n        \"x19\": ucc.UC_ARM64_REG_X19,\n        \"x20\": ucc.UC_ARM64_REG_X20,\n        \"x21\": ucc.UC_ARM64_REG_X21,\n        \"x22\": ucc.UC_ARM64_REG_X22,\n        \"x23\": ucc.UC_ARM64_REG_X23,\n        \"x24\": ucc.UC_ARM64_REG_X24,\n        \"x25\": ucc.UC_ARM64_REG_X25,\n        \"x26\": ucc.UC_ARM64_REG_X26,\n        \"x27\": ucc.UC_ARM64_REG_X27,\n        \"x28\": ucc.UC_ARM64_REG_X28,\n        \"x29\": ucc.UC_ARM64_REG_X29,\n        \"x30\": ucc.UC_ARM64_REG_X30\n    }\n\n    reg_norm_to_constant = {\n        \"R0\": ucc.UC_ARM64_REG_X0,\n        \"R1\": ucc.UC_ARM64_REG_X1,\n        \"R2\": ucc.UC_ARM64_REG_X2,\n        \"R3\": ucc.UC_ARM64_REG_X3,\n        \"R4\": ucc.UC_ARM64_REG_X4,\n        \"R5\": ucc.UC_ARM64_REG_X5,\n        \"R6\": ucc.UC_ARM64_REG_X6,\n        \"R7\": ucc.UC_ARM64_REG_X7,\n        \"R20\": ucc.UC_ARM64_REG_X20,\n        \"R30\": ucc.UC_ARM64_REG_X30,\n        \"FLAGS\": ucc.UC_ARM64_REG_NZCV,\n        \"SF\": ucc.UC_ARM64_REG_NZCV,  # N\n        \"ZF\": ucc.UC_ARM64_REG_NZCV,  # Z\n        \"CF\": ucc.UC_ARM64_REG_NZCV,  # C\n        \"OF\": ucc.UC_ARM64_REG_NZCV,  # V\n        \"RIP\": -1,\n        \"RSP\": -1,\n    }\n\n    barriers: List[str] = ['dmb', 'dsb', 'isb']\n    flags_register: int = ucc.UC_ARM64_REG_NZCV\n    pc_register: int = ucc.UC_ARM64_REG_PC\n    sp_register: int = ucc.UC_ARM64_REG_SP\n    actor_base_register: int = ucc.UC_ARM64_REG_X20\n"
  },
  {
    "path": "rvzr/arch/x86/__init__.py",
    "content": ""
  },
  {
    "path": "rvzr/arch/x86/asm_parser.py",
    "content": "\"\"\"\nFile: Parsing of assembly files into our internal representation (TestCaseCode).\n      This file contains x86-specific code.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nimport re\nfrom typing import TYPE_CHECKING, List\n\nfrom rvzr.asm_parser import AsmParser, AsmLineParser, asm_parser_assert\nfrom rvzr.instruction_spec import OT, InstructionSpec\n\nif TYPE_CHECKING:\n    from rvzr.isa_spec import InstructionSet\n    from rvzr.target_desc import TargetDesc\n\n# ==================================================================================================\n# Private: Parser of assembly lines in Intel syntax\n# ==================================================================================================\n_PATTERN_CONST_INT = re.compile(\"^-?[0-9]+$\")\n_PATTERN_CONST_HEX = re.compile(\"^-?0x[0-9abcdef]+$\")\n_PATTERN_CONST_BIN = re.compile(\"^-?0b[01]+$\")\n_PATTERN_CONST_SUM = re.compile(\"^-?[0-9]+ *[+-] *[0-9]+$\")\n\n_ASM_PREFIXES = [\"lock\", \"rex\", \"rep\", \"repe\", \"repne\"]\n_ASM_SYNONYMS = {\n    \"je\": \"jz\",\n    \"jne\": \"jnz\",\n    \"jnae\": \"jb\",\n    \"jc\": \"jb\",\n    \"jae\": \"jnb\",\n    \"jnc\": \"jnb\",\n    \"jna\": \"jbe\",\n    \"ja\": \"jnbe\",\n    \"jnge\": \"jl\",\n    \"jge\": \"jnl\",\n    \"jng\": \"jle\",\n    \"jg\": \"jnle\",\n    \"jpe\": \"jp\",\n    \"jpo\": \"jnp\",\n    \"cmove\": \"cmovz\",\n    \"cmovne\": \"cmovnz\",\n    \"cmovnae\": \"cmovb\",\n    \"cmovc\": \"cmovb\",\n    \"cmovae\": \"cmovnb\",\n    \"cmovnc\": \"cmovnb\",\n    \"cmovna\": \"cmovbe\",\n    \"cmova\": \"cmovnbe\",\n    \"cmovnge\": \"cmovl\",\n    \"cmovge\": \"cmovnl\",\n    \"cmovng\": \"cmovle\",\n    \"cmovg\": \"cmovnle\",\n    \"cmovpe\": \"cmovp\",\n    \"cmovpo\": \"cmovnp\",\n    \"sete\": \"setz\",\n    \"setne\": \"setnz\",\n    \"setnae\": \"setb\",\n    \"setc\": \"setb\",\n    \"setae\": \"setnb\",\n    \"setnc\": \"setnb\",\n    \"setna\": \"setbe\",\n    \"seta\": \"setnbe\",\n    \"setnge\": \"setl\",\n    \"setge\": \"setnl\",\n    \"setng\": \"setle\",\n    \"setg\": \"setnle\",\n    \"setpe\": \"setp\",\n    \"setpo\": \"setnp\",\n    \"movabs\": \"mov\",\n    \"repe\": \"repz\",\n    \"repne\": \"repnz\",\n    \"repnz\": \"repne\",\n    \"repz\": \"repe\",\n}\n_MEMORY_SIZES = {\n    \"byte\": 8,\n    \"word\": 16,\n    \"dword\": 32,\n    \"qword\": 64,\n    \"tbyte\": 80,\n    \"xmmword\": 128,\n    \"ymmword\": 256,\n    \"zmmword\": 512\n}\n\n\nclass _X86IntelLineParser(AsmLineParser):\n    _curr_ln: int\n\n    def __init__(self, isa_spec: InstructionSet, target_desc: TargetDesc) -> None:\n        super().__init__(isa_spec, target_desc)\n        self._comment_char = \"#\"\n\n    # ----------------------------------------------------------------------------------------------\n    # Implementation of ISA-specific hooks\n    def _tokenize(self, line: str) -> List[str]:\n        return []  # no need to tokenize in this implementation\n\n    def _get_instruction_name(self, line: str, _: List[str]) -> str:\n        \"\"\" Get the name of the instruction from an assembly line, including prefixes \"\"\"\n        name = \"\"\n        for word in line.split():\n            if word in _ASM_PREFIXES:\n                name += word + \" \"\n                continue\n            name += word\n            break\n        return name\n\n    def _get_instruction_operands(self, line: str, name: str, tokens: List[str]) -> List[str]:\n        \"\"\" Get the list of operand strings from an assembly line \"\"\"\n        operands_raw = line.removeprefix(name).split(\",\")\n        if operands_raw == [\"\"]:  # no operands\n            return []\n        operands_raw = [o.strip() for o in operands_raw]  # remove spaces\n        return operands_raw\n\n    def _get_initial_candidate_specs(self, line: str, _: str) -> List[InstructionSpec]:\n        \"\"\" Get the list of candidate specs for an instruction with the given name  \"\"\"\n        key = \"\"\n        for word in line.split():\n            # include prefixes in the key\n            if word in _ASM_PREFIXES:\n                key += word + \" \"\n                continue\n\n            # fix jump name\n            if word in _ASM_SYNONYMS:\n                key += _ASM_SYNONYMS[word]\n            else:\n                key += word\n            return self._instruction_map.get(key, [])\n        return []\n\n    def _check_if_spec_matches(self, spec: InstructionSpec, operands_raw: List[str]) -> bool:\n        \"\"\" Check if the given spec matches the given list of operand strings \"\"\"\n        # pylint: disable=too-many-return-statements  # justified for selectors\n\n        if len(spec.operands) != len(operands_raw):\n            return False\n\n        for op_id, op_raw in enumerate(operands_raw):\n            op_spec = spec.operands[op_id]\n\n            # match label\n            if op_raw[0] == \".\":\n                if op_spec.type != OT.LABEL:\n                    return False\n                continue\n\n            # match address\n            if \"[\" in op_raw:\n                if op_spec.type not in [OT.AGEN, OT.MEM]:\n                    return False\n\n                access_size = op_raw.split()[0]  # match address size\n                if access_size == \"ptr\":\n                    # out internal convention is that \"ptr\" prefix matches any size\n                    continue\n\n                asm_parser_assert(access_size in _MEMORY_SIZES, self._curr_ln,\n                                  f\"Pointer size must be declared explicitly: {op_raw}\")\n                if op_spec.width != _MEMORY_SIZES[access_size]:\n                    return False\n                continue\n\n            # match immediate value\n            if _PATTERN_CONST_BIN.match(op_raw) or \\\n                    _PATTERN_CONST_HEX.match(op_raw) or \\\n                    _PATTERN_CONST_INT.match(op_raw) or \\\n                    _PATTERN_CONST_SUM.match(op_raw):\n                if op_spec.type != OT.IMM:\n                    return False\n                continue\n\n            # match register\n            if op_spec.type == OT.REG:\n                if op_raw not in op_spec.values:\n                    return False\n                continue\n            return False\n        return True\n\n\n# ==================================================================================================\n# Public Interface: Parser of X86 assembly files\n# ==================================================================================================\nclass X86AsmParser(AsmParser):\n    \"\"\" Implementation of the AsmParser interface for X86 assembly files \"\"\"\n\n    def __init__(self, isa_spec: InstructionSet, target_desc: TargetDesc) -> None:\n        super().__init__(isa_spec, target_desc)\n        self._line_parser = _X86IntelLineParser(isa_spec, target_desc)\n        self._asm_patcher.set_macro_placeholder(\" nop qword ptr [rax + 0xff]\")\n"
  },
  {
    "path": "rvzr/arch/x86/config.py",
    "content": "\"\"\"\nFile: x86-specific Configuration Options\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom typing import List\n\n_option_values = {\n    'actor': [\n        'name',\n        'mode',\n        'privilege_level',\n        'data_properties',\n        'data_ept_properties',\n        'observer',\n        'instruction_blocklist',\n        'fault_blocklist',\n    ],\n    \"actor_mode\": [\n        'host',\n        'guest',\n    ],\n    \"actor_privilege_level\": [\n        'kernel',\n        'user',\n    ],\n    \"actor_data_properties\": [\n        'present',\n        'writable',\n        'user',\n        'write-through',\n        'cache-disable',\n        'accessed',\n        'dirty',\n        'executable',\n        'reserved_bit',\n        'randomized',\n    ],\n    \"actor_data_ept_properties\": [\n        \"present\",\n        \"writable\",\n        \"executable\",\n        \"accessed\",\n        \"dirty\",\n        'reserved_bit',\n        'randomized',\n    ],\n    'unicorn_instruction_categories': [\n        # Base x86 - user instructions\n        \"BASE-BINARY\",\n        \"BASE-BITBYTE\",\n        \"BASE-CMOV\",\n        \"BASE-COND_BR\",\n        \"BASE-CONVERT\",\n        \"BASE-DATAXFER\",\n        \"BASE-FLAGOP\",\n        \"BASE-LOGICAL\",\n        \"BASE-MISC\",\n        \"BASE-NOP\",\n        \"BASE-POP\",\n        \"BASE-PUSH\",\n        \"BASE-SEMAPHORE\",\n        \"BASE-SETCC\",\n        \"BASE-STRINGOP\",\n        \"BASE-WIDENOP\",\n\n        # Base x86 - system instructions\n        \"BASE-INTERRUPT\",\n        # \"BASE-ROTATE\",      # Unknown bug in Unicorn - emulated incorrectly\n        # \"BASE-SHIFT\",       # Unknown bug in Unicorn - emulated incorrectly\n        # \"BASE-UNCOND_BR\",   # Not supported: Complex control flow\n        # \"BASE-CALL\",        # Not supported: Complex control flow\n        # \"BASE-RET\",         # Not supported: Complex control flow\n        # \"BASE-SEGOP\",       # Not supported: System instructions\n        # \"BASE-IO\",          # Not supported: System instructions\n        # \"BASE-IOSTRINGOP\",  # Not supported: System instructions\n        # \"BASE-SYSCALL\",     # Not supported: System instructions\n        # \"BASE-SYSRET\",      # Not supported: System instructions\n        \"BASE-SYSTEM\",\n        \"LONGMODE-CONVERT\",\n        \"LONGMODE-DATAXFER\",\n        \"LONGMODE-SEMAPHORE\",\n        \"LONGMODE-SYSCALL\",\n        \"LONGMODE-SYSRET\",\n\n        # SIMD extensions\n        \"SSE-SSE\",\n        \"SSE-DATAXFER\",\n        \"SSE-MISC\",\n        \"SSE-LOGICAL_FP\",\n        # \"SSE-CONVERT\",  # require MMX\n        # \"SSE-PREFETCH\",  # prefetch does not trigger a mem access in unicorn\n        \"SSE2-SSE\",\n        \"SSE2-DATAXFER\",\n        \"SSE2-MISC\",\n        \"SSE2-LOGICAL_FP\",\n        \"SSE2-LOGICAL\",\n        # \"SSE2-CONVERT\",  # require MMX\n        # \"SSE2-MMX\",   # require MMX\n        \"SSE3-SSE\",\n        \"SSE3-DATAXFER\",\n        # \"SSE4-SSE\",  # not tested yet\n        \"SSE4-LOGICAL\",\n        \"SSE4a-BITBYTE\",\n        \"SSE4a-DATAXFER\",\n\n        # Misc\n        \"CLFLUSHOPT-CLFLUSHOPT\",\n        \"CLFSH-MISC\",\n        # \"MPX-MPX\",  # no longer supported\n        \"SMX-SYSTEM\",\n        \"VTX-VTX\",\n        \"XSAVE-XSAVE\",\n    ],\n    \"dr_instruction_categories\": [\n        # Base x86 - user instructions\n        \"BASE-BINARY\",\n        \"BASE-BITBYTE\",\n        \"BASE-CMOV\",\n        \"BASE-COND_BR\",\n        \"BASE-CONVERT\",\n        \"BASE-DATAXFER\",\n        \"BASE-FLAGOP\",\n        \"BASE-LOGICAL\",\n        \"BASE-MISC\",\n        \"BASE-NOP\",\n        \"BASE-POP\",\n        \"BASE-PUSH\",\n        \"BASE-SEMAPHORE\",\n        \"BASE-SETCC\",\n        \"BASE-STRINGOP\",\n        \"BASE-WIDENOP\",\n\n        # Base x86 - system instructions\n        \"BASE-INTERRUPT\",\n        \"BASE-ROTATE\",\n        \"BASE-SHIFT\",\n        # \"BASE-UNCOND_BR\",   # Not supported: Complex control flow\n        # \"BASE-CALL\",        # Not supported: Complex control flow\n        # \"BASE-RET\",         # Not supported: Complex control flow\n        # \"BASE-SEGOP\",       # Not supported: System instructions\n        # \"BASE-IO\",          # Not supported: System instructions\n        # \"BASE-IOSTRINGOP\",  # Not supported: System instructions\n        # \"BASE-SYSCALL\",     # Not supported: System instructions\n        # \"BASE-SYSRET\",      # Not supported: System instructions\n        \"BASE-SYSTEM\",\n        \"LONGMODE-CONVERT\",\n        \"LONGMODE-DATAXFER\",\n        \"LONGMODE-SEMAPHORE\",\n        \"LONGMODE-SYSCALL\",\n        \"LONGMODE-SYSRET\",\n\n        \"3DNOW_PREFETCH-PREFETCH\",\n        \"ADOX_ADCX-ADOX_ADCX\",\n        \"BASE-BINARY\",\n        \"BASE-BITBYTE\",\n        \"BASE-CMOV\",\n        \"BASE-COND_BR\",\n        \"BASE-CONVERT\",\n        \"BASE-DATAXFER\",\n        \"BASE-FLAGOP\",\n        \"BASE-LOGICAL\",\n        \"BASE-MISC\",\n        \"BASE-NOP\",\n        \"BASE-POP\",\n        \"BASE-PUSH\",\n        \"BASE-ROTATE\",\n        \"BASE-SEMAPHORE\",\n        \"BASE-SETCC\",\n        \"BASE-SHIFT\",\n        \"BASE-WIDENOP\",\n        \"LONGMODE-CONVERT\",\n        \"LONGMODE-DATAXFER\",\n        \"LONGMODE-POP\",\n        \"LONGMODE-PUSH\",\n        \"LONGMODE-SEMAPHORE\",\n        \"MMX-MMX\",\n        \"MMX-LOGICAL\",\n        \"MMX-DATAXFER\",\n        \"SSE2-MMX\",\n        \"SSE3-MMX\",\n        \"SSSE3-MMX\",\n        \"SSE-CONVERT\",\n        \"SSE-DATAXFER\",\n        \"SSE-MISC\",\n        \"SSE-PREFETCH\",\n        \"SSE-SSE\",\n        \"SSE2-CONVERT\",\n        \"SSE2-DATAXFER\",\n        \"SSE2-LOGICAL\",\n        \"SSE2-MISC\",\n        \"SSE2-SSE\",\n        \"SSE3-DATAXFER\",\n        \"SSE3-SSE\",\n        \"SSSE3-SSE\",\n        \"SSE4-LOGICAL\",\n        \"SSE4-SSE\",\n        \"AVX-AVX\",\n        \"AVX-BROADCAST\",\n        \"AVX-DATAXFER\",\n        \"AVX-LOGICAL\",\n        \"AVX-STTNI\",\n        \"AVX2-AVX2\",\n        \"AVX2-BROADCAST\",\n        \"AVX2-DATAXFER\",\n        \"AVX2-LOGICAL\",\n        \"AES-AES\",\n        \"AVXAES-AES\",\n        \"BMI1-BMI1\",\n        \"BMI2-BMI2\",\n        \"MOVBE-DATAXFER\",\n        \"LZCNT-LZCNT\",\n        \"PCLMULQDQ-PCLMULQDQ\",\n    ],\n}\n\n# by default, we always handle page faults\n_handled_faults: List[str] = [\"PF\"]\n\nx86_executor_enable_prefetcher: bool = False\n\"\"\" x86_executor_enable_prefetcher: enable all prefetchers\"\"\"\nx86_executor_enable_ssbp_patch: bool = True\n\"\"\" x86_executor_enable_ssbp_patch: enable a patch against Speculative Store Bypass\"\"\"\nx86_enable_hpa_gpa_collisions: bool = False\n\"\"\" x86_enable_hpa_gpa_collisions: enable collisions between HPA and GPA;\nuseful for testing Foreshadow-like leaks\"\"\"\nx86_disable_div64: bool = True\n\"\"\" x86_disable_div64: do not generate 64-bit division instructions \"\"\"\nx86_generator_align_locks: bool = True\n\"\"\" x86_generator_align_locks: align all generated locks to 8 bytes \"\"\"\n\ninstruction_categories: List[str] = [\"BASE-BINARY\", \"BASE-BITBYTE\", \"BASE-COND_BR\"]\n\"\"\" instruction_categories: a default list of tested instruction categories \"\"\"\n\n_buggy_instructions: List[str] = [\n    \"sti\",  # enables interrupts\n    \"cli\",  # disables interrupts; blocked just in case\n    \"xlat\",  # requires support of segment registers\n    \"xlatb\",  # requires support of segment registers\n    \"cmpxchg8b\",  # known bug: doesn't execute the mem. access hook\n    \"lock cmpxchg8b\",  # https://github.com/unicorn-engine/unicorn/issues/990\n    \"cmpxchg16b\",  # known bug: doesn't execute the mem. access hook\n    \"lock cmpxchg16b\",  # https://github.com/unicorn-engine/unicorn/issues/990\n    \"cpuid\",  # causes false positives: the model and the CPU will likely have different values\n    \"cmpps\",  # causes crash\n    \"cmpss\",  # causes crash\n    'cmppd',  # causes crash\n    'cmpsd',  # causes crash\n    'movq2dq',\n    'movdq2q',\n    'rcpps',  # incorrect emulation\n    'rcpss',  # incorrect emulation\n    #\n    'pcmpestriq',  # conflicting operand size modifiers\n    'pcmpestrmq',  # conflicting operand size modifiers\n    'vpcmpestriq',  # conflicting operand size modifiers\n    'vpcmpestrmq',  # conflicting operand size modifiers\n    #\n    'maskmovdqu',  # non-temp\n    'maskmovq',  # non-temp\n    'vmaskmovdqu',  # non-temp\n    'vmaskmovq',  # non-temp\n]\n\ninstruction_blocklist: List[str] = [\n    # Hard to fix:\n    # - Requires complex instrumentation\n    \"enterw\", \"enter\", \"leavew\", \"leave\",\n    # - requires support of all possible interrupts\n    \"int\",\n    # - system management instruction\n    \"encls\", \"vmxon\", \"stgi\", \"skinit\", \"ldmxcsr\", \"stmxcsr\",\n\n    # - not supported\n    \"lfence\", \"mfence\", \"sfence\", \"clflush\", \"clflushopt\",\n\n    # - under construction\n    # -- trigger FPVI (we have neither a contract nor an instrumentation for it yet)\n    \"divps\", \"divss\", 'divpd', 'divsd',\n    \"mulss\", \"mulps\", 'mulpd', 'mulsd',\n    \"rsqrtps\", \"rsqrtss\", \"sqrtps\", \"sqrtss\", 'sqrtpd', 'sqrtsd',\n    'addps', 'addss', 'addpd', 'addsd',\n    'subps', 'subss', 'subpd', 'subsd',\n    'addsubpd', 'addsubps', 'haddpd', 'haddps', 'hsubpd', 'hsubps',\n]  # yapf: disable\ninstruction_blocklist.extend(_buggy_instructions)\n\n# x86 executor internally uses R8...R15, RSP, RBP and, thus, they are excluded\n# segment registers are also excluded as we don't support their handling so far\n# same for CR* and DR*\nregister_blocklist: List[str] = [\n    # free - rax, rbx, rcx, rdx, rdi, rsi\n    'r8', 'r9', 'r10', 'r11', 'r12', 'r13', 'r14', 'r15', 'rsp', 'rbp',\n    'r8d', 'r9d', 'r10d', 'r11d', 'r12d', 'r13d', 'r14d', 'r15d', 'esp', 'ebp',\n    'r8w', 'r9w', 'r10w', 'r11w', 'r12w', 'r13w', 'r14w', 'r15w', 'sp', 'bp',\n    'r8b', 'r9b', 'r10b', 'r11b', 'r12b', 'r13b', 'r14b', 'r15b', 'spl', 'bpl',\n    'es', 'cs', 'ss', 'ds', 'fs', 'gs',\n    'cr0', 'cr2', 'cr3', 'cr4', 'cr8',\n    'dr0', 'dr1', 'dr2', 'dr3', 'dr4', 'dr5', 'dr6', 'dr7',\n    \"xcr0\", \"gdtr\", \"ldtr\", \"idtr\", \"tr\", \"fsbase\", \"gsbase\", \"msrs\", \"x87control\", \"tsc\", \"tscaux\",\n    \"mxcsr\",\n\n    # XMM8-15 are somehow broken in Unicorn\n    \"xmm8\", \"xmm9\", \"xmm10\", \"xmm11\", \"xmm12\", \"xmm13\", \"xmm14\", \"xmm15\",\n    \"ymm8\", \"ymm9\", \"ymm10\", \"ymm11\", \"ymm12\", \"ymm13\", \"ymm14\", \"ymm15\",\n]  # yapf: disable\n\n\n_generator_fault_to_fault_name = {\n    'div-by-zero': \"DE\",\n    'div-overflow': \"DE\",\n    'opcode-undefined': \"UD\",\n    'breakpoint': \"BP\",\n    'debug-register': \"DB\",\n    'non-canonical-access': \"GP\",\n    'user-to-kernel-access': \"PF\",\n    'page-fault': \"PF\"\n}\n\n_actor_default = {\n    'name': \"main\",\n    'mode': \"host\",\n    'privilege_level': \"kernel\",\n    'observer': False,\n    'data_properties': {\n        'present': True,\n        'writable': True,\n        'user': False,\n        'write-through': False,\n        'cache-disable': False,\n        'accessed': True,\n        'dirty': True,\n        'executable': False,\n        'reserved_bit': False,\n        'randomized': False,\n    },\n    'data_ept_properties': {\n        'present': True,\n        'writable': True,\n        'executable': False,\n        'accessed': True,\n        'dirty': True,\n        'user': False,\n        'reserved_bit': False,\n        'randomized': False,\n    },\n    'instruction_blocklist': set(),\n    'fault_blocklist': set(),\n}\n"
  },
  {
    "path": "rvzr/arch/x86/executor.py",
    "content": "\"\"\"\nFile: Implementation of executor for x86 architecture\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom typing import Dict, Final\n\nfrom rvzr.executor import Executor, km_write\nfrom rvzr.config import CONF, ConfigException\nfrom rvzr.target_desc import TargetDesc\n\nFAULT_IDS: Final[Dict[str, int]] = {\n    'DE': 0,\n    'DB': 1,\n    'NMI': 2,\n    'BP': 3,\n    'OF': 4,\n    'BR': 5,\n    'UD': 6,\n    'NM': 7,\n    'DF': 8,\n    'OLD_MF': 9,\n    'TS': 10,\n    'NP': 11,\n    'SS': 12,\n    'GP': 13,\n    'PF': 14,\n    'SPURIOUS': 15,\n    'MF': 16,\n    'AC': 17,\n    'MC': 18,\n    'XF': 19,\n    'IRET': 32\n}\n\n\nclass X86Executor(Executor):\n    \"\"\" Base x86 implementation of the executor \"\"\"\n\n    def __init__(self, enable_mismatch_check_mode: bool = False):\n        self._handled_faults_bitmap: int = self._identify_handled_faults()\n        super().__init__(enable_mismatch_check_mode)\n\n    def _set_vendor_specific_features(self) -> None:\n        km_write(\"1\" if getattr(CONF, 'x86_executor_enable_ssbp_patch') else \"0\",\n                 \"/sys/rvzr_executor/enable_ssbp_patch\")\n        km_write(\"1\" if getattr(CONF, 'x86_executor_enable_prefetcher') else \"0\",\n                 \"/sys/rvzr_executor/enable_prefetcher\")\n        km_write(\"1\" if getattr(CONF, 'x86_enable_hpa_gpa_collisions') else \"0\",\n                 \"/sys/rvzr_executor/enable_hpa_gpa_collisions\")\n        km_write(str(self._handled_faults_bitmap), \"/sys/rvzr_executor/handled_faults\")\n\n    def _identify_handled_faults(self) -> int:\n        handled_faults_bitmap = 0\n        for fault in CONF._handled_faults:  # type: ignore  # pylint: disable=protected-access\n            if fault in FAULT_IDS:\n                handled_faults_bitmap |= (1 << FAULT_IDS[fault])\n        return handled_faults_bitmap\n\n\nclass X86IntelExecutor(X86Executor):\n    \"\"\" Intel-specific implementation of the executor \"\"\"\n\n    def __init__(self, enable_mismatch_check_mode: bool = False):\n        super().__init__(enable_mismatch_check_mode)\n        self._vendor = TargetDesc.get_vendor()\n        if self._vendor != \"Intel\":\n            raise ConfigException(\n                \"Attempting to run Intel executor on a non-Intel CPUs!\\n\"\n                \"Change the `executor` configuration option to the appropriate vendor value.\")\n\n\nclass X86AMDExecutor(X86Executor):\n    \"\"\" AMD-specific implementation of the executor \"\"\"\n\n    def __init__(self, enable_mismatch_check_mode: bool = False):\n        super().__init__(enable_mismatch_check_mode)\n        self._vendor = TargetDesc.get_vendor()\n        if self._vendor != \"AMD\":\n            raise ConfigException(\n                \"Attempting to run AMD executor on a non-AMD CPUs!\\n\"\n                \"Change the `executor` configuration option to the appropriate vendor value.\")\n"
  },
  {
    "path": "rvzr/arch/x86/fuzzer.py",
    "content": "\"\"\"\nFile: x86 implementation of the test case generator\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import List, Generator, TYPE_CHECKING\nfrom contextlib import contextmanager\nimport tempfile\nimport os\n\nfrom rvzr.fuzzer import Fuzzer, ArchitecturalFuzzer, ArchDiffFuzzer, FuzzingMode\nfrom rvzr.traces import HTrace\nfrom rvzr.executor import Executor\nfrom rvzr.tc_components.test_case_data import InputData\nfrom rvzr.tc_components.test_case_code import TestCaseProgram\nfrom rvzr.logs import warning\nfrom rvzr.stats import FuzzingStats\nfrom rvzr.config import CONF\nfrom .config import _buggy_instructions\nfrom .executor import X86IntelExecutor\n\nif TYPE_CHECKING:\n    from rvzr.isa_spec import InstructionSet\n    from rvzr.asm_parser import AsmParser\n    from rvzr.elf_parser import ELFParser\n    from rvzr.code_generator import CodeGenerator\n\nSTAT = FuzzingStats()\n\n\n# ==================================================================================================\n# X86-specific Implementation of the Fuzzer\n# ==================================================================================================\nclass X86Fuzzer(Fuzzer):\n    \"\"\"\n    Implementation of the standard fuzzing mode for the x86 architecture.\n\n    Extends the generic Fuzzer class with:\n    1. Checking of the instruction set for compatibility with the required faults\n    2. Filtering of non-useful test cases with a Speculation Filter and an Observation Filter\n    \"\"\"\n\n    executor: X86IntelExecutor\n\n    # ----------------------------------------------------------------------------------------------\n    # Public Interface\n    def start(self, num_test_cases: int, num_inputs: int, timeout: int, nonstop: bool,\n              save_violations: bool, type_: FuzzingMode) -> bool:\n        _check_instruction_list(self._isa_spec)\n        return super().start(num_test_cases, num_inputs, timeout, nonstop, save_violations, type_)\n\n    # ----------------------------------------------------------------------------------------------\n    # Private Methods\n    def _filter(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        \"\"\"\n        This function implements a multi-stage algorithm that gradually filters out\n        uninteresting test cases\n\n        :param test_case: the target test case\n        :param inputs: list of inputs to be tested\n        :return: True if the test case should be filtered out; False otherwise\n        \"\"\"\n        # Exit if all filters are disabled\n        if not CONF.enable_speculation_filter and not CONF.enable_observation_filter:\n            return False\n\n        # Number of repetitions for each input\n        reps = CONF.executor_filtering_repetitions\n\n        with _quick_and_dirty_mode(self.executor):  # Speed up the execution by disabling some\n            # Collect hardware traces for the test case\n            try:\n                self.executor.load_test_case(test_case)\n                org_htraces = self.executor.trace_test_case(inputs, reps)\n            except IOError:\n                return True\n\n            if self._speculation_filter(org_htraces):\n                return True\n\n            if self._observation_filter(test_case, inputs, reps, org_htraces):\n                return True\n\n            return False\n\n    @staticmethod\n    def _speculation_filter(htraces: List[HTrace]) -> bool:\n        \"\"\"\n        Execute on the test case on the HW and monitor PFCs\n        if there are no mispredictions, this test case is unlikely\n        to produce a violation, so just move on to the next one\n        :param htraces: list of HTrace objects collected while executing the test case\n        :return: True if the test case should be filtered out; False otherwise\n        \"\"\"\n        if not CONF.enable_speculation_filter:\n            return False\n\n        for _, htrace in enumerate(htraces):\n            pfc_values = htrace.get_max_pfc()\n            if pfc_values[0] == 0:  # zero indicates an error; filtering is not possible\n                return False\n            if pfc_values[0] > pfc_values[1] or pfc_values[2] > 0:\n                return False\n        STAT.spec_filter += 1\n        return True\n\n    def _observation_filter(self, test_case: TestCaseProgram, inputs: List[InputData], reps: int,\n                            org_htraces: List[HTrace]) -> bool:\n        \"\"\"\n        Check if any of the htraces contain a speculative cache eviction\n        for this create a fenced version of the test case and collect traces for it\n        :param test_case: the target test case\n        :param inputs: list of inputs to be tested\n        :param reps: number of repetitions for each input\n        :param org_htraces: list of HTrace objects collected while executing the test case\n        :return: True if the test case should be filtered out; False otherwise\n        \"\"\"\n        if not CONF.enable_observation_filter:\n            return False\n\n        with tempfile.NamedTemporaryFile(delete=False) as fenced:\n            fenced_name = fenced.name\n        fenced_test_case = _create_fenced_test_case(test_case.asm_path(), fenced_name,\n                                                    self.asm_parser, self.code_gen, self.elf_parser)\n        try:\n            self.executor.load_test_case(fenced_test_case)\n            fenced_htraces = self.executor.trace_test_case(inputs, reps)\n        except IOError:\n            return True  # skip the test case if there is an error\n        os.remove(fenced.name)\n\n        traces_match = True\n        for i, _ in enumerate(inputs):\n            if not self.analyser.htraces_are_equivalent(fenced_htraces[i], org_htraces[i]):\n                traces_match = False\n                break\n        if traces_match:\n            STAT.observ_filter += 1\n            return True\n\n        return False\n\n    def _adjust_config(self, existing_test_case: str) -> None:\n        super()._adjust_config(existing_test_case)\n        _update_instruction_list()\n\n\n# ==================================================================================================\n# Non-standard Fuzzers\n# ==================================================================================================\nclass X86ArchitecturalFuzzer(ArchitecturalFuzzer):\n    \"\"\"\n    X86-specific implementation of the ArchitecturalFuzzer.\n    Essentially the same as the generic ArchitecturalFuzzer, but with some additional checks\n    on the instruction set\n    \"\"\"\n\n    def _adjust_config(self, existing_test_case: str) -> None:\n        super()._adjust_config(existing_test_case)\n        _update_instruction_list()\n\n    def start(self, num_test_cases: int, num_inputs: int, timeout: int, nonstop: bool,\n              save_violations: bool, type_: FuzzingMode) -> bool:\n        _check_instruction_list(self._isa_spec)\n        return super().start(num_test_cases, num_inputs, timeout, nonstop, save_violations, type_)\n\n\nclass X86ArchDiffFuzzer(ArchDiffFuzzer):\n    \"\"\"\n    Fuzzer that compares the execution of a test case with and without fences.\n    If the results differ, it reports a violation.\n\n    Used to detect architectural bugs caused by speculative execution.\n    \"\"\"\n\n    executor: X86IntelExecutor\n\n    def _adjust_config(self, existing_test_case: str) -> None:\n        super()._adjust_config(existing_test_case)\n        _update_instruction_list()\n\n    def start(self, num_test_cases: int, num_inputs: int, timeout: int, nonstop: bool,\n              save_violations: bool, type_: FuzzingMode) -> bool:\n        _check_instruction_list(self._isa_spec)\n        return super().start(num_test_cases, num_inputs, timeout, nonstop, save_violations, type_)\n\n    @staticmethod\n    def _create_fenced_test_case(original_asm: str, fenced_asm: str, asm_parser: AsmParser,\n                                 generator: CodeGenerator,\n                                 elf_parser: ELFParser) -> TestCaseProgram:\n        return _create_fenced_test_case(original_asm, fenced_asm, asm_parser, generator, elf_parser)\n\n\n# ==================================================================================================\n# Helper functions\n# ==================================================================================================\ndef _update_instruction_list() -> None:\n    \"\"\"\n    Remove those instructions that trigger unhandled exceptions.\n    This functionality is implemented as a module-level function\n    to avoid code duplication between X86Fuzzer and X86ArchitecturalFuzzer\n    \"\"\"\n    if 'opcode-undefined' not in CONF.faults_allowlist:\n        CONF.instruction_blocklist.extend([\"ud\", \"ud2\"])\n    if 'breakpoint' not in CONF.faults_allowlist:\n        CONF.instruction_blocklist.extend([\"int3\"])\n    if 'debug-register' not in CONF.faults_allowlist:\n        CONF.instruction_blocklist.extend([\"int1\"])\n\n\ndef _check_instruction_list(instruction_set: InstructionSet) -> None:\n    \"\"\" Check if the instruction set contains the instructions required for the faults \"\"\"\n    all_instruction_names = {i.name for i in instruction_set.instructions}\n    if 'div-by-zero' in CONF.faults_allowlist:\n        if 'div' not in all_instruction_names and 'idiv' not in all_instruction_names:\n            warning(\"fuzzer\", \"div-by-zero enabled, but DIV/IDIV instructions are missing\")\n    if 'div-overflow' in CONF.faults_allowlist:\n        if 'div' not in all_instruction_names and 'idiv' not in all_instruction_names:\n            warning(\"fuzzer\", \"div-overflow enabled, but DIV/IDIV instructions are missing\")\n    if 'breakpoint' in CONF.faults_allowlist:\n        if 'int3' not in all_instruction_names:\n            warning(\"fuzzer\", \"breakpoint enabled, but INT3 instruction is missing\")\n    if 'debug-register' in CONF.faults_allowlist:\n        if 'int1' not in all_instruction_names:\n            warning(\"fuzzer\", \"debug-register enabled, but INT1 instruction is missing\")\n\n    # Print a warning if the instruction set contains instructions that are known to be problematic\n    for inst_name in _buggy_instructions:\n        if inst_name in all_instruction_names and CONF.is_generation_enabled():\n            warning(\n                \"fuzzer\", f\"Instruction {inst_name} is known to cause false positives\\n\"\n                \"Consider adding it to instruction_blocklist\")\n\n\n@contextmanager\ndef _quick_and_dirty_mode(executor: Executor) -> Generator[None, None, None]:\n    \"\"\"\n    Context manager that enables us to use quick and dirty mode in the form of `with` statement\n    \"\"\"\n    try:\n        executor.set_quick_and_dirty(True)\n        yield\n    finally:\n        executor.set_quick_and_dirty(False)\n\n\ndef _create_fenced_test_case(original_asm: str, fenced_asm: str, asm_parser: AsmParser,\n                             generator: CodeGenerator, elf_parser: ELFParser) -> TestCaseProgram:\n    \"\"\" Add fences to all instructions in the test case \"\"\"\n    with open(original_asm, 'r') as f:\n        with open(fenced_asm, 'w') as fenced_file:\n            lines = f.readlines()\n            n_lines = len(lines)\n\n            for i, line in enumerate(lines):\n                fenced_file.write(line)\n                line = line.strip().lower()\n\n                # no need to add fences after empty lines and comments\n                if not line or line[0] == \"#\":\n                    continue\n\n                # adding a fence after a jump instruction breaks assumptions of our asm parser;\n                # this is an issue in the parser, and this check is a workaround\n                if line[0] == \"j\" or \"loop\" in line:\n                    continue\n\n                # don't add fences after assembler directives\n                if \"section\" in line \\\n                   or \"syntax\" in line \\\n                   or \"function\" in line \\\n                   or \"macro\" in line:\n                    continue\n\n                # stop adding fences after the test case exit\n                if \"test_case_exit\" in line:\n                    break\n\n                # adding a fence before the landing pad will mess with the parsing algorithm,\n                # and it won't have any meaningful effect anyways. So we skip it\n                if i < n_lines and \"landing\" in lines[i + 1]:\n                    continue\n\n                # add fences after all other instructions\n                fenced_file.write('lfence\\n')\n\n    fenced_test_case = asm_parser.parse_file(fenced_asm, generator, elf_parser)\n    return fenced_test_case\n"
  },
  {
    "path": "rvzr/arch/x86/generator.py",
    "content": "\"\"\"\nFile: x86 implementation of the test case generator\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nimport math\nimport re\nimport random\nfrom copy import deepcopy\nfrom dataclasses import dataclass\nfrom typing import List, Dict, Set, TYPE_CHECKING, Union, Final, Tuple, Callable, Literal\nfrom typing_extensions import assert_never\n\nfrom rvzr.code_generator import CodeGenerator, Pass, Printer\nfrom rvzr.config import CONF\nfrom rvzr.sandbox import SandboxLayout, DataArea, PAGE_SIZE\nfrom rvzr.instruction_spec import OT, InstructionSpec\nfrom rvzr.tc_components.actor import ActorPL, ActorID\nfrom rvzr.tc_components.instruction import Instruction, Operand, RegisterOp, FlagsOp, \\\n    MemoryOp, ImmediateOp, AgenOp, copy_op_with_flow_modification, \\\n    copy_inst_with_modification\nfrom rvzr.tc_components.test_case_code import TestCaseProgram, BasicBlock, InstructionNode\n\nfrom .target_desc import X86TargetDesc\n\nif TYPE_CHECKING:\n    from rvzr.elf_parser import ELFParser\n    from rvzr.asm_parser import AsmParser\n    from rvzr.isa_spec import InstructionSet\n    from rvzr.target_desc import TargetDesc\n\n\n# ==================================================================================================\n# Private: Fault Type Identification\n# ==================================================================================================\n@dataclass\nclass _FaultFilter:\n    \"\"\" Local service class that identifies which faults are allowed in test cases. \"\"\"\n\n    def __init__(self) -> None:\n        self.div_by_zero: bool = 'div-by-zero' in CONF.faults_allowlist\n        self.div_overflow: bool = 'div-overflow' in CONF.faults_allowlist\n        self.non_canonical_access: bool = 'non-canonical-access' in CONF.faults_allowlist\n        self.u2k_access: bool = 'user-to-kernel-access' in CONF.faults_allowlist\n\n\n# ==================================================================================================\n# Private: Assembly Printing\n# ==================================================================================================\nclass _X86Printer(Printer):\n    target_desc: X86TargetDesc\n\n    def __init__(self, target_desc: X86TargetDesc) -> None:\n        super().__init__(target_desc)\n        self.prologue_template = [\".intel_syntax noprefix\\n\"]\n        self.epilogue_template = [\n            \".section .data.main\\n\",\n            \".test_case_exit:nop\\n\",\n        ]\n\n    def _instruction_to_str(self, inst: Instruction) -> str:\n        if inst.name == \"macro\":\n            return self._macro_to_str(inst)\n\n        operands = \", \".join([self._operand_to_str(op) for op in inst.operands])\n        if inst.is_instrumentation:\n            comment = \"# instrumentation\"\n        elif inst.is_noremove:\n            comment = \"# noremove\"\n        else:\n            comment = \"\"\n        return f\"{inst.name} {operands} {comment}\"\n\n    def _operand_to_str(self, op: Operand) -> str:\n        if isinstance(op, (MemoryOp, AgenOp)):\n            prefix = self.target_desc.memory_addr_prefixes[op.width]\n            return f\"{prefix} [{op.value}]\"\n\n        return op.value\n\n    def _macro_to_str(self, inst: Instruction) -> str:\n        macro_placeholder = \"nop qword ptr [rax + 0xff]\"\n        if inst.operands[1].value.lower() == \".noarg\":\n            return f\".macro{inst.operands[0].value}: {macro_placeholder}\"\n        return f\".macro{inst.operands[0].value}{inst.operands[1].value}: {macro_placeholder}\"\n\n\n# ==================================================================================================\n# Private: Collection of Instrumentation Passes\n# ==================================================================================================\nclass _X86NonCanonicalAddressPass(Pass):\n    \"\"\"\n    A pass that selects a random memory access instruction and replaces it with an access to a\n    non-canonical address.\n    \"\"\"\n    _target_desc: X86TargetDesc\n\n    def __init__(self, target_desc: X86TargetDesc) -> None:\n        super().__init__()\n        self._target_desc = target_desc\n\n    def run_on_test_case(self, test_case: TestCaseProgram) -> None:\n        for bb in test_case.iter_basic_blocks():\n            memory_instructions = []\n            for node in bb.iter_nodes():\n                instr = node.instruction\n                if instr.is_instrumentation or instr.is_from_template:\n                    continue\n                if instr.name in [\"div\", \"idiv\"]:\n                    # Instrumentation is difficult to combine\n                    continue\n                if instr.has_mem_operand(True):\n                    memory_instructions.append(node)\n\n            # instrument random memory access instructions\n            for node in memory_instructions:\n                n = len(memory_instructions)\n                rand_bool = random.randint(0, n) == 0\n                if not rand_bool:\n                    continue\n                self._instrument(node, bb)\n\n                # Make sure #GP happens only once. Otherwise Unicorn keeps raising an exception\n                # when rolling back to the end of the code\n                return\n\n    def _instrument(self, node: InstructionNode, parent: BasicBlock) -> None:\n        \"\"\" Instrument a selected memory access instruction to make the access non-canonical. \"\"\"\n        # pylint: disable = too-many-locals\n        # NOTE: That's a fairly complex instrumentation, so the number of locals is justified\n        instr = node.instruction\n\n        # Collect src operands\n        src_operands = []\n        for o in instr.get_src_operands():\n            if isinstance(o, RegisterOp):\n                src_operands.append(o)\n\n        # Check if the instrumentation is possible\n        mem_operands = instr.get_mem_operands(include_explicit=True)\n        implicit_mem_operands = \\\n            instr.get_mem_operands(include_explicit=False, include_implicit=True)\n        if not mem_operands or implicit_mem_operands:\n            return  # this instruction is hard to instrument; skip\n\n        # Find registers suitable for the instrumentation\n        assert len(mem_operands) == 1, f\"Unexpected instruction format {instr.name}\"\n        mem_operand: Operand = mem_operands[0]\n        mask_reg = self._find_mask_register(src_operands)\n        offset_reg = self._find_offset_register(instr)\n\n        # Generate a random mask to make the address non-canonical\n        mask = hex((random.getrandbits(16) << 48))\n\n        # Add the instrumentation sequence:\n        #  lea offset_reg, [mem_operand]\n        #  mov mask_reg, mask\n        #  xor offset_reg, mask_reg\n        lea = Instruction(\"lea\", is_instrumentation=True) \\\n            .add_op(RegisterOp(offset_reg, 64, False, True)) \\\n            .add_op(MemoryOp(mem_operand.value, 64, True, False))\n        parent.insert_before(node, lea)\n        mov = Instruction(\"mov\", is_instrumentation=True) \\\n            .add_op(RegisterOp(mask_reg, 64, True, True)) \\\n            .add_op(ImmediateOp(mask, 64))\n        parent.insert_before(node, mov)\n        mask_inst = Instruction(\"xor\", is_instrumentation=True) \\\n            .add_op(RegisterOp(offset_reg, 64, True, True)) \\\n            .add_op(RegisterOp(mask_reg, 64, True, False))\n        parent.insert_before(node, mask_inst)\n\n        # Update the memory operand\n        for idx, op in enumerate(instr.operands):\n            if op == mem_operand:\n                old_op = instr.operands[idx]\n                assert isinstance(old_op, MemoryOp)\n                addr_op = MemoryOp(offset_reg, old_op.width, old_op.src, old_op.dest)\n                instr.operands[idx] = addr_op\n\n    def _find_mask_register(self, src_operands: List[RegisterOp]) -> str:\n        # Do not overwrite offset register with mask\n        candidate_list = [\"rax\", \"rbx\"]\n        mask_reg = candidate_list[0]\n        for operands in src_operands:\n            op_regs = re.split(r'\\+|-|\\*| ', operands.value)\n            for reg in op_regs:\n                if self._target_desc.reg_normalized[mask_reg] == \\\n                   self._target_desc.reg_normalized[reg]:\n                    mask_reg = candidate_list[1]\n        return mask_reg\n\n    def _find_offset_register(self, inst: Instruction) -> str:\n        # Do not reuse destination register\n        candidate_list = [\"rcx\", \"rdx\"]\n        offset_reg = candidate_list[0]\n        for op in inst.get_all_operands():\n            if not isinstance(op, RegisterOp):\n                continue\n            if self._target_desc.reg_normalized[offset_reg] == \\\n               self._target_desc.reg_normalized[op.value]:\n                offset_reg = candidate_list[1]\n        return offset_reg\n\n\nclass _X86U2KAccessPass(Pass):\n    \"\"\"\n    User-to-Kernel Access Instrumentation Pass.\n\n    This pass instruments user-privilege actors to perform memory accesses targeting\n    the kernel actor's (actor 0) FAULTY data area. This creates cross-privilege-level\n    memory access patterns useful for detecting CPU vulnerabilities like Meltdown.\n\n    The pass randomly selects memory access instructions in user actors and modifies\n    their memory operands to access kernel memory by calculating and applying a fixed\n    offset based on the sandbox memory layout.\n\n    IMPORTANT: This pass must run after _X86SandboxPass because it modifies the\n    sandboxing instrumentation to ensure memory accesses target a single page.\n    \"\"\"\n\n    def run_on_test_case(self, test_case: TestCaseProgram) -> None:\n        \"\"\"\n        Identify and instrument memory accesses in user-privilege actors.\n\n        :param test_case: The test case to process\n        \"\"\"\n        # Use enumeration order as actor ID\n        # FIXME: This is potentially fragile as it assumes that the sections in the binary\n        # will be ordered the same way as the actors were defined in the test case, which\n        # may not always hold true. However, we cannot get the actual section ID here because\n        # the assembly is not yet generated, so there is nothing to assemble. It's a chicken-and-egg\n        # problem. Thus, for now, we have to rely on this assumption.\n        for sec_id, sec in enumerate(test_case):\n            owner = sec.owner\n            # Only instrument user-privilege actors (kernel accesses don't trigger Meltdown)\n            if owner.privilege_level != ActorPL.USER:\n                continue\n\n            for func in sec:\n                to_instrument: List[InstructionNode] = []\n                for bb in func:\n                    for node in bb.iter_nodes():\n                        instr = node.instruction\n                        # Skip instrumentation code and template code\n                        if instr.is_instrumentation or instr.is_from_template:\n                            continue\n                        # Skip div/idiv as instrumentation interferes with their operand constraints\n                        if instr.name in [\"div\", \"idiv\"]:\n                            continue\n                        if instr.has_mem_operand(False):\n                            to_instrument.append(node)\n\n                    for node in to_instrument:\n                        # Randomly select instructions based on avg_mem_accesses config\n                        probability = 1 / CONF.avg_mem_accesses\n                        if random.random() > probability:\n                            continue\n\n                        self._instrument(node, bb, sec_id)\n\n    def _instrument(self, node: InstructionNode, _: BasicBlock, owner_id: ActorID) -> None:\n        \"\"\"\n        Modify a memory access instruction to target kernel memory instead of user memory.\n        :param node: The instruction node to instrument\n        :param _: The parent basic block (unused)\n        :param owner_id: The actor ID of the instruction's owner (used for offset calculation)\n        \"\"\"\n        instr = node.instruction\n\n        # Calculate the memory offset from user's MAIN area to kernel's FAULTY area.\n        # We use a dummy SandboxLayout with base (0,0) to compute the relative offset\n        # between the two memory regions based on their positions in the sandbox layout.\n        layout = SandboxLayout((0, 0), owner_id)\n        user_main_start = layout.get_data_addr(DataArea.MAIN, owner_id)\n        kernel_faulty_start = layout.get_data_addr(DataArea.FAULTY, 0)\n        offset = user_main_start - kernel_faulty_start\n\n        # Select which memory operand to modify (random if instruction has multiple)\n        mem_operands: List[MemoryOp] = instr.get_mem_operands(True)\n        if len(mem_operands) == 1:\n            mem_operand = mem_operands[0]\n        else:\n            mem_operand = random.choice(mem_operands)\n\n        # Redirect the memory access to kernel memory by subtracting the offset\n        mem_operand.value += \" - \" + str(offset)\n\n        # Adjust sandboxing masks added by X86SandboxPass to target a single page.\n        # X86SandboxPass adds AND instructions to mask memory addresses to stay within\n        # the actor's data sandbox. We need to reduce these masks to PAGE_SIZE to ensure\n        # the cross-privilege access targets only the kernel's FAULTY page.\n        previous_node = node.previous\n        while previous_node and previous_node.instruction.is_instrumentation:\n            for op in previous_node.instruction.operands:\n                if not isinstance(op, ImmediateOp):\n                    continue\n                mask_value = int(op.value, base=0)\n                if mask_value > PAGE_SIZE:\n                    mask_value %= PAGE_SIZE\n                op.value = bin(mask_value)\n            previous_node = previous_node.previous\n\n\n_DispatcherKey = Literal[\"memory\", \"division\", \"bit_test\", \"repeated\", \"corrupted_cf\", \"enclu\"]\n_SandboxDispatcher = Dict[_DispatcherKey, Tuple[List[InstructionNode],\n                                                Callable[[InstructionNode, BasicBlock], None]]]\n\n\nclass _X86SandboxPass(Pass):\n    \"\"\"\n    A pass that instruments the test case to prevent certain types of faults,\n    including:\n    - division by zero\n    - division overflow\n    - out-of-sandbox memory accesses\n    - CF corruption\n    - invalid ENCLU operands\n    \"\"\"\n\n    mask_3bits = \"0b111\"\n    bit_test_names = [\"bt\", \"btc\", \"btr\", \"bts\", \"lock bt\", \"lock btc\", \"lock btr\", \"lock bts\"]\n\n    def __init__(self, target_desc: TargetDesc, faults: _FaultFilter) -> None:\n        super().__init__()\n        self.target_desc = target_desc\n        self.faults = faults\n\n        size_of_directly_accessible_memory = SandboxLayout.data_area_size(DataArea.MAIN) \\\n            + SandboxLayout.data_area_size(DataArea.FAULTY)\n        mask_width = int(math.log(size_of_directly_accessible_memory, 2))\n        self.sandbox_address_mask = \"0b\" + \"1\" * mask_width\n\n    def run_on_test_case(self, test_case: TestCaseProgram) -> None:\n        dispatcher: _SandboxDispatcher = {\n            \"memory\": ([], self._sandbox_memory_access),\n            \"division\": ([], self._sandbox_division),\n            \"bit_test\": ([], self._sandbox_bit_test),\n            \"repeated\": ([], self._sandbox_repeated_instruction),\n            \"corrupted_cf\": ([], self._sandbox_corrupted_cf),\n            \"enclu\": ([], self._sandbox_enclu),\n        }\n\n        for bb in test_case.iter_basic_blocks():\n            dispatcher[\"memory\"][0].clear()\n            dispatcher[\"division\"][0].clear()\n            dispatcher[\"bit_test\"][0].clear()\n            dispatcher[\"repeated\"][0].clear()\n            dispatcher[\"corrupted_cf\"][0].clear()\n            dispatcher[\"enclu\"][0].clear()\n\n            # collect all instructions that require sandboxing\n            for node in bb.iter_nodes():\n                inst = node.instruction\n                if inst.is_instrumentation or inst.is_from_template:\n                    continue\n\n                if inst.has_mem_operand(True):\n                    dispatcher[\"memory\"][0].append(node)\n                if inst.name in [\"div\", \"rex div\", \"idiv\", \"rex idiv\"]:\n                    dispatcher[\"division\"][0].append(node)\n                elif inst.name in self.bit_test_names:\n                    dispatcher[\"bit_test\"][0].append(node)\n                elif \"rep\" in inst.name:\n                    dispatcher[\"repeated\"][0].append(node)\n                elif inst.category in [\"BASE-ROTATE\", \"BASE-SHIFT\"]:\n                    dispatcher[\"corrupted_cf\"][0].append(node)\n                elif inst.name == \"enclu\":\n                    dispatcher[\"enclu\"][0].append(node)\n\n            # sandbox them\n            for _, (nodes, sandbox_func) in dispatcher.items():\n                for node in nodes:\n                    sandbox_func(node, bb)\n\n    def _sandbox_memory_access(self, node: InstructionNode, parent: BasicBlock) -> None:\n        \"\"\" Force the memory accesses into the page starting from R14 \"\"\"\n        instr = node.instruction\n\n        mem_operands = instr.get_mem_operands(True)\n        implicit_mem_operands = instr.get_mem_operands(\n            include_explicit=False, include_implicit=True)\n\n        mask = self.sandbox_address_mask\n        if any(op.width >= 256 for op in mem_operands):\n            mask = mask[:-5] + \"0\" * 5\n        elif any(op.width >= 128 for op in mem_operands):\n            mask = mask[:-4] + \"0\" * 4\n\n        # FIXME: broken type\n        if CONF.x86_generator_align_locks:  # type: ignore  # pylint: disable = no-member\n            if \"lock\" in instr.name or instr.name == \"xchg\":\n                mask = mask[:-3] + \"0\" * 3\n\n        if mem_operands and not implicit_mem_operands:\n            assert len(mem_operands) == 1, \\\n                f\"Instructions with multiple memory accesses are not yet supported: {instr.name}\"\n            mem_operand = mem_operands[0]\n            address_reg = mem_operand.value\n            imm_width = mem_operand.width if mem_operand.width <= 32 else 32\n            apply_mask = Instruction(\"and\", is_instrumentation=True) \\\n                .add_op(RegisterOp(address_reg, 64, True, True)) \\\n                .add_op(ImmediateOp(mask, imm_width)) \\\n                .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n            parent.insert_before(node, apply_mask)\n            instr.get_mem_operands(True)[0].value = \"r14 + \" + address_reg\n            return\n\n        mem_operands = implicit_mem_operands\n        assert mem_operands, \"Attempt to sandbox an instruction without memory operands\"\n\n        # deduplicate operands\n        uniq_operands: Dict[str, MemoryOp] = {}\n        for o in mem_operands:\n            if o.value not in uniq_operands:\n                uniq_operands[o.value] = o\n\n        # instrument each operand to sandbox the memory accesses\n        for address_reg, mem_operand in uniq_operands.items():\n            imm_width = mem_operand.width if mem_operand.width <= 32 else 32\n            assert address_reg in self.target_desc.registers_by_size[64], \\\n                f\"Unexpected address register {address_reg} used in {instr}\"\n            apply_mask = Instruction(\"and\", is_instrumentation=True) \\\n                .add_op(RegisterOp(address_reg, mem_operand.width, True, True)) \\\n                .add_op(ImmediateOp(mask, imm_width)) \\\n                .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n            parent.insert_before(node, apply_mask)\n\n            add_base = Instruction(\"add\", is_instrumentation=True) \\\n                .add_op(RegisterOp(address_reg, mem_operand.width, True, True)) \\\n                .add_op(RegisterOp(\"r14\", 64, True, False)) \\\n                .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n            parent.insert_before(node, add_base)\n\n            # restore the original register value\n            remove_base = Instruction(\"sub\", is_instrumentation=True) \\\n                .add_op(RegisterOp(address_reg, mem_operand.width, True, True)) \\\n                .add_op(RegisterOp(\"r14\", 64, True, False)) \\\n                .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n            parent.insert_after(node, remove_base)\n\n    def _sandbox_division(self, node: InstructionNode, parent: BasicBlock) -> None:\n        \"\"\"\n        In the experiments where division errors are not permitted, we prevent them\n        through code instrumentation.\n        Specifically, we may need to prevent two types of faults:\n        - division by zero\n        - division overflow (i.e., quotient is larger than the destination register)\n\n        To prevent div by zero we OR the divider with a non-zero value:\n            divisor = divisor | 1\n\n        The mechanism for preventing div overflows depends on the division type:\n        * for unsigned division, we first mask the upper half of the dividend with the divisor,\n        which makes the quotient at most one bit larger then the destination, and then shift\n        the result by one, thus compensating for the last one overflow bit.\n            D = (D & divisor) >> 1\n        * for signed division, we make set its lower bits to 0b10000, which ensures that\n        all positive divider values are larger or equal to 15, and all negative values\n        are smaller or equal to -15.\n            divisor[0:3] = 0b1000\n        We further constraint the division by clearing the sign bit of the dividend.\n        Under these two constraints, an overflow is possible only when the dividend\n        is larger  or equal to (15 << div_size, e.g., for 32-bit division 15 * (2 ** 32)).\n        Since, the dividend is a combination of two registers (D << div_size + A),\n        an overflow happens when D is larger or equal to 15. We ensure that it does not\n        happen by masking the upper bits of D:\n            D = D & 0b11\n\n        There are also two corner cases:\n            1) The divisor is D. This case is impossible to resolve, as far as I can tell,\n            because our instrumentation would have to modify both the divisor and the dividend\n            at the same time. We just give up in this case and delete the instruction.\n            2) 8-bit division, when the divisor is the AX register alone.\n            Here the instrumentation becomes too complicated, so we simply set AX to 1.\n\n        This instrumentation has a side effect of reducing the entropy of the division operands:\n        For unsigned division:\n            * entropy of the divisor is reduced by 1 bit\n            * entropy of D is reduced by (divisor_value_size + 1) bits\n        For signed division:\n            * entropy of the divisor is reduced by 4 bits\n            * entropy of D is reduced by (division_size - 2) bits (i.e., the resulting\n              entropy of D is 2 bits, with the sign bit cleared)\n        \"\"\"\n        # pylint: disable = too-many-locals\n        # FIXME: this function has to be refactored to break it down into simpler parts\n\n        inst = node.instruction\n\n        # Determine what type of fault is allowed\n        owner_name = parent.get_owner().name\n        actor_blocklist = CONF.get_actors_conf()[owner_name][\"fault_blocklist\"]\n        enable_div_by_zero = self.faults.div_by_zero & (\"div-by-zero\" not in actor_blocklist)\n        enable_div_overflow = self.faults.div_overflow & (\"div-overflow\" not in actor_blocklist)\n\n        # Copy div source operand and label it as a destination; we may need to modify it\n        operand = inst.operands[0]\n        assert isinstance(operand, (RegisterOp, MemoryOp)), \\\n               f\"Unexpected operand type {operand}\"\n        divisor = copy_op_with_flow_modification(operand, dest=True)\n        size = divisor.width\n\n        # This option prevents triggering of Zero Division Injection in the tests\n        # FIXME: Broken type hint\n        if size == 64 and CONF.x86_disable_div64:  # type: ignore  # pylint: disable = no-member\n            parent.delete(node)\n            return\n\n        # Prevent div by zero\n        if not enable_div_by_zero:\n            if \"idiv\" not in inst.name or enable_div_overflow:\n                # for unsigned division and signed divisions with overflow permitted,\n                # it is sufficient to OR the divisor with 1 to prevent div by zero\n                instrumentation = Instruction(\"or\", is_instrumentation=True) \\\n                    .add_op(divisor) \\\n                    .add_op(ImmediateOp(\"1\", 8)) \\\n                    .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n                parent.insert_before(node, instrumentation)\n            else:\n                # for signed divisions with overflows forbidden,\n                # we need to modify the divisor to make it both non-zero\n                # and large enough to avoid overflows.\n                # We have two cases here, positive and negative divider values:\n\n                # For positive dividers, we OR the divisor with 0b10000 to make sure\n                # that the divider is at least 15\n                # (the value 15 comes from the instrumentation below, where\n                # we make the dividend at most `4 << div_size - 1`)\n                instrumentation = Instruction(\"or\", is_instrumentation=True) \\\n                    .add_op(divisor) \\\n                    .add_op(ImmediateOp(\"0b1000\", 8)) \\\n                    .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n                parent.insert_before(node, instrumentation)\n\n                # For negative dividers, we clear the lower 4 bits of the divider,\n                # thus making the value at most -15. To this end, we AND\n                # the lower 8 bits of the divider bit a bit mask 0b11110000\n                divider_8_bit: Union[RegisterOp, MemoryOp]\n                if isinstance(divisor, MemoryOp):\n                    divider_8_bit = MemoryOp(divisor.value, 8, divisor.src, divisor.dest)\n                elif isinstance(divisor, RegisterOp):\n                    reg_normalized = self.target_desc.reg_normalized[divisor.value]\n                    reg_8_bit = self.target_desc.reg_denormalized[reg_normalized][8]\n                    divider_8_bit = RegisterOp(reg_8_bit, 8, divisor.src, divisor.dest)\n                else:\n                    assert_never(divisor)\n\n                instrumentation = Instruction(\"and\", is_instrumentation=True) \\\n                    .add_op(divider_8_bit) \\\n                    .add_op(ImmediateOp(\"0b11111000\", 8)) \\\n                    .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n                parent.insert_before(node, instrumentation)\n\n        if enable_div_overflow:\n            return\n        # Prevent div overflows:\n\n        # Check for the cases that are impossible to instrument:\n        # - division by D register\n        # - division by a memory value with the RDX offset\n        # - division where AX is both the dividend and the offset in memory\n        if divisor.value in [\"rdx\", \"edx\", \"dx\", \"dh\", \"dl\"] \\\n           or \"rdx\" in divisor.value \\\n           or (\"rax\" in divisor.value and size == 8):\n            parent.delete(node)\n            return\n\n        # Special case: dividend in AX\n        # instrumentation: ax = 1\n        if size == 8:\n            instrumentation = Instruction(\"mov\", is_instrumentation=True).\\\n                add_op(RegisterOp(\"ax\", 16, False, True)).\\\n                add_op(ImmediateOp(\"1\", 16))\n            parent.insert_before(node, instrumentation)\n            return\n\n        # Normal case\n        d_register = {64: \"rdx\", 32: \"edx\", 16: \"dx\"}[size]\n\n        # signed div\n        if \"idiv\" in inst.name:\n            # it's extremely hard to prevent overflows with large signed divisions\n            # that's why we simplify the case by assigning zero to the upper bits of the dividend\n            # instrumentation, thus making the dividend at most `4 << div_size - 1`\n            # D = D & 3\n            instrumentation = Instruction(\"and\", is_instrumentation=True) \\\n                .add_op(RegisterOp(d_register, size, True, True)) \\\n                .add_op(ImmediateOp(\"0b11\", 8)) \\\n                .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n            parent.insert_before(node, instrumentation)\n\n        # unsigned div\n        else:\n            # instrumentation:\n            # D = (D & divisor) >> 1  # ensure that D is always smaller than the divisor\n            instrumentation = Instruction(\"and\", is_instrumentation=True) \\\n                .add_op(RegisterOp(d_register, size, True, True)) \\\n                .add_op(divisor) \\\n                .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n            parent.insert_before(node, instrumentation)\n\n            instrumentation = Instruction(\"shr\", is_instrumentation=True) \\\n                .add_op(RegisterOp(d_register, size, True, True)) \\\n                .add_op(ImmediateOp(\"1\", 8)) \\\n                .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\")), True)\n            parent.insert_before(node, instrumentation)\n\n    def _sandbox_bit_test(self, node: InstructionNode, parent: BasicBlock) -> None:\n        \"\"\"\n        The address accessed by a BT* instruction is based on both of its operands.\n        `sandbox_memory_access` take care of the first operand.\n        This function ensures that the offset is always within a byte.\n        \"\"\"\n        inst = node.instruction\n\n        address = inst.operands[0]\n        if isinstance(address, RegisterOp):\n            # this is a version that does not access memory\n            # no need for sandboxing\n            return\n\n        offset = inst.operands[1]\n        if isinstance(offset, ImmediateOp):\n            # The offset is an immediate\n            # Simply replace it with a smaller value\n            offset.value = str(random.randint(0, 7))\n            return\n\n        # The offset is in a register\n        assert isinstance(offset, RegisterOp)\n\n        # Mask its upper bits to reduce the stored value to at most 7\n        if address.value != offset.value:\n            new_offset = copy_op_with_flow_modification(offset, dest=True)\n            apply_mask = Instruction(\"and\", is_instrumentation=True) \\\n                .add_op(new_offset) \\\n                .add_op(ImmediateOp(self.mask_3bits, 8)) \\\n                .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n            parent.insert_before(node, apply_mask)\n            return\n\n        # Special case: offset and address use the same register\n        # Sandboxing is impossible. Give up\n        parent.delete(node)\n\n    def _sandbox_repeated_instruction(self, node: InstructionNode, parent: BasicBlock) -> None:\n        apply_mask = Instruction(\"and\", is_instrumentation=True) \\\n            .add_op(RegisterOp(\"rcx\", 64, True, True)) \\\n            .add_op(ImmediateOp(\"0xff\", 8)) \\\n            .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n        add_base = Instruction(\"add\", is_instrumentation=True) \\\n            .add_op(RegisterOp(\"rcx\", 64, True, True)) \\\n            .add_op(ImmediateOp(\"1\", 1)) \\\n            .add_op(FlagsOp((\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n        parent.insert_before(node, apply_mask)\n        parent.insert_before(node, add_base)\n\n    def _sandbox_corrupted_cf(self, node: InstructionNode, parent: BasicBlock) -> None:\n        # FIXME: This should be a separate pass\n\n        set_cf = Instruction(\"stc\", is_instrumentation=True) \\\n            .add_op(FlagsOp((\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\")), True)\n        parent.insert_after(node, set_cf)\n\n    def _sandbox_enclu(self, node: InstructionNode, parent: BasicBlock) -> None:\n        # FIXME: This should be a separate pass\n        options = [\n            \"0\",  # ereport\n            \"1\",  # egetkey\n            \"4\",  # eexit\n            \"5\",  # eaccept\n            \"6\",  # emodpe\n            \"7\",  # eacceptcopy\n        ]\n        set_rax = Instruction(\"mov\", is_instrumentation=True) \\\n            .add_op(RegisterOp(\"eax\", 32, True, True)) \\\n            .add_op(ImmediateOp(random.choice(options), 1))\n        parent.insert_before(node, set_rax)\n\n    @staticmethod\n    def requires_sandbox(inst: InstructionSpec) -> bool:\n        \"\"\" Check if the instruction requires instrumentation to prevent faults \"\"\"\n        if inst.has_mem_operand:\n            return True\n        if inst.name in [\"div\", \"rex div\"]:\n            return True\n        if inst.name in [\"bt\", \"btc\", \"btr\", \"bts\", \"lock bt\", \"lock btc\", \"lock btr\", \"lock bts\"]:\n            return True\n        if inst.category in [\"BASE-SHIFT\", \"BASE-ROTATE\"]:\n            return True\n        return False\n\n\nclass _X86PatchUndefinedFlagsPass(Pass):\n    \"\"\"\n    Some instructions have undefined effect on FLAGS (e.g., SHL may or may not overwrite OF).\n    This causes a mismatch between Model execution and Executor, if the undefined behavior\n    is implemented differently. It leads to false positives.\n    To prevent them, we analyse the test cases in search for the cases where an instruction\n    with undefined flags is followed by an instruction that uses this flag. We then\n    insert another random instruction in-between, such that this\n    instruction overwrites the undefined flag.\n\n    I.e., we replace\n        SHL eax, eax  // undefined OF\n        JNO .label    // uses OF\n    with\n        SHL eax, eax\n        ADD ebx, ecx  // random instruction that overwrites OF\n        JNO .label\n    \"\"\"\n    patch_candidates: List[InstructionSpec]\n\n    def __init__(self, instruction_set: InstructionSet, generator: CodeGenerator) -> None:\n        super().__init__()\n        self.instruction_set = instruction_set\n        self.generator = generator\n\n        self.patch_candidates = []\n        for instruction_spec in instruction_set.instructions:\n            # we don't want to change the control flow\n            if instruction_spec.is_control_flow:\n                continue\n\n            # check if the instruction is safe to use on its own\n            if _X86SandboxPass.requires_sandbox(instruction_spec):\n                continue\n\n            # check if it overwrites flags and if creates new dependencies\n            has_read = False\n            has_write = False\n            for op in instruction_spec.operands + instruction_spec.implicit_operands:\n                if op.type == OT.FLAGS:\n                    for f in op.values:\n                        if f in ['r', 'r/w', 'r/cw']:\n                            has_read = True\n                        elif f in ['w']:\n                            has_write = True\n            if not has_read and has_write:\n                self.patch_candidates.append(instruction_spec)\n\n    def run_on_test_case(self, test_case: TestCaseProgram) -> None:\n        for bb in test_case.iter_basic_blocks():\n            self._patch_flags_in_bb(bb)\n\n    def _patch_flags_in_bb(self, bb: BasicBlock) -> None:\n        # pylint: disable = too-many-branches\n        # FIXME: This function was written in a hurry and needs to be refactored\n\n        # get a list of all instruction nodes in the BB\n        all_instructions: List[InstructionNode] = []\n        for node in bb.iter_nodes():\n            all_instructions.append(node)\n\n        # Initialize a list used to track the flags that have to be set\n        flags_to_set: Set[str] = set()\n\n        # Collect the flags read by the terminators (conditional jumps)\n        # Note: we assume that terminators do not modify flags\n        # and hence no patching is needed at this point\n        for term in bb.terminators:\n            flags = term.get_flags_operand()\n            if flags:\n                for f in flags.get_flags_by_type('read'):\n                    flags_to_set.add(f)\n\n        # Walk the instruction list in the reverse order\n        # During the walk, track flags have undefined values and overwrite them by adding\n        # extra instructions in-between\n        while all_instructions:\n            node = all_instructions.pop()\n            inst = node.instruction\n            flags = inst.get_flags_operand()\n\n            # skip template instructions and instructions that do not read/write flags\n            if inst.is_from_template or not flags:\n                continue\n\n            # fix undefined flags by adding another instruction in-between\n            undef_flags = [i for i in flags.get_flags_by_type('undef') if i in flags_to_set]\n            if undef_flags:\n                patches = self._find_flags_patch(undef_flags, flags_to_set)\n                for patch in patches:\n                    bb.insert_after(node, patch)\n                    # remove the flags overwritten by the patch\n                    for f in patch.get_flags_operand().get_flags_by_type('write'):  # type: ignore\n                        flags_to_set.discard(f)\n\n            # remove the flags overwritten by the instruction\n            for f in flags.get_flags_by_type('write'):\n                flags_to_set.discard(f)\n\n            # add new flag dependencies\n            for f in flags.get_flags_by_type('read'):\n                flags_to_set.add(f)\n\n        # make sure that we do not have undefined flags when we enter the BB\n        if flags_to_set:\n            # find a place to insert the patches\n            entry_node = bb.get_first(exclude_macros=True)\n            if not entry_node:\n                raise ValueError(\"X86PatchUndefinedFlagsPass: No place to insert a patch\")\n\n            patches = self._find_flags_patch(list(flags_to_set), flags_to_set)\n            for patch in patches:\n                bb.insert_before(entry_node, patch)\n\n    def _find_flags_patch(self, undef_flags: List[str],\n                          flags_to_set: Set[str]) -> List[Instruction]:\n        \"\"\"\n        Find an instruction sequence that would overwrite a list of flags\n        :param undef_flags: list of undefined flags that have to be overwritten\n            by the patch instructions\n        :param flags_to_set: list of flags that will be read by one of the following instructions,\n            and thus should not be set to the undef state by the patch. This should be always\n            a superset of or the same as undef_flags.\n        :return: list of instructions that overwrite the undefined flags\n        \"\"\"\n        org_undef = deepcopy(undef_flags)\n        patches: List[Instruction] = []\n        for instruction_spec in self.patch_candidates:\n            patch = self.generator.generate_instruction(instruction_spec, True)\n            patch_flags = patch.get_flags_operand()\n            assert patch_flags\n            new_undef_flags = [\n                i for i in patch_flags.get_flags_by_type('undef')\n                if i not in undef_flags and i in flags_to_set\n            ]\n            not_patched_flags = [\n                i for i in undef_flags if i not in patch_flags.get_flags_by_type('write')\n            ]\n\n            if not new_undef_flags and not_patched_flags != undef_flags:\n                patches.append(patch)\n                undef_flags = not_patched_flags\n                if not undef_flags:\n                    break\n\n        if undef_flags:\n            raise ValueError(\"Could not find an instruction to patch flags.\\n\"\n                             f\"  Initial flags to be patched: {org_undef}\\n\"\n                             f\"  Flags for which a patch was not found: {undef_flags}\")\n\n        return patches\n\n\nclass _X86PatchUndefinedResultPass(Pass):\n    \"\"\"\n    Some instructions have undefined results when the source operand is zero.\n    This pass patches such instructions to avoid undefined behavior.\n    \"\"\"\n\n    def run_on_test_case(self, test_case: TestCaseProgram) -> None:\n        # call _patch_bit_scan on all bit scan instructions\n        for bb in test_case.iter_basic_blocks():\n            bit_scan = []\n            for node in bb.iter_nodes():\n                inst = node.instruction\n                if inst.is_instrumentation or inst.is_from_template:\n                    continue\n                if inst.name in [\"bsf\", \"bsr\"]:\n                    bit_scan.append(node)\n            for node in bit_scan:\n                self._patch_bit_scan(node, bb)\n\n    @staticmethod\n    def _patch_bit_scan(node: InstructionNode, parent: BasicBlock) -> None:\n        \"\"\"\n        Bit Scan instructions give an undefined result when the source operand is zero.\n        To avoid it, set the most significant bit.\n        \"\"\"\n        inst = node.instruction\n\n        # get the source operand\n        src_operand = inst.operands[1]\n        assert isinstance(src_operand, (RegisterOp, MemoryOp)), \\\n               f\"Unexpected operand type {src_operand}\"\n\n        # copy because we may modify it\n        source = copy_op_with_flow_modification(src_operand, dest=True)\n\n        mask = bin(1 << (source.width - 1))\n        mask_size = source.width\n        if source.width in [64, 32]:\n            mask = \"0b1000000000000000000000000000000\"\n            mask_size = 32\n        apply_mask = Instruction(\"or\", is_instrumentation=True) \\\n            .add_op(source) \\\n            .add_op(ImmediateOp(mask, mask_size)) \\\n            .add_op(FlagsOp((\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\")), True)\n        parent.insert_before(node, apply_mask)\n\n\nclass _X86PatchOpcodesPass(Pass):\n    \"\"\"\n    Replaces assembly instructions with their opcodes.\n    This is necessary to test instruction with multiple opcodes and\n    the instruction that are not supported/not permitted by the standard\n    assembler.\n    \"\"\"\n    _OPCODES: Final[Dict[str, List[str]]] = {\n        \"ud2\": [\n            \"0x0f, 0x0b\",  # UD2 instruction\n            # invalid in 64-bit mode;\n            # all the following opcodes are padded\n            # with NOP to prevent misinterpretation by objdump\n            \"0x06, 0x90\",  # 32-bit encoding of PUSH\n            \"0x07, 0x90\",  # 32-bit encoding of POP\n            \"0x0e, 0x90\",  # alternative 32-bit encoding of PUSH\n            \"0x16, 0x90\",  # alternative 32-bit encoding of PUSH\n            \"0x17, 0x90\",  # alternative 32-bit encoding of POP\n            \"0x1e, 0x90\",  # alternative 32-bit encoding of PUSH\n            \"0x1f, 0x90\",  # alternative 32-bit encoding of POP\n            \"0x27, 0x90\",  # DAA\n            \"0x2f, 0x90\",  # DAS\n            \"0x37, 0x90\",  # AAA\n            \"0x3f, 0x90\",  # AAS\n            \"0x60, 0x90\",  # PUSHA\n            \"0x61, 0x90\",  # POPA\n            \"0x62, 0x90\",  # BOUND\n            \"0x82, 0x90\",  # 32-bit aliases for logical instructions\n            \"0x9a, 0x90\",  # 32-bit encoding of CALLF\n            \"0xc4, 0x90\",  # LES\n            \"0xd4, 0x90\",  # AAM\n            \"0xd5, 0x90\",  # AAD\n            \"0xd6, 0x90\",  # reserved\n            \"0xea, 0x90\",  # 32-bit encoding of JMPF\n        ],\n        \"int1\": [\"0xf1\"]\n    }\n\n    def run_on_test_case(self, test_case: TestCaseProgram) -> None:\n        for bb in test_case.iter_basic_blocks():\n            # collect all UD instructions\n            to_patch = []\n            for node in bb.iter_nodes():\n                inst = node.instruction\n                if inst.is_instrumentation or inst.is_from_template:\n                    continue\n                if inst.name in self._OPCODES:\n                    to_patch.append(node)\n\n            # patch them\n            for node in to_patch:\n                self._instrument(node, bb)\n\n    def _instrument(self, node: InstructionNode, parent: BasicBlock) -> None:\n        inst = node.instruction\n        opcode_options = self._OPCODES[inst.name]\n        opcode = random.choice(opcode_options)\n        new_inst = copy_inst_with_modification(inst, name=\".byte \" + opcode)\n        parent.insert_before(node, new_inst)\n        parent.delete(node)\n\n\n# ==================================================================================================\n# Public Interface\n# ==================================================================================================\nclass X86Generator(CodeGenerator):\n    \"\"\" x86-specific implementation of the test case program generator \"\"\"\n\n    _faults: _FaultFilter\n\n    def __init__(self, seed: int, instruction_set: InstructionSet, target_desc: TargetDesc,\n                 asm_parser: AsmParser, elf_parser: ELFParser) -> None:\n        super().__init__(seed, instruction_set, target_desc, asm_parser, elf_parser)\n        assert isinstance(self._target_desc, X86TargetDesc)\n\n        self._faults = _FaultFilter()\n\n        # configure instrumentation passes\n        self._passes = [\n            _X86PatchUndefinedFlagsPass(self._instruction_set, self),\n            _X86SandboxPass(self._target_desc, self._faults),\n            _X86PatchUndefinedResultPass(),\n        ]\n        if self._faults.non_canonical_access:\n            self._passes.append(_X86NonCanonicalAddressPass(self._target_desc))\n        if self._faults.u2k_access:\n            self._passes.append(_X86U2KAccessPass())  # must be after X86SandboxPass\n        self._passes.append(_X86PatchOpcodesPass())\n        self._printer = _X86Printer(self._target_desc)\n"
  },
  {
    "path": "rvzr/arch/x86/get_spec.py",
    "content": "\"\"\"\nFile: A script that downloads the x86 instruction set from the Side Channel Fuzzer repository\n      and parses it into a JSON file that can be used by the generator.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nimport sys\nimport json\nimport subprocess\nfrom typing import List, Optional, Literal\nfrom xml.etree import ElementTree as ET\n\n# ==================================================================================================\n# x86-specific constants\n# ==================================================================================================\nREG_SIZE = {\n    \"rax\": 64,\n    \"rbx\": 64,\n    \"rcx\": 64,\n    \"rdx\": 64,\n    \"r11\": 64,\n    \"rip\": 64,\n    \"rsp\": 64,\n    \"rbp\": 64,\n    \"eax\": 32,\n    \"ebx\": 32,\n    \"ecx\": 32,\n    \"edx\": 32,\n    \"ax\": 16,\n    \"dx\": 16,\n    \"bp\": 16,\n    \"sp\": 16,\n    \"al\": 8,\n    \"ah\": 8,\n    \"cl\": 8,\n    \"spl\": 8,\n    \"tmm0\": 0,\n    \"mxcsr\": 32,\n    'es': 16,\n    'ss': 16,\n    'ds': 16,\n    'fs': 16,\n    'gs': 16,\n    'cr0': 64,\n    'cr3': 64,\n    'cr4': 64,\n    'cr8': 64,\n    'xcr0': 64,\n    'dr0': 64,\n    'dr1': 64,\n    'dr2': 64,\n    'dr3': 64,\n    'dr6': 64,\n    'dr7': 64,\n    'gdtr': 80,\n    'ldtr': 96,\n    'idtr': 80,\n    'tr': 16,\n    'msrs': 64,\n    'x87control': 16,\n    'x87pop': 16,\n    'x87status': 16,\n    'tsc': 64,\n    \"tscaux\": 64,\n    \"fsbase\": 64,\n    \"gsbase\": 64,\n}\nREG_SIZE.update({f\"mm{i}\": 64 for i in range(8)})\nREG_SIZE.update({f\"xmm{i}\": 128 for i in range(16)})\nREG_SIZE.update({f\"ymm{i}\": 256 for i in range(16)})\n# REG_SIZE.update({f\"zmm{i}\": 512 for i in range(32)})\n\n# A list of instructions that have RIP as an operand but should\n# not be considered as control-flow instructions by the generator\nNON_CONTROL_FLOW_INST = [\"int\", \"int1\", \"int3\", \"into\"]\n\n# ==================================================================================================\n# Lists of x86 extensions\n# ==================================================================================================\n\n# Instructions that can be tested without any repercussions\n# This list for our default model backend (Unicorn)\nSAFE_EXTENSIONS = [\n    \"BASE\",\n    \"SSE\",\n    \"SSE2\",\n    \"SSE3\",\n    \"SSE4\",\n    \"SSE4a\",\n    \"CLFLUSHOPT\",\n    \"CLFSH\",\n    \"SSE\",\n    \"RDTSCP\",\n    \"LONGMODE\",\n]\n\n# Instructions that can be tested without any repercussions\n# on the new (experimental) backend, DynamoRIO\nSAFE_EXTENSIONS_DR = [\n    \"3DNOW_PREFETCH\",\n    \"3DNOW\",\n    \"ADOX_ADCX\",\n    \"AES\",\n    \"AVX_VNNI\",\n    \"AVX\",\n    \"AVX2\",\n    \"AVX2GATHER\",\n    \"AVX512EVEX\",\n    \"AVX512VEX\",\n    \"AVXAES\",\n    \"BASE\",\n    \"BMI1\",\n    \"BMI2\",\n    \"CLFLUSHOPT\",\n    \"CLFSH\",\n    \"FMA\",\n    \"FMA4\",\n    \"GFNI\",\n    \"LONGMODE\",\n    \"LZCNT\",\n    \"MCOMMIT\",\n    \"MMX\",\n    \"MOVBE\",\n    \"MOVDIR\",\n    \"PCLMULQDQ\",\n    \"PCONFIG\",\n    \"PKU\",\n    \"PREFETCHWT1\",\n    \"PTWRITE\",\n    \"RDPID\",\n    \"RDPRU\",\n    \"RDRAND\",\n    \"RDSEED\",\n    \"RDWRFSGS\",\n    \"SERIALIZE\",\n    \"SHA\",\n    \"SMAP\",\n    \"SSE\",\n    \"SSE2\",\n    \"SSE3\",\n    \"SSE4\",\n    \"SSE4a\",\n    \"SSSE3\",\n    \"TBM\",\n    \"UINTR\",\n    \"VAES\",\n    \"VPCLMULQDQ\",\n    \"XOP\",\n]\n\n# Instructions that can potentially crash the system if the fuzzer is misconfigured\nALL_EXTENSIONS = SAFE_EXTENSIONS + [\n    \"VTX\",\n    \"SVM\",\n    \"SMX\",\n    \"WBNOINVD\",\n    \"XSAVE\",\n    \"XSAVEOPT\",\n    \"XSAVES\",\n    \"SGX\",\n    \"ENQCMD\",\n    \"INVPCID\",\n    \"KEYLOCKER\",\n    \"MONITOR\",\n    \"PAUSE\",\n    \"RDRAND\",\n    \"RDSEED\",\n    \"RDWRFSGS\",\n    \"HRESET\",\n    \"SMAP\",\n    \"AMD_INVLPGB\",\n    \"SNP\",\n]\n\n# ==================================================================================================\n# Internal Classes that represent the parsed XML data\n# ==================================================================================================\nOP_TYPE = Literal[\"REG\", \"MEM\", \"AGEN\", \"IMM\", \"LABEL\", \"FLAGS\"]\n\n\nclass _XMLOperandSpec:\n    \"\"\"\n    A class that represents an operand parsed from the XML file\n    \"\"\"\n    values: List[str]\n    type_: OP_TYPE\n    xtype: str\n    width: int\n    is_signed: bool = True\n    comment: str\n    src: bool = False\n    dest: bool = False\n    magic: bool = False\n\n    def to_json(self) -> str:\n        \"\"\" Converts the operand to a JSON string \"\"\"\n        values_lower = []\n        for v in self.values:\n            values_lower.append(v.lower())\n        self.values = values_lower\n        return json.dumps(self, default=vars)\n\n\nclass _XMLInstructionSpec:\n    \"\"\" A class that represents an instruction parsed from the XML file \"\"\"\n    name: str\n    category: str = \"\"\n    is_control_flow: bool = False\n    operands: List[_XMLOperandSpec]\n    implicit_operands: List[_XMLOperandSpec]\n\n    def __init__(self) -> None:\n        self.operands = []\n        self.implicit_operands = []\n\n    def __str__(self) -> str:\n        return f\"{self.name} {self.is_control_flow} {self.category} \" \\\n            f\"{len(self.operands)} {len(self.implicit_operands)}\"\n\n    def to_json(self) -> str:\n        \"\"\" Converts the instruction to a JSON string \"\"\"\n        s = \"{\"\n        s += f'\"name\": \"{self.name.lower()}\", \"category\": \"{self.category}\", '\n        s += f'\"is_control_flow\": {str(self.is_control_flow).lower()},\\n'\n        s += '  \"operands\": [\\n    '\n        s += ',\\n    '.join([o.to_json() for o in self.operands])\n        s += '\\n  ],\\n'\n        if self.implicit_operands:\n            s += '  \"implicit_operands\": [\\n    '\n            s += ',\\n    '.join([o.to_json() for o in self.implicit_operands])\n            s += '\\n  ]'\n        else:\n            s += '  \"implicit_operands\": []'\n        s += \"\\n}\"\n        return s\n\n\n# ==================================================================================================\n# Classes that parse the XML file and convert it to JSON\n# ==================================================================================================\nclass _ParseFailed(Exception):\n    \"\"\" An exception that is raised when parsing fails \"\"\"\n\n\nclass XMLSpecParser:\n    \"\"\" A class that parses the XML file and converts it to JSON \"\"\"\n    n_instructions_in_xml: int = 0\n    _tree: ET.Element\n    _instructions: List[_XMLInstructionSpec]\n    _current_spec: _XMLInstructionSpec\n\n    def __init__(self, extensions: List[str]) -> None:\n        self.extensions = extensions\n        self._instructions = []\n\n    def __len__(self) -> int:\n        return len(self._instructions)\n\n    def parse_file(self, filename: str) -> None:\n        \"\"\" Parsed the XML file and saves a list of _XMLInstructionSpec objects \"\"\"\n\n        # Get a tree from the XML file\n        parser = ET.ElementTree()\n        tree = parser.parse(filename)\n        if not tree:\n            print(\"No input. Exiting\")\n            sys.exit(1)\n        self._tree = tree\n        self.n_instructions_in_xml = len(list(self._tree.iter('instruction')))\n\n        # Check if the requested extensions are available\n        self._check_extension_list()\n\n        # Parse all nodes in the tree\n        for instruction_node in self._tree.iter('instruction'):\n            instruction_spec = self._parse_node(instruction_node)  # pylint: disable=e1128\n            if instruction_spec is not None:\n                self._instructions.append(instruction_spec)\n\n    def save_as_json(self, filename: str) -> None:\n        \"\"\" Saves the parsed instructions as a JSON file \"\"\"\n        json_str = \"[\\n\" + \",\\n\".join([i.to_json() for i in self._instructions]) + \"\\n]\"\n        # print(json_str)\n        with open(filename, \"w+\") as f:\n            f.write(json_str)\n\n    def _parse_node(self, node: ET.Element) -> Optional[_XMLInstructionSpec]:\n        # pylint: disable=too-many-branches  # Justified because it's a parser\n\n        # Check if the node should be skipped\n        if self._node_is_not_supported(node):\n            return None\n        if node.attrib['extension'] not in self.extensions:\n            return None\n\n        # Create a new instruction spec\n        instruction = _XMLInstructionSpec()\n\n        # Parse instruction attributes\n        instruction.category = f\"{node.attrib['extension']}-{node.attrib['category']}\"\n        instruction.name = node.attrib['asm'].removeprefix(\"{load} \")\\\n            .removeprefix(\"{store} \").removeprefix(\"{disp32} \").lower()\n\n        try:\n            for op_node in node.iter('operand'):\n                # Create a new operand spec based on the node type\n                op_type = op_node.attrib['type']\n                if op_type == 'reg':\n                    parsed_op = self._parse_reg_operand(op_node)\n                elif op_type == 'mem':\n                    parsed_op = self._parse_mem_operand(op_node)\n                elif op_type == 'agen':\n                    op_node.text = node.attrib['agen']\n                    parsed_op = self._parse_agen_operand(op_node)\n                elif op_type == 'imm':\n                    parsed_op = self._parse_imm_operand(op_node)\n                elif op_type == 'relbr':\n                    parsed_op = self._parse_label_operand(op_node)\n                elif op_type == 'flags':\n                    parsed_op = self._parse_flags_operand(op_node)\n                else:\n                    raise _ParseFailed(\"Unknown operand type \" + op_type)\n\n                # Add the operand to the instruction\n                if op_node.attrib.get('suppressed', '0') == '1':\n                    instruction.implicit_operands.append(parsed_op)\n                else:\n                    instruction.operands.append(parsed_op)\n\n                # Set additional operand attributes\n                if op_node.attrib.get('implicit', '0') == '1':\n                    parsed_op.magic = True\n\n                # Set additional instruction attributes based on the operand\n                if parsed_op.type_ == \"REG\":\n                    text = getattr(op_node, 'text', '').lower()\n                    if text == \"rip\" and instruction.name not in NON_CONTROL_FLOW_INST:\n                        instruction.is_control_flow = True\n                elif parsed_op.type_ == \"LABEL\":\n                    instruction.is_control_flow = True\n\n        except _ParseFailed as e:\n            # If parsing fails, skip the instruction\n            print(f\"WARN: Skipping instruction {instruction.name} due to `{e}`\")\n            return None\n\n        return instruction\n\n    def _node_is_not_supported(self, node: ET.Element) -> bool:\n        return node.attrib.get('sae', '') == '1' or \\\n            node.attrib.get('roundc', '') == '1' or \\\n            node.attrib.get('zeroing', '') == '1'\n\n    def _parse_reg_operand(self, op: ET.Element) -> _XMLOperandSpec:\n        assert op.text is not None\n\n        spec = _XMLOperandSpec()\n        spec.type_ = \"REG\"\n        if op.attrib.get('xtype', '') != '':\n            spec.xtype = op.attrib.get('xtype', '')\n\n        spec.values = op.text.lower().split(',')\n        if spec.values[0] not in REG_SIZE:\n            raise _ParseFailed(f\"Unsupported register operand {spec.values[0]}\")\n\n        spec.src = op.attrib.get('r', \"0\") == \"1\"\n        spec.dest = op.attrib.get('w', \"0\") == \"1\"\n\n        spec.width = int(op.attrib.get('width', 0))\n        if spec.width == 0:\n            spec.width = REG_SIZE[spec.values[0]]\n\n        return spec\n\n    @staticmethod\n    def _parse_mem_operand(op: ET.Element) -> _XMLOperandSpec:\n        assert op.attrib is not None\n\n        # asserts are for unsupported instructions\n        if op.attrib.get('VSIB', '0') != '0':\n            raise _ParseFailed(\"Vector SIB memory addressing is not supported\")\n        # assert op.attrib.get('VSIB', '0') == '0'  # asm += '[' + op.attrib.get('VSIB') + '0]'\n        if op.attrib.get('memory-suffix', '') != '':\n            raise _ParseFailed(f\"Unsupported memory suffix {op.attrib.get('memory-suffix', '')}\")\n\n        choices = []\n        if op.attrib.get('base', ''):\n            choices = [op.attrib.get('base', '')]\n\n        spec = _XMLOperandSpec()\n        spec.type_ = \"MEM\"\n        spec.values = choices\n        spec.src = op.attrib.get('r', \"0\") == \"1\"\n        spec.dest = op.attrib.get('w', \"0\") == \"1\"\n        spec.width = int(op.attrib.get('width', '0'))\n        return spec\n\n    @staticmethod\n    def _parse_agen_operand(_: ET.Element) -> _XMLOperandSpec:\n        spec = _XMLOperandSpec()\n        spec.type_ = \"AGEN\"\n        spec.values = []\n        spec.src = True\n        spec.dest = False\n        spec.width = 64\n        return spec\n\n    @staticmethod\n    def _parse_imm_operand(op: ET.Element) -> _XMLOperandSpec:\n        assert op.attrib is not None\n\n        spec = _XMLOperandSpec()\n        spec.type_ = \"IMM\"\n        if op.attrib.get('implicit', '0') == '1':\n            assert op.text is not None\n            spec.values = [op.text]\n        else:\n            spec.values = []\n        spec.src = True\n        spec.dest = False\n        spec.width = int(op.attrib.get('width', '0'))\n        if op.attrib.get('s', '1') == '0':\n            spec.is_signed = False\n        return spec\n\n    @staticmethod\n    def _parse_label_operand(_: ET.Element) -> _XMLOperandSpec:\n        spec = _XMLOperandSpec()\n        spec.type_ = \"LABEL\"\n        spec.values = []\n        spec.src = True\n        spec.dest = False\n        spec.width = 0\n        return spec\n\n    @staticmethod\n    def _parse_flags_operand(op: ET.Element) -> _XMLOperandSpec:\n        spec = _XMLOperandSpec()\n        spec.type_ = \"FLAGS\"\n        spec.values = [\n            op.attrib.get(\"flag_CF\", \"\"),\n            op.attrib.get(\"flag_PF\", \"\"),\n            op.attrib.get(\"flag_AF\", \"\"),\n            op.attrib.get(\"flag_ZF\", \"\"),\n            op.attrib.get(\"flag_SF\", \"\"),\n            op.attrib.get(\"flag_TF\", \"\"),\n            op.attrib.get(\"flag_IF\", \"\"),\n            op.attrib.get(\"flag_DF\", \"\"),\n            op.attrib.get(\"flag_OF\", \"\"),\n        ]\n        spec.src = False\n        spec.dest = False\n        spec.width = 0\n        return spec\n\n    def add_missing(self) -> None:  # pylint: disable=too-many-statements\n        \"\"\" Adds the instructions specs that are missing from the XML file we use \"\"\"\n        extensions = self.extensions\n        if not extensions or \"CLFSH\" in extensions:\n            for width in [8, 16, 32, 64]:\n                inst = _XMLInstructionSpec()\n                inst.name = \"clflush\"\n                inst.category = \"CLFSH-MISC\"\n                inst.is_control_flow = False\n                op = _XMLOperandSpec()\n                op.type_ = \"MEM\"\n                op.values = []\n                op.src = True\n                op.dest = False\n                op.width = width\n                inst.operands = [op]\n                self._instructions.append(inst)\n\n        if not extensions or \"CLFLUSHOPT\" in extensions:\n            for width in [8, 16, 32, 64]:\n                inst = _XMLInstructionSpec()\n                inst.name = \"clflushopt\"\n                inst.category = \"CLFLUSHOPT-CLFLUSHOPT\"\n                inst.is_control_flow = False\n                op = _XMLOperandSpec()\n                op.type_ = \"MEM\"\n                op.values = []\n                op.src = True\n                op.dest = False\n                op.width = width\n                inst.operands = [op]\n                self._instructions.append(inst)\n\n        if not extensions or \"BASE\" in extensions:\n            inst = _XMLInstructionSpec()\n            inst.name = \"int1\"\n            inst.category = \"BASE-INTERRUPT\"\n            inst.is_control_flow = False\n            op1 = _XMLOperandSpec()\n            op1.type_, op1.src, op1.dest, op1.width = \"REG\", False, True, 64\n            op1.values = [\"rip\"]\n            op2 = _XMLOperandSpec()\n            op2.type_, op2.src, op2.dest, op2.width = \"FLAGS\", False, False, 0\n            op2.values = [\"\", \"\", \"\", \"\", \"\", \"w\", \"w\", \"\", \"\"]\n            inst.implicit_operands = [op1, op2]\n            self._instructions.append(inst)\n\n    def _check_extension_list(self) -> None:\n        # get a list of all available extensions\n        available_extensions = set()\n        for instruction_node in self._tree.iter('instruction'):\n            available_extensions.add(instruction_node.attrib['extension'])\n\n        # check if the requested extensions are available\n        for ext in self.extensions:\n            if ext not in available_extensions:\n                print(f\"ERROR: Unknown extension {ext}\")\n                print(\"\\nAvailable extensions:\")\n                print(list(available_extensions))\n\n\nclass Downloader:\n    \"\"\" A class that downloads the x86 instruction set and converts it to JSON \"\"\"\n\n    def __init__(self, extensions: List[str], out_file: str) -> None:\n        if \"ALL_SUPPORTED\" in extensions:\n            extensions.extend(SAFE_EXTENSIONS)\n            extensions = list(set(extensions))\n            extensions.remove(\"ALL_SUPPORTED\")\n        elif \"ALL_SUPPORTED_DR\" in extensions:\n            extensions.extend(SAFE_EXTENSIONS_DR)\n            extensions = list(set(extensions))\n            extensions.remove(\"ALL_SUPPORTED_DR\")\n        elif \"ALL_AND_UNSAFE\" in extensions:\n            extensions.extend(ALL_EXTENSIONS)\n            extensions = list(set(extensions))\n            extensions.remove(\"ALL_AND_UNSAFE\")\n        self.extensions = extensions\n        self.out_file = out_file\n        self._transformer = XMLSpecParser(self.extensions)\n\n    def run(self) -> None:\n        \"\"\" Downloads the XML file and converts it to JSON \"\"\"\n\n        print(\"> Downloading complete instruction spec...\")\n        subprocess.run(\n            \"curl -L -o x86_instructions.xml \"\n            \"https://github.com/microsoft/side-channel-fuzzer/releases/download/\"\n            \"v1.3.0/x86_instructions.xml\",\n            shell=True,\n            check=True)\n\n        print(\"\\n> Filtering and transforming the instruction spec...\")\n        try:\n            self._transformer.parse_file(\"x86_instructions.xml\")\n            self._transformer.add_missing()\n            self._transformer.save_as_json(self.out_file)\n        finally:\n            subprocess.run(\"rm x86_instructions.xml\", shell=True, check=True)\n\n        n_parsed = len(self._transformer)\n        n_all = self._transformer.n_instructions_in_xml\n        print(f\"Produced base.json with {n_parsed} instructions (out of {n_all} possible)\")\n\n\n# NOTE: for reference, the complete list of all categories available in the XML file is:\n# \"3DNOW-3DNOW\", \"ADOX_ADCX-ADOX_ADCX\", \"AES-AES\", \"AVXAES-AES\", \"AMX_BF16-AMX_TILE\",\n# \"AMX_INT8-AMX_TILE\", \"AMX_TILE-AMX_TILE\", \"AVX2-AVX2\", \"AVX2GATHER-AVX2GATHER\",\n# \"AVX512EVEX-AVX512_4FMAPS\", \"AVX512EVEX-AVX512_4VNNIW\", \"AVX512EVEX-AVX512_BITALG\",\n# \"AVX512EVEX-AVX512\", \"AVX512EVEX-AVX512_VBMI\", \"AVX512EVEX-AVX512_VP2INTERSECT\", \"AVX-AVX\",\n# \"BASE-BINARY\", \"BASE-BITBYTE\", \"SSE4a-BITBYTE\", \"AVX512EVEX-BLEND\", \"BMI1-BMI1\", \"BMI2-BMI2\",\n# \"AVX-BROADCAST\", \"AVX2-BROADCAST\", \"AVX512EVEX-BROADCAST\", \"BASE-CALL\", \"CET-CET\",\n# \"CLDEMOTE-CLDEMOTE\", \"CLFLUSHOPT-CLFLUSHOPT\", \"CLWB-CLWB\", \"CLZERO-CLZERO\", \"BASE-CMOV\",\n# \"AVX512EVEX-COMPRESS\", \"BASE-COND_BR\", \"RTM-COND_BR\", \"AVX512EVEX-CONFLICT\", \"AVX-CONVERT\",\n# \"AVX512EVEX-CONVERT\", \"BASE-CONVERT\", \"F16C-CONVERT\", \"LONGMODE-CONVERT\", \"SSE-CONVERT\",\n# \"SSE2-CONVERT\", \"AVX-DATAXFER\", \"AVX2-DATAXFER\", \"AVX512EVEX-DATAXFER\", \"BASE-DATAXFER\",\n# \"LONGMODE-DATAXFER\", \"MMX-DATAXFER\", \"MOVBE-DATAXFER\", \"SSE-DATAXFER\", \"SSE2-DATAXFER\",\n# \"SSE3-DATAXFER\", \"SSE4a-DATAXFER\", \"ENQCMD-ENQCMD\", \"AVX512EVEX-EXPAND\", \"X87-FCMOV\",\n# \"BASE-FLAGOP\", \"FMA4-FMA4\", \"AVX512EVEX-FP16\", \"AVX512EVEX-GATHER\", \"AVX512EVEX-GFNI\",\n# \"GFNI-GFNI\", \"HRESET-HRESET\", \"AVX512EVEX-IFMA\", \"BASE-INTERRUPT\", \"BASE-IO\",\n# \"BASE-IOSTRINGOP\", \"KEYLOCKER-KEYLOCKER\", \"KEYLOCKER_WIDE-KEYLOCKER_WIDE\",\n# \"AVX512VEX-KMASK\", \"TDX-LEGACY\", \"AVX-LOGICAL\", \"AVX2-LOGICAL\", \"AVX512EVEX-LOGICAL\",\n# \"BASE-LOGICAL\", \"MMX-LOGICAL\", \"RTM-LOGICAL\", \"SSE2-LOGICAL\", \"SSE4-LOGICAL\",\n# \"AVX-LOGICAL_FP\", \"AVX512EVEX-LOGICAL_FP\", \"SSE-LOGICAL_FP\", \"SSE2-LOGICAL_FP\",\n# \"LZCNT-LZCNT\", \"BASE-MISC\", \"CLFSH-MISC\", \"INVPCID-MISC\", \"MCOMMIT-MISC\", \"MONITOR-MISC\",\n# \"MONITORX-MISC\", \"PAUSE-MISC\", \"SSE-MISC\", \"SSE2-MISC\", \"3DNOW-MMX\", \"MMX-MMX\",\n# \"SSE2-MMX\", \"SSSE3-MMX\", \"MOVDIR-MOVDIR\", \"MPX-MPX\", \"BASE-NOP\", \"PCLMULQDQ-PCLMULQDQ\",\n# \"PCONFIG-PCONFIG\", \"PKU-PKU\", \"BASE-POP\", \"LONGMODE-POP\", \"3DNOW_PREFETCH-PREFETCH\",\n# \"SSE-PREFETCH\", \"PREFETCHWT1-PREFETCHWT1\", \"PTWRITE-PTWRITE\", \"BASE-PUSH\", \"LONGMODE-PUSH\",\n# \"RDPID-RDPID\", \"RDPRU-RDPRU\", \"RDRAND-RDRAND\", \"RDSEED-RDSEED\", \"RDWRFSGS-RDWRFSGS\",\n# \"BASE-RET\", \"LONGMODE-RET\", \"BASE-ROTATE\", \"AVX512EVEX-SCATTER\", \"BASE-SEGOP\", \"BASE-SEMAPHORE\",\n# \"LONGMODE-SEMAPHORE\", \"SERIALIZE-SERIALIZE\", \"BASE-SETCC\", \"SGX-SGX\", \"SHA-SHA\",\n# \"BASE-SHIFT\", \"SMAP-SMAP\", \"SSE-SSE\", \"SSE2-SSE\", \"SSE3-SSE\", \"SSE4-SSE\", \"SSSE3-SSE\",\n# \"BASE-STRINGOP\", \"LONGMODE-STRINGOP\", \"AVX-STTNI\", \"BASE-SYSCALL\", \"LONGMODE-SYSCALL\",\n# \"BASE-SYSRET\", \"LONGMODE-SYSRET\", \"AMD_INVLPGB-SYSTEM\", \"BASE-SYSTEM\", \"LONGMODE-SYSTEM\",\n# \"RDTSCP-SYSTEM\", \"SMX-SYSTEM\", \"SNP-SYSTEM\", \"SVM-SYSTEM\", \"WBNOINVD-SYSTEM\", \"TBM-TBM\",\n# \"TSX_LDTRK-TSX_LDTRK\", \"UINTR-UINTR\", \"BASE-UNCOND_BR\", \"RTM-UNCOND_BR\", \"AVX512EVEX-VAES\",\n# \"VAES-VAES\", \"AVX512EVEX-VBMI2\", \"AVX_VNNI-VEX\", \"AVX512EVEX-VFMA\", \"FMA-VFMA\",\n# \"VIA_PADLOCK_AES-VIA_PADLOCK\", \"VIA_PADLOCK_RNG-VIA_PADLOCK\", \"VIA_PADLOCK_SHA-VIA_PADLOCK\",\n# \"AVX512EVEX-VPCLMULQDQ\", \"VPCLMULQDQ-VPCLMULQDQ\", \"VMFUNC-VTX\", \"VTX-VTX\", \"WAITPKG-WAITPKG\",\n# \"BASE-WIDENOP\", \"SSE3-X87_ALU\", \"X87-X87_ALU\", \"XOP-XOP\", \"XSAVE-XSAVE\", \"XSAVEC-XSAVE\",\n# \"XSAVES-XSAVE\", \"XSAVEOPT-XSAVEOPT\"\n"
  },
  {
    "path": "rvzr/arch/x86/target_desc.py",
    "content": "\"\"\"\nFile: x86-specific constants and lists\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom typing import List, Dict, Final, Tuple\nimport re\nimport unicorn.x86_const as ucc  # type: ignore\n\nfrom rvzr.tc_components.instruction import Instruction\nfrom rvzr.target_desc import TargetDesc, CPUDesc, UnicornTargetDesc, PTEBitNameMapper, \\\n    PTEBitOffset, PTEBitName\n\n\nclass X86TargetDesc(TargetDesc):\n    \"\"\" Target description for x86 architecture. \"\"\"\n\n    register_sizes = {\n        \"mm0\": 64, \"mm1\": 64, \"mm2\": 64, \"mm3\": 64, \"mm4\": 64, \"mm5\": 64, \"mm6\": 64, \"mm7\": 64,\n        \"xmm0\": 128, \"xmm1\": 128, \"xmm2\": 128, \"xmm3\": 128, \"xmm4\": 128, \"xmm5\": 128, \"xmm6\": 128,\n        \"xmm7\": 128,\n        \"ymm0\": 256, \"ymm1\": 256, \"ymm2\": 256, \"ymm3\": 256, \"ymm4\": 256, \"ymm5\": 256, \"ymm6\": 256,\n        \"ymm7\": 256,\n\n        \"rax\": 64, \"rbx\": 64, \"rcx\": 64, \"rdx\": 64, \"rsi\": 64, \"rdi\": 64, \"rsp\": 64, \"rbp\": 64,\n        \"r8\": 64, \"r9\": 64, \"r10\": 64, \"r11\": 64, \"r12\": 64, \"r13\": 64, \"r14\": 64, \"r15\": 64,\n\n        \"eax\": 32, \"ebx\": 32, \"ecx\": 32, \"edx\": 32, \"esi\": 32, \"edi\": 32, \"r8d\": 32, \"r9d\": 32,\n        \"r10d\": 32, \"r11d\": 32, \"r12d\": 32, \"r13d\": 32, \"r14d\": 32, \"r15d\": 32,\n\n        \"ax\": 16, \"bx\": 16, \"cx\": 16, \"dx\": 16, \"si\": 16, \"di\": 16, \"r8w\": 16, \"r9w\": 16,\n        \"r10w\": 16, \"r11w\": 16, \"r12w\": 16, \"r13w\": 16, \"r14w\": 16, \"r15w\": 16,\n\n        \"al\": 8, \"bl\": 8, \"cl\": 8, \"dl\": 8, \"sil\": 8, \"dil\": 8, \"r8b\": 8, \"r9b\": 8,\n        \"r10b\": 8, \"r11b\": 8, \"r12b\": 8, \"r13b\": 8, \"r14b\": 8, \"r15b\": 8,\n        \"ah\": 8, \"bh\": 8, \"ch\": 8, \"dh\": 8,\n    }  # yapf: disable\n\n    registers_by_size = {\n        8: [\"al\", \"bl\", \"cl\", \"dl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\",\n            \"r14b\", \"r15b\"],\n        16: [\"ax\", \"bx\", \"cx\", \"dx\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\",\n             \"r14w\", \"r15w\"],\n        32: [\"eax\", \"ebx\", \"ecx\", \"edx\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\",\n             \"r13d\", \"r14d\", \"r15d\"],\n        64: [\"rax\", \"rbx\", \"rcx\", \"rdx\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\",\n             \"r14\", \"r15\", \"rsp\", \"rbp\", \"mm0\", \"mm1\", \"mm2\", \"mm3\", \"mm4\", \"mm5\", \"mm6\", \"mm7\"],\n        128: [\"xmm0\", \"xmm1\", \"xmm2\", \"xmm3\", \"xmm4\", \"xmm5\", \"xmm6\", \"xmm7\"],\n        256: [\"ymm0\", \"ymm1\", \"ymm2\", \"ymm3\", \"ymm4\", \"ymm5\", \"ymm6\", \"ymm7\"],\n    }  # yapf: disable\n\n    reg_normalized = {\n        \"rax\": \"A\", \"eax\": \"A\", \"ax\": \"A\", \"al\": \"A\", \"ah\": \"A\",\n        \"rbx\": \"B\", \"ebx\": \"B\", \"bx\": \"B\", \"bl\": \"B\", \"bh\": \"B\",\n        \"rcx\": \"C\", \"ecx\": \"C\", \"cx\": \"C\", \"cl\": \"C\", \"ch\": \"C\",\n        \"rdx\": \"D\", \"edx\": \"D\", \"dx\": \"D\", \"dl\": \"D\", \"dh\": \"D\",\n        \"rsi\": \"SI\", \"esi\": \"SI\", \"si\": \"SI\", \"sil\": \"SI\",\n        \"rdi\": \"DI\", \"edi\": \"DI\", \"di\": \"DI\", \"dil\": \"DI\",\n        \"r8\": \"8\", \"r8d\": \"8\", \"r8w\": \"8\", \"r8b\": \"8\",\n        \"r9\": \"9\", \"r9d\": \"9\", \"r9w\": \"9\", \"r9b\": \"9\",\n        \"r10\": \"10\", \"r10d\": \"10\", \"r10w\": \"10\", \"r10b\": \"10\",\n        \"r11\": \"11\", \"r11d\": \"11\", \"r11w\": \"11\", \"r11b\": \"11\",\n        \"r12\": \"12\", \"r12d\": \"12\", \"r12w\": \"12\", \"r12b\": \"12\",\n        \"r13\": \"13\", \"r13d\": \"13\", \"r13w\": \"13\", \"r13b\": \"13\",\n        \"r14\": \"14\", \"r14d\": \"14\", \"r14w\": \"14\", \"r14b\": \"14\",\n        \"r15\": \"15\", \"r15d\": \"15\", \"r15w\": \"15\", \"r15b\": \"15\",\n        \"FLAGS\": \"FLAGS\",\n        \"rip\": \"RIP\",\n        \"rsp\": \"RSP\",\n        \"CF\": \"CF\", \"PF\": \"PF\", \"AF\": \"AF\", \"ZF\": \"ZF\", \"SF\": \"SF\", \"TF\": \"TF\", \"IF\": \"IF\",\n        \"DF\": \"DF\", \"OF\": \"OF\", \"AC\": \"AC\",\n        \"mm0\": \"MM0\",\n        \"mm1\": \"MM1\",\n        \"mm2\": \"MM2\",\n        \"mm3\": \"MM3\",\n        \"mm4\": \"MM4\",\n        \"mm5\": \"MM5\",\n        \"mm6\": \"MM6\",\n        \"mm7\": \"MM7\",\n        \"xmm0\": \"XMM0\",\n        \"xmm1\": \"XMM1\",\n        \"xmm2\": \"XMM2\",\n        \"xmm3\": \"XMM3\",\n        \"xmm4\": \"XMM4\",\n        \"xmm5\": \"XMM5\",\n        \"xmm6\": \"XMM6\",\n        \"xmm7\": \"XMM7\",\n        \"ymm0\": \"YMM0\",\n        \"ymm1\": \"YMM1\",\n        \"ymm2\": \"YMM2\",\n        \"ymm3\": \"YMM3\",\n        \"ymm4\": \"YMM4\",\n        \"ymm5\": \"YMM5\",\n        \"ymm6\": \"YMM6\",\n        \"ymm7\": \"YMM7\",\n        \"cr0\": \"CR0\",\n        \"cr2\": \"CR2\",\n        \"cr3\": \"CR3\",\n        \"cr4\": \"CR4\",\n        \"cr8\": \"CR8\",\n        \"xcr0\": \"XCR0\",\n        \"dr0\": \"DR0\",\n        \"dr1\": \"DR1\",\n        \"dr2\": \"DR2\",\n        \"dr3\": \"DR3\",\n        \"dr6\": \"DR6\",\n        \"dr7\": \"DR7\",\n        \"gdtr\": \"GDTR\",\n        \"idtr\": \"IDTR\",\n        \"ldtr\": \"LDTR\",\n        \"tr\": \"TR\",\n        \"gs\": \"GS\",\n        \"fs\": \"FS\",\n        \"es\": \"ES\",\n        \"ds\": \"DS\",\n        \"cs\": \"CS\",\n        \"ss\": \"SS\",\n        \"fsbase\": \"FSBASE\",\n        \"gsbase\": \"GSBASE\",\n        \"msrs\": \"MSRS\",\n        \"x87control\": \"X87CONTROL\",\n        \"tsc\": \"TSC\",\n        \"tscaux\": \"TSCAUX\",\n    }  # yapf: disable\n\n    reg_denormalized = {\n        \"A\": {64: \"rax\", 32: \"eax\", 16: \"ax\", 8: \"al\"},\n        \"B\": {64: \"rbx\", 32: \"ebx\", 16: \"bx\", 8: \"bl\"},\n        \"C\": {64: \"rcx\", 32: \"ecx\", 16: \"cx\", 8: \"cl\"},\n        \"D\": {64: \"rdx\", 32: \"edx\", 16: \"dx\", 8: \"dl\"},\n        \"SI\": {64: \"rsi\", 32: \"esi\", 16: \"si\", 8: \"sil\"},\n        \"DI\": {64: \"rdi\", 32: \"edi\", 16: \"di\", 8: \"dil\"},\n        \"8\": {64: \"r8\", 32: \"r8d\", 16: \"r8w\", 8: \"r8b\"},\n        \"9\": {64: \"r9\", 32: \"r9d\", 16: \"r9w\", 8: \"r9b\"},\n        \"10\": {64: \"r10\", 32: \"r10d\", 16: \"r10w\", 8: \"r10b\"},\n        \"11\": {64: \"r11\", 32: \"r11d\", 16: \"r11w\", 8: \"r11b\"},\n        \"12\": {64: \"r12\", 32: \"r12d\", 16: \"r12w\", 8: \"r12b\"},\n        \"13\": {64: \"r13\", 32: \"r13d\", 16: \"r13w\", 8: \"r13b\"},\n        \"14\": {64: \"r14\", 32: \"r14d\", 16: \"r14w\", 8: \"r14b\"},\n        \"15\": {64: \"r15\", 32: \"r15d\", 16: \"r15w\", 8: \"r15b\"},\n        \"RIP\": {64: \"rip\", 32: \"rip\", 16: \"rip\", 8: \"rip\"},\n        \"RSP\": {64: \"rsp\", 32: \"rsp\", 16: \"rsp\", 8: \"rsp\"},\n        \"MM0\": {64: \"mm0\"},\n        \"MM1\": {64: \"mm1\"},\n        \"MM2\": {64: \"mm2\"},\n        \"MM3\": {64: \"mm3\"},\n        \"MM4\": {64: \"mm4\"},\n        \"MM5\": {64: \"mm5\"},\n        \"MM6\": {64: \"mm6\"},\n        \"MM7\": {64: \"mm7\"},\n        \"XMM0\": {128: \"xmm0\"},\n        \"XMM1\": {128: \"xmm1\"},\n        \"XMM2\": {128: \"xmm2\"},\n        \"XMM3\": {128: \"xmm3\"},\n        \"XMM4\": {128: \"xmm4\"},\n        \"XMM5\": {128: \"xmm5\"},\n        \"XMM6\": {128: \"xmm6\"},\n        \"XMM7\": {128: \"xmm7\"},\n        \"YMM0\": {256: \"ymm0\"},\n        \"YMM1\": {256: \"ymm1\"},\n        \"YMM2\": {256: \"ymm2\"},\n        \"YMM3\": {256: \"ymm3\"},\n        \"YMM4\": {256: \"ymm4\"},\n        \"YMM5\": {256: \"ymm5\"},\n        \"YMM6\": {256: \"ymm6\"},\n        \"YMM7\": {256: \"ymm7\"}\n    }  # yapf: disable\n\n    mem_index_registers = [\"rax\", \"rbx\", \"rcx\", \"rdx\", \"rsi\", \"rdi\"]\n\n    page_property_to_pte_bit_name = {\n        \"present\": (\"present\", False),\n        \"writable\": (\"writable\", False),\n        \"user\": (\"user\", False),\n        'write-through': (\"write-through\", False),\n        \"cache-disable\": (\"cache-disable\", False),\n        \"accessed\": (\"accessed\", False),\n        \"dirty\": (\"dirty\", False),\n        \"executable\": (\"non_executable\", True),\n        \"reserved_bit\": (\"reserved_bit\", False),\n    }\n\n    pte_bits: Dict[PTEBitName, Tuple[PTEBitOffset, bool]] = {\n        # NAME: (position, default value)\n        \"present\": (0, True),\n        \"writable\": (1, True),\n        \"user\": (2, False),\n        \"write-through\": (3, False),\n        \"cache-disable\": (4, False),\n        \"accessed\": (5, True),\n        \"dirty\": (6, True),\n        \"reserved_bit\": (51, False),\n        \"non_executable\": (63, True),\n    }\n\n    _page_property_to_epte_bit_name: PTEBitNameMapper = {\n        \"present\": (\"present\", False),\n        \"writable\": (\"writable\", False),\n        \"user\": (\"user\", False),\n        \"accessed\": (\"accessed\", False),\n        \"dirty\": (\"dirty\", False),\n        \"executable\": (\"executable\", False),\n        \"reserved_bit\": (\"reserved_bit\", False),\n    }\n\n    _epte_bits_intel: Dict[PTEBitName, Tuple[PTEBitOffset, bool]] = {\n        # NAME: (position, default value)\n        \"present\": (0, True),\n        \"writable\": (1, True),\n        \"executable\": (2, False),\n        \"accessed\": (8, True),\n        \"dirty\": (9, True),\n        \"user\": (10, False),\n        \"reserved_bit\": (51, False),\n    }\n\n    _page_property_to_npte_bit_name: PTEBitNameMapper = {\n        \"present\": (\"present\", False),\n        \"writable\": (\"writable\", False),\n        \"user\": (\"user\", False),\n        \"accessed\": (\"accessed\", False),\n        \"dirty\": (\"dirty\", False),\n        \"executable\": (\"non_executable\", True),\n        \"reserved_bit\": (\"reserved_bit\", False),\n    }\n\n    _npte_bits_amd: Dict[PTEBitName, Tuple[PTEBitOffset, bool]] = {\n        # NAME: (position, default value)\n        \"present\": (0, True),\n        \"writable\": (1, True),\n        \"user\": (2, True),\n        \"accessed\": (5, True),\n        \"dirty\": (6, True),\n        \"reserved_bit\": (51, False),\n        \"non_executable\": (63, True),\n    }\n\n    memory_addr_prefixes: Final[Dict[int, str]] = {\n        8: \"byte ptr\",\n        16: \"word ptr\",\n        32: \"dword ptr\",\n        64: \"qword ptr\",\n        80: \"tbyte ptr\",\n        128: \"xmmword ptr\",\n        256: \"ymmword ptr\",\n        512: \"zmmword ptr\",\n        4608: \"ptr\",\n    }\n\n    def __init__(self) -> None:\n        super().__init__()\n\n        # modify/set target parameters based on the CPU under test and the configuration\n        self.registers_by_size = self._filter_blocked_registers()\n        self.cpu_desc = self._build_cpu_desc()\n\n        # Select VM page table bits and property mapping based on vendor\n        if self.cpu_desc.vendor == 'Intel':\n            self.vm_pte_bits = self._epte_bits_intel\n            self.page_property_to_vm_pte_bit_name = self._page_property_to_epte_bit_name\n        else:\n            self.vm_pte_bits = self._npte_bits_amd\n            self.page_property_to_vm_pte_bit_name = self._page_property_to_npte_bit_name\n\n        # connect Unicorn TD\n        self.uc_target_desc = X86UnicornTargetDesc()\n\n    @staticmethod\n    def is_unconditional_branch(inst: Instruction) -> bool:\n        return inst.category == \"BASE-UNCOND_BR\"\n\n    @staticmethod\n    def is_call(inst: Instruction) -> bool:\n        return inst.category == \"BASE-CALL\"\n\n    def _build_cpu_desc(self) -> CPUDesc:\n        vendor = self.get_vendor()\n        if vendor not in [\"Intel\", \"AMD\"]:\n            return CPUDesc(vendor, 0, 0, 0)\n\n        with open(\"/proc/cpuinfo\", \"r\") as f:\n            cpuinfo = f.read()\n\n            family_match = re.search(r\"cpu family\\s+:\\s+(.*)\", cpuinfo)\n            assert family_match, \"Failed to find family in /proc/cpuinfo\"\n            family = int(family_match.group(1), 16)\n\n            model_match = re.search(r\"model\\s+:\\s+(.*)\", cpuinfo)\n            assert model_match, \"Failed to find model name in /proc/cpuinfo\"\n            model = int(model_match.group(1), 16)\n\n            stepping_match = re.search(r\"stepping\\s+:\\s+(.*)\", cpuinfo)\n            assert stepping_match, \"Failed to find stepping in /proc/cpuinfo\"\n            stepping = int(stepping_match.group(1), 16)\n\n        return CPUDesc(vendor, model, family, stepping)\n\n\nclass X86UnicornTargetDesc(UnicornTargetDesc):  # pylint: disable=too-few-public-methods\n    \"\"\" x86 target description in the context of a Unicorn-based model. \"\"\"\n\n    usable_registers: List[int] = [\n        ucc.UC_X86_REG_RAX, ucc.UC_X86_REG_RBX, ucc.UC_X86_REG_RCX, ucc.UC_X86_REG_RDX,\n        ucc.UC_X86_REG_RSI, ucc.UC_X86_REG_RDI, ucc.UC_X86_REG_EFLAGS, ucc.UC_X86_REG_RSP\n    ]\n\n    usable_simd128_registers: List[int] = [\n        ucc.UC_X86_REG_XMM0, ucc.UC_X86_REG_XMM1, ucc.UC_X86_REG_XMM2, ucc.UC_X86_REG_XMM3,\n        ucc.UC_X86_REG_XMM4, ucc.UC_X86_REG_XMM5, ucc.UC_X86_REG_XMM6, ucc.UC_X86_REG_XMM7\n    ]\n\n    reg_str_to_constant = {\n        \"al\": ucc.UC_X86_REG_AL,\n        \"bl\": ucc.UC_X86_REG_BL,\n        \"cl\": ucc.UC_X86_REG_CL,\n        \"dl\": ucc.UC_X86_REG_DL,\n        \"dil\": ucc.UC_X86_REG_DIL,\n        \"sil\": ucc.UC_X86_REG_SIL,\n        \"spl\": ucc.UC_X86_REG_SPL,\n        \"bpl\": ucc.UC_X86_REG_BPL,\n        \"ah\": ucc.UC_X86_REG_AH,\n        \"bh\": ucc.UC_X86_REG_BH,\n        \"ch\": ucc.UC_X86_REG_CH,\n        \"dh\": ucc.UC_X86_REG_DH,\n        \"ax\": ucc.UC_X86_REG_AX,\n        \"bx\": ucc.UC_X86_REG_BX,\n        \"cx\": ucc.UC_X86_REG_CX,\n        \"dx\": ucc.UC_X86_REG_DX,\n        \"di\": ucc.UC_X86_REG_DI,\n        \"si\": ucc.UC_X86_REG_SI,\n        \"sp\": ucc.UC_X86_REG_SP,\n        \"bp\": ucc.UC_X86_REG_BP,\n        \"eax\": ucc.UC_X86_REG_EAX,\n        \"ebx\": ucc.UC_X86_REG_EBX,\n        \"ecx\": ucc.UC_X86_REG_ECX,\n        \"edx\": ucc.UC_X86_REG_EDX,\n        \"edi\": ucc.UC_X86_REG_EDI,\n        \"esi\": ucc.UC_X86_REG_ESI,\n        \"esp\": ucc.UC_X86_REG_ESP,\n        \"ebp\": ucc.UC_X86_REG_EBP,\n        \"rax\": ucc.UC_X86_REG_RAX,\n        \"rbx\": ucc.UC_X86_REG_RBX,\n        \"rcx\": ucc.UC_X86_REG_RCX,\n        \"rdx\": ucc.UC_X86_REG_RDX,\n        \"rdi\": ucc.UC_X86_REG_RDI,\n        \"rsi\": ucc.UC_X86_REG_RSI,\n        \"rsp\": ucc.UC_X86_REG_RSP,\n        \"rbp\": ucc.UC_X86_REG_RBP,\n        \"xmm0\": ucc.UC_X86_REG_XMM0,\n        \"xmm1\": ucc.UC_X86_REG_XMM1,\n        \"xmm2\": ucc.UC_X86_REG_XMM2,\n        \"xmm3\": ucc.UC_X86_REG_XMM3,\n        \"xmm4\": ucc.UC_X86_REG_XMM4,\n        \"xmm5\": ucc.UC_X86_REG_XMM5,\n        \"xmm6\": ucc.UC_X86_REG_XMM6,\n        \"xmm7\": ucc.UC_X86_REG_XMM7\n    }\n\n    reg_norm_to_constant = {\n        \"A\": ucc.UC_X86_REG_RAX,\n        \"B\": ucc.UC_X86_REG_RBX,\n        \"C\": ucc.UC_X86_REG_RCX,\n        \"D\": ucc.UC_X86_REG_RDX,\n        \"DI\": ucc.UC_X86_REG_RDI,\n        \"SI\": ucc.UC_X86_REG_RSI,\n        \"SP\": ucc.UC_X86_REG_RSP,\n        \"BP\": ucc.UC_X86_REG_RBP,\n        \"8\": ucc.UC_X86_REG_R8,\n        \"9\": ucc.UC_X86_REG_R9,\n        \"10\": ucc.UC_X86_REG_R10,\n        \"11\": ucc.UC_X86_REG_R11,\n        \"12\": ucc.UC_X86_REG_R12,\n        \"13\": ucc.UC_X86_REG_R13,\n        \"14\": ucc.UC_X86_REG_R14,\n        \"15\": ucc.UC_X86_REG_R15,\n        \"FLAGS\": ucc.UC_X86_REG_EFLAGS,\n        \"CF\": ucc.UC_X86_REG_EFLAGS,\n        \"PF\": ucc.UC_X86_REG_EFLAGS,\n        \"AF\": ucc.UC_X86_REG_EFLAGS,\n        \"ZF\": ucc.UC_X86_REG_EFLAGS,\n        \"SF\": ucc.UC_X86_REG_EFLAGS,\n        \"TF\": ucc.UC_X86_REG_EFLAGS,\n        \"IF\": ucc.UC_X86_REG_EFLAGS,\n        \"DF\": ucc.UC_X86_REG_EFLAGS,\n        \"OF\": ucc.UC_X86_REG_EFLAGS,\n        \"AC\": ucc.UC_X86_REG_EFLAGS,\n        \"XMM0\": ucc.UC_X86_REG_XMM0,\n        \"XMM1\": ucc.UC_X86_REG_XMM1,\n        \"XMM2\": ucc.UC_X86_REG_XMM2,\n        \"XMM3\": ucc.UC_X86_REG_XMM3,\n        \"XMM4\": ucc.UC_X86_REG_XMM4,\n        \"XMM5\": ucc.UC_X86_REG_XMM5,\n        \"XMM6\": ucc.UC_X86_REG_XMM6,\n        \"XMM7\": ucc.UC_X86_REG_XMM7,\n        \"XMM8\": ucc.UC_X86_REG_XMM8,\n        \"XMM9\": ucc.UC_X86_REG_XMM9,\n        \"XMM10\": ucc.UC_X86_REG_XMM10,\n        \"XMM11\": ucc.UC_X86_REG_XMM11,\n        \"XMM12\": ucc.UC_X86_REG_XMM12,\n        \"XMM14\": ucc.UC_X86_REG_XMM14,\n        \"XMM15\": ucc.UC_X86_REG_XMM15,\n        \"RIP\": -1,\n        \"RSP\": -1,\n        \"CR0\": -1,\n        \"CR2\": -1,\n        \"CR3\": -1,\n        \"CR4\": -1,\n        \"CR8\": -1,\n        \"XCR0\": -1,\n        \"DR0\": -1,\n        \"DR1\": -1,\n        \"DR2\": -1,\n        \"DR3\": -1,\n        \"DR6\": -1,\n        \"DR7\": -1,\n        \"GDTR\": -1,\n        \"IDTR\": -1,\n        \"LDTR\": -1,\n        \"TR\": -1,\n        \"FSBASE\": -1,\n        \"GSBASE\": -1,\n        \"MSRS\": -1,\n        \"X87CONTROL\": -1,\n        \"TSC\": -1,\n        \"TSCAUX\": -1,\n    }\n\n    barriers: List[str] = ['mfence', 'lfence']\n    flags_register: int = ucc.UC_X86_REG_EFLAGS\n    pc_register: int = ucc.UC_X86_REG_RIP\n    sp_register: int = ucc.UC_X86_REG_RSP\n    actor_base_register: int = ucc.UC_X86_REG_R14\n"
  },
  {
    "path": "rvzr/asm_parser.py",
    "content": "\"\"\"\nFile: Parsing of assembly files into our internal representation (TestCaseCode).\n      This file contains ISA-independent code; see <isa>/<isa>_asm_parser.py for ISA-specific code.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing import TYPE_CHECKING, Dict, List, Final, Optional\nfrom abc import ABC, abstractmethod\nfrom collections import OrderedDict\nimport re\nimport os\n\nfrom .tc_components.test_case_code import TestCaseProgram, Function, BasicBlock, TC_EXIT_LABEL\nfrom .tc_components.instruction import Instruction, LabelOp, Operand, RegisterOp, MemoryOp, \\\n    ImmediateOp, AgenOp, CondOp, AnyOperand\nfrom .instruction_spec import OT, InstructionSpec, OperandSpec\nfrom .tc_components.actor import ActorPL, ActorMode\nfrom .code_generator import assemble\nfrom .config import CONF\n\nif TYPE_CHECKING:\n    from .target_desc import TargetDesc\n    from .isa_spec import InstructionSet\n    from .elf_parser import ELFParser\n    from .code_generator import CodeGenerator\n\nRE_REDUNDANT_SPACES = re.compile(r\"(?<![a-zA-Z0-9]) +\")\nTC_EXIT_LINE = TC_EXIT_LABEL + \":\"\n\n\nclass AsmParserError(Exception):\n    \"\"\" Base exception class for AsmParser module \"\"\"\n\n    def __init__(self, line_num: int, message: str) -> None:\n        full_msg = f\"[AsmParser] Error parsing assembly (line {line_num + 1}):\\n{message}\"\n        super().__init__(full_msg)\n\n\ndef asm_parser_assert(condition: bool, line_number: int, explanation: str) -> None:\n    \"\"\" Raise an AsmParserError if the condition is not met \"\"\"\n\n    if not condition:\n        raise AsmParserError(line_number, explanation)\n\n\n@dataclass\nclass _ASMLine:\n    \"\"\" Metadata for a single line of assembly code \"\"\"\n\n    def __init__(self, str_: str, line_num: int) -> None:\n        self.str = str_\n        self.clean_str = str_.strip().lower()\n        self.clean_str = RE_REDUNDANT_SPACES.sub(\"\", self.clean_str)\n        self.line_num = line_num\n        self.patched_line_num = 0\n        self.clean_line_num = 0\n\n\n# ==================================================================================================\n# Private: Code Map Builder\n# ==================================================================================================\n_BBLabel = str\n_BasicBlockMap = OrderedDict[_BBLabel, List[_ASMLine]]\n_FunctionName = str\n_FunctionMap = OrderedDict[_FunctionName, _BasicBlockMap]\n_SectionLabel = str\n_CodeMap = OrderedDict[_SectionLabel, _FunctionMap]\n\n\nclass _CodeMapBuilder:\n    \"\"\"\n    Class responsible for building a dictionary of sections, functions, basic blocks,\n    and relationships between them in an assembly file\n    \"\"\"\n\n    section_label: Optional[str] = None\n    function_label: Optional[str] = None\n    bb_label: Optional[str] = None\n    bb_is_autogenerated: bool = False\n    code_map: _CodeMap\n\n    def create_code_map(self, lines: List[_ASMLine]) -> _CodeMap:\n        \"\"\"\n        Parse lines and build a map of sections, functions, and basic blocks\n        in the test case\n        :param lines: representation of the assembly file as a list of ASMLine objects\n        :return: a dictionary representing the code structure of the test case (_CodeMap)\n        \"\"\"\n        self.code_map: _CodeMap = OrderedDict()\n\n        # Parse each line according to its type and accumulate the code structure as we go\n        for line_obj in lines:\n            line_num = line_obj.line_num\n            line = line_obj.clean_str\n            type_ = self._get_line_type(line_obj)\n\n            if type_ == \"ignore\":\n                continue\n\n            if type_ == \"section\":\n                self._start_section(line_obj)\n                continue\n\n            if type_ == \"function\":\n                self._start_function(line_obj)\n                continue\n            if self.function_label is None and self.section_label is not None:\n                self._create_default_main_function()  # allow for implicit functions\n\n            if type_ == \"basic_block\":\n                self._start_basic_block(line_obj)\n                continue\n\n            if type_ == \"instruction\":\n                self._add_instruction(line_obj)\n                continue\n\n            if type_ == \"opcode\":\n                self._add_instruction(_ASMLine(\"opcode\", line_num))\n                continue\n\n            if type_ == \"macro\":\n                str_ = self._macro_label_to_instr(line, line_num)\n                self._add_instruction(_ASMLine(str_, line_num))\n\n        return self.code_map\n\n    def _get_line_type(self, line: _ASMLine) -> str:\n        # pylint: disable=too-many-return-statements\n        # NOTE: This is a selector, so it is expected to have many returns\n\n        line_str = line.clean_str\n        if line_str[0] != \".\":\n            return \"instruction\"\n        if line_str.startswith(\".global\") or line_str.startswith(\".intel_syntax\"):\n            return \"ignore\"\n        if line_str.startswith(\".section\"):\n            return \"section\"\n        if line_str.startswith(\".function_\"):\n            return \"function\"\n        if line_str.startswith(\".macro\"):\n            return \"macro\"\n        if line_str[:4] == \".bcd \" or line_str[:5] in [\".byte\", \".long\", \".quad\"] \\\n           or line_str[6:] in [\".value\", \".2byte\", \".4byte\", \".8byte\"]:\n            return \"opcode\"\n        if line_str[-1] == \":\":\n            return \"basic_block\"\n        raise NotImplementedError(f\"Line {line.line_num}: Unknown line type: {line_str}\")\n\n    def _start_section(self, line: _ASMLine) -> None:\n        line_str = line.clean_str\n        words = line.clean_str.split()\n        assert len(words) == 2, \\\n            f\"Line {line.line_num}: Invalid section label {line_str}; expected .section <label>\"\n\n        # exit section does not represent any actor; ignore it\n        if words[1] == \"exit\":\n            return\n\n        # parse the section label\n        sub_words = words[1].split(\".\")\n        assert len(sub_words) == 3, \\\n            f\"Line {line.line_num}: Invalid section label: {line_str}\"\n\n        label = sub_words[2]\n        assert label in CONF.get_actors_conf(), \\\n            f\"Line {line.line_num}: Actor {label} was not defined in the config file\"\n\n        # update the parser state to reflect that a new section has started\n        if label not in self.code_map:\n            self.code_map[label] = OrderedDict()\n        self.section_label = label\n        self.function_label = None\n        self.bb_label = None\n\n    def _start_function(self, line: _ASMLine) -> None:\n        assert self.section_label is not None, \\\n            f\"Line {line.line_num}: Function started before a .section was declared\"\n\n        # get label\n        line_str = line.clean_str\n        assert line_str[-1] == \":\", f\"Invalid function header: {line_str}\"\n        label = line_str[:-1]\n\n        # update the parser state to reflect that a new function has started\n        self.function_label = label\n        self.code_map[self.section_label][label] = OrderedDict()\n\n        default_start_bb = \".bb_\" + label.removeprefix(\".function_\") + \".entry\"\n        self.bb_label = default_start_bb\n        self.bb_is_autogenerated = True\n        self.code_map[self.section_label][label][default_start_bb] = []\n\n    def _create_default_main_function(self) -> None:\n        assert self.section_label is not None, \\\n            \"Function started before a .section was declared\"\n\n        default_label = f\".function_{self.section_label}_0\"\n        self.function_label = default_label\n        self.code_map[self.section_label][default_label] = OrderedDict()\n\n        default_start_bb = f\".bb_{self.section_label}_0.entry\"\n        self.bb_label = default_start_bb\n        self.bb_is_autogenerated = True\n        self.code_map[self.section_label][default_label][default_start_bb] = []\n\n    def _start_basic_block(self, line: _ASMLine) -> None:\n        assert self.section_label is not None, \\\n            f\"Line {line.line_num}: Basic block started before a .section was declared\"\n        assert self.function_label is not None, \\\n            f\"Line {line.line_num}: Basic block started before a .function was declared\"\n        assert self.bb_label is not None, \\\n            f\"Line {line.line_num}: Default basic block was not created\"\n\n        # get label\n        line_str = line.clean_str\n        assert line_str[-1] == \":\", f\"Invalid basic block header: {line_str}\"\n        label = line_str[:-1]\n\n        # overwrite default BB added to every function, if it is still empty\n        bb_map = self.code_map[self.section_label][self.function_label]\n        if len(bb_map) == 1 and len(bb_map[self.bb_label]) == 0 \\\n           and self.bb_is_autogenerated:\n            del self.code_map[self.section_label][self.function_label][self.bb_label]\n\n        # update the parser state to reflect that a new basic block has started\n        self.bb_label = label\n        self.bb_is_autogenerated = False\n        assert label not in self.code_map[self.section_label][self.function_label], \\\n            f\"Line {line.line_num}: Duplicate basic block label: {label}\"\n        self.code_map[self.section_label][self.function_label][label] = []\n\n    def _add_instruction(self, line: _ASMLine) -> None:\n        assert self.section_label is not None, \\\n            f\"Line {line.line_num}: Instruction found before a .section was declared\"\n        assert self.function_label is not None, \\\n            f\"Line {line.line_num}: Instruction found before a .function was declared\"\n        assert self.bb_label is not None, \\\n            f\"Line {line.line_num}: Instruction found before a basic block was declared\"\n\n        self.code_map[self.section_label][self.function_label][self.bb_label].append(line)\n\n    def _macro_label_to_instr(self, line: str, line_num: int) -> str:\n        \"\"\"\n        This function replaces label-like macros with a pseudo-instruction MACRO\n        As such, we simplify further parsing of the test case\n        \"\"\"\n        # get rid of the NOP placeholder\n        words = line.split(\":\")\n        asm_parser_assert(len(words) == 2, line_num, \"Invalid macro declaration\")\n        asm_parser_assert(words[1][:3] == \"nop\", line_num, \"Patching error\")\n\n        # get the macro name and its arguments\n        subwords = words[0].split(\".\")\n        asm_parser_assert(len(subwords) >= 3, line_num, f\"Invalid macro: {line}\")\n        asm_parser_assert(len(subwords) <= 7, line_num, f\"Invalid macro: {line}\")\n        macro_id = subwords[2]\n        args = '.'.join(subwords[3:])\n        if args:\n            instr = f\"macro .{macro_id}, .{args}\"\n        else:\n            instr = f\"macro .{macro_id}, .noarg\"\n\n        return instr\n\n\n# ==================================================================================================\n# Private: Patching of assembly files\n# ==================================================================================================\nclass _AsmPatcher:\n    _main_function_label: str = \"\"\n    _macro_placeholder_str: Optional[str] = None\n\n    def set_macro_placeholder(self, macro_placeholder_str: str) -> None:\n        \"\"\"\n        Assign a string to be used as a placeholder for macro instructions.\n\n        This method exist to allow re-use of the _AsmPatcher class between different ISAs.\n        \"\"\"\n        assert self._macro_placeholder_str is None, \\\n            \"Macro placeholder string was already set\"\n        self._macro_placeholder_str = macro_placeholder_str\n\n    def patch_asm(self, asm_file: str, patched_asm_file: str) -> None:\n        \"\"\"\n        Ensure that the assembly file is in the correct format for parsing:\n        - all function labels are exposed by adding a global label\n        - NOP is added at the end of each function to make size calculations easier\n        - .function_0 is inserted at the beginning of the file if it is missing\n        - .test_case_exit is within the .data.main section and contains a single NOP\n        \"\"\"\n        self._main_function_label = \"\"\n        self._pre_clean(asm_file, patched_asm_file)\n\n        # apply the patches\n        self._add_exit_section(patched_asm_file)\n        self._add_default_main(patched_asm_file)\n        self._add_macro_placeholders(patched_asm_file)\n        self._add_default_measurements(patched_asm_file)\n\n    def _is_instruction(self, line: str) -> bool:\n        return line != '' and line[0] not in [\"#\", \"/\"] \\\n            and (line[0] != '.' or line[:4] == \".bcd\"\n                 or line[:5] in [\".byte\", \".long\", \".quad\"] or line[:6] == '.macro'\n                 or line[6:] in [\".value\", \".2byte\", \".4byte\", \".8byte\"])\n\n    def _pre_clean(self, asm_file: str, patched_asm_file: str) -> None:\n        with open(asm_file, \"r\") as f:\n            with open(patched_asm_file, \"w\") as patched:\n                for line in f:\n                    line = line.strip().lower()\n                    patched.write(line + \"\\n\")\n\n    def _add_exit_section(self, patched_asm_file: str) -> None:\n        prev_line = \"\"\n        with open(patched_asm_file, \"r\") as f:\n            with open(patched_asm_file + \".tmp\", \"w\") as patched:\n                for line in f:\n                    line = line[:-1]\n                    if \".test_case_exit:\" in line:\n                        if \".data.main\" not in prev_line or \"measurement_end\" in prev_line:\n                            patched.write(\".section .data.main\\n\")\n                        patched.write(\".test_case_exit:\" + \"nop\" + \"\\n\")\n                        continue\n                    patched.write(line + \"\\n\")\n                    prev_line = line\n        os.rename(patched_asm_file + \".tmp\", patched_asm_file)\n\n    def _add_default_main(self, patched_asm_file: str) -> None:\n        main_function_label = \"\"\n        with open(patched_asm_file, \"r\") as f:\n            with open(patched_asm_file + \".tmp\", \"w\") as patched:\n                for line in f:\n                    line = line[:-1]\n\n                    # if we already have a main function, just copy the rest of the file\n                    if main_function_label:\n                        patched.write(line + \"\\n\")\n                        continue\n\n                    # reached the end of the file\n                    if \".test_case_exit:\" in line:\n                        main_function_label = \".function_0\"\n                        patched.write(\".function_0:\\n\")\n                        patched.write(line + \"\\n\")\n                        continue\n\n                    # found the main function\n                    if line.startswith(\".function_\"):\n                        main_function_label = line[:-1]\n                        patched.write(line + \"\\n\")\n                        continue\n\n                    # found an instruction before the main function\n                    if self._is_instruction(line):\n                        patched.write(\".function_0:\\n\")\n                        main_function_label = \".function_0\"\n                        patched.write(line + \"\\n\")\n                        continue\n\n                    # copy non-instruction lines\n                    patched.write(line + \"\\n\")\n\n        self._main_function_label = main_function_label\n        os.rename(patched_asm_file + \".tmp\", patched_asm_file)\n\n    def _add_macro_placeholders(self, patched_asm_file: str) -> None:\n        \"\"\" add NOP placeholders after macros \"\"\"\n        assert self._macro_placeholder_str is not None, \\\n            \"set_macro_placeholder() was not called before patching\"\n\n        with open(patched_asm_file, \"r\") as f:\n            with open(patched_asm_file + \".tmp\", \"w\") as patched:\n                for line in f:\n                    line = line.lower()\n                    if line.startswith(\".macro\"):\n                        if \"nop\" not in line:\n                            patched.write(line[:-1] + self._macro_placeholder_str + \"\\n\")\n                        else:\n                            assert self._macro_placeholder_str in line, \\\n                                \"Unexpected NOP placeholder: \" + line\n                            patched.write(line)\n                    else:\n                        patched.write(line)\n        os.rename(patched_asm_file + \".tmp\", patched_asm_file)\n\n    def _add_default_measurements(self, patched_asm_file: str) -> None:\n        assert self._macro_placeholder_str is not None, \\\n            \"set_macro_placeholder() was not called before patching\"\n\n        # identify if the file already has the measurement macros;\n        # this information is used by multiple patching steps\n        has_measurement_start = False\n        has_measurement_end = False\n        with open(patched_asm_file, \"r\") as f:\n            for line in f:\n                line = line.lower()\n                if line.startswith(\".macro.measurement_start\"):\n                    has_measurement_start = True\n                elif line.startswith(\".macro.measurement_end\"):\n                    has_measurement_end = True\n\n        # add .macro.measurement_start after .function_0\n        if not has_measurement_start:\n            with open(patched_asm_file, \"r\") as f:\n                with open(patched_asm_file + \".tmp\", \"w\") as patched:\n                    for line in f:\n                        line = line.lower()\n                        patched.write(line)\n                        if line.startswith(self._main_function_label):\n                            patched.write(\".macro.measurement_start:\" + self._macro_placeholder_str\n                                          + \"\\n\")\n            os.rename(patched_asm_file + \".tmp\", patched_asm_file)\n\n        # add .macro.measurement_end before .test_case_exit\n        if not has_measurement_end:\n            with open(patched_asm_file, \"r\") as f:\n                with open(patched_asm_file + \".tmp\", \"w\") as patched:\n                    prev_line = \"\"\n                    for line in f:\n                        line = line.lower()\n                        if line.startswith(\".test_case_exit:\"):\n                            if prev_line.startswith(\".section\"):\n                                patched.write(\".function_end:\\n\")\n                            patched.write(\".macro.measurement_end:\" + self._macro_placeholder_str\n                                          + \"\\n\")\n                        patched.write(line)\n                        prev_line = line\n            os.rename(patched_asm_file + \".tmp\", patched_asm_file)\n\n\n# ==================================================================================================\n# Public (intended for <isa>/asm_parser.py): Line parsing\n# ==================================================================================================\nclass AsmLineParser(ABC):\n    \"\"\"\n    Class responsible for parsing an assembly line into an Instruction object.\n\n    This class is a common ISA-independent functionality with hooks to be implemented by\n    ISA-specific subclasses.\n    \"\"\"\n    _instruction_map: Final[Dict[str, List[InstructionSpec]]]\n    _curr_ln: int\n    _comment_char: str  # set by subclasses\n\n    def __init__(self, isa_spec: InstructionSet, target_desc: TargetDesc) -> None:\n        self._instruction_map = self._build_instruction_map(isa_spec)\n        self._target_desc = target_desc\n        self._curr_ln = -1\n\n    # ----------------------------------------------------------------------------------------------\n    # Hooks for ISA-specific subclasses\n    @abstractmethod\n    def _tokenize(self, line: str) -> List[str]:\n        \"\"\" Tokenize the line and store the results internally \"\"\"\n\n    @abstractmethod\n    def _get_instruction_name(self, line: str, tokens: List[str]) -> str:\n        \"\"\" Get the instruction name from the line, based on the tokenized data \"\"\"\n\n    @abstractmethod\n    def _get_instruction_operands(self, line: str, name: str, tokens: List[str]) -> List[str]:\n        \"\"\" Get the instruction operands from the line, based on the tokenized data \"\"\"\n\n    @abstractmethod\n    def _get_initial_candidate_specs(self, line: str, name: str) -> List[InstructionSpec]:\n        \"\"\" Get a list of candidate specs for the given assembly line \"\"\"\n\n    @abstractmethod\n    def _check_if_spec_matches(self, spec: InstructionSpec, operands_raw: List[str]) -> bool:\n        \"\"\" Check if the given spec matches the given list of operand strings \"\"\"\n\n    # ----------------------------------------------------------------------------------------------\n    # Common ISA-independent Functionality\n    @staticmethod\n    def _build_instruction_map(isa_spec: InstructionSet) -> Dict[str, List[InstructionSpec]]:\n        instruction_map: Dict[str, List[InstructionSpec]] = {}\n        for spec in isa_spec.instructions_unfiltered:\n            if spec.name in instruction_map:\n                instruction_map[spec.name].append(spec)\n            else:\n                instruction_map[spec.name] = [spec]\n\n            # add an entry for direct opcodes\n            opcode_spec = InstructionSpec(\"opcode\", \"opcode\")\n            instruction_map[\"opcode\"] = [opcode_spec]\n\n            # entry for macros\n            macro_spec = InstructionSpec(\"macro\", \"macro\")\n            macro_spec.operands = [\n                OperandSpec([], OT.LABEL, False, False),\n                OperandSpec([], OT.LABEL, False, False)\n            ]\n            instruction_map[\"macro\"] = [macro_spec]\n        return instruction_map\n\n    def parse_line(self, line: str, line_num: int) -> Instruction:\n        \"\"\" Implementation of the AsmLineParser interface for x86 assembly lines (Intel syntax) \"\"\"\n        self._curr_ln = line_num\n\n        # Identify the line type\n        line = line.lower()\n        is_instrumentation = \"instrumentation\" in line\n        is_noremove = \"noremove\" in line\n\n        # Remove comments\n        if self._comment_char in line:\n            line = line.split(self._comment_char)[0].strip()\n\n        # Get instruction name and operands\n        tokens = self._tokenize(line)\n        name = self._get_instruction_name(line, tokens)\n        operands_raw = self._get_instruction_operands(line, name, tokens)\n\n        # Find a matching spec\n        spec = self._find_matching_spec(line, name, operands_raw)\n\n        # generate a corresponding Instruction\n        inst = self._create_instruction(spec, operands_raw, is_instrumentation, is_noremove)\n        inst.assign_line_num(line_num)\n\n        return inst\n\n    def _find_matching_spec(self, line: str, name: str, operands_raw: List[str]) -> InstructionSpec:\n        \"\"\" Find the InstructionSpec that matches the given assembly line \"\"\"\n\n        # Get candidate specs\n        specs = self._get_initial_candidate_specs(line, name)\n        if len(specs) == 0:\n            raise AsmParserError(self._curr_ln, f\"Unknown instruction {line}\")\n\n        # find a matching spec\n        matching_specs: List[InstructionSpec] = []\n        for spec_candidate in specs:\n            if self._check_if_spec_matches(spec_candidate, operands_raw):\n                matching_specs.append(spec_candidate)\n        if len(matching_specs) == 0:\n            raise AsmParserError(self._curr_ln, f\"Could not find a matching spec for {line}\")\n\n        # we might find several matches if the instruction has a magic operand value\n        if len(matching_specs) > 1:\n            magic_value_specs = list(filter(lambda x: (x.has_magic_value), matching_specs))\n            if magic_value_specs:\n                matching_specs = magic_value_specs\n\n        # at this point we should have only one spec, but even if we don't, all of them should\n        # be equivalent. Just pick the first\n        return matching_specs[0]\n\n    def _create_instruction(self, spec: InstructionSpec, operands_raw: List[str],\n                            is_instrumentation: bool, is_noremove: bool) -> Instruction:\n        \"\"\"\n        Create an Instruction object and its operands based on the assembly line\n        and the spec that describes the instruction\n        \"\"\"\n        # create the instruction with no operands\n        inst = Instruction.from_spec(\n            spec, is_instrumentation=is_instrumentation, is_noremove=is_noremove)\n\n        # create operands\n        op: AnyOperand\n        for op_id, op_raw in enumerate(operands_raw):\n            op_spec = spec.operands[op_id]\n            if op_spec.type == OT.REG:\n                op = RegisterOp(op_raw, op_spec.width, op_spec.src, op_spec.dest)\n            elif op_spec.type == OT.MEM:\n                address_match = re.search(r'\\[(.*)\\]', op_raw)\n                asm_parser_assert(address_match is not None, self._curr_ln,\n                                  \"Invalid memory address\")\n                address = address_match.group(1)  # type: ignore\n                op = MemoryOp(address, op_spec.width, op_spec.src, op_spec.dest)\n            elif op_spec.type == OT.IMM:\n                op = ImmediateOp(op_raw, op_spec.width)\n            elif op_spec.type == OT.LABEL:\n                assert spec.is_control_flow or spec.name == \"macro\"\n                op = LabelOp(op_raw)\n            elif op_spec.type == OT.COND:\n                op = CondOp(op_raw)\n            else:  # AGEN\n                address_match = re.search(r'\\[(.*)\\]', op_raw)\n                asm_parser_assert(address_match is not None, self._curr_ln,\n                                  \"Invalid memory address\")\n                address = address_match.group(1)  # type: ignore\n                op = AgenOp(address, op_spec.width)\n            inst.operands.append(op)\n        # add implicit operands\n        for op_spec in spec.implicit_operands:\n            # implicit operands should always have fixed spec, hence it's safe to use\n            # the from_fixed_spec constructor\n            op = Operand.from_fixed_spec(op_spec)\n            inst.implicit_operands.append(op)\n        return inst\n\n\n# ==================================================================================================\n# Public: High-level ASM Parser\n# ==================================================================================================\nclass AsmParser(ABC):\n    \"\"\"\n    Class responsible for parsing (and optionally patching) assembly files\n    and producing TestCaseCode objects from them\n    \"\"\"\n\n    _target_desc: Final[TargetDesc]\n    _isa_spec: Final[InstructionSet]\n    _asm_patcher: _AsmPatcher\n    _line_parser: AsmLineParser\n    _lines: List[_ASMLine]\n\n    def __init__(self, isa_spec: InstructionSet, target_desc: TargetDesc) -> None:\n        self._isa_spec = isa_spec\n        self._target_desc = target_desc\n        self._asm_patcher = _AsmPatcher()\n\n    # ----------------------------------------------------------------------------------------------\n    # Public Interface\n    def parse_file(self,\n                   asm_file: str,\n                   generator: CodeGenerator,\n                   elf_parser: ELFParser,\n                   is_template: bool = False) -> TestCaseProgram:\n        \"\"\"\n        Read a test case from a file, patch it to make it parsable (if necessary),\n        create a complete TestCaseCode object based on it, and populate it with ELF data.\n\n        This function is used instead of ProgramGenerator.create_test_case() when Revizor works\n        with a user-provided test case. Hence, this function's output is expected to be\n        equivalent to the output of ProgramGenerator.create_test_case().\n\n        :param asm_file: path to the input assembly file to parse\n        :param generator: an instance of ProgramGenerator object to assist in creating the test case\n        :param elf_parser: an instance of ELFParser object to assist in creating the test case\n        :return: a fully constructed TestCaseCode object\n        \"\"\"\n        # Transform the input file into a list of ASMLine objects\n        with open(asm_file, 'r') as f:\n            dirty_lines = [_ASMLine(l, i) for i, l in enumerate(f)]\n\n        # Apply patches to the assembly file to make it parsable\n        patched_asm_file = asm_file + \".patched.asm\"\n        self._patch_asm(asm_file, patched_asm_file)\n        dirty_lines = self._update_lines_after_patch(patched_asm_file, dirty_lines)\n\n        # Build a map of the code structure\n        clean_lines = self._get_clean_lines(dirty_lines)\n        code_map = _CodeMapBuilder().create_code_map(clean_lines)\n\n        # Create an empty test case with actors set up according to the configuration\n        test_case = TestCaseProgram(patched_asm_file)\n        generator.generate_actors_with_sections(test_case, CONF.get_actors_conf())\n\n        # Fill the test case with the object representations of the parsed assembly\n        self._fill_test_case_structure(test_case, code_map)\n        assert len(test_case) > 0, \"Default section not found\"\n        self._connect_control_flow(test_case)\n\n        # Handle empty and trivial assembly files\n        if len(test_case) == 1 and len(test_case[0]) == 0 and len(test_case[0][0]) == 0:\n            self._handle_empty_test_case(test_case)\n\n        # Perform final correctness checks\n        self._check_test_case_correctness(test_case)\n\n        # Assemble the test case and populate with ELF data\n        if not is_template:  # there is no point in assembling a template\n            test_case.assign_obj(asm_file[:-4] + \".o\")\n            assemble(test_case)\n            elf_parser.populate_elf_data(test_case.get_obj(), test_case)\n\n        return test_case\n\n    # ----------------------------------------------------------------------------------------------\n    # Private: Assembly Patching\n    def _patch_asm(self, asm_file: str, patched_asm_file: str) -> None:\n        return self._asm_patcher.patch_asm(asm_file, patched_asm_file)\n\n    def _update_lines_after_patch(self, patched_asm_file: str,\n                                  org_lines: List[_ASMLine]) -> List[_ASMLine]:\n        \"\"\"\n        Create a list of ASMLine objects from the patched assembly file, such that\n        the line number correspond to the original assembly file if the line was not patched\n        \"\"\"\n\n        patched_lines = []\n        with open(patched_asm_file, 'r') as patched:\n            org_line_num = 0\n            for patched_line_num, p_line in enumerate(patched):\n                o_line_obj = org_lines[org_line_num]\n                o_line = o_line_obj.str.strip().lower()\n\n                p_line = p_line.strip().lower()\n                p_line_obj = _ASMLine(p_line, 0)\n                p_line_obj.patched_line_num = patched_line_num\n\n                if o_line in p_line:\n                    p_line_obj.line_num = o_line_obj.line_num\n                    org_line_num += 1\n\n                patched_lines.append(p_line_obj)\n        return patched_lines\n\n    # ----------------------------------------------------------------------------------------------\n    # Private: Line Parsing\n    def _get_clean_lines(self, org_lines: List[_ASMLine]) -> List[_ASMLine]:\n        \"\"\" Remove comments and empty lines from a list of ASMLine objects \"\"\"\n        self._validate_dirty_lines(org_lines)\n\n        lines = []\n        finished = False\n        for line_obj in org_lines:\n            line_str = line_obj.str\n\n            # Skip comments and empty lines\n            if not line_str or line_str[0] in [\"\", \"#\", \"/\"]:\n                continue\n\n            # Skip lines after test case exit\n            if line_str[:16] == TC_EXIT_LINE:\n                finished = True\n                continue\n            if finished:\n                continue\n\n            # Save all other lines\n            lines.append(line_obj)\n\n        return lines\n\n    def _validate_dirty_lines(self, lines: List[_ASMLine]) -> None:\n        \"\"\" Check that the list of lines is well-formed \"\"\"\n        finished = False\n        for line_obj in lines:\n            line_str = line_obj.clean_str\n            if not line_str:\n                continue\n            if line_str[:16] == TC_EXIT_LINE:\n                finished = True\n                continue\n\n            # Check that there are no instructions after .test_case_exit\n            if finished and line_str[0] not in [\"\", \".\", \"#\", \"/\"]:\n                raise AsmParserError(line_obj.line_num,\n                                     f\"Found instructions after .test_case_exit: {line_obj.str}\")\n\n        if not finished:\n            raise AsmParserError(0, \".test_case_exit not found\")\n\n    def _parse_line(self, line: str, line_num: int) -> Instruction:\n        return self._line_parser.parse_line(line, line_num)\n\n    # ----------------------------------------------------------------------------------------------\n    # Private: Building Test Case Structure\n    def _fill_test_case_structure(self, test_case: TestCaseProgram, code_map: _CodeMap) -> None:\n        \"\"\"\n        Initialize the structure of the test case according to the code map.\n        The function fills the test case with sections, functions, basic blocks,\n        instructions, and connects them\n        :param test_case: the test case to be filled\n        :param code_map: the code map representing the structure of the parsed assembly\n        :return: None\n        \"\"\"\n        for section_name, function_map in code_map.items():\n            section = test_case.find_section(name=section_name)\n\n            for func_name, bb_map in function_map.items():\n                func = Function(func_name, section)\n                section.append(func)\n\n                for bb_name, inst_lines in bb_map.items():\n                    # print(\">>\", bb_name)\n                    bb = BasicBlock(bb_name, func)\n                    func.append(bb)\n\n                    terminators_started = False\n                    for line in inst_lines:\n                        # print(f\"    {line}\")\n                        inst = self._parse_line(line.clean_str, line.line_num)\n                        if not inst.is_control_flow:\n                            assert not terminators_started, \\\n                                f\"Line {line.line_num}: Terminator not at the end of BB\"\n                            bb.insert_after(bb.get_last(), inst)\n                            continue\n\n                        terminators_started = True\n                        bb.terminators.append(inst)\n\n    def _connect_control_flow(self, test_case: TestCaseProgram) -> None:\n        \"\"\"\n        Connect the basic blocks in the test case by following their terminators\n        :param test_case: the test case to connect\n        :return: None\n        \"\"\"\n        # connect basic blocks\n        bb_names = {bb.name.lower(): bb for func in test_case.iter_functions() for bb in func}\n        bb_names[TC_EXIT_LABEL] = test_case.get_tc_exit_bb()\n        previous_bb = None\n        for func in test_case.iter_functions():\n            for bb in func:\n                # fallthrough\n                if previous_bb:  # skip the first BB\n                    # there is a fallthrough only if the last terminator is not a direct jump\n                    if not previous_bb.terminators or \\\n                       not self._target_desc.is_unconditional_branch(previous_bb.terminators[-1]):\n                        previous_bb.successors.append(bb)\n                previous_bb = bb\n\n                # taken branches\n                for terminator in bb.terminators:\n                    # skip calls as they target functions, not basic blocks\n                    if self._target_desc.is_call(terminator):\n                        continue\n\n                    for op in terminator.operands:\n                        if isinstance(op, LabelOp):\n                            asm_parser_assert(op.value in bb_names, -1, \"Unknown label \" + op.value)\n                            successor = bb_names[op.value]\n                            bb.successors.append(successor)\n\n            # last BB always falls through to the exit\n            func[-1].successors.append(func.get_exit_bb())\n\n    def _handle_empty_test_case(self, test_case: TestCaseProgram) -> None:\n        main = Function(\".function_0\", test_case.find_section(name=\"main\"))\n        test_case[0].append(main)\n\n        bb = BasicBlock(\".bb_0\", main)\n        main.append(bb)\n\n        instr = Instruction(\"nop\", \"BASE-NOP\")\n        bb.insert_after(bb.get_last(), instr)\n        bb.successors.append(main.get_exit_bb())\n\n    # ----------------------------------------------------------------------------------------------\n    # Private: Correctness Checks\n    def _check_test_case_correctness(self, test_case: TestCaseProgram) -> None:\n        \"\"\" Check that the TestCaseCode object created from the assembly file is correct \"\"\"\n        self._check_landing_sites(test_case)\n        self._check_fault_handler(test_case)\n        self._check_set_data_permission(test_case)\n\n    def _check_landing_sites(self, test_case: TestCaseProgram) -> None:\n        \"\"\" check that all actor switch macros have landing sites \"\"\"\n        # pylint: disable=too-many-branches\n        # NOTE: there are many checks to be performed on the set_* family of macros, so\n        # having many branches is expected\n\n        switch_labels = [\".set_k2u_target\", \".set_u2k_target\", \".set_h2g_target\", \".set_g2h_target\"]\n        switches = []\n        for func in test_case.iter_functions():\n            for bb in func:\n                for inst in bb:\n                    if inst.name == \"macro\" and inst.operands[0].value in switch_labels:\n                        switches.append(inst)\n\n        for switch in switches:\n            target = switch.operands[1].value.split(\".\")[2]\n            for func in test_case.iter_functions():\n                stripped_name = func.name[1:]\n                if stripped_name == target:\n                    target_function = func\n                    break\n            else:\n                raise AsmParserError(-1, f\"Macro {switch} targets a non-existing function\")\n\n            first_bb = target_function.get_first_bb()\n            assert first_bb is not None, f\"Macro {switch} targets a function without basic blocks\"\n            first_node = first_bb.get_first()\n            assert first_node is not None, f\"Macro {switch} targets a basic block without a landing\"\n            if not first_node.instruction.operands or \\\n               \"landing\" not in first_node.instruction.operands[0].value:\n                raise AsmParserError(-1, f\"{switch} does not target a landing site macro\")\n            target_name = first_node.instruction.operands[0].value\n\n            if switch.operands[0].value == \".set_k2u_target\" and target_name != \".landing_k2u\":\n                raise AsmParserError(switch.line_num(), f\"{switch} does not target landing_k2u\")\n            if switch.operands[0].value == \".set_u2k_target\" and target_name != \".landing_u2k\":\n                raise AsmParserError(switch.line_num(), f\"{switch} does not target landing_u2k\")\n            if switch.operands[0].value == \".set_h2g_target\" and target_name != \".landing_h2g\":\n                raise AsmParserError(switch.line_num(), f\"{switch} does not target landing_h2g\")\n            if switch.operands[0].value == \".set_g2h_target\" and target_name != \".landing_g2h\":\n                raise AsmParserError(switch.line_num(), f\"{switch} does not target landing_g2h\")\n\n    def _check_fault_handler(self, test_case: TestCaseProgram) -> None:\n        \"\"\" check that there is at most one fault handler \"\"\"\n        n_fault_handlers = 0\n        for func in test_case.iter_functions():\n            for bb in func:\n                for inst in bb:\n                    if inst.name == \"macro\" and inst.operands[0].value == \".fault_handler\":\n                        n_fault_handlers += 1\n                    if n_fault_handlers > 1:\n                        raise AsmParserError(inst.line_num(), \"Found more than one fault handler\")\n\n    def _check_set_data_permission(self, test_case: TestCaseProgram) -> None:\n        \"\"\" check that PT modification happens only in kernel host mode \"\"\"\n        for sec in test_case:\n            owner = sec.owner\n            for func in sec:\n                for bb in func:\n                    for inst in bb:\n                        if inst.name == \"macro\" \\\n                           and inst.operands[0].value == \".set_data_permissions\":\n                            asm_parser_assert(\n                                owner.privilege_level == ActorPL.KERNEL\n                                and owner.mode == ActorMode.HOST, inst.line_num(),\n                                \"PT modification is allowed only in kernel host mode\")\n"
  },
  {
    "path": "rvzr/cli.py",
    "content": "\"\"\"\nFile: Function definitions for using Revizor as command-line tool\n(Note: the actual CLI is accessed via revizor.py)\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n\nimport os\nimport sys\nfrom typing import TYPE_CHECKING, Any\nfrom argparse import ArgumentParser, ArgumentTypeError, ArgumentDefaultsHelpFormatter\n\nimport unicorn\n\nfrom .factory import get_minimizer, get_fuzzer, get_downloader\nfrom .config import CONF\nfrom .logs import update_logging_after_config_change\n\nif TYPE_CHECKING:\n    from .fuzzer import FuzzingMode\n\n\ndef _arg2bool(arg: Any) -> bool:\n    if isinstance(arg, bool):\n        return arg\n    if arg.lower() in ('yes', 'true', 't', 'y', '1'):\n        return True\n    if arg.lower() in ('no', 'false', 'f', 'n', '0'):\n        return False\n    raise ArgumentTypeError('Boolean value expected.')\n\n\ndef _parse_args() -> Any:  # pylint: disable=r0915\n    parser = ArgumentParser(add_help=False)\n    subparsers = parser.add_subparsers(dest='subparser_name')\n    subparsers.required = True\n\n    # ==============================================================================================\n    # Common arguments\n    common_parser = ArgumentParser(add_help=False)\n    common_parser.add_argument(\n        \"-c\",\n        \"--config\",\n        type=str,\n        required=False,\n        help=\"Path to the configuration file (YAML) that will be used during fuzzing.\",\n    )\n    common_parser.add_argument(\n        \"-I\",\n        \"--include-dir\",\n        type=str,\n        default=\".\",\n        required=False,\n        help=\"Path to the directory containing configuration files that included by the main \"\n        \" configuration file (received via --config).\",\n    )\n    common_parser.add_argument(\n        \"-s\",\n        \"--instruction-set\",\n        type=str,\n        required=True,\n        help=\"Path to the instruction set specification (JSON) file.\",\n    )\n\n    # ==============================================================================================\n    # Fuzzing\n    parser_fuzz = subparsers.add_parser(\n        'fuzz',\n        add_help=True,\n        parents=[common_parser],\n        formatter_class=ArgumentDefaultsHelpFormatter)\n    parser_fuzz.add_argument(\n        \"-n\",\n        \"--num-test-cases\",\n        type=int,\n        default=1,\n        help=\"Number of test cases.\",\n    )\n    parser_fuzz.add_argument(\n        \"-i\",\n        \"--num-inputs\",\n        type=int,\n        default=100,\n        help=\"Number of inputs per test case.\",\n    )\n    parser_fuzz.add_argument(\n        '-w',\n        '--working-directory',\n        type=str,\n        default='.',\n    )\n    parser_fuzz.add_argument(\n        '-t',\n        '--testcase',\n        type=str,\n        default=None,\n        help=\"Use an existing test case [DEPRECATED - see reproduce]\")\n    parser_fuzz.add_argument(\n        '--timeout',\n        type=int,\n        default=0,\n        help=\"Run fuzzing with a time limit [seconds]. No timeout when set to zero.\")\n    parser_fuzz.add_argument(\n        '--nonstop', action='store_true', help=\"Don't stop after detecting an unexpected result\")\n    parser_fuzz.add_argument(\n        '--save-violations',\n        type=_arg2bool,\n        default=True,\n        help=\"If set, store all detected violations in working directory.\",\n    )\n\n    # ==============================================================================================\n    # Template-based fuzzing\n    parser_tfuzz = subparsers.add_parser(\n        'tfuzz',\n        add_help=True,\n        parents=[common_parser],\n        formatter_class=ArgumentDefaultsHelpFormatter)\n    parser_tfuzz.add_argument(\n        \"-n\",\n        \"--num-test-cases\",\n        type=int,\n        default=1,\n        help=\"Number of test cases.\",\n    )\n    parser_tfuzz.add_argument(\n        \"-i\",\n        \"--num-inputs\",\n        type=int,\n        default=100,\n        help=\"Number of inputs per test case.\",\n    )\n    parser_tfuzz.add_argument(\n        '-w',\n        '--working-directory',\n        type=str,\n        default='',\n    )\n    parser_tfuzz.add_argument(\n        '-t',\n        '--template',\n        type=str,\n        required=True,\n        help=\"The template to use for generating test cases\")\n    parser_tfuzz.add_argument(\n        '--timeout',\n        type=int,\n        default=0,\n        help=\"Run fuzzing with a time limit [seconds]. No timeout when set to zero.\")\n    parser_tfuzz.add_argument(\n        '--nonstop', action='store_true', help=\"Don't stop after detecting an unexpected result\")\n    parser_tfuzz.add_argument(\n        '--save-violations',\n        type=_arg2bool,\n        default=True,\n        help=\"If set, store all detected violations in working directory.\",\n    )\n\n    # ==============================================================================================\n    # Standalone interface to trace analysis\n    parser_analyser = subparsers.add_parser(\n        'analyse',\n        add_help=True,\n        parents=[common_parser],\n        formatter_class=ArgumentDefaultsHelpFormatter)\n    parser_analyser.add_argument(\n        '--ctraces',\n        type=str,\n        required=True,\n    )\n    parser_analyser.add_argument(\n        '--htraces',\n        type=str,\n        required=True,\n    )\n\n    # ==============================================================================================\n    # Reproducing violation\n    parser_reproduce = subparsers.add_parser(\n        'reproduce',\n        add_help=True,\n        parents=[common_parser],\n        formatter_class=ArgumentDefaultsHelpFormatter)\n    parser_reproduce.add_argument(\n        '-t',\n        '--testcase',\n        type=str,\n        default=None,\n        required=True,\n        help=\"Path to the test case\",\n    )\n    parser_reproduce.add_argument(\n        '-i',\n        '--inputs',\n        type=str,\n        nargs='*',\n        default=None,\n        help=\"Path to the directory with inputs\")\n    parser_reproduce.add_argument(\n        \"-n\",\n        \"--num-inputs\",\n        type=int,\n        default=100,\n        help=\"Number of inputs per test case. [IGNORED if --inputs is set]\",\n    )\n\n    # ==============================================================================================\n    # Postprocessing interface\n    parser_mini = subparsers.add_parser(\n        'minimize',\n        add_help=True,\n        parents=[common_parser],\n        help=\"Minimize a test case by executing a series of minimization passes. \"\n        \"The set of passes is controlled via CLI arguments.\",\n        formatter_class=ArgumentDefaultsHelpFormatter)\n    parser_mini.add_argument(\n        '--testcase',\n        '-t',\n        type=str,\n        required=True,\n        help=\"Path to the test case program that needs to be minimized.\",\n    )\n    parser_mini.add_argument(\n        \"-i\",\n        \"--num-inputs\",\n        type=int,\n        required=True,\n        help=\"Number of inputs to the program that will be used during minimization.\",\n    )\n    parser_mini.add_argument(\n        '--testcase-outfile',\n        '-o',\n        type=str,\n        required=True,\n        help=\"Output path for the minimized test case program.\",\n    )\n    parser_mini.add_argument(\n        '--input-outdir',\n        type=str,\n        default=None,\n        help=\"Output directory for storing minimized inputs.\",\n    )\n    parser_mini.add_argument(\n        '--num-attempts',\n        type=int,\n        default=1,\n        help=\"Number of attempts to minimize the test case.\",\n    )\n    parser_mini.add_argument(\n        '--enable-instruction-pass',\n        type=_arg2bool,\n        default=True,\n        help=\"Enable the instruction minimization pass that iteratively removes \"\n        \"instructions while preserving the violation.\",\n    )\n    parser_mini.add_argument(\n        '--enable-simplification-pass',\n        type=_arg2bool,\n        default=False,\n        help=\"Enable the instruction simplification pass that replaces complex \"\n        \"instructions with simpler ones while preserving the violation.\",\n    )\n    parser_mini.add_argument(\n        '--enable-nop-pass',\n        type=_arg2bool,\n        default=False,\n        help=\"Enable the NOP replacement pass that replaces instructions with NOPs \"\n        \"while preserving the violation.\",\n    )\n    parser_mini.add_argument(\n        '--enable-constant-pass',\n        type=_arg2bool,\n        default=False,\n        help=\"Enable the constant simplification pass that replaces constants with 0s \"\n        \"while preserving the violation.\",\n    )\n    parser_mini.add_argument(\n        '--enable-mask-pass',\n        type=_arg2bool,\n        default=False,\n        help=\"Enable the mask simplification pass that reduces the size of instrumentation \"\n        \"masks while preserving the violation.\",\n    )\n    parser_mini.add_argument(\n        '--enable-label-pass',\n        type=_arg2bool,\n        default=True,\n        help=\"Enable the label removal pass that removes unused labels from the assembly file.\",\n    )\n    parser_mini.add_argument(\n        '--enable-fence-pass',\n        type=_arg2bool,\n        default=False,\n        help=\"Enable the fence insertion pass that adds LFENCEs after instructions \"\n        \"while preserving the violation.\",\n    )\n    parser_mini.add_argument(\n        \"--enable-input-seq-pass\",\n        type=_arg2bool,\n        default=False,\n        help=\"Enable the input sequence minimization pass that removes inputs from \"\n        \"the original generated sequence while preserving the violation.\",\n    )\n    parser_mini.add_argument(\n        \"--enable-input-diff-pass\",\n        type=_arg2bool,\n        default=False,\n        help=\"Enable the violating input difference minimization pass that removes \"\n        \"inputs that do not contribute to the violation.\",\n    )\n    parser_mini.add_argument(\n        \"--enable-comment-pass\",\n        type=_arg2bool,\n        default=False,\n        help=\"Enable the violation comment pass that adds comments to the assembly file \"\n        \"with details about the violation.\",\n    )\n\n    # ==============================================================================================\n    # Standalone interface to test case generation\n    parser_generator = subparsers.add_parser(\n        'generate',\n        add_help=True,\n        parents=[common_parser],\n        formatter_class=ArgumentDefaultsHelpFormatter)\n    parser_generator.add_argument(\n        \"-r\",\n        \"--seed\",\n        type=int,\n        default=0,\n        help=\"Add seed to generate test case.\",\n    )\n    parser_generator.add_argument(\n        \"-n\",\n        \"--num-test-cases\",\n        type=int,\n        default=5,\n        help=\"Number of test cases.\",\n    )\n    parser_generator.add_argument(\n        \"-i\",\n        \"--num-inputs\",\n        type=int,\n        default=100,\n        help=\"Number of inputs per test case.\",\n    )\n    parser_generator.add_argument(\n        '-w',\n        '--working-directory',\n        type=str,\n        default='',\n    )\n    parser_generator.add_argument(\n        '--permit-overwrite',\n        action='store_true',\n    )\n\n    # ==============================================================================================\n    # Loading of ISA specs\n    parser_get_isa = subparsers.add_parser('download_spec', add_help=True)\n    parser_get_isa.add_argument(\"-a\", \"--architecture\", type=str, required=True)\n    parser_get_isa.add_argument(\n        '--outfile',\n        '-o',\n        type=str,\n        required=True,\n    )\n    parser_get_isa.add_argument(\"--extensions\", nargs=\"*\", default=[])\n    return parser.parse_args()\n\n\ndef main() -> int:  # pylint: disable=r0911,r0912,r0915  # this function is necessarily complex\n    \"\"\"\n    Parse command-line arguments and launch the fuzzer in the requested mode.\n    \"\"\"\n    args = _parse_args()\n\n    # Update configuration\n    if getattr(args, 'config', None):\n        CONF.load(args.config, args.include_dir)\n    if getattr(args, 'testcase', None):\n        CONF.disable_generation()\n    update_logging_after_config_change()\n\n    # Check if the file and directory arguments are valid\n    if getattr(args, 'testcase', None) and not os.path.isfile(args.testcase):\n        print(\"[ERROR]\", f\"The test case file `{args.testcase}` does not exist\")\n        return 1\n    if getattr(args, 'working_directory', None) and not os.path.isdir(args.working_directory):\n        print(\"[ERROR]\", f\"The working directory `{args.working_directory}` does not exist\")\n        return 1\n    if (getattr(args, 'enable_input_seq_pass', None)\n        or getattr(args, 'enable_input_diff_pass', None)) \\\n            and not args.input_outdir:\n        print(\n            \"[ERROR]\", \"Passes --enable-input-seq-pass and --enable-input-diff-pass \"\n            \"require flag --input-outdir to be set.\")\n        return 1\n\n    # Enforce the Unicorn version: New versions of Unicorn have a bug that causes false positives\n    # in the fuzzer. This is a temporary workaround until the bug is fixed.\n    if unicorn.__version__ != '1.0.3' and CONF.instruction_set == 'x86-64':  # type: ignore\n        print(\n            \"[ERROR]\", \"The fuzzer requires Unicorn version 1.0.3. Please install it using \"\n            \"`pip install unicorn==1.0.3`.\")\n        return 1\n\n    # Fuzzing\n    if args.subparser_name in ('fuzz', 'tfuzz'):\n        testcase = args.testcase if args.subparser_name == 'fuzz' else args.template\n        fuzzer = get_fuzzer(args.instruction_set, args.working_directory, testcase, None)\n        type_: FuzzingMode\n        if args.subparser_name == 'tfuzz':\n            type_ = 'template'\n        elif testcase:\n            type_ = 'asm'\n        else:\n            type_ = 'random'\n        exit_code = fuzzer.start(\n            args.num_test_cases,\n            args.num_inputs,\n            args.timeout,\n            args.nonstop,\n            args.save_violations,\n            type_=type_)\n        return exit_code\n\n    # Reproducing a violation\n    if args.subparser_name == 'reproduce':\n        fuzzer = get_fuzzer(args.instruction_set, \"\", args.testcase, args.inputs)\n        exit_code = fuzzer.start(1, args.num_inputs, 0, False, False, type_='asm')\n        return exit_code\n\n    # Stand-alone generation\n    if args.subparser_name == \"generate\":\n        fuzzer = get_fuzzer(args.instruction_set, args.working_directory, \"\", None)\n        fuzzer.standalone_generate(args.seed, args.num_test_cases, args.num_inputs,\n                                   args.permit_overwrite)\n        return 0\n\n    # Trace analysis\n    if args.subparser_name == 'analyse':\n        fuzzer = get_fuzzer(args.instruction_set, \"\", \"\", None)\n        fuzzer.standalone_analyse(args.ctraces, args.htraces)\n        return 0\n\n    # Test case minimization\n    if args.subparser_name == \"minimize\":\n        if (args.enable_input_seq_pass or args.enable_input_diff_pass) and not args.input_outdir:\n            raise SystemExit(\"ERROR: Passes --enable-input-seq-pass and --enable-input-diff-pass \\n\"\n                             \"require flag --input_outdir to be set.\")\n\n        fuzzer = get_fuzzer(args.instruction_set, \"\", args.testcase, None)\n        minimizer = get_minimizer(fuzzer, args.instruction_set)\n        minimizer.run(\n            test_case_asm=args.testcase,\n            n_inputs=args.num_inputs,\n            test_case_outfile=args.testcase_outfile,\n            input_outdir=args.input_outdir,\n            n_attempts=args.num_attempts,\n            enable_instruction_pass=args.enable_instruction_pass,\n            enable_simplification_pass=args.enable_simplification_pass,\n            enable_nop_pass=args.enable_nop_pass,\n            enable_constant_pass=args.enable_constant_pass,\n            enable_mask_pass=args.enable_mask_pass,\n            enable_label_pass=args.enable_label_pass,\n            enable_fence_pass=args.enable_fence_pass,\n            enable_input_seq_pass=args.enable_input_seq_pass,\n            enable_input_diff_pass=args.enable_input_diff_pass,\n            enable_comment_pass=args.enable_comment_pass,\n        )\n        return 0\n\n    if args.subparser_name == \"download_spec\":\n        get_downloader(args.architecture, args.extensions, args.outfile).run()  # type: ignore\n        return 0\n\n    print(\"[ERROR]\", \"Invalid subcommand\")\n    return 1\n\n\nif __name__ == '__main__':\n    print(\"[ERROR]\", \"This file is not meant to be run directly. Use `revizor.py` instead.\")\n    sys.exit(1)\n"
  },
  {
    "path": "rvzr/code_generator.py",
    "content": "\"\"\"\nFile: Test Case Generation\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nimport random\nimport re\nfrom typing import TYPE_CHECKING, List, Tuple, Optional, Final, Callable, Dict, TextIO, Iterable\nfrom subprocess import CalledProcessError, run\nfrom copy import deepcopy\nfrom abc import ABC, abstractmethod\n\nfrom .tc_components.actor import Actor\nfrom .tc_components.instruction import Instruction, RegisterOp, FlagsOp, MemoryOp, \\\n    ImmediateOp, AgenOp, LabelOp, CondOp, AnyOperand\nfrom .tc_components.test_case_code import TestCaseProgram, Function, BasicBlock, CodeSection, \\\n    TC_EXIT_LABEL\nfrom .instruction_spec import OT\nfrom .logs import GeneratorLogger, error, inform\nfrom .config import CONF, ActorsConf\n\nif TYPE_CHECKING:\n    from .tc_components.test_case_code import InstructionNode\n    from .target_desc import TargetDesc\n    from .asm_parser import AsmParser\n    from .instruction_spec import InstructionSpec, OperandSpec\n    from .isa_spec import InstructionSet\n    from .elf_parser import ELFParser\n\n\n# ==================================================================================================\n# Interfaces and common functionality of ISA-specific service classes\n# ==================================================================================================\nclass Pass(ABC):\n    \"\"\"\n    Interface to an instrumentation pass that modifies a generated test case\n    \"\"\"\n\n    @abstractmethod\n    def run_on_test_case(self, test_case: TestCaseProgram) -> None:\n        \"\"\"\n        Run the pass on all instructions in a given test case\n        \"\"\"\n\n\nclass Printer(ABC):\n    \"\"\"\n    Interface to an ISA-specific assembly printer; that is, a class that prints\n    a valid assembly representation of a test case\n    \"\"\"\n\n    prologue_template: List[str]\n    \"\"\" List of lines that must be printed at the beginning of the assembly file \"\"\"\n\n    epilogue_template: List[str]\n    \"\"\" List of lines that must be printed at the end of the assembly file \"\"\"\n\n    def __init__(self, target_desc: TargetDesc) -> None:\n        self.target_desc = target_desc\n\n    def print(self, test_case: TestCaseProgram) -> None:\n        \"\"\"\n        Print the assembly representation of a test case to a file associated with the test case\n        (i.e., test_case.asm_file)\n\n        :param test_case: The test case to print\n        \"\"\"\n        with open(test_case.asm_path(), \"w\") as f:\n            for line in self.prologue_template:\n                f.write(line)\n\n            for section in test_case:\n                self._print_section(section, f)\n\n            for line in self.epilogue_template:\n                f.write(line)\n\n    def _print_section(self, sec: CodeSection, file_: TextIO) -> None:\n        file_.write(f\".section .data.{sec.name}\\n\")\n        for func in sec:\n            self._print_function(func, file_)\n\n    def _print_function(self, func: Function, file_: TextIO) -> None:\n        file_.write(f\"{func.name}:\\n\")\n        for bb in func:\n            self._print_basic_block(bb, file_)\n\n        self._print_basic_block(func.get_exit_bb(), file_)\n\n    def _print_basic_block(self, bb: BasicBlock, file_: TextIO) -> None:\n        file_.write(f\"{bb.name.lower()}:\\n\")\n        for inst in bb:\n            file_.write(self._instruction_to_str(inst) + \"\\n\")\n        for inst in bb.terminators:\n            file_.write(self._instruction_to_str(inst) + \"\\n\")\n\n    @abstractmethod\n    def _instruction_to_str(self, inst: Instruction) -> str:\n        \"\"\" Convert an instruction object to its assembly representation \"\"\"\n\n    @abstractmethod\n    def _operand_to_str(self, op: AnyOperand) -> str:\n        \"\"\" Convert an operand object to its assembly representation \"\"\"\n\n    @abstractmethod\n    def _macro_to_str(self, inst: Instruction) -> str:\n        \"\"\" Convert a macro instruction object to its assembly representation \"\"\"\n\n\n# ==================================================================================================\n# ISA-independent Code Generator\n# ==================================================================================================\nclass CodeGenerator(ABC):\n    \"\"\"\n    ISA-independent implementation of the class responsible for generating test case code.\n\n    Some of the methods are abstract and must be implemented by the ISA-specific subclasses.\n    \"\"\"\n\n    _instruction_set: InstructionSet  # Specification of the tested instruction set\n    _target_desc: TargetDesc  # Description of the tested architecture\n    _asm_parser: AsmParser  # Parser for assembly files\n    _elf_parser: ELFParser  # Parser for ELF files\n    _passes: List[Pass]  # List of passes to run on the generated test case; set by subclasses\n    _printer: Printer  # Printer for the generated test case; set by subclasses\n\n    _state: int = 0  # Current seed value\n    _cached_template: Optional[TestCaseProgram] = None  # Parsed template assembly file\n\n    _function_generator: Final[_FunctionGenerator]\n    _instruction_generator: Final[_InstructionGenerator]\n\n    __log: Final[GeneratorLogger]\n\n    def __init__(self, seed: int, instruction_set: InstructionSet, target_desc: TargetDesc,\n                 asm_parser: AsmParser, elf_parser: ELFParser) -> None:\n        self._instruction_set = instruction_set\n        self._target_desc = target_desc\n        self._asm_parser = asm_parser\n        self._elf_parser = elf_parser\n        self._set_seed(seed)\n\n        self.__log = GeneratorLogger()\n        self.__log.dbg_dump_instruction_pool(instruction_set.instructions)\n        self._passes = []\n\n        self._function_generator = _FunctionGenerator(self._target_desc, instruction_set)\n        self._instruction_generator = _InstructionGenerator(self._target_desc)\n\n    # ----------------------------------------------------------------------------------------------\n    # Public Interface\n    def create_test_case(self, asm_file: str, disable_assembler: bool = False) -> TestCaseProgram:\n        \"\"\"\n        Generate a random test case, write its assembly code to a file,\n        and assemble it into an object (unless disabled).\n        :param asm_file: the path to the output file\n        :param disable_assembler: if True, the function will not assemble the test case\n        :return: the generated test case object\n        \"\"\"\n        if not asm_file:\n            asm_file = 'generated.asm'\n        test_case = TestCaseProgram(asm_file, seed=self._state)\n\n        # create actors and their corresponding sections\n        actors_config: ActorsConf = CONF.get_actors_conf()\n        if len(actors_config) != 1:\n            error(\"Generation of test cases with multiple actors is not yet supported\")\n        self.generate_actors_with_sections(test_case, actors_config)\n\n        # create empty main function and fill it with random instructions\n        main_section = test_case[0]\n        default_actor = main_section.owner\n        assert default_actor.is_main\n        main_func = self._function_generator.generate_empty(\".function_0\", main_section)\n        self._function_generator.fill_function(main_func)\n\n        # add it to the test case, in the first section\n        test_case[0].append(main_func)\n\n        # process the test case\n        for p in self._passes:\n            p.run_on_test_case(test_case)\n\n        # add symbols to test case\n        self._add_required_symbols(test_case)\n\n        self._printer.print(test_case)\n\n        if disable_assembler:\n            return test_case\n\n        test_case.assign_obj(asm_file[:-4] + \".o\")\n        assemble(test_case)\n        self._elf_parser.populate_elf_data(test_case.get_obj(), test_case)\n\n        self._update_state()\n        return test_case\n\n    def create_test_case_from_template(self, template_file: str) -> TestCaseProgram:\n        \"\"\"\n        Generate a test case based on a template by expanding RANDOM_* macros.\n        Run instrumentation _passes and print the result into a file\n\n        :param template_file: The path to the template file\n        :return: The generated test case object\n        :raises FileNotFoundError: if the template file does not exist\n        :raises CalledProcessError: if the assembler fails to assemble the test case\n        \"\"\"\n        # create a TestCaseProgram object from the template file\n        if self._cached_template:\n            test_case = deepcopy(self._cached_template)\n            test_case.generator_seed = self._state\n        else:\n            test_case = self._asm_parser.parse_file(\n                template_file, self, self._elf_parser, is_template=True)\n            test_case.generator_seed = self._state\n            self._cached_template = deepcopy(test_case)\n\n        # Label all instructions from the template as such\n        for func in test_case.iter_functions():\n            for bb in func:\n                for instr in bb:\n                    instr.is_from_template = True\n\n        # Expand the template\n        self._set_seed(self._state)  # reset the seed in case it was updated by other modules\n        self._expand_template(test_case, CONF.get_actors_conf())\n        for p in self._passes:\n            p.run_on_test_case(test_case)\n\n        # Print into assembly and assemble into an object file\n        asm_file = 'generated.asm'\n        test_case.reassign_asm_file(asm_file)\n        self._printer.print(test_case)\n\n        test_case.assign_obj(asm_file[:-4] + \".o\")\n        assemble(test_case)\n        self._elf_parser.populate_elf_data(test_case.get_obj(), test_case)\n\n        self._update_state()\n        return test_case\n\n    def generate_actors_with_sections(self, test_case: TestCaseProgram,\n                                      actors_dict: ActorsConf) -> None:\n        \"\"\"\n        Stand-alone interface to create actors for the given test case and\n        populate them with the corresponding sections.\n\n        NOTE: This method leaves the sections *empty*; i.e., it does not populate the test case\n        with functions, basic blocks, and instructions.\n\n        :param test_case: The test case to which the actors will be added\n        :param actors_dict: The configuration of the actors\n        :return: None\n        \"\"\"\n        for name, actor_dict in actors_dict.items():\n            actor = Actor.from_dict(actor_dict, self._target_desc)\n\n            # add the actor to the test case\n            if name == \"main\":  # the main actor is created by default; overwrite it\n                test_case.add_actor_with_section(actor, allow_overwrite=True)\n            else:  # all other actors should not exist yet\n                test_case.add_actor_with_section(actor)\n\n    def generate_instruction(self,\n                             spec: InstructionSpec,\n                             is_instrumentation: Optional[bool] = None) -> Instruction:\n        \"\"\"\n        Stand-alone interface to generate a random instruction based on the specification.\n\n        :param spec: The specification of the instruction\n        :return: The generated instruction\n        \"\"\"\n        # To correctly inherit the default value of is_instrumentation from the instruction\n        # generator, we have two separate calls\n        if is_instrumentation is None:\n            return self._instruction_generator.generate(spec)\n        return self._instruction_generator.generate(spec, is_instrumentation)\n\n    # ----------------------------------------------------------------------------------------------\n    # Private: Seed Management\n    def _set_seed(self, seed: int) -> None:\n        \"\"\"\n        Set the seed value used to generate test programs\n        :param seed: The seed value\n        \"\"\"\n        if seed == 0:\n            seed = random.randint(1, 1000000)\n            inform(\"prog_gen\", f\"Setting program_generator_seed to random value: {seed}\")\n        self._state = seed\n        random.seed(self._state)\n\n    def _update_state(self) -> None:\n        self._state += 1\n        random.seed(self._state)\n\n    # ----------------------------------------------------------------------------------------------\n    # Private: Misc.\n    def _add_required_symbols(self, test_case: TestCaseProgram) -> None:\n        # add measurement_start and measurement_end symbols\n        sec_main = test_case[0]\n        assert sec_main.owner.is_main\n        func_main = sec_main[0]\n\n        bb_first = func_main[0]\n        instr = Instruction(\"macro\", category=\"MACRO\") \\\n            .add_op(LabelOp(\".measurement_start\")) \\\n            .add_op(LabelOp(\".noarg\"))\n        bb_first.insert_before(bb_first.get_first(), instr)\n\n        bb_last = func_main.get_exit_bb()\n        instr = Instruction(\"macro\", category=\"MACRO\") \\\n            .add_op(LabelOp(\".measurement_end\")) \\\n            .add_op(LabelOp(\".noarg\"))\n        bb_last.insert_after(bb_last.get_last(), instr)\n\n    def _expand_template(self, test_case: TestCaseProgram, actors_config: ActorsConf) -> None:\n        nodes_to_expand: List[Tuple[InstructionNode, str]] = []\n\n        # find all instances of .macro.random_instructions\n        for bb in test_case.iter_basic_blocks():\n            for node in bb.iter_nodes():\n                inst = node.instruction\n                if inst.name == \"macro\" and inst.operands[0].value == \".random_instructions\":\n                    nodes_to_expand.append((node, bb.get_owner().name))\n\n        # replace all instances of .macro.random_instructions with random instructions\n        for node, actor_name in nodes_to_expand:\n            inst = node.instruction\n            bb = node.parent\n            operands = inst.operands[1].value.split(\".\")\n            assert len(operands) >= 3 and len(operands) <= 5\n            n_instr = int(operands[1])\n            n_mem = int(operands[2])\n\n            # determine the instruction set for this actor\n            block = actors_config[actor_name][\"instruction_blocklist\"]\n            non_memory_access_instructions = \\\n                [i for i in self._instruction_set.non_memory_access_specs if i.name not in block]\n            store_instructions = \\\n                [i for i in self._instruction_set.store_instructions if i.name not in block]\n            load_instruction = \\\n                [i for i in self._instruction_set.load_instruction if i.name not in block]\n\n            # replace the macro with random instructions\n            bb.delete(node)\n            for _ in range(n_instr):\n                inst = self._instruction_generator.generate_from_random_spec(\n                    non_memory_access_instructions=non_memory_access_instructions,\n                    store_instructions=store_instructions,\n                    load_instructions=load_instruction,\n                    memory_access_probability=n_mem / n_instr)\n                if node.previous:\n                    bb.insert_after(node.previous, inst)\n                else:\n                    bb.insert_before(bb.get_first(), inst)\n\n\ndef assemble(test_case: TestCaseProgram) -> None:\n    \"\"\"\n    Assemble an assembly file into an object file and creates a stripped binary\n    :param test_case: The test case to be assembled\n    \"\"\"\n\n    def pretty_error_msg(error_msg: str) -> str:\n        with open(asm_file, \"r\") as f:\n            lines = f.read().split(\"\\n\")\n\n        msg = \"Error appeared while assembling the test case:\\n\"\n        for line in error_msg.split(\"\\n\"):\n            line = line.removeprefix(asm_file + \":\")\n            line_num_str = re.search(r\"(\\d+):\", line)\n            if not line_num_str:\n                msg += line\n            else:\n                parsed = lines[int(line_num_str.group(1)) - 1]\n                msg += f\"\\n  Line {line}\\n    (the line was parsed as {parsed})\"\n        return msg\n\n    asm_file = test_case.asm_path()\n    obj_container = test_case.get_obj()\n    obj_file = obj_container.obj_path\n\n    try:\n        out = run(f\"as {asm_file} -o {obj_file}\", shell=True, check=True, capture_output=True)\n    except CalledProcessError as e:\n        error_msg = e.stderr.decode()\n        if \"Assembler messages:\" in error_msg:\n            print(pretty_error_msg(error_msg))\n        else:\n            print(error_msg)\n        exit(1)\n    finally:\n        pass\n        # run(f\"rm {patched_asm_file}\", shell=True, check=True)\n\n    output = out.stderr.decode()\n    if \"Assembler messages:\" in output:\n        print(\"WARNING: [generator]\" + pretty_error_msg(output))\n\n    obj_container.mark_as_assembled()\n\n\n# ==================================================================================================\n# Private Service Classes\n# ==================================================================================================\nclass _FunctionGenerator:\n    \"\"\" Class responsible for generating random functions \"\"\"\n\n    _instruction_generator: _InstructionGenerator\n    _isa_spec: InstructionSet\n\n    def __init__(self, target_desc: TargetDesc, isa_spec: InstructionSet) -> None:\n        self._instruction_generator = _InstructionGenerator(target_desc)\n        self._isa_spec = isa_spec\n\n    def generate_empty(self, label: str, parent: CodeSection) -> Function:\n        \"\"\" Generates an empty function with a random DAG of basic blocks \"\"\"\n        func = Function(label, parent)\n\n        # Define the maximum allowed number of successors for any BB\n        if self._isa_spec.has_conditional_branch:\n            max_successors = CONF.max_successors_per_bb if CONF.max_successors_per_bb < 2 else 2\n            min_successors = CONF.min_successors_per_bb if CONF.min_successors_per_bb < 2 else 2\n            assert min_successors <= max_successors, \"min_successors_per_bb > max_successors_per_bb\"\n        else:\n            max_successors = 1\n            min_successors = 1\n\n        # Create basic blocks\n        if CONF.min_bb_per_function == CONF.max_bb_per_function:\n            node_count = CONF.min_bb_per_function\n        else:\n            node_count = random.randint(CONF.min_bb_per_function, CONF.max_bb_per_function)\n        func_name = label.removeprefix(\".function_\")\n        nodes = [BasicBlock(f\".bb_{func_name}.{i}\", func) for i in range(node_count)]\n\n        # Connect BBs into a graph\n        for i in range(node_count):\n            current_bb = nodes[i]\n\n            # the last node has only one successor - exit\n            if i == node_count - 1:\n                current_bb.successors = [func.get_exit_bb()]\n                break\n\n            # the rest of the node have a random number of successors\n            successor_count = random.randint(min_successors, max_successors)\n            if successor_count + i > node_count:\n                # the number is adjusted to the position when close to the end\n                successor_count = node_count - i\n\n            # one of the targets (the first successor) is always the next node - to avoid dead code\n            current_bb.successors.append(nodes[i + 1])\n\n            # all other successors are random, selected from next nodes\n            options = nodes[i + 2:]\n            options.append(func.get_exit_bb())\n            for _ in range(1, successor_count):\n                target = random.choice(options)\n                options.remove(target)\n                current_bb.successors.append(target)\n\n        # Function returns are not yet supported\n        # hence all functions end with an unconditional jump to the exit\n        inst = self._instruction_generator.generate(self._isa_spec.get_unconditional_jump_spec())\n        assert isinstance(inst.operands[0], LabelOp)\n        inst.operands[0].value = TC_EXIT_LABEL\n        func.get_exit_bb().terminators = [inst]\n\n        # Finalize the function\n        func.extend(nodes)\n        return func\n\n    def fill_function(self, func: Function) -> None:\n        \"\"\"\n        Fill an (assumed empty) function with random instructions\n        :param func: the function to fill\n        :return: None\n        :raises AssertionError: if the function is not empty\n        :raises NotImplementedError: if one of the basic blocks has more than two successors\n        \"\"\"\n        self._add_terminators_in_function(func)\n        self._add_instructions_in_function(func)\n\n    def _add_terminators_in_function(self, func: Function) -> None:\n\n        def add_fallthrough(bb: BasicBlock, destination: BasicBlock) -> None:\n            # create an unconditional branch and add it\n            terminator_spec = self._isa_spec.get_unconditional_jump_spec()\n            terminator = self._instruction_generator.generate(terminator_spec)\n            label = terminator.get_label_operand()\n            assert label is not None\n            label.value = destination.name\n            bb.terminators.append(terminator)\n\n        for bb in func:\n            assert not bb.terminators, \"Basic block already has terminators\"\n            if len(bb.successors) == 0:\n                # Return instruction\n                continue\n\n            if len(bb.successors) == 1:\n                # Unconditional branch\n                dest = bb.successors[0]\n                if dest.is_exit:\n                    # DON'T insert a branch to the exit\n                    # the last basic block always falls through implicitly\n                    continue\n                add_fallthrough(bb, dest)\n                continue\n\n            if len(bb.successors) == 2:\n                # Conditional branch\n                spec = random.choice(self._isa_spec.cond_branches)\n                terminator = self._instruction_generator.generate(spec)\n                label = terminator.get_label_operand()\n                assert label\n                label.value = bb.successors[0].name\n                bb.terminators.append(terminator)\n\n                add_fallthrough(bb, bb.successors[1])\n                continue\n\n            # > 2 successors\n            raise NotImplementedError(\"Indirect jumps/calls are not yet supported\")\n\n    def _add_instructions_in_function(self, func: Function) -> None:\n        \"\"\"\n        Fill the function with random instructions.\n        Ensures that all basic blocks are filled with roughly the same number of instructions\n        :param func: the function to fill\n        :return: None\n        \"\"\"\n        bb_list: List[BasicBlock] = list(func)\n        assert all(len(bb) == 0 for bb in bb_list), \"Basic blocks are not empty\"\n        for _ in range(0, CONF.program_size):\n            bb = random.choice(bb_list)\n            inst = self._instruction_generator.generate_from_random_spec(\n                self._isa_spec.non_memory_access_specs, self._isa_spec.store_instructions,\n                self._isa_spec.load_instruction, CONF.avg_mem_accesses / CONF.program_size)\n            bb.insert_after(bb.get_last(), inst)\n\n\nclass _InstructionGenerator:\n    \"\"\"\n    Class responsible for generating random instructions\n    \"\"\"\n\n    _operand_generator: _OperandGenerator\n\n    def __init__(self, target_desc: TargetDesc) -> None:\n        self._operand_generator = _OperandGenerator(target_desc)\n\n    def generate(self, spec: InstructionSpec, is_instrumentation: bool = False) -> Instruction:\n        \"\"\"\n        Generate a random instruction object based on the specification\n        :param spec: The specification of the instruction\n        :param is_instrumentation: Whether to label the instruction as instrumentation\n        :return: The generated instruction\n        \"\"\"\n\n        # fill up with random operands, following the spec\n        inst = Instruction.from_spec(spec, is_instrumentation=is_instrumentation)\n\n        # generate explicit operands\n        for operand_spec in spec.operands:\n            operand = self._operand_generator.generate(operand_spec, inst)\n            inst.operands.append(operand)\n\n        # generate implicit operands\n        for operand_spec in spec.implicit_operands:\n            operand = self._operand_generator.generate(operand_spec, inst)\n            inst.implicit_operands.append(operand)\n\n        return inst\n\n    def generate_from_random_spec(self,\n                                  non_memory_access_instructions: List[InstructionSpec],\n                                  store_instructions: List[InstructionSpec],\n                                  load_instructions: List[InstructionSpec],\n                                  memory_access_probability: float = 0.0) -> Instruction:\n        \"\"\"\n        Generate an instruction from a randomly-selected specification\n        :param non_memory_access_instructions: The list of available non-memory access instructions\n        :param store_instructions: The list of available store instructions\n        :param load_instructions: The list of available load instructions\n        :return: The generated instruction\n        \"\"\"\n\n        def pick_spec() -> InstructionSpec:\n            # ensure the requested avg. number of mem. accesses\n            search_for_memory_access = random.random() < memory_access_probability\n            if not search_for_memory_access:\n                return random.choice(non_memory_access_instructions)\n\n            if store_instructions:\n                search_for_store = random.random() < 0.5  # 50% probability of stores\n            else:\n                search_for_store = False\n\n            if search_for_store:\n                return random.choice(store_instructions)\n\n            return random.choice(load_instructions)\n\n        spec = pick_spec()\n        return self.generate(spec)\n\n\nclass _OperandGenerator:\n    \"\"\"\n    Class responsible for generating random operands for instructions\n    \"\"\"\n\n    def __init__(self, target_desc: TargetDesc) -> None:\n        self.target_desc = target_desc\n\n    def generate(self, spec: OperandSpec, parent: Instruction) -> AnyOperand:\n        \"\"\"\n        Generate a random operand object based on the specification\n        \"\"\"\n        generators: Dict[OT, Callable[[OperandSpec, Instruction], AnyOperand]] = {\n            OT.REG: self._generate_reg_operand,\n            OT.MEM: self._generate_mem_operand,\n            OT.IMM: self._generate_imm_operand,\n            OT.LABEL: self._generate_label_operand,\n            OT.AGEN: self._generate_agen_operand,\n            OT.FLAGS: self._generate_flags_operand,\n            OT.COND: self._generate_cond_operand,\n        }\n        return generators[spec.type](spec, parent)\n\n    def _generate_reg_operand(self, spec: OperandSpec, _: Instruction) -> RegisterOp:\n        choices = spec.values\n        reg = random.choice(choices)\n        return RegisterOp(reg, spec.width, spec.src, spec.dest)\n\n    def _generate_mem_operand(self, spec: OperandSpec, _: Instruction) -> MemoryOp:\n        if spec.values:\n            address_reg = random.choice(spec.values)\n        else:\n            address_reg = random.choice(self.target_desc.mem_index_registers)\n        return MemoryOp(address_reg, spec.width, spec.src, spec.dest)\n\n    def _generate_imm_operand(self, spec: OperandSpec, inst: Instruction) -> ImmediateOp:\n        # generate bitmask\n        if spec.values and spec.values[0] == \"bitmask\":\n            return self._generate_bitmask_operand(spec, inst)\n\n        # generate from a predefined list\n        if spec.values and \"[\" not in spec.values[0]:\n            options: Iterable[str] | Iterable[int]\n            try:\n                options = [int(v) for v in spec.values]\n            except ValueError:\n                # handle non-digit immediates (e.g., dsb SY in ARM64)\n                options = list(spec.values)\n            value = str(random.choice(options))\n            return ImmediateOp(value, spec.width)\n\n        # generate from a predefined range\n        if spec.values:\n            assert \"[\" in spec.values[0], f\"Invalid IMM spec for instruction: {inst}\"\n            range_ = spec.values[0][1:-1].split(\"-\")\n            if range_[0] == \"\":\n                range_ = range_[1:]\n                range_[0] = \"-\" + range_[0]\n            assert len(range_) == 2\n            value = str(random.randint(int(range_[0]), int(range_[1])))\n            return ImmediateOp(value, spec.width)\n\n        # generate from width\n        if spec.is_signed:\n            range_min = pow(2, spec.width - 1) * -1\n            range_max = pow(2, spec.width - 1) - 1\n        else:\n            range_min = 0\n            range_max = pow(2, spec.width) - 1\n        value = str(random.randint(range_min, range_max))\n        return ImmediateOp(value, spec.width)\n\n    def _generate_bitmask_operand(self, spec: OperandSpec, _: Instruction) -> ImmediateOp:\n        assert CONF.instruction_set == \"arm64\"\n\n        if spec.width == 64:\n            imms_zero_pos = random.randint(1, 6)\n        else:\n            imms_zero_pos = random.randint(1, 5)\n        imms_ones = random.randint(1, 2**imms_zero_pos - 1)\n\n        immr = random.randint(0, spec.width - 1)\n\n        pattern = \"0\" * (2**imms_zero_pos - imms_ones) + \"1\" * imms_ones\n        multiplier = spec.width // (2**imms_zero_pos)\n        value_str = pattern * multiplier\n        value = int(value_str, 2)\n        value = (value >> immr) | (value << (spec.width - immr)) & (2**spec.width - 1)\n\n        if spec.width == 64:\n            value_str = f\"0x{value:016x}\"\n        else:\n            value_str = f\"0x{value:08x}\"\n        return ImmediateOp(value_str, spec.width)\n\n    def _generate_label_operand(self, _: OperandSpec, __: Instruction) -> LabelOp:\n        return LabelOp(\"\")  # the actual label will be set in add_terminators_in_function\n\n    def _generate_agen_operand(self, spec: OperandSpec, __: Instruction) -> AgenOp:\n        n_operands = random.randint(1, 3)\n        reg1 = random.choice(self.target_desc.mem_index_registers)\n        if n_operands == 1:\n            return AgenOp(reg1, spec.width)\n\n        reg2 = random.choice(self.target_desc.mem_index_registers)\n        if n_operands == 2:\n            return AgenOp(reg1 + \" + \" + reg2, spec.width)\n\n        imm = str(random.randint(0, pow(2, 16) - 1))\n        return AgenOp(reg1 + \" + \" + reg2 + \" + \" + imm, spec.width)\n\n    def _generate_flags_operand(self, spec: OperandSpec, parent: Instruction) -> FlagsOp:\n        # pylint: disable=too-many-branches\n        # NOTE: there are many options for COND flags, so many branches are needed\n\n        cond_op = parent.get_cond_operand()\n        if not cond_op:\n            return FlagsOp(spec.values)\n        raise NotImplementedError(\"COND operand is not yet supported\")\n        # pylint: disable=unreachable\n        # NOTE: the code below is temporary disabled\n\n        flag_values = self.target_desc.branch_conditions[cond_op.value]\n        if not spec.values:\n            return FlagsOp(flag_values)\n\n        # combine implicit flags with the condition\n        merged_flags = []\n        for flag_pair in zip(flag_values, spec.values):\n            if \"undef\" in flag_pair:\n                merged_flags.append(\"undef\")\n            elif \"r/w\" in flag_pair:\n                merged_flags.append(\"r/w\")\n            elif \"w\" in flag_pair:\n                if \"r\" in flag_pair:\n                    merged_flags.append(\"r/w\")\n                else:\n                    merged_flags.append(\"w\")\n            elif \"cw\" in flag_pair:\n                if \"r\" in flag_pair:\n                    merged_flags.append(\"r/cw\")\n                else:\n                    merged_flags.append(\"cw\")\n            elif \"r\" in flag_pair:\n                merged_flags.append(\"r\")\n            else:\n                merged_flags.append(\"\")\n        return FlagsOp(merged_flags)\n\n    def _generate_cond_operand(self, _: OperandSpec, __: Instruction) -> CondOp:\n        cond = random.choice(list(self.target_desc.branch_conditions))\n        return CondOp(cond)\n"
  },
  {
    "path": "rvzr/config.py",
    "content": "\"\"\"\nFile: Fuzzing Configuration Options\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nimport os\nfrom copy import deepcopy\nfrom collections import OrderedDict\nfrom typing import List, Dict, TextIO, Any, TypedDict, Set, Literal\nfrom types import ModuleType\n\nimport yaml\n\nfrom .arch.x86 import config as x86_config\nfrom .arch.arm64 import config as arm64_config\n\n# ==================================================================================================\n# Custom Types\n# ==================================================================================================\nPagePropertyName = Literal['present', 'writable', 'user', 'write-through', 'cache-disable',\n                           'accessed', 'dirty', 'executable', 'reserved_bit', 'randomized']\nPageConf = Dict[PagePropertyName, bool]\n\n\nclass ActorConf(TypedDict):\n    \"\"\" Type definition for actor configuration \"\"\"\n    name: str\n    mode: str\n    privilege_level: str\n    observer: bool\n    data_properties: PageConf\n    data_ept_properties: PageConf\n    instruction_blocklist: Set[str]\n    fault_blocklist: Set[str]\n\n\nActorConfKey = Literal[\"name\", \"mode\", \"privilege_level\", \"observer\", \"data_properties\",\n                       \"data_ept_properties\", \"instruction_blocklist\", \"fault_blocklist\"]\n\nActorsConf = Dict[str, ActorConf]\n\nArchitecture = Literal[\"x86-64\", \"arm64\"]\n\n\n# ==================================================================================================\n# Helper classes\n# ==================================================================================================\nclass IncludeLoader(yaml.SafeLoader):\n    \"\"\"\n    Helper class to enable `!include` statements in configuration files\n    \"\"\"\n    visited: List[str] = []\n    file_id_counter: int = 0\n\n    def __init__(self, stream: TextIO, include_dir: str = \"\") -> None:\n        self._search_paths = [os.path.split(stream.name)[0]]\n        if include_dir:\n            self._search_paths.append(include_dir)\n        self.visited.append(os.path.abspath(stream.name))\n        super(IncludeLoader, self).__init__(stream)\n\n    def __del__(self) -> None:\n        if self.visited:\n            self.visited.pop()\n\n    def include(self, node: yaml.Node) -> Any:\n        \"\"\"\n        Include another YAML file\n        \"\"\"\n        # find the included file\n        relative_filename: str = self.construct_scalar(node)  # type: ignore\n        for root in self._search_paths:\n            filename = os.path.join(root, relative_filename)\n            if os.path.exists(filename):\n                break\n        else:\n            raise ConfigException(f\"Included file {relative_filename} does not exist\")\n\n        # check for cycles\n        if os.path.abspath(filename) in self.visited:\n            raise ConfigException(f\"Circular include detected in {filename}\")\n\n        with open(filename, 'r') as f:\n            return yaml.load(f, IncludeLoader)\n\n    def construct_yaml_map(self, node: yaml.MappingNode) -> Dict[Any, Any]:\n        \"\"\"\n        Custom constructor that renames all `file` keys to `file_<unique_id>` to prevent multiple\n        include statements from overwriting each other\n        \"\"\"\n        for k, _ in node.value:\n            if k.value == 'file':\n                k.value = f'file_{self.file_id_counter}'\n                self.file_id_counter += 1\n        data = self.construct_mapping(node)\n        return data\n\n\nIncludeLoader.add_constructor('!include', IncludeLoader.include)\nIncludeLoader.add_constructor(u'tag:yaml.org,2002:map', IncludeLoader.construct_yaml_map)\n\n\nclass ConfigException(SystemExit):\n\n    def __init__(self, message: str) -> None:\n        super().__init__(\"\\nCONFIG ERROR: \" + message + \"\\n\")\n\n\ndef _get_architecture() -> Architecture:\n    with open('/proc/cpuinfo', 'r') as f:\n        for line in f:\n            if 'AuthenticAMD' in line or 'GenuineIntel' in line:\n                return 'x86-64'\n            if 'CPU implementer' in line:\n                return 'arm64'\n        return 'x86-64'\n\n\ndef _get_cpu_vendor() -> str:\n    with open('/proc/cpuinfo', 'r') as f:\n        for line in f:\n            if 'AuthenticAMD' in line:\n                return 'x86-64-amd'\n            if 'GenuineIntel' in line:\n                return 'x86-64-intel'\n            if 'CPU implementer' in line:\n                return 'arm64'\n        return 'x86-64-intel'\n\n\n# ==================================================================================================\n# Main configuration class\n# ==================================================================================================\nclass Conf:\n    \"\"\"\n    Data class to store global configuration options. It implements the Borg pattern to ensure that\n    all instances share the same state. The configuration options are loaded from a YAML file and\n    can be accessed as class attributes.\n    \"\"\"\n\n    # ==============================================================================================\n    # Fuzzer\n    fuzzer: str = \"basic\"\n    \"\"\" fuzzer: type of the fuzzing algorithm \"\"\"\n    enable_priming: bool = True\n    \"\"\" enable_priming: whether to check violations with priming \"\"\"\n    enable_speculation_filter: bool = False\n    \"\"\" enable_speculation_filter: if True, discard test cases that don't trigger speculation\"\"\"\n    enable_observation_filter: bool = False\n    \"\"\" enable_observation_filter: if True,discard test cases that don't leave speculative traces\"\"\"\n    enable_fast_path_model: bool = True\n    \"\"\" enable_fast_path_boosting: if enabled, the same contract trace will be used\n    for all inputs in the same taint-based input class \"\"\"\n\n    # ==============================================================================================\n    # Program Generator\n    generator: str = \"random\"\n    \"\"\" generator: type of the program generator \"\"\"\n    instruction_set: Architecture = _get_architecture()\n    \"\"\" instruction_set: ISA under test \"\"\"\n    instruction_categories: List[str] = []\n    \"\"\" instruction_categories: list of instruction categories to use for generating programs \"\"\"\n    instruction_allowlist: List[str] = []\n    \"\"\" instruction_allowlist: list of instructions to use for generating programs;\n    combined with instruction_categories; has priority over instruction_blocklist.\n    The resulting list is:\n     (instructions from instruction_categories - instruction_blocklist) + instruction_allowlist \"\"\"\n    instruction_blocklist: List[str] = []\n    \"\"\" instruction_blocklist: list of instruction that will NOT be used for generating programs;\n    filters out instructions from instruction_categories, but not from instruction_allowlist.\n    The resulting list is:\n     (instructions from instruction_categories - instruction_blocklist) + instruction_allowlist \"\"\"\n    instruction_blocklist_append: List[str] = []\n    \"\"\" instruction_blocklist_append: same as instruction_blocklist, but the list is added\n    to the existing blocklist instead of replacing it \"\"\"\n    program_generator_seed: int = 0\n    \"\"\" program_generator_seed: seed of the program generator; if set to zero, a random seed\n    will be used \"\"\"\n    program_size: int = 24\n    \"\"\" program_size: size of generated programs \"\"\"\n    avg_mem_accesses: int = 12\n    \"\"\" avg_mem_accesses: average number of memory accesses in generated programs \"\"\"\n    min_bb_per_function: int = 1\n    \"\"\" min_bb_per_function: minimal number of basic blocks per function in generated programs \"\"\"\n    max_bb_per_function: int = 2\n    \"\"\" max_bb_per_function: maximum number of basic blocks per function in generated programs \"\"\"\n    min_successors_per_bb: int = 2\n    \"\"\" min_bb_per_function: min. number of successors for each basic block in generated programs\n    Note 1: this config option is a *hint*; it could be ignored if the instruction set does not\n    have the necessary instructions to satisfy it, or if a certain number of successor is required\n    for correctness\"\"\"\n    max_successors_per_bb: int = 2\n    \"\"\" min_bb_per_function: min. number of successors for each basic block in generated programs\n    Note: this config option is a *hint*; it could be ignored if the instruction set does not\n    have the necessary instructions to satisfy it, or if a certain number of successor is required\n    for correctness \"\"\"\n    register_allowlist: List[str] = []\n    \"\"\" register_allowlist: list of registers that CAN be used for generating programs;\n     has higher priority than register_blocklist.\n     The resulting list is: (all registers - register_blocklist) + register_allowlist \"\"\"\n    register_blocklist: List[str] = []\n    \"\"\" register_blocklist: list of registers that will NOT be used for generating programs;\n     has lower priority than register_allowlist.\n     The resulting list is: (all registers - register_blocklist) + register_allowlist \"\"\"\n    faults_allowlist: List[str] = []\n    \"\"\" faults_allowlist: by default, generator will produce programs that never\n    trigger exceptions. This option modifies this behavior by permitting the generator to produce\n    'unsafe' instruction sequences that could potentially trigger an exception. Model and executor\n     will also be configured to handle these exceptions gracefully \"\"\"\n\n    # ==============================================================================================\n    # Input Data Generator\n    data_generator: str = 'random'\n    \"\"\" data_generator: type of the input generator \"\"\"\n    data_generator_seed: int = 10\n    \"\"\" data_generator_seed: input generation seed; will use a random seed if set to zero \"\"\"\n    data_generator_entropy_bits: int = 31\n    \"\"\" data_generator_entropy_bits: entropy of the random values created by the input generator \"\"\"\n    inputs_per_class: int = 2\n    \"\"\" inputs_per_class: number of inputs per input class \"\"\"\n    input_gen_probability_of_special_value: float = 0.05\n    \"\"\" input_gen_probability_of_special_value: probability of generating a special value\n    (zero or maximum value) when setting a register value in the input generator. This is used\n    to test fast paths in the microarchitecture \"\"\"\n\n    # ==============================================================================================\n    # Contract Model\n    model_backend: str = 'unicorn'\n    \"\"\" model_backend: The backend used to collect contract traces on generated test cases \"\"\"\n    contract_execution_clause: List[str] = [\"seq\"]\n    \"\"\" contract_execution_clause: \"\"\"\n    contract_observation_clause: str = 'ct'\n    \"\"\" contract_observation_clause: \"\"\"\n    model_min_nesting: int = 1\n    \"\"\" model_max_nesting: \"\"\"\n    model_max_nesting: int = 30\n    \"\"\" model_max_nesting: \"\"\"\n    model_max_spec_window: int = 250\n    \"\"\" model_max_spec_window: \"\"\"\n\n    # ==============================================================================================\n    # Executor\n    executor: str = _get_cpu_vendor()\n    \"\"\" executor: executor type \"\"\"\n    executor_mode: str = 'P+P'\n    \"\"\" executor_mode: hardware trace collection mode \"\"\"\n    executor_warmups: int = 5\n    \"\"\" executor_warmups: number of warmup rounds executed before starting to collect\n    hardware traces \"\"\"\n    executor_sample_sizes: List[int] = [10, 50, 100, 500]\n    \"\"\" executor_sample_sizes: a list of sample sizes to be used during the measurements;\n    the executor will first collect the hardware traces with the first sample size in the list,\n    and if a violation is detected, it will try to reproduce it with all the following\n    sample sizes \"\"\"\n    executor_filtering_repetitions: int = 10\n    \"\"\" executor_filtering_repetitions: number of repetitions while filtering test cases \"\"\"\n    executor_taskset: int = 0\n    \"\"\" executor_taskset: id of the CPU core on which the executor is running test cases \"\"\"\n    enable_pre_run_flush: bool = True\n    \"\"\" enable_pre_run_flush: if enabled, the executor will do its best to flush\n    the microarchitectural state before running test cases \"\"\"\n\n    # ==============================================================================================\n    # Analyser\n    analyser: str = 'chi2'\n    \"\"\" analyser: type of the analyser \"\"\"\n    analyser_subsets_is_violation: bool = False\n    \"\"\" analyser_subsets_is_violation: [only for analyser='sets' or analyser='bitmaps']\n    if False, the analyser will not label hardware traces as mismatching if they form\n    a subset relation \"\"\"\n    analyser_outliers_threshold: float = 0.1\n    \"\"\" analyser_outliers_threshold: [only for analyser='sets' or analyser='bitmaps']\n    analyser will ignore the htraces that appear in less then this percentage of the repetitions.\n    I.e., a htrace passes the filter if it is observed at least\n        (analyser_outliers_threshold * len(htrace)) times \"\"\"\n    analyser_stat_threshold: float = 0.5\n    \"\"\" analyser_stat_threshold: [only for analyser='chi2' and analyser='mwu']\n    Threshold for the statistical tests. If a pair of hardware traces has the (normalized)\n    statistics below the threshold, then the traces are considered equivalent.\n\n    Note: The threshold default value (0.5) is conservative and avoids false positives\n    at cost of false negatives. For more precise results, set the threshold to a lower value.\n\n    For the chi2 test, the threshold is the statistics / (len(htrace1) + len(htrace2))\n    For the mwu test, the threshold is the p-value \"\"\"\n\n    # ==============================================================================================\n    # Coverage\n    coverage_type: str = 'none'\n    \"\"\" coverage_type: coverage type \"\"\"\n\n    # ==============================================================================================\n    # Minimizer\n    minimizer_retries: int = 1\n    \"\"\" minimizer_retries: number of attempts to reproduce the violation when minimizing \"\"\"\n\n    # ==============================================================================================\n    # Output\n    multiline_output: bool = False\n    \"\"\" multiline_output: \"\"\"\n    logging_modes: List[str] = [\"info\", \"stat\"]\n    \"\"\" logging_modes: \"\"\"\n    color: bool = False\n\n    # ==============================================================================================\n    # Alternatives for config options (also extended by ISA-specific config.py)\n    _option_values: Dict[str, List[str]] = {\n        \"fuzzer\": [\"basic\", \"architectural\", \"archdiff\"],\n        \"generator\": [\"random\"],\n        \"instruction_set\": [\"x86-64\", \"arm64\"],\n        \"data_generator\": [\"random\"],\n        \"model_backend\": [\"dummy\", \"unicorn\", \"dynamorio\"],\n        \"contract_execution_clause\": [\n            \"seq\", \"no_speculation\", \"seq-assist\", \"cond\", \"conditional_br_misprediction\", \"bpas\",\n            \"nullinj-fault\", \"nullinj-assist\", \"delayed-exception-handling\", \"div-zero\",\n            \"div-overflow\", \"meltdown\", \"fault-skip\", \"noncanonical\", \"vspec-ops-div\",\n            \"vspec-ops-memory-faults\", \"vspec-ops-memory-assists\", \"vspec-ops-gp\", \"vspec-all-div\",\n            \"vspec-all-memory-faults\", \"vspec-all-memory-assists\"\n        ],\n        \"contract_observation_clause\": [\n            \"none\", \"l1d\", \"pc\", \"memory\", \"ct\", \"loads+stores+pc\", \"ct-nonspecstore\", \"ctr\",\n            \"arch\", \"tct\", \"tcto\", \"ct-ni\"\n        ],\n        \"executor\": [\"x86-64-intel\", \"x86-64-amd\", \"arm64\"],\n        'executor_mode': [\n            'P+P',\n            'F+R',\n            'E+R',\n            'PP+P',\n            'TSC',\n            # 'GPR' is intentionally left out\n        ],\n        'faults_allowlist': [\n            'div-by-zero',\n            'div-overflow',\n            'opcode-undefined',\n            'breakpoint',\n            'debug-register',\n            'non-canonical-access',\n            'user-to-kernel-access',\n        ],\n        \"analyser\": [\"bitmaps\", \"sets\", \"mwu\", \"chi2\"],\n        \"coverage_type\": [\"none\", \"model_instructions\"],\n        \"logging_modes\": [\n            \"info\",\n            \"stat\",\n            \"dbg_generator\",\n            \"dbg_timestamp\",\n            \"dbg_violation\",\n            \"dbg_dump_htraces\",\n            \"dbg_dump_ctraces\",\n            \"dbg_dump_traces_unlimited\",\n            \"dbg_model\",\n            \"dbg_coverage\",\n            \"dbg_priming\",\n            \"dbg_executor_raw\",\n            \"dbg_isa_filter\",\n        ],\n    }\n\n    # ==============================================================================================\n    # Internal\n    _borg_shared_state: Dict[Any, Any] = {}\n    _no_generation: bool = False\n    _handled_faults: List[str]  # set by ISA-specific config.py\n    _generator_fault_to_fault_name: Dict[str, str]  # set by ISA-specific config.py\n    _actors: ActorsConf\n    _actor_default: ActorConf\n    _config_path: str = \"\"\n\n    def __init__(self) -> None:\n        # implementation of Borg pattern\n        setattr(self, '__dict__', self._borg_shared_state)\n        if not getattr(self, '_actors', None):\n            self._actors = OrderedDict()\n\n    def load(self, config_path: str, include_dir: str = \"\") -> None:\n        self._config_path = config_path\n        config_update: Dict[str, Any] = {}\n        with open(config_path, \"r\") as f:\n            loader = IncludeLoader(f, include_dir)\n            try:\n                config_update = loader.get_single_data()\n            except yaml.scanner.ScannerError as e:  # type: ignore\n                raise ConfigException(\n                    f\"Error parsing the configuration file {config_path}:\\nError: {e}\") from e\n            finally:\n                loader.dispose()  # type: ignore\n        self._load_from_dict(config_update)\n        self._value_sanity_check()\n\n    def _load_from_dict(self, config_update: Dict[str, Any]) -> None:\n        # make sure to set the architecture-dependent defaults first\n        if 'instruction_set' in config_update:\n            self.instruction_set = config_update['instruction_set']\n            self.set_to_arch_defaults()\n            config_update.pop('instruction_set')\n\n        # recursively load included files\n        file_keys = []\n        for k, v in config_update.items():\n            if \"file_\" in k:\n                self._load_from_dict(v)\n                file_keys.append(k)\n        for k in file_keys:  # remove the `file_*` keys as they have already been processed\n            config_update.pop(k)\n\n        # set the rest of the options\n        for var, value in config_update.items():\n            # print(f\"CONF: setting {var} to {value}\")\n            if var == \"faults_allowlist\":\n                self.update_handled_faults_with_generator_faults(value)\n                self.safe_set(var, value)\n                continue\n            if var == \"instruction_blocklist_append\":\n                self.instruction_blocklist.extend(value)\n                continue\n            if var == \"actors\":\n                self.set_actor_properties(value)\n                continue\n            if var == \"instruction_categories\":\n                backend = config_update.get(\"model_backend\", self.model_backend)\n                if backend == \"unicorn\":\n                    options_name = \"unicorn_instruction_categories\"\n                elif backend == \"dynamorio\":\n                    options_name = \"dr_instruction_categories\"\n                else:\n                    options_name = \"dr_instruction_categories\"\n                self.safe_set(var, value, options_name)\n\n            self.safe_set(var, value)\n\n    def safe_set(self, name: str, value: Any, options_name: str = \"\") -> None:\n        assert name not in [\"instruction_set\"]\n\n        # sanity checks\n        if name[0] == \"_\":\n            raise ConfigException(f\"Attempting to set an internal configuration variable {name}.\")\n        if getattr(self, name, None) is None:\n            raise ConfigException(f\"Unknown configuration variable {name}.\\n\"\n                                  f\"It's likely a typo in the configuration file.\")\n        if type(self.__getattribute__(name)) != type(value):\n            raise ConfigException(f\"Wrong type of the configuration variable {name}.\\n\"\n                                  f\"It's likely a typo in the configuration file.\")\n\n        if options_name:\n            self._check_options(options_name, value)\n        else:\n            self._check_options(name, value)\n        setattr(self, name, value)\n\n    def _check_options(self, name: str, value: Any) -> None:\n        if name not in self._option_values:\n            return\n        options = self._option_values[name]\n\n        invalid_value = None\n        if isinstance(value, str):\n            invalid_value = value if value not in options else None\n        elif isinstance(value, List):\n            for v in value:\n                if v in options:\n                    continue\n                if isinstance(v, Dict):\n                    for k in v:\n                        if k not in options:\n                            break\n                    else:\n                        continue\n                invalid_value = v\n                break\n        else:\n            raise ConfigException(f\"Unexpected type of config variable {name}\")\n\n        if invalid_value:\n            raise ConfigException(f\"Unknown value '{invalid_value}' of config variable '{name}'\\n\"\n                                  f\"Possible options: {options}\")\n        return\n\n    def _value_sanity_check(self) -> None:\n        \"\"\"\n        Check if the configuration values make sense\n        \"\"\"\n        if self.data_generator_entropy_bits > 32:\n            raise ConfigException(\"data_generator_entropy_bits must be less or equal to 32 bits\")\n        if self.min_successors_per_bb > self.max_successors_per_bb:\n            raise ConfigException(\"min_successors_per_bb is larger than max_successors_per_bb\")\n\n    def set_to_arch_defaults(self) -> None:\n        \"\"\" Set config options according to the architecture-specific defaults \"\"\"\n\n        config: ModuleType\n        if self.instruction_set == \"x86-64\":\n            config = x86_config\n        elif self.instruction_set == \"arm64\":\n            config = arm64_config\n        else:\n            raise ConfigException(f\"Unknown architecture {self.instruction_set}\")\n\n        config_defaults = {}\n        for c in dir(config):\n            if c.startswith(\"__\"):\n                continue\n            values = getattr(config, c)\n            if type(values) not in [bool, int, float, str, dict, list]:\n                continue\n            config_defaults[c] = values\n\n        if \"_option_values\" not in config_defaults:\n            raise ConfigException(\"ISA-specific config.py must define _option_values\")\n\n        for name, value in config_defaults.items():\n            if name == \"faults_allowlist\":\n                self.update_handled_faults_with_generator_faults(value)\n                continue\n            if name == \"_actor_default\":\n                self._actor_default = deepcopy(value)\n                self._actors = OrderedDict()\n                self._actors['main'] = deepcopy(value)\n                continue\n            if name == \"_option_values\":\n                for k, v in value.items():\n                    self._option_values[k] = v\n                continue\n\n            setattr(self, name, value)\n\n    def update_handled_faults_with_generator_faults(self, new: List[str]) -> None:\n        for gen_fault in new:\n            if not gen_fault:\n                continue\n            if gen_fault not in self._generator_fault_to_fault_name:\n                raise ConfigException(f\"Unknown generator fault {gen_fault}\")\n            fault = self._generator_fault_to_fault_name[gen_fault]\n            if fault not in self._handled_faults:\n                self._handled_faults.append(fault)\n\n    def set_actor_properties(self, new: List[Dict[str, List[Dict[ActorConfKey, Any]]]]) -> None:\n        for actor_dict in new:\n            name = next(iter(actor_dict))\n            self._check_options(\"actor\", actor_dict[name])\n            update = {k: v for tmp_dict in actor_dict[name] for k, v in tmp_dict.items()}\n\n            if name == \"main\":\n                if update.get('mode', 'host') != 'host':\n                    raise ConfigException(\"The main actor must be in 'host' mode\")\n                if update.get('privilege_level', 'kernel') != 'kernel':\n                    raise ConfigException(\"The main actor must have 'kernel' privilege_level\")\n\n            if name in self._actors:\n                entry = self._actors[name]\n            else:\n                entry = deepcopy(self._actor_default)\n                entry[\"name\"] = name\n\n            for k, v in update.items():\n                if k == \"mode\" and v not in self._option_values[\"actor_mode\"]:\n                    raise ConfigException(f\"Unsupported actor mode {v}\")\n                if k == \"privilege_level\" and v not in self._option_values[\"actor_privilege_level\"]:\n                    raise ConfigException(f\"Unsupported actor privilege_level {v}\")\n\n                if k == \"data_properties\":\n                    for property_ in v:\n                        for p_key, p_value in property_.items():\n                            if p_key not in self._option_values[\"actor_data_properties\"]:\n                                raise ConfigException(\n                                    f\"Unsupported actor data_properties value {p_key}\")\n                            entry[k][p_key] = p_value\n                    continue\n                if k == \"data_ept_properties\":\n                    if update.get('mode', 'host') != 'guest':\n                        raise ConfigException(\"data_ept_properties can only be used in guest mode\")\n                    for property_ in v:\n                        for p_key, p_value in property_.items():\n                            if p_key not in self._option_values[\"actor_data_ept_properties\"]:\n                                raise ConfigException(\n                                    f\"Unsupported actor data_ept_properties value {p_key}\")\n                            entry[k][p_key] = p_value\n                    continue\n                if k == \"instruction_blocklist\" or k == \"fault_blocklist\":\n                    if v:\n                        entry[k].update(v)\n                    continue\n\n                entry[k] = v\n            self._actors[name] = entry\n\n    def disable_generation(self) -> None:\n        \"\"\" Disable random-generation mode \"\"\"\n        self._no_generation = True\n\n    def is_generation_enabled(self) -> bool:\n        \"\"\" Check if the generation is globally enabled \"\"\"\n        return not self._no_generation\n\n    def get_actors_conf(self) -> ActorsConf:\n        \"\"\" Get the configuration dictionary describing all actors \"\"\"\n        return self._actors\n\n\nCONF = Conf()\nCONF.set_to_arch_defaults()\n"
  },
  {
    "path": "rvzr/data_generator.py",
    "content": "\"\"\"\nFile: Input Generation.\n\n      An input is a sequence of bytes that is used to initialize memory and registers in\n      the model or executor before running a test case program. The input generator\n      is responsible for generating random inputs for the test cases.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nimport os\nimport random\nfrom typing import List, Tuple, Final\n\nimport numpy as np\n\nfrom .tc_components.test_case_data import InputData, InputTaint\nfrom .config import CONF\nfrom .logs import inform\n\nPOW32 = pow(2, 32)\n\n\nclass DataGenerator:\n    \"\"\" Class responsible for generating random inputs for test cases. \"\"\"\n\n    _state: int = 0\n    _boosting_state: int = 0\n    _max_gpr_value: Final[int] = pow(2, 64) - 1\n\n    def __init__(self, seed: int):\n        self.max_input_value = pow(2, CONF.data_generator_entropy_bits)\n        self._state = seed\n\n        self._skip_special_values = CONF.input_gen_probability_of_special_value == 0\n        self._probability_of_zero = CONF.input_gen_probability_of_special_value\n        self._probability_of_max = CONF.input_gen_probability_of_special_value * 2\n        assert self._probability_of_max < 1, \\\n            \"The sum of probabilities of special values must be less than 1.\"\n\n    def get_state(self) -> int:\n        \"\"\"\n        Return the current state of the generator.\n        State is the seed value that will be used to generate the next input.\n        \"\"\"\n        return self._state\n\n    def _reset_boosting_state(self) -> None:\n        \"\"\" Reset the state (i.e., seed) of the generator to the last state before boosting \"\"\"\n        self._boosting_state = self._state\n\n    def generate(self, count: int, n_actors: int) -> List[InputData]:\n        \"\"\"\n        Generate a list of random inputs.\n        :param count: The number of inputs to generate\n        :return: A list of generated inputs\n        \"\"\"\n        # if it's the first invocation and the seed is zero - use random seed\n        if self._state == 0:\n            self._state = random.randint(0, pow(2, 32) - 1)\n            inform(\"data_gen\", f\"Setting input seed to: {self._state}\")\n\n        generated_inputs = []\n        for _ in range(count):\n            input_, self._state = self._generate_one(self._state, n_actors)\n            generated_inputs.append(input_)\n\n        # make sure that boosted inputs will continue from the updated state\n        self._boosting_state = self._state\n        return generated_inputs\n\n    def generate_boosted(self, inputs: List[InputData], taints: List[InputTaint],\n                         inputs_per_class: int) -> List[InputData]:\n        \"\"\"\n        Extend the given input sequence with new inputs such that the new inputs should produce\n        the same contract traces as the original inputs. This achieved by copying the original\n        inputs and modifying them based on the taints collected by the model while tracing the\n        test case with the original inputs (i.e, non-tainted values are replaced with random values,\n        and the tainted values are copied).\n\n        For example, if the original inputs are [A, B, C] and inputs_per_class=3,\n        then the new sequence will be [A, B, C, A', B', C', A'', B'', C''],\n        where A, A', and A'' produce the same contract traces, and so on.\n\n        NOTE: The function is idempotent, i.e., calling it multiple times with the same inputs\n        and taints will produce the same sequence of new inputs. This is because the state of the\n        generator is reset to the last state before boosting every time the function is called.\n        \"\"\"\n        if not inputs:\n            return []\n        assert len(inputs) == len(taints), \"Error: Cannot extend inputs. The number of taints\" \\\n                                           \" does not match the number of inputs.\"\n        n_actors = len(inputs[0])\n        input_size = InputData.n_data_entries_per_actor()\n\n        self._reset_boosting_state()\n        boosted_inputs = list(inputs)  # make a copy\n        for _ in range(inputs_per_class - 1):\n            for i, input_ in enumerate(inputs):\n                # Generate new, fully random input\n                new_input, self._boosting_state = self._generate_one(self._boosting_state, n_actors)\n\n                # Copy tainted values from the original input\n                for actor_id in range(n_actors):\n                    taint = taints[i].linear_view(actor_id)\n                    input_old = input_.linear_view(actor_id)\n                    input_new = new_input.linear_view(actor_id)\n                    for j in range(input_size):\n                        if taint[j]:\n                            input_new[j] = input_old[j]\n\n                # Add the new input to the sequence\n                boosted_inputs.append(new_input)\n        return boosted_inputs\n\n    def load(self, input_paths: List[str]) -> List[InputData]:\n        \"\"\"\n        Load a sequence of inputs from a directory with binary inputs.\n        \"\"\"\n        # mirror the state update in generate() as 'load' function is used for reproducing\n        # violations, which requires the generator state to be identical to the one during\n        # fuzzing\n        if self._state == 0:\n            self._state = random.randint(0, pow(2, 32) - 1)\n            inform(\"data_gen\", f\"Setting input seed to: {self._state}\")\n\n        inputs = []\n        n_actors = len(CONF.get_actors_conf())\n        for input_path in input_paths:\n            input_ = InputData(n_actors)\n\n            # check that the file is not corrupted\n            size = os.path.getsize(input_path)\n            expected = input_.itemsize * n_actors\n            if size != expected:\n                raise ValueError(f\"Incorrect size of input `{input_path}` \"\n                                 f\"({size} B, expected {expected} B)\")\n\n            input_.load(input_path)\n            inputs.append(input_)\n            self._state += 1\n\n        self._boosting_state = self._state\n        return inputs\n\n    def _generate_one(self, state: int, n_actors: int) -> Tuple[InputData, int]:\n        input_ = InputData(n_actors)\n        input_.seed = state\n\n        per_actor_data_size = input_.itemsize // 8\n        n_registers = input_[0]['gpr'].itemsize\n\n        rng = np.random.default_rng(seed=state)\n        for i in range(n_actors):\n            # generate random data\n            data = rng.integers(\n                self.max_input_value, size=per_actor_data_size, dtype=np.uint64)  # type: ignore\n\n            # copy lower 32-bits to upper 32-bits, for every 8-byte word\n            data = (data << np.uint64(32)) + data\n\n            # for each of the registers and with a probability of 0.01\n            # set the register to zero or to max value\n            if not self._skip_special_values:\n                for reg_id in range(n_registers):\n                    roll = rng.random()\n                    if roll < self._probability_of_zero:\n                        input_[i]['gpr'][reg_id] = 0\n                    elif roll < self._probability_of_max:\n                        input_[i]['gpr'][reg_id] = self._max_gpr_value\n\n            input_.set_actor_data(i, data)\n\n        return input_, state + 1\n"
  },
  {
    "path": "rvzr/elf_parser.py",
    "content": "\"\"\"\nFile: Parsing of ELF files to populate sections of a TestCaseCode object.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nimport re\nfrom typing import TYPE_CHECKING, Dict, List, Tuple, TypedDict, NamedTuple, Final\nfrom subprocess import run\nfrom elftools.elf.elffile import ELFFile, SymbolTableSection  # type: ignore\n\nfrom rvzr.tc_components.test_case_binary import SymbolTable, SymbolTableEntry, TestCaseBinary\nfrom rvzr.tc_components.actor import ActorPL, ActorMode\nfrom rvzr.tc_components.instruction import Instruction\nfrom rvzr.config import CONF\n\nif TYPE_CHECKING:\n    from rvzr.tc_components.test_case_code import TestCaseProgram, CodeSection\n    from rvzr.tc_components.test_case_binary import InstructionMap\n    from rvzr.target_desc import TargetDesc\n\n\nclass _ParsingError(Exception):\n\n    def __init__(self, message: str):\n        full_msg = f\"[ELFParser] Error while parsing assembly\\n       Issue: {message}\"\n        super().__init__(full_msg)\n\n\n# ==================================================================================================\n# Private: ELF Symbol Table Parser\n# ==================================================================================================\nclass _ELFData(TypedDict):\n    section_data: Dict[int, _SectionData]\n    exit_addr: int\n\n\nclass _SectionData(TypedDict):\n    id_: int\n    name: str\n    offset: int\n    size: int\n    functions: Dict[str, _FunctionData]\n\n\nclass _FunctionData(TypedDict):\n    id_: int\n    name: str\n    offset: int\n\n\nclass _SymtabParser:\n\n    def parse(self, obj_file: str) -> _ELFData:\n        \"\"\"\n        Parse the ELF symbol table to get the addresses of all functions and sections.\n        The section and function IDs are assigned in the order they appear in the ELF file.\n        :param obj_file: path to the ELF file\n        :return: a dictionary containing the section data and the exit address\n        \"\"\"\n        elf_data = self._get_unsorted_data(obj_file)\n        self._sort_elf_data(elf_data)\n        return elf_data\n\n    def _get_unsorted_data(self, obj_file: str) -> _ELFData:\n        \"\"\" Transform the ELF symbol table into a dictionary of sections and functions \"\"\"\n        elf_data: _ELFData = {\"section_data\": {}, \"exit_addr\": -1}\n\n        with open(obj_file, \"rb\") as f:\n            data = ELFFile(f)\n\n            # sanity check: we build test cases in such a way that there should be no segments\n            assert data.num_segments() == 0, f\"{data.num_segments()}\"\n\n            # collect section info\n            for s_id, s in enumerate(data.iter_sections()):\n                if s.name[:6] != \".data.\":\n                    continue\n                s_entry: _SectionData = {\n                    \"id_\": s_id,\n                    \"name\": s.name.split(\".\")[2],\n                    \"offset\": s['sh_offset'],\n                    \"size\": s['sh_size'],\n                    \"functions\": {}\n                }\n                elf_data[\"section_data\"][s_id] = s_entry\n\n            # get addresses of functions and macros\n            symtab: SymbolTableSection = data.get_section_by_name(\".symtab\")  # type: ignore\n            for s in symtab.iter_symbols():\n                if s.name.startswith(\".function\"):\n                    f_entry: _FunctionData = {\n                        \"id_\": -1,  # will be assigned later\n                        \"name\": s.name,\n                        \"offset\": s.entry.st_value\n                    }\n                    s_id = s['st_shndx']\n                    elf_data[\"section_data\"][s_id][\"functions\"][s.name] = f_entry\n\n                if \".test_case_exit\" in s.name:\n                    elf_data[\"exit_addr\"] = s.entry.st_value\n        assert elf_data[\"exit_addr\"] != -1, \"Failed to find exit address\"\n        return elf_data\n\n    def _sort_elf_data(self, elf_data: _ELFData) -> None:\n        \"\"\" Sort sections and functions by their appearance in the ELF file \"\"\"\n\n        # assign consecutive ids to sections, in the order they appear in ELF\n        sorted_section_ids = sorted(elf_data[\"section_data\"].keys())\n        new_section_data = {}\n        for new_s_id, org_s_id in enumerate(sorted_section_ids):\n            new_section_data[new_s_id] = elf_data[\"section_data\"][org_s_id]\n            new_section_data[new_s_id][\"id_\"] = new_s_id\n        elf_data[\"section_data\"] = new_section_data\n\n        # assign consecutive ids to functions, in the order they appear in ELF\n        sorted_new_section_ids = sorted(elf_data[\"section_data\"].keys())\n        new_f_id = 0  # function ids are unique across all sections\n        for s_id in sorted_new_section_ids:\n            function_data = elf_data[\"section_data\"][s_id][\"functions\"]\n            sorted_function_data = sorted(function_data.values(), key=lambda x: x[\"offset\"])\n            for f_data in sorted_function_data:\n                f_data[\"id_\"] = new_f_id\n                new_f_id += 1\n\n\n# ==================================================================================================\n# Private: Objdump Output Parser\n# ==================================================================================================\n_SectionName = str\n_InstructionAddr = int\n_InstrAddrMap = Dict[_SectionName, List[_InstructionAddr]]\n\n\nclass _ObjdumpSectionDesc(NamedTuple):\n    name: str\n    skip: bool\n\n\nclass _ObjdumpOutputParser:\n\n    def __init__(self) -> None:\n        self._objdump_flags = \"--no-show-raw-insn -D -M intel -m i386:x86-64\"\n        if CONF.instruction_set == \"arm64\":\n            self._objdump_flags = \"--no-show-raw-insn -D -m aarch64\"\n\n    def parse(self, obj_file: str) -> _InstrAddrMap:\n        \"\"\"\n        Parse the output of objdump to get the addresses of all instructions\n        :param obj_file: path to the ELF file\n        :return: a dictionary mapping section names to lists of its instruction addresses\n        \"\"\"\n        # Get raw objdump output\n        dump = run(\n            f\"objdump {self._objdump_flags} {obj_file} \"\n            \"| awk '/ [0-9a-f]+:/{print $1} /section/{print $0}'\",\n            shell=True,\n            check=True,\n            capture_output=True)\n\n        # Prepare for parsing\n        instruction_addresses: Dict[_SectionName, List[_InstructionAddr]] = {}\n        section_desc = _ObjdumpSectionDesc(\"\", False)\n\n        # Loop over output lines, keeping track of the latest section header,\n        # and recording addresses of instructions for each section\n        for line in dump.stdout.decode().split(\"\\n\"):\n            if not line:\n                continue\n\n            # Enter a new section\n            if \"section\" in line:\n                section_desc = self._parse_section_header(line)\n                assert section_desc.name not in instruction_addresses\n                instruction_addresses[section_desc.name] = []\n                continue\n\n            # Skip instruction in ignored sections\n            if section_desc.skip:\n                continue\n\n            # Parse instruction addresses\n            assert section_desc.name != \"\", \"Failed to parse objdump output (section_name)\"\n            instruction_addresses[section_desc.name].append(int(line[:-1], 16))\n\n        return instruction_addresses\n\n    def _parse_section_header(self, line: str) -> _ObjdumpSectionDesc:\n        if \".note.gnu\" in line:\n            return _ObjdumpSectionDesc(\"\", True)\n        if \".data.\" not in line:\n            return _ObjdumpSectionDesc(\"\", False)\n\n        # Use regex to find .data.<section_name> pattern anywhere in the line\n        match = re.search(r'\\.data\\.(\\w+)', line)\n        if match:\n            section_name = match.group(1)\n            return _ObjdumpSectionDesc(section_name, False)\\\n\n        # no match found\n        raise _ParsingError(\"Failed to parse objdump output (section_name)\\n\"\n                            f\"       Could not find .data.<section_name> pattern in: '{line}'\")\n\n\n# ==================================================================================================\n# Public Interface: Parser Class\n# ==================================================================================================\nclass ELFParser:\n    \"\"\"\n    ELF parser that extracts the following data from the ELF file:\n    - Section data\n    - Function data\n    - Instruction addresses\n    - Macros\n    \"\"\"\n    _target_desc: Final[TargetDesc]\n\n    def __init__(self, target_desc: TargetDesc) -> None:\n        self._target_desc = target_desc\n        self._instruction_per_macro = 3 if CONF.instruction_set == 'arm64' else 1\n\n    # ----------------------------------------------------------------------------------------------\n    # Public Methods\n    def populate_elf_data(self, test_case_bin: TestCaseBinary,\n                          test_case_code: TestCaseProgram) -> None:\n        \"\"\"\n        Populate .symbol_table and .instruction_map attributes of a TestCaseBinary object\n        by parsing the ELF file associated with this object (TestCaseBinary.obj_path).\n        \"\"\"\n        # get metadata from the ELF file and objdump output\n        symbol_table: SymbolTable\n        instruction_map: InstructionMap\n        symbol_table, instruction_map = self._assign_bin_metadata(test_case_bin.obj_path,\n                                                                  test_case_code)\n\n        # check that the data was populated correctly and the macros are well-formed\n        self._validate_sections(test_case_code.get_sections(), instruction_map)\n        self._validate_macros(test_case_code, symbol_table)\n\n        # assign the parsed data to the test case\n        test_case_bin.assign_elf_data(symbol_table, instruction_map)\n\n    # ----------------------------------------------------------------------------------------------\n    # Private: Assignment of metadata to Section -> Function -> Instruction\n\n    def _assign_bin_metadata(self, obj_file: str,\n                             test_case_code: TestCaseProgram) -> Tuple[SymbolTable, InstructionMap]:\n        # pylint: disable=too-many-locals\n        # NOTE: the check is disabled because I haven't found a way to reduce the number of locals\n\n        # Initialize data structures\n        symbol_table: SymbolTable = []\n        instruction_map: InstructionMap = {}\n\n        # Extract data from the ELF file and objdump output\n        elf_data = _SymtabParser().parse(obj_file)\n        instr_addr_map = _ObjdumpOutputParser().parse(obj_file)\n\n        # Use the data to construct the symbol table and instruction map\n        sorted_sections = sorted(elf_data[\"section_data\"].values(), key=lambda x: x[\"id_\"])\n        all_functions = [f for s in sorted_sections for f in s[\"functions\"].values()]\n        for section_data in sorted_sections:\n            # Assign section metadata\n            section_obj = test_case_code.find_section(name=section_data[\"name\"])\n            self._assign_section_metadata(section_data, section_obj)\n\n            # Assign function metadata\n            sorted_functions = sorted(section_data[\"functions\"].values(), key=lambda x: x[\"id_\"])\n            for func_data in sorted_functions:\n                self._assign_function_metadata(func_data, section_data, symbol_table)\n\n            # Create a local instruction map for the section\n            instruction_map[section_data[\"id_\"]] = {}\n\n            # Assign instruction metadata\n            cursor = 0\n            for func_data in sorted_functions:\n                function_object = test_case_code.find_function(func_data[\"name\"])\n                assert function_object.get_owner() == section_obj.owner\n                assert func_data[\"offset\"] == instr_addr_map[section_data[\"name\"]][cursor], \\\n                    f\"offsets: {func_data['offset']} {instr_addr_map[section_data['name']][cursor]}\"\n\n                for bb in list(function_object) + [function_object.get_exit_bb()]:\n                    for inst in list(bb) + bb.terminators:\n                        self._assign_instruction_metadata(inst, instr_addr_map, cursor,\n                                                          section_data, instruction_map)\n                        if inst.name != \"macro\":\n                            cursor += 1\n                            continue\n\n                        # Assign metadata for macros\n                        self._assign_macro_metadata(inst, sorted_sections, all_functions,\n                                                    symbol_table)\n                        cursor += self._instruction_per_macro\n\n        # Fixup: the last instruction in .data.main is the test case exit, and it must map to a NOP\n        exit_nop = Instruction(\"nop\", \"BASE-NOP\", is_instrumentation=True)\n        instr_addr_map[\"main\"].append(elf_data[\"exit_addr\"])\n        self._assign_instruction_metadata(exit_nop, instr_addr_map, len(instruction_map[0]),\n                                          sorted_sections[0], instruction_map)\n\n        # Sort symbols in the symbol table by section id and offset within the section\n        symbol_table.sort(key=lambda x: (x.sid, x.offset))\n\n        return symbol_table, instruction_map\n\n    @staticmethod\n    def _assign_section_metadata(section_data: _SectionData, section_obj: CodeSection) -> None:\n        section_obj.assign_elf_data(\n            offset=section_data[\"offset\"], size=section_data[\"size\"], id_=section_data[\"id_\"])\n\n    @staticmethod\n    def _assign_function_metadata(func_data: _FunctionData, section_data: _SectionData,\n                                  symbol_table: SymbolTable) -> None:\n        func_symbol = SymbolTableEntry(\n            sid=section_data[\"id_\"],\n            type_=0,\n            offset=func_data[\"offset\"],\n            arg=func_data[\"id_\"],\n        )\n        symbol_table.append(func_symbol)\n\n    def _assign_instruction_metadata(self, inst: Instruction, instr_addr_map: _InstrAddrMap,\n                                     cursor: int, section_data: _SectionData,\n                                     instr_map: InstructionMap) -> None:\n        section_name = section_data[\"name\"]\n        instr_addr_map_in_sec = instr_addr_map[section_name]\n\n        # get instruction info\n        address = instr_addr_map_in_sec[cursor]\n        if cursor + 1 < len(instr_addr_map_in_sec):\n            size = instr_addr_map_in_sec[cursor + 1] - address\n        else:\n            size = 0\n\n        # assign instruction metadata\n        inst.assign_binary_properties(section_id=section_data[\"id_\"], offset=address, size=size)\n\n        # add instruction to the instruction map\n        instr_map[section_data[\"id_\"]][address] = inst\n\n        # if the instruction is a macro, it may span several instructions;\n        # make it look like it does by adding NOPs to the instruction map\n        if inst.name == \"macro\":\n            for i in range(1, self._instruction_per_macro):\n                address = instr_addr_map_in_sec[cursor + i]\n                nop_placeholder = Instruction(\"nop\", \"BASE-NOP\")\n                nop_placeholder.is_macro_placeholder = True\n                instr_map[section_data[\"id_\"]][address] = nop_placeholder\n\n    def _assign_macro_metadata(self, inst: Instruction, sections_data: List[_SectionData],\n                               functions_data: List[_FunctionData],\n                               symbol_table: SymbolTable) -> None:\n        \"\"\"\n        Convert a macro instruction to a symbol table entry by parsing its symbolic arguments\n        according to the macro specification (see x86_target_desc.py). Add the resulting\n        symbol to the symbol table.\n\n        Example:\n        - Input (macro instruction): MACRO 1, .main.function_1\n        - Processing:\n            type: 1 (actor switch)\n            arg 1: main -> 0 (offset of section main)\n            arg 2: function_1 -> 12 (offset of function function_1 within section main)\n            arg 3: none\n            arg 4: none\n            compressed macro argument: 0 + (12 << 16) + (0 << 32) + (0 << 48) = 786432\n        - Output (symbol table entry): SymbolTableEntry(0, 1, 0, 786432)\n        \"\"\"\n\n        # pylint: disable=too-many-locals\n        # NOTE: the check is disabled because I haven't found a way to reduce the number of locals\n\n        def section_name_to_id(name: str) -> int:\n            for entry in sections_data:\n                if entry[\"name\"] == name:\n                    return entry[\"id_\"]\n            raise _ParsingError(f\"Macro references an unknown actor {name}\")\n\n        def function_name_to_id(name: str) -> int:\n            for entry in functions_data:\n                if entry[\"name\"] == name:\n                    return entry[\"id_\"]\n            raise _ParsingError(f\"Macro references an unknown function {name}\")\n\n        assert inst.name == \"macro\"\n\n        # find the spec for this macro arguments\n        macro_name = inst.operands[0].value[1:].lower()\n        try:\n            macro_spec = self._target_desc.macro_specs[macro_name]\n        except IndexError as e:\n            raise _ParsingError(f\"Unknown macro {macro_name} in {inst}\") from e\n\n        # convert macro operands to compressed symbol arguments\n        str_args = inst.operands[1].value.split('.')[1:]\n        symbol_args: int = 0\n        for i, str_arg in enumerate(str_args):\n            str_arg = str_arg.lower()\n            if macro_spec.args[i] == \"\":\n                continue\n            if macro_spec.args[i] == \"actor_id\":\n                actor_id = section_name_to_id(str_arg)\n                symbol_args += (actor_id << i * 16)\n                continue\n            if macro_spec.args[i] == \"function_id\":\n                symbol_args += (function_name_to_id(\".\" + str_arg) << i * 16)\n                continue\n            if macro_spec.args[i] == \"int\":\n                if str_arg.startswith(\"0x\"):\n                    val = int(str_arg, 16) & 0xFFFF\n                else:\n                    val = int(str_arg) & 0xFFFF\n                symbol_args += (val << i * 16)\n                continue\n            raise ValueError(f\"Invalid macro argument {macro_spec.args[i]}\")\n\n        # add the macro to the symbol table\n        symbol_table.append(\n            SymbolTableEntry(\n                sid=inst.section_id(),\n                type_=macro_spec.type_,\n                offset=inst.section_offset(),\n                arg=symbol_args,\n            ))\n\n    # ----------------------------------------------------------------------------------------------\n    # Private: Validation of the parsed data\n    def _validate_sections(self, sections: List[CodeSection],\n                           instruction_map: InstructionMap) -> None:\n        \"\"\"\n        Validate that all sections in the test case have been populated with ELF data\n        :param sections: list of sections in the test case\n        :param instruction_map: constructed InstructionMap\n        :return: None\n        :raises _ParsingError: if at least one section was not populated\n        :raises _ParsingError: if the instruction map does not match the sections\n        \"\"\"\n        if len(instruction_map) != len(sections):\n            raise _ParsingError(\n                \"InstructionMap does not have the same number of sections as the test case\")\n\n        for section_obj in sections:\n            try:\n                _ = section_obj.get_elf_data()  # will throw an exception if the section is not set\n            except AssertionError as e:\n                raise _ParsingError(f\"Failed to find section for actor `{section_obj.name}`\") from e\n\n    def _validate_macros(self, test_case: TestCaseProgram, symbol_table: SymbolTable) -> None:\n        \"\"\" Validate that all macros in the test case are well-formed \"\"\"\n        for symbol in symbol_table:\n            if symbol.type_ == 0:  # function\n                continue\n            macro_spec = self._target_desc.get_macro_spec_from_type(symbol.type_)\n\n            # validate that the actor id is valid\n            for i in range(4):\n                if macro_spec.args[i] != \"actor_id\":\n                    continue\n                target_actor_id = (symbol.arg >> (i * 16)) & 0xFFFF\n\n                # check that the actor exists\n                try:\n                    actor = test_case.find_actor(actor_id=target_actor_id)\n                except KeyError as e:\n                    raise _ParsingError(\n                        f\"Macro references an unknown actor id {target_actor_id}\") from e\n\n                # validate that the actor type matches the macro\n                if macro_spec.name == \"set_k2u_target\" and \\\n                   actor.privilege_level != ActorPL.USER and actor.mode != ActorMode.HOST:\n                    raise _ParsingError(\"Macro set_k2u_target expects a user actor\")\n                if macro_spec.name == \"set_u2k_target\" and \\\n                   actor.privilege_level != ActorPL.KERNEL and actor.mode != ActorMode.HOST:\n                    raise _ParsingError(\"Macro set_u2k_target expects a kernel actor\")\n                if macro_spec.name == \"set_h2g_target\" and \\\n                   actor.mode != ActorMode.HOST and actor.privilege_level != ActorPL.KERNEL:\n                    raise _ParsingError(\"Macro set_h2g_target expects a host actor\")\n                if macro_spec.name == \"set_g2h_target\" and \\\n                   actor.mode != ActorMode.GUEST and actor.privilege_level != ActorPL.KERNEL:\n                    raise _ParsingError(\"Macro set_g2h_target expects a guest actor\")\n"
  },
  {
    "path": "rvzr/executor.py",
    "content": "\"\"\"\nFile: Architecture-independent parts of the adaptor to the executor kernel module\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom typing import TYPE_CHECKING, List, Tuple, Set, Generator, Optional, Final\nfrom abc import ABC\n\nimport sys\nimport subprocess\nimport os.path\n\nimport numpy as np\nimport numpy.typing as npt\n\nfrom rvzr.logs import ExecutorLogger, warning\nfrom rvzr.config import CONF, ConfigException\nfrom rvzr.sandbox import BaseAddrTuple\nfrom rvzr.stats import FuzzingStats\nfrom rvzr.traces import HTrace, RawHTraceSample, HTraceType\nfrom rvzr.tc_components.test_case_data import save_input_sequence_as_rdbf\n\nif TYPE_CHECKING:\n    from rvzr.tc_components.test_case_code import TestCaseProgram\n    from rvzr.tc_components.test_case_data import InputData\n\nKMOutputLine = Tuple[int, int, int, int, int, int]\nReadingsArray = npt.NDArray[np.void]\n\nSTAT = FuzzingStats()\n\n\n# ==================================================================================================\n# Helper functions\n# ==================================================================================================\ndef km_write(value: str, path: str) -> None:\n    \"\"\"\n    Write a value to a file in the /sys filesystem.\n    This is used to configure the executor kernel module.\n    \"\"\"\n    subprocess.run(f\"echo -n {value} > {path}\", shell=True, check=True)\n\n\ndef _is_smt_enabled() -> bool:\n    \"\"\"\n    Check if SMT is enabled on the current CPU.\n\n    :return: True if SMT is enabled, False otherwise\n    \"\"\"\n    try:\n        out = subprocess.run(\"lscpu\", shell=True, check=True, capture_output=True)\n    except subprocess.CalledProcessError:\n        warning(\"executor\", \"Could not check if SMT is enabled. Is lscpu installed?\")\n        return True\n    for line in out.stdout.decode().split(\"\\n\"):\n        if line.startswith(\"Thread(s) per core:\"):\n            if line[-1] == \"1\":\n                return False\n            return True\n    return True\n\n\ndef _can_set_reserved() -> bool:\n    \"\"\"\n    Check if setting reserved bits is possible on the current CPU.\n    :return: True if it's possible, False otherwise\n    \"\"\"\n    actors_conf = CONF.get_actors_conf()\n    reserved_requested = False\n    for a in actors_conf:\n        if 'reserved_bit' in actors_conf[a]['data_properties'] and \\\n           actors_conf[a]['data_properties']['reserved_bit']:\n            reserved_requested = True\n            break\n        if 'reserved_bit' in actors_conf[a]['data_ept_properties'] and \\\n           actors_conf[a]['data_ept_properties']['reserved_bit']:\n            reserved_requested = True\n            break\n    if not reserved_requested:\n        return True\n\n    if CONF.instruction_set == 'arm64':\n        return False  # reserved bits are not (yet?) supported on ARM64\n\n    assert CONF.instruction_set == 'x86-64'\n    physical_bits = int(\n        subprocess.run(\n            \"lscpu | grep 'Address sizes' | awk '{print $3}'\",\n            shell=True,\n            check=True,\n            capture_output=True).stdout.decode().strip())\n    if physical_bits > 51:\n        return False\n    return True\n\n\ndef _is_kernel_module_installed() -> bool:\n    return os.path.isfile(\"/sys/rvzr_executor/trace\")\n\n\ndef _configure_kernel_module() -> None:\n    km_write(str(CONF.executor_warmups), '/sys/rvzr_executor/warmups')\n    km_write(\"1\" if CONF.enable_pre_run_flush else \"0\", \"/sys/rvzr_executor/enable_pre_run_flush\")\n    km_write(CONF.executor_mode, \"/sys/rvzr_executor/measurement_mode\")\n\n\ndef _read_trace(n_reps: int,\n                n_inputs: int,\n                arch_mode: bool = False) -> Generator[Tuple[int, int, KMOutputLine], None, None]:\n    \"\"\"\n    ProgramGenerator function that reads and parses the output of the kernel module.\n    The generator handles the batched output of the kernel module and yields the traces one by one.\n    The traces are read in reverse order.\n\n    Example:\n    Assume the kernel module output for n_reps=2 and n_inputs=2 is:\n    ```\n    htrace1, pfc0, .., pfc4\n    htrace0, pfc0, .., pfc4\n    done\n    htrace1, pfc0, .., pfc4\n    htrace0, pfc0, .., pfc4\n    done\n    ```\n    then the generator will yield the following tuples:\n    ```\n    (0, 1, [htrace1, pfc0, .., pfc4])\n    (0, 0, [htrace0, pfc0, .., pfc4])\n    (1, 1, [htrace1, pfc0, .., pfc4])\n    (1, 0, [htrace0, pfc0, .., pfc4])\n    ```\n\n    :param n_reps: number of repetitions of the measurements\n    :param n_inputs: number of inputs\n    :param arch_mode: if True, the kernel module is in architecture mode\n    :return: a generator that yields a tuple (repetition, input_id, htrace, [pfc1, ..., pfc5])\n    :raises IOError: if the kernel module output is malformed\n    \"\"\"\n    if n_inputs <= 0:\n        return\n\n    rep_id = 0\n    last_input_id = n_inputs - 1\n    while rep_id < n_reps:\n        input_id: int = last_input_id\n        reading_finished: bool = False\n        while not reading_finished:\n            # read the next batch of traces from the kernel module\n            output = subprocess.check_output(\n                f\"taskset -c {CONF.executor_taskset} cat /sys/rvzr_executor/trace\", shell=True)\n            lines = output.decode().split(\"\\n\")\n\n            # parse the output\n            for line in lines:\n                # print(rep_id, input_id, line)\n                # skip empty lines\n                if not line:\n                    continue\n\n                # we reached the end of the batch? read the next batch\n                if 'done' in line:\n                    reading_finished = True\n                    break\n\n                # transform the line into a sequence of ints\n                line_ints = tuple(int(x) for x in line.split(\",\"))\n\n                # if the line width is unexpected, it's an error\n                if len(line_ints) != 6:\n                    warning(\"executor\", f\"Unexpected line width: {len(line_ints)}\")\n                    _rewind_km_output_to_end()\n                    raise IOError()\n\n                # if the hardware trace is zero, it's an error (except for arch mode)\n                if line_ints[0] == 0 and not arch_mode:\n                    warning(\"executor\", \"Kernel module error; see dmesg for details\")\n                    _rewind_km_output_to_end()\n                    raise IOError()\n\n                # yield the trace\n                yield rep_id, input_id, line_ints\n\n                # move to next input\n                input_id -= 1\n                if input_id < 0:\n                    # if we reached the end of a repetition, restart the input counter\n                    input_id = last_input_id\n                    rep_id += 1\n        assert input_id == last_input_id, f\"input_id: {input_id}, rep_id: {rep_id}\"\n    return\n\n\ndef _rewind_km_output_to_end() -> None:\n    \"\"\"\n    Read to the end of the kernel module output, until the 'done' line.\n    \"\"\"\n    while True:\n        output = subprocess.check_output(\n            f\"taskset -c {CONF.executor_taskset} cat /sys/rvzr_executor/trace\", shell=True)\n        if 'done' in output.decode():\n            break\n\n\n# ==================================================================================================\n# Public: Implementation of the python adapter to the executor kernel module\n# ==================================================================================================\nclass Executor(ABC):\n    \"\"\"\n    Interface for the executor module. The executor is a module responsible for executing\n    test cases on the CPU-under-test and collecting the corresponding hardware traces.\n\n    The high-level workflow is as follows:\n    1. Load the test case code into the kernel module.\n    2. Load the test case data (i.e., input sequence) into the kernel module.\n    3. Run the measurements by calling the kernel module (see _get_raw_measurements). Each\n       measurement is repeated `n_reps` times.\n    4. Aggregate the measurements into sets of traces (see _aggregate_measurements).\n    \"\"\"\n\n    _curr_test_case: Optional[TestCaseProgram] = None\n    _ignore_list: Set[int]\n    _log: Final[ExecutorLogger]\n    _TSC_MASK: Final[np.uint64] = np.uint64(0x0FFFFFFFFFFFFFF0)\n\n    _enable_mismatch_check_mode: Final[bool]\n    \"\"\" mismatch_check_mode: If True, the executor will return GPR values instead of\n    hardware traces, which is used to check for mismatches between the model and the executor \"\"\"\n\n    def __init__(self, enable_mismatch_check_mode: bool = False, skip_setup: bool = False):\n        super().__init__()\n        self._enable_mismatch_check_mode = enable_mismatch_check_mode\n\n        self._ignore_list = set()\n        self._log = ExecutorLogger()\n        if skip_setup:\n            warning(\"executor\", \"Executor starting without setting up the kernel module\")\n            return\n\n        # Check the execution environment:\n        if _is_smt_enabled() and not enable_mismatch_check_mode:\n            warning(\"executor\", \"SMT is on! You may experience false positives.\")\n        if not _can_set_reserved():\n            raise ConfigException(\"Cannot set reserved bits on this CPU\")\n\n        # Initialize the kernel module\n        if not _is_kernel_module_installed():\n            print(\"x86 executor: kernel module not installed\\n\\n\"\n                  \"Go to https://microsoft.github.io/side-channel-fuzzer/quick-start/ for \"\n                  \"installation instructions.\")\n            sys.exit(1)\n        _configure_kernel_module()\n        self._set_vendor_specific_features()\n\n    # ==============================================================================================\n    # Public Interface: Test Case Loading and Tracing\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        \"\"\"\n        Load a test case into the executor.\n        This function must be called before calling `trace_test_case`.\n\n        This function also sets the mismatch check mode in the kernel module if requested.\n        The flag has to be set before loading the test case because the kernel module links\n        the test case code with different measurement functions based on this flag.\n\n        :param test_case: the test case object to load\n        :return: None\n        \"\"\"\n        # enable mismatch check mode if requested\n        km_write(\"1\" if self._enable_mismatch_check_mode else \"0\",\n                 \"/sys/rvzr_executor/enable_dbg_gpr_mode\")\n\n        # write the test case to the kernel module\n        test_case.get_obj().save_rcbf('/sys/rvzr_executor/test_case')\n        self._curr_test_case = test_case\n\n        # reset the ignore list; as we are testing a new program now, the old ignore list is not\n        # relevant anymore\n        self._ignore_list = set()\n\n    def trace_test_case(self, inputs: List[InputData], n_reps: int) -> List[HTrace]:\n        \"\"\" Call the executor kernel module to collect the hardware traces for\n         the test case (previously loaded with `load_test_case`) and the given inputs.\n\n        :param inputs: list of inputs to be used for the test case\n        :param n_reps: number of times to repeat each measurement\n        :return: a list of HTrace objects, one for each input\n        :raises IOError: if the kernel module output is malformed\n        \"\"\"\n        # Skip if it's a dummy call\n        if not inputs:\n            return []\n        n_inputs = len(inputs)\n\n        # Skip if all inputs are ignored\n        if n_inputs <= len(self._ignore_list):\n            warning(\"executor\", \"All inputs are ignored. Skipping measurements\")\n            return [HTrace.empty_trace() for _ in range(n_inputs)]\n\n        # Store statistics\n        STAT.executor_reruns += n_reps * n_inputs\n\n        # Transfer inputs to the kernel module\n        # TODO: that's a quick-and-dirty optimization to reduce the number of KM calls;\n        # it should be rewritten\n        input_sequence = inputs if n_reps % 5 != 0 or n_inputs >= 1000 else inputs * 5\n        save_input_sequence_as_rdbf(input_sequence, '/sys/rvzr_executor/inputs')\n\n        # Check that the transfer was successful\n        with open('/sys/rvzr_executor/inputs', 'r') as f:\n            if f.readline() != '1\\n':\n                raise IOError(\"Error writing inputs to the kernel module\")\n\n        # Call the kernel module and read traces\n        all_readings: ReadingsArray = np.ndarray(shape=(n_inputs, n_reps), dtype=RawHTraceSample)\n        for rep_id, input_id, readings in \\\n                _read_trace(n_reps, n_inputs, arch_mode=self._enable_mismatch_check_mode):\n            all_readings[input_id][rep_id] = readings\n\n        # Post-process results and return a list of HTrace objects\n        traces = self._raw_readings_to_traces(all_readings, n_inputs)\n        self._log.dbg_dump_raw_traces(traces)\n        return traces\n\n    def _identify_trace_type(self) -> HTraceType:\n        \"\"\" Identify the type of the traces based on the configuration \"\"\"\n        if self._enable_mismatch_check_mode:\n            return \"reg\"\n        if CONF.executor_mode == 'TSC':\n            return \"tsc\"\n        return \"cache\"\n\n    def _raw_readings_to_traces(self, all_readings: ReadingsArray, n_inputs: int) -> List[HTrace]:\n        \"\"\" Convert the raw readings into HTrace objects and perform post-processing if needed \"\"\"\n        traces = []\n        trace_type = self._identify_trace_type()\n        for input_id in range(n_inputs):\n            raw = all_readings[input_id]\n\n            # No post-processing in mismatch check mode\n            if self._enable_mismatch_check_mode:\n                traces.append(HTrace(raw, trace_type))\n                continue\n\n            # Zero-out traces for ignored inputs\n            if input_id in self._ignore_list:\n                traces.append(HTrace.invalid_trace(trace_type))\n                continue\n\n            # When using TSC mode, we need to mask the lower 4 bits of the trace\n            if CONF.executor_mode == 'TSC':\n                raw['trace'] &= self._TSC_MASK\n\n            traces.append(HTrace(raw, trace_type))\n        return traces\n\n    # ==============================================================================================\n    # Public Interface: Base Addresses\n    def read_base_addresses(self) -> BaseAddrTuple:\n        \"\"\"\n        Reads the base addresses of two sandbox regions (data and code) from the executor\n        kernel module and returns them as a tuple.\n        This data is primarily used to synchronize the memory layout between the executor\n        and the model.\n        :return: a tuple with the base addresses of the data and code regions\n        \"\"\"\n\n        with open('/sys/rvzr_executor/print_data_base', 'r') as f:\n            data_start = f.readline()\n        with open('/sys/rvzr_executor/print_code_base', 'r') as f:\n            code_start = f.readline()\n        return int(data_start, 16), int(code_start, 16)\n\n    # ==============================================================================================\n    # Public Interface: Ignore List\n    def set_ignore_list(self, ignore_list: List[int]) -> None:\n        \"\"\"\n        Sets a list of inputs IDs that should be ignored by the executor.\n        The executor will executed the inputs with these IDs as normal (in case they are\n        necessary for priming the uarch state), but their htraces will be set to zero\n\n        :param ignore_list: a list of input IDs to ignore\n        \"\"\"\n        self._ignore_list = set(ignore_list)\n\n    def extend_ignore_list(self, ignore_list: List[int]) -> None:\n        \"\"\"\n        Add a list of new inputs IDs to the current ignore list.\n\n        :param ignore_list: a list of input IDs to add to the ignore list\n        \"\"\"\n        self._ignore_list.update(ignore_list)\n\n    # ==============================================================================================\n    # Public Interface: Quick and Dirty Mode\n    def set_quick_and_dirty(self, state: bool) -> None:\n        \"\"\"\n        Enable or disable the quick and dirty mode in the executor. In this mode, the executor\n        will skip some of the stabilization phases, which will make the measurements faster but\n        less reliable.\n\n        :param state: True to enable the quick and dirty mode, False to disable it\n        \"\"\"\n        km_write(\"1\" if state else \"0\", \"/sys/rvzr_executor/enable_quick_and_dirty_mode\")\n\n    # ==============================================================================================\n    # Private Interface: Vendor-specific Features\n    def _set_vendor_specific_features(self) -> None:\n        \"\"\" Set vendor-specific features in the kernel module \"\"\"\n"
  },
  {
    "path": "rvzr/executor_km/.clang-format",
    "content": "ColumnLimit: 100\nIndentWidth: 4\nBreakBeforeBraces: Linux\nAllowShortIfStatementsOnASingleLine: false\nAlignConsecutiveMacros:\n  Enabled: true\n  AcrossEmptyLines: false\n  AcrossComments: false\n"
  },
  {
    "path": "rvzr/executor_km/.gitignore",
    "content": ".tmp*\n*.o\n*.cmd\n*.symvers\n*.order\n*.ko\n*.mod\n*.mod.c\nstart_qemu.sh\nupdate_module.sh"
  },
  {
    "path": "rvzr/executor_km/Makefile",
    "content": "NAME = rvzr_executor\nKDIR=/lib/modules/$(shell uname -r)/build\n\n# ==================================================================================================\n# HW configuration\n# ==================================================================================================\n# Determine CPU vendor\nifneq ($(shell grep \"Intel\" /proc/cpuinfo),)\nVENDOR_ID = 1\nelse ifneq ($(shell grep \"AMD\" /proc/cpuinfo),)\nVENDOR_ID = 2\nelse ifneq ($(shell lscpu | grep \"aarch64\"),)\nVENDOR_ID = 3\nelse\n$(error \"Unknown CPU vendor\")\nendif\n\n# Physical address width\nPHYSICAL_WIDTH = $(shell grep -m1 \"bits physical,\" /proc/cpuinfo | awk '{print $$4}')\n\n# Cache configuration\nifeq ($(shell ls /sys/devices/system/cpu/cpu0/cache/index0/ways_of_associativity 2>/dev/null),)\n$(error \"Cache info not found\")\nendif\nL1D_ASSOCIATIVITY=$(shell cat /sys/devices/system/cpu/cpu0/cache/index0/ways_of_associativity)\nL1D_SIZE_KB=$(shell cat /sys/devices/system/cpu/cpu0/cache/index0/size | sed 's/[^0-9]*//g')\n\n# Check if VMBUILD is required\nifndef VMBUILD\nsys_vendor = $(shell cat /sys/devices/virtual/dmi/id/sys_vendor)\nis_vm_vendor = 'no'\nifeq ($(sys_vendor), QEMU)\nis_vm_vendor = 'yes'\nendif\nifeq ($(sys_vendor), Microsoft Corporation)\nis_vm_vendor = 'yes'\nendif\nendif\n\n# ==================================================================================================\n# Files\n# ==================================================================================================\n# Object files\nSRC_ = input_parser.c test_case_parser.c code_loader.c data_loader.c sandbox_manager.c \\\n\tmeasurement.c main.c macro_expansion.c page_tables_host.c\nSRC_X86 = x86/vmx.c x86/svm.c x86/perf_counters.c x86/page_tables_guest.c \\\n\tx86/special_registers.c x86/idt.c x86/macros.c\nASM_X86 = x86/fault_handlers.S\nSRC_ARM64 = arm64/fault_handler.c arm64/perf_counters.c arm64/special_registers.c \\\n\tarm64/page_tables_guest.c arm64/macros.c\nASM_ARM64 = arm64/exception.S\n\nifeq ($(VENDOR_ID), 1)\nSRC_ += $(SRC_X86)\nASM_ += $(ASM_X86)\nelse ifeq ($(VENDOR_ID), 2)\nSRC_ += $(SRC_X86)\nASM_ += $(ASM_X86)\nelse ifeq ($(VENDOR_ID), 3)\nSRC_ += $(SRC_ARM64)\nASM_ += $(ASM_ARM64)\nendif\n\nobj-m += $(NAME).o\n$(NAME)-objs += $(SRC_:.c=.o) $(ASM_:.S=.o)\n\n# ==================================================================================================\n# Flags\n# ==================================================================================================\n# Common build flags\nCOMMON_CFLAGS := -I$(src)/include\nCOMMON_CFLAGS += -std=gnu11 -Wno-declaration-after-statement -Wno-comment\nCOMMON_CFLAGS += -g -DDEBUG\nCOMMON_CFLAGS += -DVENDOR_ID=$(VENDOR_ID)\nCOMMON_CFLAGS += -DFORCE_SMAP_OFF\nCOMMON_CFLAGS += -DPHYSICAL_WIDTH=$(PHYSICAL_WIDTH)\nCOMMON_CFLAGS += -DL1D_ASSOCIATIVITY=$(L1D_ASSOCIATIVITY)\nCOMMON_CFLAGS += -DL1D_SIZE_KB=$(L1D_SIZE_KB)\n\nifeq ($(VENDOR_ID), 1)\nCOMMON_CFLAGS += -msse2\nelse ifeq ($(VENDOR_ID), 2)\nCOMMON_CFLAGS += -msse2\nendif\n\nifdef VMBUILD\nCOMMON_CFLAGS += -DVMBUILD\nendif\n\n# Set both EXTRA_CFLAGS (old kernels) and ccflags-y (new kernels) for compatibility\nEXTRA_CFLAGS += $(COMMON_CFLAGS)\nccflags-y += $(COMMON_CFLAGS)\n\nCOMMON_AFLAGS := -I$(src)/include\nCOMMON_AFLAGS += -DVENDOR_ID=$(VENDOR_ID) -DPHYSICAL_WIDTH=$(PHYSICAL_WIDTH)\nCOMMON_AFLAGS += -DL1D_ASSOCIATIVITY=$(L1D_ASSOCIATIVITY) -DL1D_SIZE_KB=$(L1D_SIZE_KB)\n\n# Set both EXTRA_AFLAGS (old kernels) and asflags-y (new kernels) for compatibility\nEXTRA_AFLAGS += $(COMMON_AFLAGS)\nasflags-y += $(COMMON_AFLAGS)\n\n# ==================================================================================================\n# Workarounds\n# ==================================================================================================\n# Suppress objtool warnings - a lot of code in the module violates the checks intentionally,\n# so it's impossible to fix\nGREP_FILTER = \"return found in RETHUNK|indirect call found in RETPOLINE|call without frame pointer|undefined stack stae|return with modified stack frame|unsupported instruction in callable|undefined stack state|ENDBR: \"\nOBJECT_FILES_NON_STANDARD := y\n\n# File-specific flags\nCFLAGS_code_loader.o := -Wno-attribute-warning # workaround for __write_overflow_field warning\n\n# ==================================================================================================\n# build targets\n# ==================================================================================================\nall:\nifndef VMBUILD\nifeq ($(is_vm_vendor), 'yes')\n\t$(error ERROR: VM or WSL environment detected; use `make VMBUILD=1`)\nendif\nendif\n\tmake -C $(KDIR) M=$(PWD) -j modules 2>&1 | grep -vE $(GREP_FILTER)\n\nclean:\n\tmake -C $(KDIR) M=$(PWD) clean\n\ninstall:\n\tsudo insmod $(NAME).ko\n\nuninstall:\n\tsudo rmmod $(NAME) || true\n\n# --------------- debugging crushes ---------------\ndbg_symbols:\n\tobjcopy --only-keep-debug $(NAME).o $(NAME).dbg\n\tsudo cat /sys/module/$(NAME)/sections/.text\n# continue manually with gdb:\n# (if the executor is in a VM):\n#       scp vm:revizor_dir/$rvzr_executor.dbg .\n# gdb -ex \"target remote localhost:1234\" -ex \"set substitute-path /home/revizor_dir /home/revizor_dir\"\n#   target remote localhost:1234\n#   add-symbol-file rvzr_executor.dbg <addr>  # addr is the address printed by the cat command above\n#   b run_experiment  # or any other function that you want to debug\n#   (if guest VM path does not match the host path):\n#       set substitute-path /home/revizor_dir /home/revizor_dir\n#   c\n"
  },
  {
    "path": "rvzr/executor_km/arm64/asm_snippets.h",
    "content": "/// File: Building blocks for creating macros; ARM64 version\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _ARM64_ASM_SNIPPETS_H_\n#define _ARM64_ASM_SNIPPETS_H_\n\n#include \"hardware_desc.h\"\n#include \"measurement.h\"\n#include \"registers.h\"\n\n/// State machine of the tracing process\n#define SET_SR_STARTED()                                                                           \\\n    \"and \" STATUS_REGISTER_32 \", \" STATUS_REGISTER_32 \", #0xFFFFFF00 \\n\"                           \\\n    \"orr \" STATUS_REGISTER_32 \", \" STATUS_REGISTER_32 \", \" xstr(STATUS_STARTED) \" \\n\"\n#define SET_SR_ENDED()                                                                             \\\n    \"and \" STATUS_REGISTER_32 \", \" STATUS_REGISTER_32 \", #0xFFFFFF00 \\n\"                           \\\n    \"orr \" STATUS_REGISTER_32 \", \" STATUS_REGISTER_32 \", \" xstr(STATUS_ENDED) \" \\n\"\n#define TEST_SR_ENDED()                                                                            \\\n    \"mov x16, \" STATUS_REGISTER \" \\n\"                                                              \\\n    \"and x16, x16, #0xFF \\n\"                                                                       \\\n    \"cmp x16, \" xstr(STATUS_ENDED) \" \\n\"\n\n/// ================================================================================================\n/// Shortcuts\n/// ================================================================================================\n#define SPEC_FENCE()      \"dsb SY \\n isb \\n\"\n#define CACHE_FLUSH(ADDR) \"dc civac, \" ADDR \"\\n\"\n\n// clang-format off\n#define mov_imm_to_reg(DEST, SRC)                                                                      \\\n    \"movz \" DEST \", #(\" xstr(SRC) \") & 0xFFFF, lsl #0 \\n\"                                          \\\n    \"movk \" DEST \", #(\" xstr(SRC) \" >> 16) & 0xFFFF, lsl #16 \\n\"                                   \\\n    \"movk \" DEST \", #(\" xstr(SRC) \" >> 32) & 0xFFFF, lsl #32 \\n\"                                   \\\n    \"movk \" DEST \", #(\" xstr(SRC) \" >> 48) & 0xFFFF, lsl #48 \\n\"\n// clang-format on\n\n/// ================================================================================================\n/// MSR and Performance Counter accessors\n/// ================================================================================================\n\n// clobber: x16\n#define READ_MSR_START(ID, DEST)                                                                   \\\n    SPEC_FENCE()                                                                                   \\\n    \"mov \" DEST \", #0 \\n\"                                                                          \\\n    \"mrs x16, \" ID \" \\n\"                                                                           \\\n    \"sub \" DEST \", \" DEST \", x16 \\n\"\n\n// clobber: x16\n#define READ_MSR_END(ID, DEST)                                                                     \\\n    SPEC_FENCE()                                                                                   \\\n    \"mrs x16, \" ID \" \\n\"                                                                           \\\n    \"add \" DEST \", \" DEST \", x16 \\n\"\n\n// clobber: x16 (dest)\n#define READ_ONE_PFC(ID, DEST)                                                                     \\\n    \"mov \" DEST \", \" ID \" \\n\"                                                                      \\\n    \"msr pmselr_el0, \" DEST \" \\n\"                                                                  \\\n    \"mrs \" DEST \", pmxevcntr_el0 \\n\"\n\n// clobber: x16, PFC0, PFC1, PFC2\n// clang-format off\n#define READ_PFC_START() \\\n        SPEC_FENCE() \\\n        \"mov \" PFC0 \", #0 \\n\" \\\n        \"mov \" PFC1 \", #0 \\n\" \\\n        \"mov \" PFC2 \", #0 \\n\" \\\n        READ_ONE_PFC(\"1\", \"x16\") \\\n        \"sub \" PFC0 \", \" PFC0 \", x16 \\n\" \\\n        READ_ONE_PFC(\"2\", \"x16\") \\\n        \"sub \" PFC1 \", \" PFC1 \", x16 \\n\"\n\n// clobber: rax, rcx, rdx\n#define READ_PFC_END() \\\n        SPEC_FENCE() \\\n        READ_ONE_PFC(\"1\", \"x16\") \\\n        \"add \" PFC0 \", \" PFC0 \", x16 \\n\" \\\n        READ_ONE_PFC(\"2\", \"x16\") \\\n        \"add \" PFC1 \", \" PFC1 \", x16 \\n\"\n// clang-format on\n\n/// ================================================================================================\n/// Detection of Interrupts\n/// ================================================================================================\n/// @brief Start monitoring SMIs by reading the current value of the SMI counter (MSR ???)\n///        and storing it in the STATUS_REGISTER[63:32]\n///  clobber:\n#define READ_SMI_START() // FIXME: unimplemented\n\n/// @brief End monitoring SMIs by reading the current value of the SMI counter (MSR ???))\n///        and storing the difference between the current and the previous value\n///        in the STATUS_REGISTER[31:0]\n/// clobber: x1 [dest]\n#define READ_SMI_END() // FIXME: unimplemented\n\n/// ================================================================================================\n/// Pre- and Post- measurement macros\n/// ================================================================================================\n\n/// @brief Loading of register values from the main actor's memory\n/// clobber: x0-x7, nzcv, sp\n// clang-format off\n#define SET_REGISTER_FROM_INPUT() \\\n    asm volatile(\"\\n\"   \\\n    \"mov x0, #\"xstr(REG_INIT_OFFSET)\" \\n\" \\\n    \"add sp, \"MEMORY_BASE_REGISTER\", x0 \\n\" \\\n    \"ldp x0, x1, [sp], #16\\n\" \\\n    \"ldp x2, x3, [sp], #16\\n\" \\\n    \"ldp x4, x5, [sp], #16\\n\" \\\n    \"ldp x6, x7, [sp], #16\\n\" \\\n    \"msr nzcv, x6\\n\" \\\n    \"mov sp, x7\\n\");\n// clang-format on\n\n/// ================================================================================================\n/// Measurement primitives\n/// ================================================================================================\n\n// clang-format off\n#if L1D_ASSOCIATIVITY == 2\n#define PRIME_ONE_SET(BASE, OFFSET, TMP, ACC) \\\n    \"mov \"TMP\", \"BASE\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"OFFSET\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\"\n#elif L1D_ASSOCIATIVITY == 4\n#define PRIME_ONE_SET(BASE, OFFSET, TMP, ACC) \\\n    \"mov \"TMP\", \"BASE\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"OFFSET\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\"\n#elif L1D_ASSOCIATIVITY == 8\n#define PRIME_ONE_SET(BASE, OFFSET, TMP, ACC) \\\n    \"mov \"TMP\", \"BASE\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"OFFSET\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\" \\\n    \"add \"TMP\", \"TMP\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"add \"TMP\", \"TMP\", \"ACC\" \\n\" \\\n    \"ldr \"ACC\", [\"TMP\"]\\n\"\n#else\n#error \"Unsupported L1D_ASSOCIATIVITY\"\n#endif\n// clang-format on\n\n/// @brief Prime part of the Prime+Probe attack\n// clobber: none\n// clang-format off\n#define PRIME(BASE, OFFSET, TMP, DEPENDENCY_REGISTER, REP_COUNTER, MAX_REPS) \\\n    SPEC_FENCE() \\\n    \"mov \"REP_COUNTER\", \"MAX_REPS\"\\n\" \\\n    \"1: \\n\" \\\n        \"mov \"OFFSET\", 0 \\n\" \\\n        \"mov \"DEPENDENCY_REGISTER\", 0 \\n\" \\\n        \"2: \\n\" \\\n            SPEC_FENCE() \\\n            PRIME_ONE_SET(BASE, OFFSET, TMP, DEPENDENCY_REGISTER) \\\n            \"add \"OFFSET\", \"OFFSET\", #64 \\n\" \\\n            \"cmp \"OFFSET\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n            \"b.lt 2b \\n\" \\\n        \"sub \"REP_COUNTER\", \"REP_COUNTER\", #1 \\n\" \\\n        \"cmp \"REP_COUNTER\", xzr \\n\" \\\n        \"b.ne 1b \\n\" \\\n    SPEC_FENCE()\n// clang-format on\n\n// clang-format off\n/// @brief Probe part of the Prime+Probe attack\n// clobber: none\n// clang-format off\n#define PROBE(BASE, OFFSET, DEPENDENCY_REGISTER, EVICT_COUNT, TMP, TRACE) \\\n    \"mov \"TRACE\", 0 \\n\" \\\n    \"mov \"DEPENDENCY_REGISTER\", 0 \\n\" \\\n    \"mov \"OFFSET\", #\"xstr(L1D_CONFLICT_DISTANCE)\" \\n\" \\\n    \"sub \"OFFSET\", \"OFFSET\", #64 \\n\" \\\n    \"1: \\n\" \\\n        SPEC_FENCE() \\\n        READ_ONE_PFC(\"0\", EVICT_COUNT) \\\n        SPEC_FENCE() \\\n        PRIME_ONE_SET(BASE, OFFSET, TMP, DEPENDENCY_REGISTER) \\\n        SPEC_FENCE() \\\n        READ_ONE_PFC(\"0\", TMP) \\\n        \"cmp \"TMP\", \"EVICT_COUNT\" \\n\" \\\n        \"b.eq 2f \\n\" \\\n        \"  orr \"TRACE\", \"TRACE\", #1 \\n\" \\\n        \"2: \\n\" \\\n        \"mov \"TRACE\", \"TRACE\", ror #1 \\n\" \\\n        \"sub \"OFFSET\", \"OFFSET\", #64 \\n\" \\\n        \"cmp \"OFFSET\", xzr \\n\" \\\n        \"b.ge 1b \\n\" \\\n    SPEC_FENCE()\n// clang-format on\n\n/// @brief Flush part of the Flush+Reload\n// clobber: none\n// clang-format off\n#define FLUSH(BASE, OFFSET, TMP) \\\n    \"mov \"OFFSET\", #0 \\n\" \\\n    \"1: \\n\" \\\n        \"add \"TMP\", \"BASE\", \"OFFSET\" \\n\" \\\n        CACHE_FLUSH(TMP) \\\n        \"add \"OFFSET\", \"OFFSET\", #64 \\n\" \\\n        \"cmp \"OFFSET\", #0x1000\\n\" \\\n        \"b.lt 1b \\n\" \\\n    SPEC_FENCE()\n// clang-format on\n\n/// @brief Reload part of the Flush+Reload\n// clobber: none\n// clang-format off\n#define RELOAD(BASE, OFFSET, TMP, EVICT_COUNT, TRACE) \\\n    \"mov \"OFFSET\", 0 \\n\" \\\n    \"mov \"TRACE\", 0 \\n\" \\\n    \"1: \\n\" \\\n        SPEC_FENCE() \\\n        READ_ONE_PFC(\"0\", EVICT_COUNT) \\\n        SPEC_FENCE() \\\n        \"add \"TMP\", \"BASE\", \"OFFSET\" \\n\" \\\n        \"ldr \"TMP\", [\"TMP\"] \\n\" \\\n        SPEC_FENCE() \\\n        READ_ONE_PFC(\"0\", TMP) \\\n        \"mov \"TRACE\", \"TRACE\", lsl #1 \\n\" \\\n        \"cmp \"TMP\", \"EVICT_COUNT\" \\n\" \\\n        \"b.ne 2f \\n\" \\\n        \"  orr \"TRACE\", \"TRACE\", #1 \\n\" \\\n        \"2: \\n\" \\\n        \"add \"OFFSET\", \"OFFSET\", #64 \\n\" \\\n        \"cmp \"OFFSET\", #0x1000 \\n\" \\\n        \"b.lt 1b \\n\" \\\n    SPEC_FENCE()\n// clang-format on\n\n#endif // _ARM64_ASM_SNIPPETS_H_\n"
  },
  {
    "path": "rvzr/executor_km/arm64/entry_exit_points.h",
    "content": "/// File: Test case entry and exit points; used by code_loader.c\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n// -----------------------------------------------------------------------------------------------\n// Note on registers.\n// Some of the registers are reserved for a specific purpose and should never be overwritten.\n// See ./docs/registers.md for more information.\n\n#ifndef _ENTRY_EXIT_H_\n#define _ENTRY_EXIT_H_\n\n#include \"asm_snippets.h\"\n\n#define TEMPLATE_START                     0x0000111100001111\n#define TEMPLATE_INSERT_TC                 0x0000222200002222\n#define TEMPLATE_DEFAULT_EXCEPTION_LANDING 0x0000333300003333\n#define TEMPLATE_END                       0x0000444400004444\n#define TEMPLATE_MARKER_SIZE               8\n\n// clang-format off\nstatic inline void prologue(void)\n{\n    // As we don't use a compiler to track clobbering,\n    // we have to save the callee-saved regs\n    asm volatile(\"\" \\\n        \"stp x16, x17, [sp, #-16]!\\n\"\n        \"stp x18, x19, [sp, #-16]!\\n\"\n        \"stp x20, x21, [sp, #-16]!\\n\"\n        \"stp x22, x23, [sp, #-16]!\\n\"\n        \"stp x24, x25, [sp, #-16]!\\n\"\n        \"stp x26, x27, [sp, #-16]!\\n\"\n        \"stp x28, x29, [sp, #-16]!\\n\"\n        \"str x30, [sp, #-16]!\\n\"\n\n        // x20 = main_area of actor 0 (passed in x0, the first argument of measurement_code)\n        \"mov \"MEMORY_BASE_REGISTER\", x0\\n\"\n\n        // x21 = sandbox->util (x20 - UTIL_REL_TO_MAIN)\n        \"mov \"UTIL_BASE_REGISTER\", \"MEMORY_BASE_REGISTER\"\\n\"\n        mov_imm_to_reg(\"x0\", UTIL_REL_TO_MAIN)\n        \"sub \"UTIL_BASE_REGISTER\", \"UTIL_BASE_REGISTER\", x0\\n\"\n\n        // sandbox->util->stored_rsp = sp\n        \"mov x0, sp\\n\"\n        \"mov x1, #\"xstr(STORED_RSP_OFFSET)\"\\n\"\n        \"add x1, \"UTIL_BASE_REGISTER\", x1\\n\"\n        \"str x0, [x1]\\n\"\n\n        // clear the rest of the registers\n        \"mov x0, 0\\n\"\n        \"mov x1, 0\\n\"\n        \"mov x2, 0\\n\"\n        \"mov x3, 0\\n\"\n        \"mov x4, 0\\n\"\n        \"mov x5, 0\\n\"\n        \"mov x6, 0\\n\"\n        \"mov x7, 0\\n\"\n        \"mov x8, 0\\n\"\n        \"mov x9, 0\\n\"\n        \"mov x10, 0\\n\"\n        \"mov x11, 0\\n\"\n        \"mov x12, 0\\n\"\n        \"mov x13, 0\\n\"\n        \"mov x14, 0\\n\"\n        \"mov x15, 0\\n\"\n\n        // initialize special registers\n        \"mov \"HTRACE_REGISTER\", 0\\n\"\n        mov_imm_to_reg(STATUS_REGISTER, STATUS_UNINITIALIZED)\n\n        // create space on stack\n        // \"mov rbp, rsp\\n\"\n        \"sub sp, sp, #0x1000\\n\"\n\n        // start monitoring interrupts\n        READ_SMI_START()\n    );\n\n\n}\n\nstatic inline void epilogue(void)\n{\n    asm volatile(\"\"\n        READ_SMI_END()\n\n        // x0 = &latest_measurement\n        \"mov x0, \"UTIL_BASE_REGISTER\"\\n\"\n        mov_imm_to_reg(\"x1\", MEASUREMENT_OFFSET)\n        \"add x0, x0, x1\\n\"\n\n        // Store the results\n        \"str \"HTRACE_REGISTER\", [x0]\\n\"     // HTrace\n        \"str \"PFC0\", [x0, #8]\\n\"            // PFC0\n        \"str \"PFC1\", [x0, #16]\\n\"           // PFC1\n        \"str \"PFC2\", [x0, #24]\\n\"           // PFC2\n        \"str xzr, [x0, #32]\\n\"              // PFC3 (unused)\n        \"str xzr, [x0, #40]\\n\"              // PFC4 (unused)\n        \"str \"STATUS_REGISTER\", [x0, #48]\\n\" // Measurement status\n\n        // rsp = sandbox->util->stored_rsp\n        mov_imm_to_reg(\"x1\", STORED_RSP_OFFSET)\n        \"add x1, \"UTIL_BASE_REGISTER\", x1\\n\"\n        \"ldr x0, [x1]\\n\"\n        \"mov sp, x0\\n\"\n\n        // restore registers\n        \"ldr x30, [sp], #16\\n\"\n        \"ldp x28, x29, [sp], #16\\n\"\n        \"ldp x26, x27, [sp], #16\\n\"\n        \"ldp x24, x25, [sp], #16\\n\"\n        \"ldp x22, x23, [sp], #16\\n\"\n        \"ldp x20, x21, [sp], #16\\n\"\n        \"ldp x18, x19, [sp], #16\\n\"\n        \"ldp x16, x17, [sp], #16\\n\"\n\n        // return 0\n        \"mov x0, 0\\n\"\n        \"ret\\n\"\n    );\n}\n\nstatic inline void epilogue_dbg_gpr(void)\n{\n    asm volatile(\"\"\n        READ_SMI_END()\n\n        // x7 = &latest_measurement\n        \"mov x7, \"UTIL_BASE_REGISTER\"\\n\"\n        mov_imm_to_reg(\"x8\", MEASUREMENT_OFFSET)\n        \"add x7, x7, x8\\n\"\n\n        // Store the results\n        \"str x0, [x7]\\n\"\n        \"str x1, [x7, #8]\\n\"\n        \"str x2, [x7, #16]\\n\"\n        \"str x3, [x7, #24]\\n\"\n        \"str x4, [x7, #32]\\n\"\n        \"str x5, [x7, #40]\\n\"\n        \"str \"STATUS_REGISTER\", [x7, #48]\\n\"\n\n        // rsp = sandbox->util->stored_rsp\n        mov_imm_to_reg(\"x0\", STORED_RSP_OFFSET)\n        \"add x0, \"UTIL_BASE_REGISTER\", x0\\n\"\n        \"ldr x0, [x0]\\n\"\n        \"mov sp, x0\\n\"\n\n        // restore registers\n        \"ldr x30, [sp], #16\\n\"\n        \"ldp x28, x29, [sp], #16\\n\"\n        \"ldp x26, x27, [sp], #16\\n\"\n        \"ldp x24, x25, [sp], #16\\n\"\n        \"ldp x22, x23, [sp], #16\\n\"\n        \"ldp x20, x21, [sp], #16\\n\"\n        \"ldp x18, x19, [sp], #16\\n\"\n        \"ldp x16, x17, [sp], #16\\n\"\n\n        // return 0\n        \"mov x0, 0\\n\"\n        \"ret\\n\"\n    );\n}\n// clang-format on\n\nstatic void main_segment_template(void)\n{\n    asm volatile(\".quad \" xstr(TEMPLATE_START));\n    prologue();\n\n    SET_REGISTER_FROM_INPUT();\n\n    // test case placeholder\n    asm volatile(\"isb\\n dsb SY \\n\");\n    asm volatile(\".quad \" xstr(TEMPLATE_INSERT_TC) \"\\n\");\n    asm volatile(\"isb\\n dsb SY \\n\");\n\n    // fault handler\n    asm volatile(\"b 1f\\n\"\n                 \".quad \" xstr(TEMPLATE_DEFAULT_EXCEPTION_LANDING) \"\\n\"\n                                                                   \"nop\\n\"\n                                                                   \"1:nop\\n\");\n\n    epilogue();\n    asm volatile(\".quad \" xstr(TEMPLATE_END));\n}\n\nstatic void main_segment_template_dbg_gpr(void)\n{\n    asm volatile(\".quad \" xstr(TEMPLATE_START));\n    prologue();\n\n    SET_REGISTER_FROM_INPUT();\n\n    // test case placeholder\n    asm volatile(\"isb\\n dsb SY \\n\");\n    asm volatile(\".quad \" xstr(TEMPLATE_INSERT_TC) \"\\n\");\n    asm volatile(\"isb\\n dsb SY \\n\");\n\n    asm volatile(\"b 1f\\n\"\n                 \".quad \" xstr(TEMPLATE_DEFAULT_EXCEPTION_LANDING) \"\\n\"\n                                                                   \"nop\\n\"\n                                                                   \"1:nop\\n\");\n\n    epilogue_dbg_gpr();\n    asm volatile(\".quad \" xstr(TEMPLATE_END));\n}\n\n#endif // _ENTRY_EXIT_H_\n"
  },
  {
    "path": "rvzr/executor_km/arm64/exception.S",
    "content": "// File: Low-level exception handling code for ARM64\n//\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <asm/asm-bug.h>\n// #include <asm/exception.h>\n\n#include \"sandbox_constants.h\"\n#include \"registers.h\"\n\n.extern _printk\n.extern set_outer_fault_handlers\n.extern unset_outer_fault_handlers\n.extern run_experiment\n.extern orig_vector_table_ptr\n.extern loaded_test_case_entry\n\n.extern fault_handler\n.extern is_nested_fault\n\n// =================================================================================================\n// Global variables\n// =================================================================================================\n.data\n\nrecovery_sp:\n.quad 0\n\n.Lunreachable_msg:\n.asciz\t\"ERROR: rvzr_executor: unreachable code\\n\"\n.align\n\n.Lunexpected_fault_msg:\n.asciz\t\"\\0013 ERROR: [fallback_handler] Unexpected fault in run_experiment: \\n\\\n1 [Error Syndrome]:\\t0x%llx\\n\\\n2 [Fault Address]:\\t0x%llx\\n\\\n  (run_experiment start:\\t0x%llx)\\n\\\n3 [SP]:\\t0x%llx\\n\"\n.align\n\n.Ldefault_fault_msg:\n.asciz\t\"\\0013 ERROR: [test_case_handler] Unhandled fault in the test case: \\n\\\n1 [Error Syndrome]:\\t0x%llx\\n\\\n2 [Fault Address]:\\t0x%llx\\n\\\n  (test case start:\\t0x%llx)\\n\\\n3 [SP]:\\t0x%llx\\n\"\n.align\n\n\n// =================================================================================================\n// Macros\n// =================================================================================================\n.macro unreachable\n    adr\tx0, .Lunreachable_msg\n    mov\tx2, lr\n    b panic\n.endm\n\n\n// =================================================================================================\n// Custom vector tables\n// =================================================================================================\n.text\n\n/// @brief The vector table used for catching bugs in run_experiment function\n///        This table exists purely for convenience during the development process and\n///        should be unreachable during normal operation\n.global outer_vector_table\n.balign 2048\nouter_vector_table:\n    // Interrupts/exceptions from EL1 to EL1; using SP_EL1 as sp\n        b fallback_handler\n    .balign 0x80\n        b fallback_handler\n    .balign 0x80\n        b fallback_handler\n    .balign 0x80\n        b fallback_handler\n    // Interrupts/exceptions from EL1 to EL1; using SP_ELX as sp\n    .balign 0x80\n        b fallback_handler\n    .balign 0x80\n        b fallback_handler\n    .balign 0x80\n        b fallback_handler\n    .balign 0x80\n        b fallback_handler\n    // Interrupts/exceptions from EL0 to EL1; in AArch64 mode\n    .balign 0x80\n        b fallback_handler\n    .balign 0x80\n        b fallback_handler\n    .balign 0x80\n        b fallback_handler\n    .balign 0x80\n        b fallback_handler\n    // Interrupts/exceptions from EL0 to EL1; in AArch32 mode\n    .balign 0x80\n        unreachable\n    .balign 0x80\n        unreachable\n    .balign 0x80\n        unreachable\n    .balign 0x80\n        unreachable\n\n/// @brief The vector table used for redirecting exceptions in tests cases to the recovery code\n///        In contrast to the outer_vector_table, this table is actively used during normal\n///        operation, especially when testing for Meltdown-like vulnerabilities\n.global inner_vector_table\n.balign 2048\ninner_vector_table:\n    // Interrupts/exceptions from EL1 to EL1; using SP_EL1 as sp (EL1t)\n    .balign 0x80   // Synchronous\n        b test_case_handler\n    .balign 0x80  // IRQ\n        b test_case_handler\n    .balign 0x80  // FIQ\n        b test_case_handler\n    .balign 0x80  // SError\n        b test_case_handler\n    // Interrupts/exceptions from EL1 to EL1; using SP_ELX as sp (EL1h)\n    .balign 0x80  // Synchronous\n        // Handle all other exceptions\n        b test_case_handler\n    .balign 0x80  // IRQ\n        b test_case_handler\n    .balign 0x80  // FIQ\n        b test_case_handler\n    .balign 0x80  // SError\n        b test_case_handler\n    // Interrupts/exceptions from EL0 to EL1; in AArch64 mode\n    .balign 0x80   // Synchronous\n        b test_case_handler\n    .balign 0x80  // IRQ\n        b test_case_handler\n    .balign 0x80  // FIQ\n        b test_case_handler\n    .balign 0x80  // SError\n        b test_case_handler\n    // Interrupts/exceptions from EL0 to EL1; in AArch32 mode\n    .balign 0x80\n        b test_case_handler\n    .balign 0x80\n        b test_case_handler\n    .balign 0x80\n        b test_case_handler\n    .balign 0x80\n        b test_case_handler\n\n// =================================================================================================\n// Handlers\n// =================================================================================================\n.text\n\n/// @brief The default handler that terminates the tracing process\n///        by directly jumping to the exit point of the run_experiment function\n.global fallback_handler\nfallback_handler:\n    mrs x1, esr_el1\n    mrs x2, far_el1  // FIXME: this doesn't work for many exception types\n    adr x3, run_experiment\n    mov x4, sp\n    adr x5, .Lunexpected_fault_msg\n\n    // set the return address to the recovery code\n    adr x0, .run_experiment_recovery\n    msr elr_el1, x0\n    eret\n\n\n/// @brief A handler for all exceptions that occur during the test case execution\n.global test_case_handler\ntest_case_handler:\n    // check for nested faults\n    adr TMP_REG1_, is_nested_fault\n    ldr TMP_REG2_, [TMP_REG1_]\n    cmp TMP_REG2_, #0\n    b.ne .test_case_handler.default\n    mov TMP_REG2_, #1\n    str TMP_REG2_, [TMP_REG1_]\n\n    // check if a custom handler is registered\n    adr TMP_REG2_, fault_handler\n    ldr TMP_REG2_, [TMP_REG2_]\n    cmp TMP_REG2_, #0\n    b.eq .test_case_handler.default\n\n    .test_case_handler.registered_handler:\n    msr elr_el1, TMP_REG2_\n    eret\n\n    .test_case_handler.default:\n    mrs x1, esr_el1\n    mrs x2, far_el1  // FIXME: this doesn't work for many exception types\n    adr x3, loaded_test_case_entry\n    ldr x3, [x3]\n    mov x4, sp\n    adr x5, .Ldefault_fault_msg\n\n    adr x0, .run_experiment_recovery\n    msr elr_el1, x0\n    eret\n\n\n// =================================================================================================\n// run_experiment_outer: Fault-tolerant wrapper for run_experiment\n// =================================================================================================\n.text\n\n/// @brief A wrapper over run_experiment that ensures that any bugs that cause an\n///        exception will be handled gracefully and won't crash the system\n/// @param void\n.global run_experiment_outer\nrun_experiment_outer:\n    // A bug in run_experiment may corrupt the CPU state, so we need to save the current state\n    // before calling run_experiment\n    stp x1, x2, [sp, -16]!\n    stp x3, x4, [sp, -16]!\n    stp x5, x6, [sp, -16]!\n    stp x7, x8, [sp, -16]!\n    stp x9, x10, [sp, -16]!\n    stp x11, x12, [sp, -16]!\n    stp x13, x14, [sp, -16]!\n    stp x15, x16, [sp, -16]!\n    stp x17, x18, [sp, -16]!\n    stp x19, x20, [sp, -16]!\n    stp x21, x22, [sp, -16]!\n    stp x23, x24, [sp, -16]!\n    stp x25, x26, [sp, -16]!\n    stp x27, x28, [sp, -16]!\n    stp x29, x30, [sp, -16]!\n\n    // ??? no idea why this is needed ???\n    mov x21, sp\n\n    // Save the SP into a global variables so that we can recover it after a destructive bug\n    mov x0, sp\n    adr x1, recovery_sp\n    str x0, [x1]\n\n    // Set up a custom vector table\n    bl set_outer_fault_handlers\n\n    // Call run_experiment\n    bl run_experiment\n    b .run_experiment_normal_exit\n.run_experiment_recovery:\n    // Overwrite SP with recovery_sp\n    adr TMP_REG1_, recovery_sp\n    ldr TMP_REG2_, [TMP_REG1_]\n    mov sp, TMP_REG2_\n\n    // ??? no idea why this is needed ???\n    mov x21, sp\n\n    // Print an error message\n    // x1 already contains the fault code from the fault handler\n    mov x0, x5\n    bl _printk\n    mov x0, #1\n\n.run_experiment_normal_exit:\n    // Restore the original vector table\n    stp x0, x1, [sp, -16]!\n    bl unset_outer_fault_handlers\n    ldp x0, x1, [sp], 16\n\n    // Restore the CPU state in case run_experiment has corrupted it\n    ldp x29, x30, [sp], 16\n    ldp x27, x28, [sp], 16\n    ldp x25, x26, [sp], 16\n    ldp x23, x24, [sp], 16\n    ldp x21, x22, [sp], 16\n    ldp x19, x20, [sp], 16\n    ldp x17, x18, [sp], 16\n    ldp x15, x16, [sp], 16\n    ldp x13, x14, [sp], 16\n    ldp x11, x12, [sp], 16\n    ldp x9, x10, [sp], 16\n    ldp x7, x8, [sp], 16\n    ldp x5, x6, [sp], 16\n    ldp x3, x4, [sp], 16\n    ldp x1, x2, [sp], 16\n\n    ret\n"
  },
  {
    "path": "rvzr/executor_km/arm64/fault_handler.c",
    "content": "/// File: Fault handling and vector table management on ARM64 (i.e., aarch64)\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <linux/interrupt.h>\n\n#include \"code_loader.h\"\n#include \"main.h\"\n#include \"measurement.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n#include \"test_case_parser.h\"\n\n#include \"fault_handler.h\"\n\ntypedef uint32_t opcode_t; // opcodes in ARM64 are 32-bit\n\ntypedef struct {\n    opcode_t code[32];\n} __attribute__((packed)) vector_table_entry_t;\n\ntypedef struct {\n    vector_table_entry_t vector_table[16];\n} __attribute__((packed)) vector_table_t;\n\nextern vector_table_t outer_vector_table;\nextern vector_table_t inner_vector_table;\n\nuint32_t handled_faults = 0;  // global\nchar *fault_handler = NULL;   // global\nuint64_t is_nested_fault = 0; // shared with exception.S\n\nvector_table_t *orig_vector_table_ptr = NULL;\n\n// =================================================================================================\n// Vector table management\n// =================================================================================================\nstatic inline vector_table_t *vbar_el1_read(void)\n{\n    vector_table_t *vbar_el1 = NULL;\n    asm volatile(\"mrs %0, vbar_el1\" : \"=r\"(vbar_el1));\n    return vbar_el1;\n}\n\nstatic inline void vbar_el1_write(vector_table_t *vbar_el1)\n{\n    asm volatile(\"msr vbar_el1, %0\" ::\"r\"(vbar_el1));\n}\n\nvoid set_outer_fault_handlers(void)\n{\n    // Save the original vector table\n    orig_vector_table_ptr = vbar_el1_read();\n\n    // Set VBAR to point to our custom vector table\n    vbar_el1_write(&outer_vector_table);\n}\n\nvoid unset_outer_fault_handlers(void)\n{\n    // Restore the original vector table\n    vbar_el1_write(orig_vector_table_ptr);\n}\n\nvoid set_inner_fault_handlers(void)\n{\n    is_nested_fault = 0;\n    vbar_el1_write(&inner_vector_table);\n}\n\nvoid unset_inner_fault_handlers(void) { vbar_el1_write(&outer_vector_table); }\n\n// =================================================================================================\nint init_fault_handler(void)\n{\n    handled_faults = HANDLED_FAULTS_DEFAULT;\n    fault_handler = NULL;\n    return 0;\n}\n\nvoid free_fault_handler(void) {}\n"
  },
  {
    "path": "rvzr/executor_km/arm64/macros.c",
    "content": "/// File: Management of test case macros\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"asm_snippets.h\"\n#include \"fault_handler.h\"\n#include \"macro_expansion.h\"\n#include \"main.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n\n// =================================================================================================\n// Convenience shortcuts for writing constants to memory\n// =================================================================================================\n#define APPEND_U8_TO_DEST(value) dest[cursor++] = value;\n\n#define APPEND_U16_TO_DEST(value)                                                                  \\\n    {                                                                                              \\\n        *((uint16_t *)(dest + cursor)) = value;                                                    \\\n        cursor += 2;                                                                               \\\n    }\n\n#define APPEND_U32_TO_DEST(value)                                                                  \\\n    {                                                                                              \\\n        *((uint32_t *)(dest + cursor)) = value;                                                    \\\n        cursor += 4;                                                                               \\\n    }\n\n#define APPEND_U64_TO_DEST(value)                                                                  \\\n    {                                                                                              \\\n        *((uint64_t *)(dest + cursor)) = value;                                                    \\\n        cursor += 8;                                                                               \\\n    }\n\n#define APPEND_BYTES_TO_DEST(...)                                                                  \\\n    {                                                                                              \\\n        static const uint8_t bytes[] = {__VA_ARGS__};                                              \\\n        for (size_t i = 0; i < sizeof(bytes); i++) {                                               \\\n            dest[cursor++] = bytes[i];                                                             \\\n        }                                                                                          \\\n    }\n\n// =================================================================================================\n// Instruction opcodes\n// =================================================================================================\nstatic inline uint32_t movz(uint8_t rd, uint16_t imm16, uint8_t shift)\n{\n    uint32_t opcode = 0xd2800000;\n    opcode |= rd;                    // set destination register\n    opcode |= (imm16 & 0xffff) << 5; // set immediate\n    opcode |= shift << 21;           // set shift\n    return opcode;\n}\n\nstatic inline uint32_t movk(uint8_t rd, uint16_t imm16, uint8_t shift)\n{\n    uint32_t opcode = 0xf2800000;\n    opcode |= rd;                    // set destination register\n    opcode |= (imm16 & 0xffff) << 5; // set immediate\n    opcode |= shift << 21;           // set shift\n    return opcode;\n}\n\nstatic inline uint32_t mov_to_sp(uint8_t rd) { return 0x9100001f | (rd << 5); }\n\nstatic inline uint32_t b_imm(uint32_t offset)\n{\n    // offsets in ARM are in dwords\n    offset = offset / 4;\n\n    // the target for a jump is a 26-bit signed offset from the current PC\n    int sign = offset < 0 ? 1 : 0;\n    offset = (offset & 0x3FFFFFF) | (sign << 25);\n\n    return 0x14000000 | offset;\n}\n\n/// @brief mov rd, rn\nstatic inline uint32_t mov_reg(uint8_t rd, uint8_t rn)\n{\n    uint32_t opcode = 0xaa0003e0;\n    opcode |= rd;               // set destination register\n    opcode |= (rn & 0x1f) << 5; // set source register\n    return opcode;\n}\n\n/// @brief add rd, rn, rm\nstatic inline uint32_t add_reg(uint8_t rd, uint8_t rn, uint8_t rm)\n{\n    uint32_t opcode = 0x8b000000;\n    opcode |= rd;                // set destination register\n    opcode |= (rn & 0x1f) << 5;  // set first source register\n    opcode |= (rm & 0x1f) << 16; // set second source register\n    return opcode;\n}\n\n/// @brief str rt, [rn]\nstatic inline uint32_t str_reg(uint8_t rt, uint8_t rn)\n{\n    uint32_t opcode = 0xf9000000;\n    opcode |= rt;               // set source register\n    opcode |= (rn & 0x1f) << 5; // set base register\n    return opcode;\n}\n\n/// @brief ldr rt, [rn]\nstatic inline uint32_t ldr_reg(uint8_t rt, uint8_t rn)\n{\n    uint32_t opcode = 0xf9400000;\n    opcode |= rt;               // set destination register\n    opcode |= (rn & 0x1f) << 5; // set base register\n    return opcode;\n}\n\n// =================================================================================================\n// Helper functions\n// =================================================================================================\n/// @brief Insert a sequence of instructions into dest that moves a 64-bit immediate value\n///        into a register\n/// @param rd Destination register (0-31)\n/// @param value 64-bit immediate value\n/// @param dest Pointer to the destination of the code sequence\n/// @param cursor Current position in the destination buffer\n/// @return Number of bytes written to the destination buffer\nstatic inline uint64_t mov_uint64_to_reg(uint8_t rd, uint64_t value, uint8_t *dest, uint64_t cursor)\n{\n    int old_cursor = cursor;\n    uint32_t opcode = movz(rd, value & 0xffff, 0);\n    APPEND_U32_TO_DEST(opcode);\n\n    opcode = movk(rd, value >> 16 & 0xffff, 1);\n    APPEND_U32_TO_DEST(opcode);\n\n    opcode = movk(rd, value >> 32 & 0xffff, 2);\n    APPEND_U32_TO_DEST(opcode);\n\n    opcode = movk(rd, value >> 48 & 0xffff, 3);\n    APPEND_U32_TO_DEST(opcode);\n\n    return cursor - old_cursor;\n}\n\n/// @brief Get the address of a function within a section\n/// @param section_id ID of the section\n/// @param function_id ID of the function\n/// @return Virtual address of the function\nstatic uint64_t get_function_addr(int section_id, int function_id)\n{\n    uint64_t section_base = 0;\n    section_base = (uint64_t)sandbox->code[section_id].section;\n\n    // The code section of the main actor begins after a hardcoded prologue,\n    // which we need to take into account when calculating the function address\n    if (section_id == 0)\n        section_base += get_main_prologue_size();\n\n    return section_base + test_case->symbol_table[function_id].offset;\n}\n\n/// @brief Insert a sequence of instructions into dest that updates memory base register\n///        to point to the base address of the memory owned by actor with `section_id`\n/// @param section_id ID of the section\n/// @param dest Pointer to the destination of the code sequence\n/// @param cursor Current position in the destination buffer\n/// @return Number of bytes written to the destination buffer\nstatic uint64_t update_memory_base_reg(int section_id, uint8_t *dest, uint64_t cursor)\n{\n    int old_cursor = cursor;\n\n    // calculate the new memory base register value\n    uint64_t new_val = 0;\n    new_val = (uint64_t)sandbox->data[section_id].main_area;\n    uint8_t rd = MEMORY_BASE_REGISTER_ID;\n    cursor += mov_uint64_to_reg(rd, new_val, dest, cursor);\n\n    return cursor - old_cursor;\n}\n\n/// @brief Insert a sequence of instructions into dest that updates memory base register\n///        and sp to match the actor owning section_id\n/// @param section_id ID of the section\n/// @param dest Pointer to the destination of the code sequence\n/// @param cursor Current position in the destination buffer\n/// @return Number of bytes written to the destination buffer\nstatic uint64_t update_mem_base_and_sp(int section_id, uint8_t *dest, uint64_t cursor)\n{\n    int old_cursor = cursor;\n    cursor += update_memory_base_reg(section_id, dest, cursor);\n\n    // calculate the new sp value\n    uint64_t new_sp = 0;\n    new_sp = (uint64_t)sandbox->data[section_id].main_area + LOCAL_RSP_OFFSET;\n    cursor += mov_uint64_to_reg(TMP_REG1_ID, new_sp, dest, cursor);\n\n    // ASM: mov sp, SCRATCH_REG\n    uint32_t opcode = mov_to_sp(TMP_REG1_ID);\n    APPEND_U32_TO_DEST(opcode);\n\n    return cursor - old_cursor;\n}\n\n/// @brief Insert a sequence of instructions into dest that updates x21 (util base register)\n///        to point to the base address of the util region\n/// @param section_id ID of the section\n/// @param dest Pointer to the destination of the code sequence\n/// @param cursor Current position in the destination buffer\n/// @return Number of bytes written to the destination buffer\nstatic uint64_t update_util_base_reg(int section_id, uint8_t *dest, uint64_t cursor)\n{\n    int old_cursor = cursor;\n\n    // calculate the new x21 value\n    uint64_t new_val = 0;\n    new_val = (uint64_t)sandbox->util;\n    uint8_t rd = UTIL_BASE_REGISTER_ID;\n    cursor += mov_uint64_to_reg(rd, new_val, dest, cursor);\n\n    return cursor - old_cursor;\n}\n\n// =================================================================================================\n// Macro implementations\n//\n// Note: A macro consists of two parts: it starts with the dynamically-generated part,\n// and the main body is static.\n// The dynamic part is generated by the start_macro* functions, and the generated code\n// can be configured according to the macro arguments.\n// The body_macro* functions are not configurable, and are copied directly into the test case\n// macro memory.\n// =================================================================================================\n\n// MEASUREMENT_START and MEASUREMENT_END -----------------------------------------------------------\n// Prime+Probe variants\nstatic void __attribute__((noipa)) body_macro_prime(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm volatile(\"\"                                                               //\n                 \"mrs \" TMP_REG6 \", nzcv \\n\"                                      //\n                 \"mov \" TMP_REG1 \", \" UTIL_BASE_REGISTER \"\\n\"                     //\n                 \"add \" TMP_REG1 \", \" TMP_REG1 \", \" xstr(L1D_PRIMING_OFFSET) \"\\n\" //\n                 PRIME(TMP_REG1, TMP_REG2, TMP_REG3, TMP_REG4, TMP_REG5, \"8\")     //\n                 READ_PFC_START()                                                 //\n                 SET_SR_STARTED()                                                 //\n                 \"msr nzcv, \" TMP_REG6 \"\\n\"                                       //\n                 SPEC_FENCE()                                                     //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\nstatic void __attribute__((noipa)) body_macro_fast_prime(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm volatile(\"\"                                                               //\n                 \"mrs \" TMP_REG6 \", nzcv \\n\"                                      //\n                 \"mov \" TMP_REG1 \", \" UTIL_BASE_REGISTER \"\\n\"                     //\n                 \"add \" TMP_REG1 \", \" TMP_REG1 \", \" xstr(L1D_PRIMING_OFFSET) \"\\n\" //\n                 PRIME(TMP_REG1, TMP_REG2, TMP_REG3, TMP_REG4, TMP_REG5, \"1\")     //\n                 READ_PFC_START()                                                 //\n                 SET_SR_STARTED()                                                 //\n                 \"msr nzcv, \" TMP_REG6 \"\\n\"                                       //\n                 SPEC_FENCE()                                                     //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\nstatic void __attribute__((noipa)) body_macro_probe(void)\n{\n    // pseudocode:\n    // tmp_reg6 = NZCV  // ensure that the macro doesn't corrupt flags\n    // if (status != STATUS_ENDED)  // ensure that macro is executed only once\n    //    read_pfc_end()\n    //    htrace_register = probe(sandbox->util.l1d_priming_area)\n    //    status = STATUS_ENDED\n    // NZCV = tmp_reg6\n\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm volatile(\"\"                                                                       //\n                 \"mrs \" TMP_REG6 \", nzcv \\n\"                                              //\n                 TEST_SR_ENDED()                                                          //\n                 \"b.eq 99f\\n\"                                                             //\n                 READ_PFC_END()                                                           //\n                 \"mov \" TMP_REG1 \", \" UTIL_BASE_REGISTER \"\\n\"                             //\n                 \"add \" TMP_REG1 \", \" TMP_REG1 \", \" xstr(L1D_PRIMING_OFFSET) \"\\n\"         //\n                 PROBE(TMP_REG1, TMP_REG2, TMP_REG3, TMP_REG4, TMP_REG5, HTRACE_REGISTER) //\n                 SET_SR_ENDED()                                                           //\n                 \"99:\\n\"                                                                  //\n                 \"msr nzcv, \" TMP_REG6 \"\\n\"                                               //\n                 SPEC_FENCE()                                                             //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// Flush + Reload and variants\nstatic void __attribute__((noipa)) body_macro_flush(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm volatile(\"\"                                             //\n                 \"mrs \" TMP_REG6 \", nzcv \\n\"                    //\n                 \"mov \" TMP_REG1 \", \" MEMORY_BASE_REGISTER \"\\n\" //\n                 FLUSH(TMP_REG1, TMP_REG2, TMP_REG3)            //\n                 READ_PFC_START()                               //\n                 SET_SR_STARTED()                               //\n                 \"msr nzcv, \" TMP_REG6 \"\\n\"                     //\n                 SPEC_FENCE()                                   //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\nstatic void __attribute__((noipa)) body_macro_reload(void)\n{\n    // pseudocode:\n    // tmp_reg6 = NZCV  // ensure that the macro doesn't corrupt flags\n    // if (status != STATUS_ENDED)  // ensure that macro is executed only once\n    //    read_pfc_end()\n    //    htrace_register = reload(sandbox->main)\n    //    status = STATUS_ENDED\n    // NZCV = tmp_reg6\n\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm volatile(\"\"                                                              //\n                 \"mrs \" TMP_REG6 \", nzcv \\n\"                                     //\n                 TEST_SR_ENDED()                                                 //\n                 \"b.eq 99f\\n\"                                                    //\n                 READ_PFC_END()                                                  //\n                 \"mov \" TMP_REG1 \", \" MEMORY_BASE_REGISTER \"\\n\"                  //\n                 RELOAD(TMP_REG1, TMP_REG2, TMP_REG3, TMP_REG4, HTRACE_REGISTER) //\n                 SET_SR_ENDED()                                                  //\n                 \"99:\\n\"                                                         //\n                 \"msr nzcv, \" TMP_REG6 \"\\n\"                                      //\n                 SPEC_FENCE()                                                    //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// FAULT_HANDLER -------------------------------------------------------------------------------\nstatic inline size_t start_macro_fault_handler(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n\n    // The fault handler must be owned by the main actor\n    ASSERT(args.owner == 0, \"inject_macro_configurable_part\");\n\n    // Set new global address to the fault handler\n    fault_handler = (char *)((uint64_t)dest + cursor);\n\n    // Ensure that SP, memory base, and util base\n    // are set to correct values after (potential) actor switch\n    cursor += update_mem_base_and_sp(0, dest, cursor);\n    cursor += update_util_base_reg(0, dest, cursor);\n\n    return cursor;\n}\n\n// MACRO_SWITCH ------------------------------------------------------------------------------------\nstatic inline size_t start_macro_switch(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    // Update sp and x30 to the addresses within the new actor's memory\n    cursor += update_mem_base_and_sp(args.arg1, dest, cursor);\n\n    // Determine the target address for the switch\n    uint64_t switch_target = get_function_addr(args.arg1, args.arg2);\n    uint32_t relative_offset = switch_target - (uint64_t)dest - cursor;\n\n    // Jump to the target address (in a different actor) via a relative offset\n    uint32_t opcode = b_imm(relative_offset);\n    APPEND_U32_TO_DEST(opcode);\n\n    return cursor;\n}\n\n// =================================================================================================\n// Macro descriptors\n// =================================================================================================\nmacro_descr_t macro_descriptors[] = {\n    [TYPE_UNDEFINED] = {.start = NULL, .body = NULL},\n    [TYPE_PRIME] = {.start = NULL, .body = body_macro_prime},\n    [TYPE_FAST_PRIME] = {.start = NULL, .body = body_macro_fast_prime},\n    [TYPE_PARTIAL_PRIME] = {.start = NULL, .body = NULL},\n    [TYPE_FAST_PARTIAL_PRIME] = {.start = NULL, .body = NULL},\n    [TYPE_PROBE] = {.start = NULL, .body = body_macro_probe},\n    [TYPE_FLUSH] = {.start = NULL, .body = body_macro_flush},\n    [TYPE_EVICT] = {.start = NULL, .body = body_macro_prime},\n    [TYPE_RELOAD] = {.start = NULL, .body = body_macro_reload},\n    [TYPE_TSC_START] = {.start = NULL, .body = NULL},\n    [TYPE_TSC_END] = {.start = NULL, .body = NULL},\n    [TYPE_FAULT_HANDLER] = {.start = start_macro_fault_handler, .body = NULL},\n    [TYPE_FAULT_AND_PROBE] = {.start = start_macro_fault_handler, .body = body_macro_probe},\n    [TYPE_FAULT_AND_RELOAD] = {.start = start_macro_fault_handler, .body = body_macro_reload},\n    [TYPE_FAULT_AND_TSC_END] = {.start = NULL, .body = NULL},\n    [TYPE_SWITCH] = {.start = start_macro_switch, .body = NULL},\n    [TYPE_SET_K2U_TARGET] = {.start = NULL, .body = NULL},\n    [TYPE_SWITCH_K2U] = {.start = NULL, .body = NULL},\n    [TYPE_SET_U2K_TARGET] = {.start = NULL, .body = NULL},\n    [TYPE_SWITCH_U2K] = {.start = NULL, .body = NULL},\n    [TYPE_SET_H2G_TARGET] = {.start = NULL, .body = NULL},\n    [TYPE_SWITCH_H2G] = {.start = NULL, .body = NULL},\n    [TYPE_SET_G2H_TARGET] = {.start = NULL, .body = NULL},\n    [TYPE_SWITCH_G2H] = {.start = NULL, .body = NULL},\n    [TYPE_LANDING_K2U] = {.start = NULL, .body = NULL},\n    [TYPE_LANDING_U2K] = {.start = NULL, .body = NULL},\n    [TYPE_LANDING_H2G] = {.start = NULL, .body = NULL},\n    [TYPE_LANDING_G2H] = {.start = NULL, .body = NULL},\n    [TYPE_SET_DATA_PERMISSIONS] = {.start = NULL, .body = NULL},\n};\n"
  },
  {
    "path": "rvzr/executor_km/arm64/page_tables_guest.c",
    "content": "/// File:\n///  - Guest page table management\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n// #include <asm/io.h>\n// #include <asm/msr.h>\n\n#include \"page_tables_guest.h\"\n#include \"actor.h\"\n#include \"main.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n#include \"page_tables_guest.h\"\n\n// eptp_t *ept_ptr = NULL; // global\n\n// =================================================================================================\n// Page table management interface\n// =================================================================================================\n\nint map_sandbox_to_guest_memory(void)\n{\n    int err = 0;\n    UNIMPLEMENTED(\"map_sandbox_to_guest_memory\");\n    return err;\n}\n\n/// @brief Set permissions on the faulty page based on the actor's metadata (for each actor)\n/// @param void\nvoid set_faulty_page_guest_permissions(void) { return; }\n\nvoid restore_faulty_page_guest_permissions(void) { return; }\n\n/// @brief Set EPT permissions on the faulty page based on the actor's metadata (for each actor)\n/// @param void\nvoid set_faulty_page_ept_permissions(void) { return; }\n\nvoid restore_faulty_page_ept_permissions(void) { return; }\n\n// =================================================================================================\n// Debugging Interfaces\n// =================================================================================================\n\n/// @brief Dump the guest page tables for a given actor\n/// @param actor_id\n/// @return 0 on success, -1 on failure\nint dbg_dump_guest_page_tables(int actor_id) { return 0; }\n\nint dbg_dump_ept(int actor_id) { return 0; }\n\n// =================================================================================================\nint allocate_guest_page_tables(void) { return 0; }\n\nvoid free_guest_page_tables(void) {}\n"
  },
  {
    "path": "rvzr/executor_km/arm64/perf_counters.c",
    "content": "/// File: Configuration and use of performance counters\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <linux/kernel.h>\n#include <linux/types.h>\n\n#include \"main.h\"\n#include \"shortcuts.h\"\n\n#include \"perf_counters.h\"\n#include \"shortcuts.h\"\n\n#define REQUIRED_N_COUNTERS 3\n\n// =================================================================================================\n// Constants and event IDs\n\n// Performance Monitor Events\n#define EVENT_L1D_CACHE_REFILL 0x03\n#define EVENT_INST_RETIRED     0x08\n#define EVENT_INST_SPEC        0x1b\n\n// Perf counter controls\n#define PMCR_ENABLE           BIT_(0)\n#define PMCR_EVENT_CNTR_RESET BIT_(1)\n#define PMCR_CYCLE_CNTR_RESET BIT_(2)\n#define PMCR_DP               BIT_(5)\n#define PMCR_N_COUNTER_START  11\n#define PMCR_N_COUNTER_MASK   0b11111\n#define MDCR_HPME             BIT_(7)\n#define MDCR_HPMD             BIT_(17)\n#define PMCNTENSET_P0         BIT_(0)\n#define PMCNTENSET_P1         BIT_(1)\n#define PMCNTENSET_P2         BIT_(2)\n#define PMCNTENSET_C          BIT_(31)\n#define PMCCFILTR_NSH         BIT_(27)\n#define PMSELR_CYCLE_CNTR     0x1f\n\n// =================================================================================================\n// Private module-level functions\n// =================================================================================================\n\n/// @brief Get the current exception level (EL)\n/// @param void\n/// @return Exception level\nstatic inline int get_current_exception_level(void)\n{\n    int val = 0;\n    read_msr(\"CurrentEL\", val);\n    val = (val >> 2) & 0b11;\n    return val;\n}\n\n/// @brief Enable the Performance Monitoring Unit in EL2\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic inline int pmu_enable_el2(void)\n{\n    uint64_t mdcr = 0;\n    read_msr(\"MDCR_EL2\", mdcr);\n    mdcr = mdcr | MDCR_HPME;    // set MDCR_EL2.HPME = 1\n    mdcr = mdcr & (~MDCR_HPMD); // set MDCR_EL2.HPMD = 0\n    write_msr(\"MDCR_EL2\", mdcr);\n    return 0;\n}\n\n/// @brief Enable the Performance Monitoring Unit\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic inline int pmu_enable(void)\n{\n    uint64_t pmcr = 0;\n    read_msr(\"PMCR_EL0\", pmcr);\n    write_msr(\"PMCR_EL0\", (pmcr | PMCR_ENABLE) & (~PMCR_DP));\n    return 0;\n}\n\n/// @brief Reset the Performance Monitoring Unit\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic inline int pmu_reset(void)\n{\n    uint64_t pmcr = 0;\n    read_msr(\"PMCR_EL0\", pmcr);\n    write_msr(\"PMCR_EL0\", pmcr | PMCR_EVENT_CNTR_RESET | PMCR_CYCLE_CNTR_RESET);\n    return 0;\n}\n\n/// @brief Enable all counters\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic inline int enable_all_counters(void)\n{\n    // Check that the number of available counters matches our expected value\n    uint64_t pmcr_value = 0;\n    read_msr(\"PMCR_EL0\", pmcr_value);\n    uint64_t pmcr_n = (pmcr_value >> PMCR_N_COUNTER_START) & PMCR_N_COUNTER_MASK;\n    ASSERT(pmcr_n >= REQUIRED_N_COUNTERS, \"pmu_enable\");\n\n    // Enable all counters\n    uint64_t enable_all = PMCNTENSET_P0 | PMCNTENSET_P1 | PMCNTENSET_P2 | PMCNTENSET_C;\n    write_msr(\"PMCNTENSET_EL0\", enable_all);\n\n    return 0;\n}\n\n/// @brief Disable all PMU filtering\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic inline int disable_filtering(void)\n{\n    write_msr(\"PMCCFILTR_EL0\", PMCCFILTR_NSH);\n    return 0;\n}\n\n/// @brief Set perf events to the expected values\n/// Currently, the events are hardcoded to:\n///   - counter 0: L1D_CACHE_REFILL\n///   - counter 1: INST_RETIRED\n///   - counter 2: INST_SPEC\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic inline int configure_events(void)\n{\n    // Configure the cycle counter\n    write_msr(\"PMSELR_EL0\", PMSELR_CYCLE_CNTR);\n    write_msr(\"PMXEVTYPER_EL0\", PMCCFILTR_NSH);\n\n    // Configure event counters\n    write_msr(\"PMSELR_EL0\", 0);\n    write_msr(\"PMXEVTYPER_EL0\", (PMCCFILTR_NSH | EVENT_L1D_CACHE_REFILL));\n\n    write_msr(\"PMSELR_EL0\", 1);\n    write_msr(\"PMXEVTYPER_EL0\", EVENT_INST_SPEC);\n\n    write_msr(\"PMSELR_EL0\", 2);\n    write_msr(\"PMXEVTYPER_EL0\", EVENT_INST_RETIRED);\n\n    return 0;\n}\n\n// =================================================================================================\n// Public interface\n// =================================================================================================\nint pfc_configure(void)\n{\n    // NOTE: the below implementation is based on the instructions from\n    // \"Arm Architecture Reference Manual for A-profile architecture\"\n    // Section \"D13.1 About the Performance Monitors\"\n\n    int err = 0;\n\n#ifndef VMBUILD\n    if (get_current_exception_level() >= 2) {\n        err = pmu_enable_el2();\n        CHECK_ERR(\"pmu_enable_el2\");\n    }\n\n    err = configure_events();\n    CHECK_ERR(\"configure_events\");\n\n    err = pmu_reset();\n    CHECK_ERR(\"pmu_reset\");\n\n    err = disable_filtering();\n    CHECK_ERR(\"disable_filtering\");\n\n    err = enable_all_counters();\n    CHECK_ERR(\"enable_all_counters\");\n\n    err = pmu_enable();\n    CHECK_ERR(\"pmu_enable\");\n\n#endif // VMBUILD\n\n    return err;\n}\n\n// =================================================================================================\nint init_perf_counters(void) { return 0; }\nvoid free_perf_counters(void) {}\n"
  },
  {
    "path": "rvzr/executor_km/arm64/registers.h",
    "content": "/// File: Symbolic names for pre-allocated registers; ARM64 version\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _ARM64_REGISTERS_H_\n#define _ARM64_REGISTERS_H_\n\n/// Reserved registers\n#define STATUS_REGISTER         \"x12\"\n#define STATUS_REGISTER_32      \"w12\"\n\n#define HTRACE_REGISTER         \"x13\"\n\n#define MEMORY_BASE_REGISTER    \"x20\"\n#define MEMORY_BASE_REGISTER_ID 0x14\n\n#define UTIL_BASE_REGISTER      \"x21\"\n#define UTIL_BASE_REGISTER_     x21\n#define UTIL_BASE_REGISTER_ID   0x15\n\n#define TMP_REG1                \"x28\"\n#define TMP_REG1_               x28\n#define TMP_REG1_ID             0x1c\n\n#define TMP_REG2                \"x27\"\n#define TMP_REG2_               x27\n#define TMP_REG2_ID             0x1b\n\n#define TMP_REG3                \"x26\"\n#define TMP_REG3_               x26\n\n#define TMP_REG4                \"x25\"\n#define TMP_REG4_               x25\n\n#define TMP_REG5                \"x24\"\n#define TMP_REG5_               x24\n\n#define TMP_REG6                \"x23\"\n#define TMP_REG6_               x23\n\n// NOTE: x16 is used internally by some of the code in asm_snippets.h; avoid using it\n\n/// Performance counter registers\n#define PFC0 \"x10\"\n#define PFC1 \"x9\"\n#define PFC2 \"x8\"\n\n#endif // _ARM64_REGISTERS_H_\n"
  },
  {
    "path": "rvzr/executor_km/arm64/special_registers.c",
    "content": "/// File:\n///  - Management of model-specific registers (MSRs)\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"special_registers.h\"\n#include \"fault_handler.h\"\n#include \"main.h\"\n#include \"shortcuts.h\"\n#include \"test_case_parser.h\"\n\nspecial_registers_t *orig_special_registers_state = NULL; // global\n\nstatic int store_special_registers(void)\n{\n    ASSERT(orig_special_registers_state != NULL, \"store_special_registers\");\n    memset(orig_special_registers_state, 0, sizeof(special_registers_t));\n\n    // read_msr(\"SPSR_EL1\", orig_special_registers_state->spsr_el1);\n    // read_msr(\"SP_EL0\", orig_special_registers_state->sp_el0);\n    // read_msr(\"SP_EL1\", orig_special_registers_state->sp_el1);\n    // read_msr(\"ELR_EL1\", orig_special_registers_state->elr_el1);\n    return 0;\n}\n\nint set_special_registers(void)\n{\n    int err = store_special_registers();\n    CHECK_ERR(\"set_special_registers\");\n    return err;\n}\n\nvoid restore_special_registers(void)\n{\n    // if (orig_special_registers_state->spsr_el1 != 0) {\n    //     write_msr(\"SPSR_EL1\", orig_special_registers_state->spsr_el1);\n    // }\n    // if (orig_special_registers_state->sp_el0 != 0) {\n    //     write_msr(\"SP_EL0\", orig_special_registers_state->sp_el0);\n    // }\n    // if (orig_special_registers_state->sp_el1 != 0) {\n    //     write_msr(\"SP_EL1\", orig_special_registers_state->sp_el1);\n    // }\n    // if (orig_special_registers_state->elr_el1 != 0) {\n    //     write_msr(\"ELR_EL1\", orig_special_registers_state->elr_el1);\n    // }\n    memset(orig_special_registers_state, 0, sizeof(special_registers_t));\n}\n\n// =================================================================================================\nint init_special_register_manager(void)\n{\n    orig_special_registers_state = CHECKED_ZALLOC(sizeof(special_registers_t));\n    return 0;\n}\n\nvoid free_special_register_manager(void) { SAFE_FREE(orig_special_registers_state); }\n"
  },
  {
    "path": "rvzr/executor_km/code_loader.c",
    "content": "/// File: Multiple variants of test case entry and exit points, for ARM64 architecture\n///      used exclusively by code_loader.c\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"code_loader.h\"\n#include \"macro_expansion.h\"\n#include \"main.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n\n#include \"fault_handler.h\"\n\n#ifdef ARCH_X86_64\n#include \"x86/entry_exit_points.h\"\n#elif defined(ARCH_ARM)\n#include \"arm64/entry_exit_points.h\"\n#endif\n\n// =================================================================================================\n// Local constants and declarations\n// =================================================================================================\n\n#define PER_SECTION_ALLOC_SIZE (MAX_EXPANDED_SECTION_SIZE + MAX_EXPANDED_MACROS_SIZE)\n#define MAX_TEMPLATE_SIZE      0x1000 // for sanity checking\n\nuint8_t *loaded_test_case_entry = NULL; // global\n\nstatic int load_section_main(void);\nstatic int load_section(uint64_t section_id);\nstatic tc_symbol_entry_t *get_section_macros_start(uint64_t section_id);\nstatic int expand_section(uint64_t section_id, uint8_t *dest, uint8_t *macros_dest,\n                          size_t *size_section, size_t *size_macros);\n\n// =================================================================================================\n// Code Loader logic\n// =================================================================================================\nint load_sandbox_code(void)\n{\n    int err = 0;\n    ASSERT(sandbox->code != NULL, \"load_sandbox_code\");\n\n    // Re-initialize the code area with NOPs\n    reset_code_area();\n\n    // Load the code for each section\n    for (int section_id = 0; section_id < n_actors; section_id++) {\n        if (section_id == 0)\n            err |= load_section_main();\n        else\n            err |= load_section(section_id);\n    }\n    return err;\n}\n\nstatic int load_section(uint64_t section_id)\n{\n    uint8_t *section = sandbox->code[section_id].section;\n    uint8_t *macros = sandbox->code[section_id].macros;\n    size_t size_section = 0, size_macros = 0;\n    int err = expand_section(section_id, section, macros, &size_section, &size_macros);\n    CHECK_ERR(\"load_section\");\n\n    return 0;\n}\n\nstatic int load_section_main(void)\n{\n    int err = 0;\n\n    ASSERT(test_case->metadata[0].owner == 0, \"load_section_main\");\n    uint8_t *dest = (uint8_t *)&sandbox->code[0].section;\n    uint8_t *macro_dest = sandbox->code[0].macros;\n\n    uint64_t src_cursor = 0;\n    uint64_t dest_cursor = 0;\n    uint64_t macros_cursor = 0;\n\n    // reset globals\n    fault_handler = NULL;\n    loaded_test_case_entry = NULL;\n\n    // select a template based on the debug mode\n    uint8_t *src = (dbg_gpr_mode) ? (uint8_t *)main_segment_template_dbg_gpr\n                                  : (uint8_t *)main_segment_template;\n\n    // skip instructions inserted by the compiler and start at the TEMPLATE_START marker\n    for (;; src_cursor++) {\n        ASSERT(src_cursor < MAX_TEMPLATE_SIZE, \"load_section_main; TEMPLATE_START\");\n        if (*(uint64_t *)&src[src_cursor] == TEMPLATE_START)\n            break;\n    }\n    src_cursor += TEMPLATE_MARKER_SIZE;\n\n    // copy the first part of the template\n    for (;; src_cursor++, dest_cursor++) {\n        ASSERT(src_cursor < MAX_TEMPLATE_SIZE, \"load_section_main; TEMPLATE_INSERT_TC\");\n        if (*(uint64_t *)&src[src_cursor] == TEMPLATE_INSERT_TC)\n            break;\n        dest[dest_cursor] = src[src_cursor];\n    }\n    src_cursor += TEMPLATE_MARKER_SIZE;\n\n    // notify Macro Loader about the prologue size of the main section\n    set_main_prologue_size(dest_cursor);\n\n    // copy the test case into the template and expand macros\n    size_t size_section = 0, size_macros = 0;\n    err = expand_section(0, &dest[dest_cursor], macro_dest, &size_section, &size_macros);\n    CHECK_ERR(\"load_section_main\");\n    dest_cursor += size_section;\n    macros_cursor += size_macros;\n\n    // set fault handler if the test case does not already declare an explicit one\n    for (;; src_cursor++, dest_cursor++) {\n        ASSERT(src_cursor < MAX_TEMPLATE_SIZE, \"load_section_main; EXCEPTION_LANDING\");\n        if (*(uint64_t *)&src[src_cursor] == TEMPLATE_DEFAULT_EXCEPTION_LANDING) {\n\n            // if the test case has an explicit fault handler, we just skip the macro\n            // and leave the 8 NOP bytes for compatibility\n            if (test_case->features.has_explicit_fault_handler) {\n                dest_cursor += MACRO_PLACEHOLDER_SIZE;\n                break;\n            }\n\n            // set the fault handler to the default one (end of the main actor)\n            fault_handler = (char *)&dest[dest_cursor];\n\n            // expand the macro for the default fault handler\n            tc_symbol_entry_t measurement_end = (tc_symbol_entry_t){\n                .id = MACRO_FAULT_HANDLER_WITH_MEASUREMENT, .offset = 0, .owner = 0, .args = 0};\n            size_macros = 0;\n            err = expand_macro(&measurement_end, &dest[dest_cursor], &macro_dest[macros_cursor],\n                               &size_macros);\n            CHECK_ERR(\"load_section_main\");\n\n            macros_cursor += size_macros;\n            dest_cursor += MACRO_PLACEHOLDER_SIZE;\n            break;\n        }\n        dest[dest_cursor] = src[src_cursor];\n    }\n    src_cursor += TEMPLATE_MARKER_SIZE;\n\n    // write the rest of the template\n    for (;; src_cursor++, dest_cursor++) {\n        ASSERT(src_cursor < MAX_TEMPLATE_SIZE, \"load_section_main: TEMPLATE_END\");\n        if (*(uint64_t *)&src[src_cursor] == TEMPLATE_END)\n            break;\n        dest[dest_cursor] = src[src_cursor];\n    }\n    ASSERT(dest_cursor < MAX_EXPANDED_SECTION_SIZE, \"load_section_main\");\n\n    loaded_test_case_entry = dest;\n    return 0;\n}\n\n/// @brief Get the first macro in a section\n/// @param section_id ID of the section\n/// @return Pointer to the first macro in the section, or NULL if there are no macros\nstatic tc_symbol_entry_t *get_section_macros_start(uint64_t section_id)\n{\n    tc_symbol_entry_t *entry = test_case->symbol_table;\n    tc_symbol_entry_t *end = entry + (test_case->symbol_table_size / sizeof(*entry));\n    while (entry->owner != section_id || entry->id == 0) {\n        entry++;\n        if (entry >= end)\n            return NULL;\n    }\n    return entry;\n}\n\n/// @brief Expand a section and its macros into destination buffers\n/// @param[in] section_id ID of the section to expand\n/// @param[in] dest Destination address for the expanded section code\n/// @param[in] macros_dest Destination address for the expanded macros\n/// @param[out] size_section Size of the expanded section\n/// @param[out] size_macros Size of the expanded macros\n/// @return 0 on success, -1 on failure\nstatic int expand_section(uint64_t section_id, uint8_t *dest, uint8_t *macros_dest,\n                          size_t *size_section, size_t *size_macros)\n{\n    int err = 0;\n    uint64_t src_cursor = 0;\n    uint64_t dest_cursor = 0;\n    uint64_t macros_cursor = 0;\n\n    // get the unexpanded section\n    uint8_t *section = test_case->sections[section_id].code;\n    size_t section_size = test_case->metadata[section_id].size;\n    ASSERT(section_size <= MAX_SECTION_SIZE, \"expand_section\");\n\n    // get the first macro in the section\n    tc_symbol_entry_t *macro = get_section_macros_start(section_id);\n\n    // If there are no macros to expand, just copy the code\n    if (macro == NULL) {\n        memcpy(dest, section, section_size);\n        *size_section = section_size;\n        *size_macros = 0;\n        return 0;\n    }\n\n    // Otherwise, expand macros by iterating over the section and calling expand_macro\n    // whenever we encounter a macro placeholder\n    for (src_cursor = 0; src_cursor < section_size; src_cursor++, dest_cursor++) {\n        // if a byte is *not* a macro placeholder, just copy it\n        if (macro == NULL || src_cursor != macro->offset) {\n            dest[dest_cursor] = section[src_cursor];\n            continue;\n        }\n        // PRINT_ERR(\"macro id: %d, macro owner: %d, macro args: %d, offset: %d\\n\", macro->id,\n        //   macro->owner, macro->args, macro->offset);\n\n        // if we're here, we have a macro placeholder\n        ASSERT(macro->owner == section_id, \"expand_section\");\n        ASSERT(macro->id != 0, \"expand_section\");\n\n        // expand the macro into the destination buffers\n        size_t macro_size = 0;\n        err = expand_macro(macro, &dest[dest_cursor], &macros_dest[macros_cursor], &macro_size);\n        CHECK_ERR(\"expand_section\");\n\n        // move the cursors\n        src_cursor += MACRO_PLACEHOLDER_SIZE - 1;  // -1 because it will be incremented in the loop\n        dest_cursor += MACRO_PLACEHOLDER_SIZE - 1; // -1 because it will be incremented in the loop\n        macros_cursor += macro_size;\n        macro++;\n\n        // if we're done with macros in this section, set the macro pointer to NULL\n        if (macro->owner != section_id)\n            macro = NULL;\n    }\n\n    // ensure that we did not have an overrun\n    ASSERT(src_cursor == section_size, \"expand_section\");\n\n    *size_section = dest_cursor;\n    *size_macros = macros_cursor;\n    return 0;\n}\n\n// =================================================================================================\nint init_code_loader(void)\n{\n    // NOTE: we assume the sandbox is already allocated by sandbox_manager\n    return 0;\n}\n\nvoid free_code_loader(void) {}\n"
  },
  {
    "path": "rvzr/executor_km/data_loader.c",
    "content": "/// File:\n///  - Parsing inputs and test cases\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"hardware_desc.h\"\n\n#include \"actor.h\"\n#include \"data_loader.h\"\n#include \"input_parser.h\"\n#include \"main.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n\n/// @brief This function serves a dual purpose:\n/// - it initializes the data area of the sandbox with the values from the current input\n/// - it (indirectly) sets the microarchitectural state of some of the memory buffers (e.g., the\n///   store buffer) to a the state that depends on the current input; hence, we reduce the\n///   non-determinism of the measurements\n/// @param input_id\n/// @return\nint load_sandbox_data(int input_id)\n{\n    // NOTE: this function intentionally does not use memset (with a few exceptions), because\n    // we found that direct initialization is more effective at priming the uarch state\n\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        actor_data_t *dest = &sandbox->data[actor_id];\n        input_fragment_t *source = get_input_fragment_unsafe(input_id, actor_id);\n\n        // Zero-initialize the areas surrounding the sandbox\n        if (!quick_and_dirty_mode) {\n            memset(&dest->underflow_pad[0], 0, UNDERFLOW_PAD_SIZE * sizeof(char));\n            for (int j = 0; j < OVERFLOW_PAD_SIZE / 8; j += 1) {\n                // ((uint64_t *) sandbox->underflow_pad)[j] = 0;\n                ((uint64_t *)dest->overflow_pad)[j] = 0;\n            }\n        }\n\n        // Initialize the main and faulty areas of the sandbox data\n        uint64_t *main_src = (uint64_t *)source->main_area;\n        uint64_t *main_dest = (uint64_t *)dest->main_area;\n        for (int j = 0; j < MAIN_AREA_SIZE / 8; j += 1) {\n            main_dest[j] = main_src[j];\n        }\n\n        uint64_t *faulty_src = (uint64_t *)source->faulty_area;\n        uint64_t *faulty_dest = (uint64_t *)dest->faulty_area;\n        for (int j = 0; j < FAULTY_AREA_SIZE / 8; j += 1) {\n            faulty_dest[j] = faulty_src[j];\n        }\n\n        // Initial register values\n        // (the registers will be set to these values in code_loader template)\n        uint64_t *reg_src = (uint64_t *)source->reg_init_region;\n        uint64_t *reg_dest = (uint64_t *)dest->reg_init_area;\n        for (int j = 0; j < REG_INIT_AREA_SIZE / 8; j += 1) {\n            reg_dest[j] = reg_src[j];\n        }\n\n        // - Ensure that the flags are valid\n#if defined(ARCH_X86_64)\n        reg_dest[6] = (reg_src[6] & 2263) | 2;\n#elif defined(ARCH_ARM)\n        reg_dest[6] = (reg_src[6] << 28);\n#endif\n\n        // Note: RSP and RBP are do not take a value from the input,\n        //       and are rather set to the stack base\n    }\n\n#if defined(ARCH_X86_64)\n    // - Initialize SIMD registers\n    // Note: GPRs will be initialized directly by the test case template; see code_loader.c\n    uint64_t *simd_src = (uint64_t *)&get_input_fragment_unsafe(input_id, 0)->reg_init_region[64];\n    asm volatile(\"\"\n                 \"movq 0x00(%0), %%mm0\\n\"\n                 \"movq 0x08(%0), %%mm1\\n\"\n                 \"movq 0x10(%0), %%mm2\\n\"\n                 \"movq 0x18(%0), %%mm3\\n\"\n                 \"movq 0x20(%0), %%mm4\\n\"\n                 \"movq 0x28(%0), %%mm5\\n\"\n                 \"movq 0x30(%0), %%mm6\\n\"\n                 \"movq 0x38(%0), %%mm7\\n\"\n                 // Note: overlap between YMM and MMX init values is intentional\n                 \"vmovdqa 0x00(%0), %%ymm0\\n\"\n                 \"vmovdqa 0x20(%0), %%ymm1\\n\"\n                 \"vmovdqa 0x40(%0), %%ymm2\\n\"\n                 \"vmovdqa 0x60(%0), %%ymm3\\n\"\n                 \"vmovdqa 0x80(%0), %%ymm4\\n\"\n                 \"vmovdqa 0xa0(%0), %%ymm5\\n\"\n                 \"vmovdqa 0xc0(%0), %%ymm6\\n\"\n                 \"vmovdqa 0xe0(%0), %%ymm7\\n\" ::\"r\"(&simd_src[0]));\n#endif\n\n    return 0;\n}\n\n// =================================================================================================\nint init_data_loader(void) { return 0; }\n\nvoid free_data_loader(void) {}\n"
  },
  {
    "path": "rvzr/executor_km/include/actor.h",
    "content": "/// File: Header describing actor metadata\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _ACTOR_H_\n#define _ACTOR_H_\n\n#include <linux/types.h>\n\n#define MAX_ACTORS 16\n\ntypedef uint64_t actor_id_t;\ntypedef uint64_t actor_mode_t;\ntypedef uint64_t actor_pl_t;\n\nenum {\n    MODE_HOST = 0,\n    MODE_GUEST = 1,\n};\n\nenum {\n    PL_KERNEL = 0,\n    PL_USER = 1,\n};\n\ntypedef struct {\n    actor_id_t id;\n    actor_mode_t mode;\n    actor_pl_t pl;\n    uint64_t data_permissions;\n    uint64_t data_ept_properties;\n    uint64_t code_permissions;\n} actor_metadata_t;\n\nextern size_t n_actors;\nextern actor_metadata_t *actors;\n\n#endif // _ACTOR_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/asm_snippets.h",
    "content": "/// File: Building blocks for creating macros;\n///       This file re-directs to the correct architecture-specific file.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _ASM_SNIPPETS_H_\n#define _ASM_SNIPPETS_H_\n\n#include \"hardware_desc.h\"\n\n#if defined(ARCH_X86_64)\n#include \"../x86/asm_snippets.h\"\n#elif defined(ARCH_ARM)\n#include \"../arm64/asm_snippets.h\"\n#endif\n\n#endif // _ASM_SNIPPETS_H_"
  },
  {
    "path": "rvzr/executor_km/include/code_loader.h",
    "content": "/// File: Header for code_loader.c\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _CODE_LOADER_H_\n#define _CODE_LOADER_H_\n\n#include <linux/types.h>\n\nextern uint8_t *loaded_test_case_entry;\n\nint load_sandbox_code(void);\n\nint init_code_loader(void);\nvoid free_code_loader(void);\n\n#endif // _CODE_LOADER_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/data_loader.h",
    "content": "/// File: Header for data_loader.c\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _DATA_LOADER_H_\n#define _DATA_LOADER_H_\n\n#include <linux/types.h>\n\nint load_sandbox_data(int input_id);\n\nint init_data_loader(void);\nvoid free_data_loader(void);\n\n#endif // _DATA_LOADER_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/fault_handler.h",
    "content": "/// File: Header for fault handling\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _FAULT_HANDLER_H_\n#define _FAULT_HANDLER_H_\n\n#include \"hardware_desc.h\"\n#include <linux/interrupt.h>\n#include <linux/version.h>\n\n#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)\nstruct idt_data {\n    unsigned int vector;\n    unsigned int segment;\n    struct idt_bits bits;\n    const void *addr;\n};\n#endif\n\n#ifdef ARCH_X86_64\n\n#include <../arch/x86/include/asm/traps.h>\n\n// By default, we handle General Protection Fault and Page Fault\n#define HANDLED_FAULTS_DEFAULT ((1 << X86_TRAP_GP) | (1 << X86_TRAP_PF))\n\n#elif defined(ARCH_ARM)\n\n// FIXME: exception handling is not implemented for ARM\n#define HANDLED_FAULTS_DEFAULT 0\n\n#endif\n\nextern char *fault_handler;\nextern uint32_t handled_faults;\n\n// x86-only globals\nextern struct desc_ptr test_case_idtr;\n\nvoid set_outer_fault_handlers(void);\nvoid unset_outer_fault_handlers(void);\nvoid set_inner_fault_handlers(void);\nvoid unset_inner_fault_handlers(void);\n\nint init_fault_handler(void);\nvoid free_fault_handler(void);\n\n#endif // _FAULT_HANDLER_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/hardware_desc.h",
    "content": "/// File: Header for hardware configuration\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _HARDWARE_DESC_H_\n#define _HARDWARE_DESC_H_\n\n#include <linux/types.h>\n\n#ifndef VENDOR_ID\n#error \"Undefined VENDOR_ID\"\n#define VENDOR_ID 0\n#endif\n\n#if VENDOR_ID != 1 && VENDOR_ID != 2 && VENDOR_ID != 3\n#error \"Unsupported/corrupted VENDOR_ID\"\n#endif\n\n#define VENDOR_INTEL_ 1\n#define VENDOR_AMD_   2\n#define VENDOR_ARM_   3\n#undef VENDOR_INTEL\n#undef VENDOR_AMD\n#undef VENDOR_ARM\n\n#if VENDOR_ID == VENDOR_INTEL_\n#define ARCH_X86_64\n#elif VENDOR_ID == VENDOR_AMD_\n#define ARCH_X86_64\n#elif VENDOR_ID == VENDOR_ARM_\n#define ARCH_ARM\n#endif\n\n// =================================================================================================\n// CPU identification\n// =================================================================================================\n#ifndef __ASSEMBLER__\n#if defined(ARCH_X86_64)\ntypedef struct cpuinfo_x86 cpuinfo_t;\n#elif defined(ARCH_ARM)\ntypedef struct {\n    int implementer;\n    int variant;\n    int architecture;\n    int part;\n    int revision;\n} cpuinfo_t;\n#endif\n#endif // __ASSEMBLER__\n\n// =================================================================================================\n// Memory configuration\n// =================================================================================================\n#ifndef PHYSICAL_WIDTH\n#define PHYSICAL_WIDTH 51 // unused in the build; used only for syntax highlighting\n#error \"PHYSICAL_WIDTH must be defined by the makefile\"\n#endif\n\n#define MAX_PHYSICAL_ADDRESS ((1ULL << PHYSICAL_WIDTH) - 1)\n\n// =================================================================================================\n// Cache configuration\n// =================================================================================================\n#ifndef L1D_ASSOCIATIVITY\n#error \"Undefined L1D_ASSOCIATIVITY\"\n#define L1D_ASSOCIATIVITY 0\n#elif L1D_ASSOCIATIVITY != 12 && L1D_ASSOCIATIVITY != 8 && L1D_ASSOCIATIVITY != 4 &&               \\\n    L1D_ASSOCIATIVITY != 2\n#warning \"Unsupported/corrupted L1D associativity. Falling back to 8-way\"\n#define L1D_ASSOCIATIVITY 8\n#endif\n\n#ifndef L1D_SIZE_KB\n#error \"Undefined L1D_SIZE\"\n#define L1D_SIZE_KB 32 // unused in the build; used only for syntax highlighting\n#else\n#endif\n\n#define L1D_CONFLICT_DISTANCE (L1D_SIZE_KB * 1024 / L1D_ASSOCIATIVITY)\n\n// =================================================================================================\n// Misc.\n// =================================================================================================\n\n// Definitions of MSRs missing in the kernel\n#define MSR_SYSCFG 0xc0010010\n\n#endif // _HARDWARE_DESC_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/input_parser.h",
    "content": "/// File: Header for the input parser\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _INPUT_PARSER_H_\n#define _INPUT_PARSER_H_\n\n#include \"sandbox_manager.h\"\n\n#define REG_INIT_AREA_SIZE_ALIGNED 4096\n\ntypedef uint64_t input_fragment_size_t;\ntypedef uint64_t input_fragment_reserved_field_t;\n\ntypedef struct {\n    input_fragment_size_t size;\n    input_fragment_reserved_field_t reserved;\n} input_fragment_metadata_entry_t;\n\ntypedef struct {\n    char main_area[MAIN_AREA_SIZE];\n    char faulty_area[FAULTY_AREA_SIZE];\n    char reg_init_region[REG_INIT_AREA_SIZE_ALIGNED];\n} input_fragment_t;\n\ntypedef struct {\n    size_t metadata_size;\n    size_t data_size;\n    input_fragment_metadata_entry_t *metadata;\n    input_fragment_t *data;\n} input_batch_t;\n\n#define MAX_INPUTS            (1024 * 1024)\n#define BATCH_HEADER_SIZE     16 // sizeof(n_actors) + sizeof(n_inputs)\n#define FRAGMENT_SIZE_ALIGNED (MAIN_AREA_SIZE + FAULTY_AREA_SIZE + REG_INIT_AREA_SIZE_ALIGNED)\n\nextern input_batch_t *inputs;\nextern size_t n_inputs;\n\ninput_fragment_t *get_input_fragment(uint64_t input_id, uint64_t actor_id);\ninput_fragment_t *get_input_fragment_unsafe(uint64_t input_id, uint64_t actor_id);\nssize_t parse_input_buffer(const char *buf, size_t count, bool *finished);\nbool input_parsing_completed(void);\n\nint init_input_parser(void);\nvoid free_input_parser(void);\n\n#endif // _INPUT_PARSER_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/macro_expansion.h",
    "content": "/// File: Header for test case macro loader\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _RVZR_MACRO_LOADER_H_\n#define _RVZR_MACRO_LOADER_H_\n\n#include \"hardware_desc.h\"\n\n#include \"asm_snippets.h\"\n#include \"test_case_parser.h\"\n#include <linux/types.h>\n\n// =================================================================================================\n// Lists of possible macros\n// =================================================================================================\ntypedef enum {\n    NONMACRO_FUNCTION = 0,\n    MACRO_MEASUREMENT_START = 1,\n    MACRO_MEASUREMENT_END = 2,\n    MACRO_FAULT_HANDLER = 3,\n    MACRO_SWITCH = 4,\n    MACRO_SET_K2U_TARGET = 5,\n    MACRO_SWITCH_K2U = 6,\n    MACRO_SET_U2K_TARGET = 7,\n    MACRO_SWITCH_U2K = 8,\n    MACRO_SET_H2G_TARGET = 9,\n    MACRO_SWITCH_H2G = 10,\n    MACRO_SET_G2H_TARGET = 11,\n    MACRO_SWITCH_G2H = 12,\n    MACRO_LANDING_K2U = 13,\n    MACRO_LANDING_U2K = 14,\n    MACRO_LANDING_H2G = 15,\n    MACRO_LANDING_G2H = 16,\n    MACRO_FAULT_HANDLER_WITH_MEASUREMENT = 17,\n    MACRO_SET_DATA_PERMISSIONS = 18,\n} macro_name_e;\n\ntypedef enum {\n    TYPE_UNDEFINED,\n    TYPE_PRIME,\n    TYPE_FAST_PRIME,\n    TYPE_PARTIAL_PRIME,\n    TYPE_FAST_PARTIAL_PRIME,\n    TYPE_PROBE,\n    TYPE_FLUSH,\n    TYPE_EVICT,\n    TYPE_RELOAD,\n    TYPE_TSC_START,\n    TYPE_TSC_END,\n    TYPE_FAULT_HANDLER,\n    TYPE_FAULT_AND_PROBE,\n    TYPE_FAULT_AND_RELOAD,\n    TYPE_FAULT_AND_TSC_END,\n    TYPE_SWITCH,\n    TYPE_SET_K2U_TARGET,\n    TYPE_SWITCH_K2U,\n    TYPE_SET_U2K_TARGET,\n    TYPE_SWITCH_U2K,\n    TYPE_SET_H2G_TARGET,\n    TYPE_SWITCH_H2G,\n    TYPE_SET_G2H_TARGET,\n    TYPE_SWITCH_G2H,\n    TYPE_LANDING_K2U,\n    TYPE_LANDING_U2K,\n    TYPE_LANDING_H2G,\n    TYPE_LANDING_G2H,\n    TYPE_SET_DATA_PERMISSIONS,\n} macro_subtype_e;\n\n// =================================================================================================\n// Macro descriptors\n// =================================================================================================\n// Arguments for a macro\ntypedef struct {\n    uint16_t arg1;\n    uint16_t arg2;\n    uint16_t arg3;\n    uint16_t arg4;\n    uint64_t owner;\n} macro_args_t;\n\n// Descriptor of a macro\ntypedef struct {\n    size_t (*start)(macro_args_t args, uint8_t *dest);\n    void (*body)(void);\n} macro_descr_t;\n\nextern macro_descr_t macro_descriptors[];\n\n// =================================================================================================\n// Constants for parsing macro bodies\n// =================================================================================================\n// Code tokens\n#define MACRO_START              0x0fff379000000000\n#define MACRO_END                0x0fff2f9000000000\n#define MACRO_START_TOKEN_LENGTH 8\n#define MACRO_END_TOKEN_LENGTH   8\n\n#if defined(ARCH_X86_64)\n#define MACRO_PLACEHOLDER_SIZE 8\n#elif defined(ARCH_ARM)\n#define MACRO_PLACEHOLDER_SIZE 12\n#endif\n\n// =================================================================================================\n// Public interface\n// =================================================================================================\nint expand_macro(tc_symbol_entry_t *macro, uint8_t *dest, uint8_t *macro_dest, size_t *macro_size);\nvoid set_main_prologue_size(size_t size);\nsize_t get_main_prologue_size(void);\n\n#endif // _RVZR_MACRO_LOADER_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/main.h",
    "content": "/// File: Main Header\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _RVZR_EXECUTOR_MAIN_H_\n#define _RVZR_EXECUTOR_MAIN_H_\n\n#include <asm/cpu.h>\n#include <linux/types.h>\n#include <linux/version.h>\n\n#include \"hardware_desc.h\"\n\ntypedef enum {\n    PRIME_PROBE,\n    PARTIAL_PRIME_PROBE,\n    FAST_PRIME_PROBE,\n    FAST_PARTIAL_PRIME_PROBE,\n    FLUSH_RELOAD,\n    EVICT_RELOAD,\n    TSC,\n} measurement_mode_e;\n\n#define EXECUTOR_DEBUG 0\n\n// Executor Configuration Interface\nextern bool quick_and_dirty_mode;\nextern measurement_mode_e measurement_mode;\n#define MEASUREMENT_MODE_DEFAULT PRIME_PROBE\nextern long uarch_reset_rounds;\n#define UARCH_RESET_ROUNDS_DEFAULT 1\nextern bool enable_ssbp_patch;\n#define SSBP_PATCH_DEFAULT true\nextern bool enable_prefetchers;\n#define PREFETCHER_DEFAULT false\nextern char pre_run_flush;\n#define PRE_RUN_FLUSH_DEFAULT 1\nextern bool enable_hpa_gpa_collisions;\n#define HPA_GPA_COLLISIONS_DEFAULT false\nextern bool dbg_gpr_mode;\n#define DBG_GPR_MODE_DEFAULT false\n\n// Linux Kernel compatibility\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)\n#include <linux/kallsyms.h>\nextern int (*set_memory_x)(unsigned long, int);\nextern int (*set_memory_nx)(unsigned long, int);\n#else\n#include <linux/set_memory.h>\n#endif\n\nextern cpuinfo_t *cpuinfo; // cached result of cpu_data for CPU 0\n\n#endif // _RVZR_EXECUTOR_MAIN_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/measurement.h",
    "content": "/// File: Header for the measurement manager\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _MEASUREMENT_H_\n#define _MEASUREMENT_H_\n\n#include <linux/types.h>\n#include <linux/version.h>\n\n#define HTRACE_WIDTH 1\n#define NUM_PFC      5\n\n#define STATUS_UNINITIALIZED 0\n#define STATUS_STARTED       1\n#define STATUS_ENDED         2\n\ntypedef struct measurement_status {\n    uint8_t measurement_state;\n    uint8_t reserved[3];\n    uint32_t smi_count;\n} __attribute__((packed)) measurement_status_t;\n\ntypedef struct Measurement {\n    uint64_t htrace[HTRACE_WIDTH];\n    uint64_t pfc_reading[NUM_PFC];\n    measurement_status_t status;\n} __attribute__((packed)) measurement_t;\n\nextern measurement_t *measurements;\n\nint trace_test_case(void);\nint run_experiment(void);\n\nvoid recover_orig_state(void);\n\nint alloc_measurements(void);\nint init_measurements(void);\nvoid free_measurements(void);\n\n#endif // _MEASUREMENT_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/page_tables_common.h",
    "content": "/// File: Dispatch header that includes the correct page tables definitions for the architecture\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _PAGE_TABLES_COMMON_H_\n#define _PAGE_TABLES_COMMON_H_\n\n#include \"hardware_desc.h\"\n#include <linux/slab.h> // PAGE_SIZE\n#include <linux/types.h>\n\n#define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(uint64_t))\n\n// =================================================================================================\n// X86\n// =================================================================================================\n#if defined(ARCH_X86_64)\n\n#define MODIFIABLE_PTE_BITS                                                                        \\\n    (_PAGE_PRESENT | _PAGE_RW | _PAGE_PWT | _PAGE_PCD | _PAGE_ACCESSED | _PAGE_DIRTY |             \\\n     _PAGE_PKEY_BIT0 | _PAGE_PKEY_BIT1 | _PAGE_PKEY_BIT2 | _PAGE_PKEY_BIT3 | _PAGE_NX |            \\\n     (1ULL << 51))\n\n#define _E_PAGE_PRESENT  (1 << 0)\n#define _E_PAGE_RW       (1 << 1)\n#define _E_PAGE_X        (1 << 2)\n#define _E_PAGE_ACCESSED (1 << 8)\n#define _E_PAGE_DIRTY    (1 << 9)\n#define _E_PAGE_USER     (1 << 10)\n\n#if VENDOR_ID == VENDOR_INTEL_ // Intel\n#define MODIFIABLE_EPTE_BITS                                                                       \\\n    (_E_PAGE_PRESENT | _E_PAGE_RW | _E_PAGE_X | _E_PAGE_ACCESSED | _E_PAGE_DIRTY | _E_PAGE_USER |  \\\n     (1ULL << 51))\n#else\n#define MODIFIABLE_EPTE_BITS MODIFIABLE_PTE_BITS\n#endif\n\n// -------------------------------------------------------------------------------------------------\n// Normal page tables\n// -------------------------------------------------------------------------------------------------\n#define PML4_SHIFT     39\n#define PDPT_SHIFT     30\n#define PDT_SHIFT      21\n#define PT_SHIFT       12\n#define MAX_VADDR_BITS 48\n\n#define PML4_INDEX(vaddr) (((uint64_t)(vaddr) >> PML4_SHIFT) & 0x1FF)\n#define PDPT_INDEX(vaddr) (((uint64_t)(vaddr) >> PDPT_SHIFT) & 0x1FF)\n#define PDT_INDEX(vaddr)  (((uint64_t)(vaddr) >> PDT_SHIFT) & 0x1FF)\n#define PT_INDEX(vaddr)   (((uint64_t)(vaddr) >> PT_SHIFT) & 0x1FF)\n\n// Table 4-15. Format of a PML4 Entry (PML4E) that References a Page-Directory-Pointer Table\ntypedef struct {\n    uint64_t present : 1;\n    uint64_t write_access : 1;\n    uint64_t user_supervisor : 1;\n    uint64_t page_write_through : 1;\n    uint64_t page_cache_disable : 1;\n    uint64_t accessed : 1;\n    uint64_t ignored : 1;\n    uint64_t reserved_zero : 1;\n    uint64_t ignored_11_8 : 4;\n    uint64_t paddr : (PHYSICAL_WIDTH - 12);\n#if PHYSICAL_WIDTH < 52\n    uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH);\n#endif\n    uint64_t ignored_62_52 : 11;\n    uint64_t execute_disable : 1;\n} __attribute__((packed)) pml4e_t;\n\n// Table 4-17. Format of a Page-Directory-Pointer-Table Entry (PDPTE) that\n// References a Page Directory\ntypedef struct {\n    uint64_t present : 1;\n    uint64_t write_access : 1;\n    uint64_t user_supervisor : 1;\n    uint64_t page_write_through : 1;\n    uint64_t page_cache_disable : 1;\n    uint64_t accessed : 1;\n    uint64_t ignored : 1;\n    uint64_t reserved_zero : 1;\n    uint64_t ignored_11_8 : 4;\n    uint64_t paddr : (PHYSICAL_WIDTH - 12);\n#if PHYSICAL_WIDTH < 52\n    uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH);\n#endif\n    uint64_t ignored_62_52 : 11;\n    uint64_t execute_disable : 1;\n} __attribute__((packed)) pdpte_t;\n\n// Table 4-19. Format of a Page-Directory Entry that References a Page Table\ntypedef struct {\n    uint64_t present : 1;\n    uint64_t write_access : 1;\n    uint64_t user_supervisor : 1;\n    uint64_t page_write_through : 1;\n    uint64_t page_cache_disable : 1;\n    uint64_t accessed : 1;\n    uint64_t ignored : 1;\n    uint64_t reserved_zero : 1;\n    uint64_t ignored_11_8 : 4;\n    uint64_t paddr : (PHYSICAL_WIDTH - 12);\n#if PHYSICAL_WIDTH < 52\n    uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH);\n#endif\n    uint64_t ignored_62_52 : 11;\n    uint64_t execute_disable : 1;\n} __attribute__((packed)) pdte_t;\n\n// Table 4-20. Format of a Page-Table Entry that Maps a 4-KByte Page\ntypedef struct {\n    uint64_t present : 1;\n    uint64_t write_access : 1;\n    uint64_t user_supervisor : 1;\n    uint64_t page_write_through : 1;\n    uint64_t page_cache_disable : 1;\n    uint64_t accessed : 1;\n    uint64_t dirty : 1;\n    uint64_t page_attribute_table : 1;\n    uint64_t global_page : 1;\n    uint64_t ignored_11_9 : 3;\n    uint64_t paddr : (PHYSICAL_WIDTH - 12);\n#if PHYSICAL_WIDTH < 52\n    uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH);\n#endif\n    uint64_t ignored_58_52 : 7;\n    uint64_t protection_key : 4;\n    uint64_t execute_disable : 1;\n} __attribute__((packed)) pte_t_; // using pte_t_ as pte_t is already defined in linux/types.h\n\n// -------------------------------------------------------------------------------------------------\n// Extended page tables\n// -------------------------------------------------------------------------------------------------\n\n// Figure 29-1. Formats of EPTP and EPT Paging-Structure Entries\ntypedef struct {\n    uint64_t memory_type : 3;\n    uint64_t page_walk_length : 3;\n    uint64_t ad_enabled : 1;\n    uint64_t superv_sdw_stack : 1;\n    uint64_t reserved_11_08 : 4;\n    uint64_t paddr : (PHYSICAL_WIDTH - 12);\n#if PHYSICAL_WIDTH < 52\n    uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH);\n#endif\n    uint64_t reserved_63_52 : 12;\n} __attribute__((packed)) eptp_t;\n\n#if VENDOR_ID == 1 // Intel\n// Table 28-1. Format of an EPT PML4E\ntypedef struct {\n    uint64_t read_access : 1;\n    uint64_t write_access : 1;\n    uint64_t execute_access : 1;\n    uint64_t reserved_7_3 : 5;\n    uint64_t accessed : 1;\n    uint64_t ignored_9 : 1;\n    uint64_t user_ex_access : 1;\n    uint64_t ignored_11 : 1;\n    uint64_t paddr : (PHYSICAL_WIDTH - 12);\n#if PHYSICAL_WIDTH < 52\n    uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH);\n#endif\n    uint64_t ignored_63_52 : 12;\n} __attribute__((packed)) epml4e_t;\n#else\ntypedef pml4e_t epml4e_t;\n#endif\n\n#if VENDOR_ID == 1 // Intel\n// Table 28-3. Format of an EPT Page-Directory-Pointer-Table Entry (EPT PDPTE)\ntypedef struct {\n    uint64_t read_access : 1;\n    uint64_t write_access : 1;\n    uint64_t execute_access : 1;\n    uint64_t reserved_6_3 : 4;\n    uint64_t reserved_7 : 1;\n    uint64_t accessed : 1;\n    uint64_t ignored_9 : 1;\n    uint64_t user_ex_access : 1;\n    uint64_t ignored_11 : 1;\n    uint64_t paddr : (PHYSICAL_WIDTH - 12);\n#if PHYSICAL_WIDTH < 52\n    uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH);\n#endif\n    uint64_t ignored_63_52 : 12;\n} __attribute__((packed)) epdpte_t;\n#else\ntypedef pdpte_t epdpte_t;\n#endif\n\n#if VENDOR_ID == 1 // Intel\ntypedef struct {\n    uint64_t read_access : 1;\n    uint64_t write_access : 1;\n    uint64_t execute_access : 1;\n    uint64_t reserved_6_3 : 4;\n    uint64_t reserved_7 : 1;\n    uint64_t accessed : 1;\n    uint64_t ignored_9 : 1;\n    uint64_t user_ex_access : 1;\n    uint64_t ignored_11 : 1;\n    uint64_t paddr : (PHYSICAL_WIDTH - 12);\n#if PHYSICAL_WIDTH < 52\n    uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH);\n#endif\n    uint64_t ignored_63_52 : 12;\n} __attribute__((packed)) epdte_t;\n#else\ntypedef pdte_t epdte_t;\n#endif\n\n#if VENDOR_ID == 1 // Intel\ntypedef struct {\n    uint64_t read_access : 1;\n    uint64_t write_access : 1;\n    uint64_t execute_access : 1;\n    uint64_t ept_mem_type : 3;\n    uint64_t ignore_pat : 1;\n    uint64_t ignored_7 : 1;\n    uint64_t accessed : 1;\n    uint64_t dirty : 1;\n    uint64_t user_ex_access : 1;\n    uint64_t ignored_11 : 1;\n    uint64_t paddr : (PHYSICAL_WIDTH - 12);\n#if PHYSICAL_WIDTH < 52\n    uint64_t reserved_51_M : (52 - PHYSICAL_WIDTH);\n#endif\n    uint64_t ignored_56_52 : 5;\n    uint64_t verif_guest_pag : 1;\n    uint64_t pag_write_access : 1;\n    uint64_t ignored_59 : 1;\n    uint64_t superv_sdw_stack : 1;\n    uint64_t subpg_write_perm : 1;\n    uint64_t ignored_62 : 1;\n    uint64_t suppress_ve : 1;\n} __attribute__((packed)) epte_t_;\n#else\ntypedef pte_t_ epte_t_;\n#endif\n\nstatic inline void set_user_bit(pte_t_ *pte) { pte->user_supervisor = 1; }\n\n// =================================================================================================\n// ARM\n// =================================================================================================\n#elif defined(ARCH_ARM)\n\n// NOTE: All definitions below assume 4KB pages\n//\n// NOTE: The formats are described in the ARMv8-A Architecture Reference Manual\n//       see D8.3.1 VMSAv8-64 descriptor formats\n\n#define MODIFIABLE_PTE_BITS (PTE_VALID | PTE_USER | PTE_RDONLY)\n\ntypedef struct {\n    uint64_t valid : 1;\n    uint64_t type : 1;\n    uint64_t ignored_2_7 : 6;\n    uint64_t nlta_high : 2;\n    uint64_t access_flag : 1;\n    uint64_t ignored_11 : 1;\n    uint64_t nlta_low : 38;\n    uint64_t reserved_50 : 1;\n    uint64_t ignored_51_58 : 8;\n    uint64_t pxn_table : 1;\n    uint64_t uxn_table : 1;\n    uint64_t ap_table : 2;\n    uint64_t ns_table : 1;\n} __attribute__((packed)) l1_descr_t;\n\ntypedef struct {\n    uint64_t valid : 1;\n    uint64_t type : 1;\n    uint64_t ignored_2_7 : 6;\n    uint64_t nlta_high : 2;\n    uint64_t access_flag : 1;\n    uint64_t ignored_11 : 1;\n    uint64_t nlta_low : 38;\n    uint64_t reserved_50 : 1;\n    uint64_t ignored_51_58 : 8;\n    uint64_t reserved_59_63 : 5;\n} __attribute__((packed)) l2_descr_t;\n\ntypedef struct {\n    uint64_t valid : 1;\n    uint64_t type : 1;\n    uint64_t attr_index : 3;\n    uint64_t non_secure : 1;\n    uint64_t access_permissions : 2;\n    uint64_t shareability : 2;\n    uint64_t access_flag : 1;\n    uint64_t not_global : 1;\n    uint64_t paddr : 38;\n    uint64_t guarded : 1;\n    uint64_t dirty : 1;\n    uint64_t contiguous : 1;\n    uint64_t privileged_execute_never : 1;\n    uint64_t execute_never : 1;\n    uint64_t reserved_58_55 : 4;\n    uint64_t ignored_63_59 : 5;\n} __attribute__((packed)) l3_descr_t;\n\nstatic inline void set_user_bit(l3_descr_t *pte)\n{\n    // pte->user_supervisor = 1;  // TODO\n}\n\ntypedef l3_descr_t pte_t_;\n\n#endif // ARCH_X86_64\n\n#endif // _PAGE_TABLES_COMMON_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/page_tables_guest.h",
    "content": "/// File: Dispatch header that includes the guest page table definitions for the architecture\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _GUEST_PAGE_TABLES_H_\n#define _GUEST_PAGE_TABLES_H_\n\n#include \"hardware_desc.h\"\n#include \"page_tables_common.h\"\n#include \"sandbox_manager.h\"\n\n// =================================================================================================\n// Memory layout\n// =================================================================================================\n\n// start of guest's physical memory; this is an arbitrary large aligned number\n#define GUEST_P_MEMORY_START 0\n#define GUEST_V_MEMORY_START 0x0ULL\n#define GUEST_MEMORY_SIZE    (512 * 4096) // max size that could be mapped by a single last-level PT\n\n// =================================================================================================\n// Extended page tables\n// =================================================================================================\n#if defined(ARCH_X86_64)\n\n// Kernel Constant Compatibility\n#ifndef VMX_BASIC_MEM_TYPE_WB\n#define VMX_BASIC_MEM_TYPE_WB 6LLU\n#endif\n\n// Memory layout within the guest memory\ntypedef struct {\n    pte_t_ l1[ENTRIES_PER_PAGE];  // PT\n    pdte_t l2[ENTRIES_PER_PAGE];  // PDT\n    pdpte_t l3[ENTRIES_PER_PAGE]; // PDPT\n    pml4e_t l4[ENTRIES_PER_PAGE]; // PML4\n} actor_page_table_t;\n\ntypedef struct {\n    epte_t_ l1[ENTRIES_PER_PAGE];  // EPT PT\n    epdte_t l2[ENTRIES_PER_PAGE];  // EPT PDT\n    epdpte_t l3[ENTRIES_PER_PAGE]; // EPT PDPT\n    epml4e_t l4[ENTRIES_PER_PAGE]; // EPT PML4\n} actor_ept_t;\n\ntypedef struct {\n    uint8_t entries[PAGE_SIZE];\n} __attribute__((packed)) actor_gdt_t;\n\n// Guest memory layout; it is identical for both physical and virtual memory\ntypedef struct {\n    util_t util;\n    actor_data_t data;\n    actor_code_t code;\n    uint8_t vmlaunch_page[PAGE_SIZE];\n    actor_gdt_t gdt;\n    actor_page_table_t guest_page_tables;\n} __attribute__((packed)) guest_memory_t;\n\n// Translation from virtual to guest and host physical addresses\ntypedef struct {\n    uint64_t hpa;\n    uint64_t gpa;\n    void *gva;\n    void *hva;\n} __attribute__((packed)) hgpa_t;\n\n// Specialized translation data structure to speed up virtual-to-physical translations\ntypedef struct {\n    hgpa_t util[sizeof(util_t) / PAGE_SIZE];\n    hgpa_t data[sizeof(actor_data_t) / PAGE_SIZE];\n    hgpa_t code[sizeof(actor_code_t) / PAGE_SIZE];\n    hgpa_t vmlaunch_page[1];\n    hgpa_t gdt[1];\n    hgpa_t guest_page_tables[4];\n} __attribute__((packed)) guest_memory_translations_t;\n\nextern eptp_t *ept_ptr;\n\n#elif defined(ARCH_ARM)\n\n// nothing here yet\n\n#endif // ARCH_ARM\n\n// =================================================================================================\n// Public interfaces\n// =================================================================================================\nint dbg_dump_guest_page_tables(int actor_id);\nint dbg_dump_ept(int actor_id);\n\nint map_sandbox_to_guest_memory(void);\n\nvoid set_faulty_page_guest_permissions(void);\nvoid restore_faulty_page_guest_permissions(void);\n\nvoid set_faulty_page_ept_permissions(void);\nvoid restore_faulty_page_ept_permissions(void);\n\nint allocate_guest_page_tables(void);\nvoid free_guest_page_tables(void);\n\n#endif // _GUEST_PAGE_TABLES_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/page_tables_host.h",
    "content": "/// File: Header for page table functions\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _PAGE_TABLE_H_\n#define _PAGE_TABLE_H_\n\n#include \"page_tables_common.h\"\n#include <linux/kernel.h>\n\ntypedef struct {\n    pte_t_ *data_ptes;\n    pte_t_ *code_ptes;\n    pte_t_ *util_ptes;\n} sandbox_ptes_t;\n\ntypedef struct {\n    pte_t_ **data_pteps;\n    pte_t_ **code_pteps;\n    pte_t_ **util_pteps;\n} sandbox_pteps_t;\n\nextern sandbox_pteps_t *sandbox_pteps;\n\npte_t *get_pte(uint64_t hva);\n\nint cache_host_pteps(void);\nint store_orig_host_permissions(void);\nint restore_orig_host_permissions(void);\n\nint set_user_pages(void);\nvoid set_faulty_page_host_permissions(void);\nvoid restore_faulty_page_host_permissions(void);\n\nint init_page_table_manager(void);\nvoid free_page_table_manager(void);\n\n#endif // _PAGE_TABLE_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/perf_counters.h",
    "content": "/// File: Header for perf_counters.c\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _PERF_COUNTERS_H_\n#define _PERF_COUNTERS_H_\n\nint pfc_configure(void);\n\nint init_perf_counters(void);\nvoid free_perf_counters(void);\n\n#endif // _PERF_COUNTERS_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/sandbox_constants.h",
    "content": "/// File: Collection of constants that define the layout of the sandbox;\n///       This file is intentionally separate from sandbox_manager.h so that\n///       it can be included in assembly files as well.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _SANDBOX_CONSTANTS_H_\n#define _SANDBOX_CONSTANTS_H_\n\n#include \"hardware_desc.h\"\n\n#define SIZE_UINT64 (8)\n\n// layout of util_t\n#define UTIL_VARS_MAX         4096\n#define L1D_PRIMING_AREA_SIZE (L1D_SIZE_KB * 1024ULL)\n#define STORED_RSP_SIZE       SIZE_UINT64\n#define MEASUREMENT_SIZE      56ULL // see measurement.h\n#define NESTED_FAULT_SIZE     SIZE_UINT64\n\n// layout of actor_data_t\n#define MACRO_STACK_SIZE   64\n#define UNDERFLOW_PAD_SIZE (4096 - MACRO_STACK_SIZE)\n#define MAIN_AREA_SIZE     4096\n#define FAULTY_AREA_SIZE   4096\n#define REG_INIT_AREA_SIZE 320 // 8 64-bit GPRs + 8 256-bit YMMs\n#define OVERFLOW_PAD_SIZE  (4096 - REG_INIT_AREA_SIZE)\n\n// Section sizes\n#define MAX_EXPANDED_SECTION_SIZE (0x1000ULL * 2)\n#define MAX_EXPANDED_MACROS_SIZE  (0x1000ULL)\n\n// offsets w.r.t. the base of util_t (r15 will be initialized to point there)\n#define L1D_PRIMING_OFFSET (0)\n#define UTIL_VARS_OFFSET   (L1D_PRIMING_OFFSET + L1D_PRIMING_AREA_SIZE)\n#define STORED_RSP_OFFSET  (UTIL_VARS_OFFSET + 0)\n#define MEASUREMENT_OFFSET (STORED_RSP_OFFSET + STORED_RSP_SIZE)\n#define UNUSED1_OFFSET     (MEASUREMENT_OFFSET + MEASUREMENT_SIZE)\n#define K2U_TARGET_OFFSET  (UNUSED1_OFFSET + NESTED_FAULT_SIZE)\n#define U2K_TARGET_OFFSET  (K2U_TARGET_OFFSET + SIZE_UINT64)\n\n// offsets of util_t w.r.t. the base of main_area of the main actor\n#define UTIL_REL_TO_MAIN                                                                           \\\n    (L1D_PRIMING_AREA_SIZE + UTIL_VARS_MAX + UNDERFLOW_PAD_SIZE + MACRO_STACK_SIZE)\n\n// offsets w.r.t. the base of main_area of the current actor (r14 will contain the base)\n#define MACRO_STACK_TOP_OFFSET (UNDERFLOW_PAD_SIZE)\n#define MAIN_AREA_OFFSET       (0)\n#define FAULTY_AREA_OFFSET     (MAIN_AREA_SIZE)\n#define REG_INIT_OFFSET        (FAULTY_AREA_OFFSET + FAULTY_AREA_SIZE)\n#define OVERFLOW_PAD_OFFSET    (REG_INIT_OFFSET + REG_INIT_AREA_SIZE)\n#define LOCAL_RSP_OFFSET       (FAULTY_AREA_OFFSET - 8)\n\n// area page IDs\n#define MAIN_PAGE_ID   ((MACRO_STACK_SIZE + UNDERFLOW_PAD_SIZE) / 4096)\n#define FAULTY_PAGE_ID ((MACRO_STACK_SIZE + UNDERFLOW_PAD_SIZE + MAIN_AREA_SIZE) / 4096)\n\n// number of pages for each component\n#define N_UTIL_PAGES           (sizeof(util_t) / 4096)\n#define N_DATA_PAGES_PER_ACTOR (sizeof(actor_data_t) / 4096)\n#define N_CODE_PAGES_PER_ACTOR (sizeof(actor_code_t) / 4096)\n\n#endif // _SANDBOX_CONSTANTS_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/sandbox_manager.h",
    "content": "/// File: Header for sandbox management\n///       See docs/sandbox.md for the description of the sandboxing mechanism.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _SANDBOX_MANAGER_H_\n#define _SANDBOX_MANAGER_H_\n\n#include <linux/types.h>\n\n#include \"sandbox_constants.h\"\n\n#include \"hardware_desc.h\" // L1D_ASSOCIATIVITY\n#include \"measurement.h\"   // measurement_t\n\n// =================================================================================================\n// Sandbox data layout\n// =================================================================================================\n/// @brief Area with test-case global variables that are used to communicate with the executor\n///        and store intermediate results\ntypedef struct {\n    uint64_t stored_rsp;              // stores the stack pointer before calling the test case\n    measurement_t latest_measurement; // measurement results\n    uint64_t unused1;\n#ifdef ARCH_X86_64\n    uint8_t unused[UTIL_VARS_MAX - sizeof(measurement_t) - (2 * sizeof(uint64_t))];\n#elif defined(ARCH_ARM)\n    uint64_t k2u_target_address; // target address for k2u switches\n    uint64_t u2k_target_address; // target address for u2k switches\n    uint8_t unused[UTIL_VARS_MAX - sizeof(measurement_t) - (4 * sizeof(uint64_t))];\n#endif // ARCH_ARM\n} util_vars_t;\n\n/// @brief Utility data structure used by various primitives in the test case.\n///        Must be allocated strictly before the main actor data as its code accesses\n///        fields of util_t by using constant offsets from the base of its main_area.\ntypedef struct {\n    uint8_t l1d_priming_area[L1D_PRIMING_AREA_SIZE];\n    util_vars_t vars;\n} __attribute__((packed)) util_t;\n\n/// @brief Data structure representing the memory accessible by the actor's code\ntypedef struct {\n    uint8_t macro_stack[MACRO_STACK_SIZE];     // stack for storing registers when calling macros\n    uint8_t underflow_pad[UNDERFLOW_PAD_SIZE]; // zero-initialized region for accidental underflows\n    uint8_t main_area[MAIN_AREA_SIZE];         // first input page; does not cause faults\n    uint8_t faulty_area[FAULTY_AREA_SIZE];     // second input page; causes a (configurable) fault\n    uint8_t reg_init_area[REG_INIT_AREA_SIZE]; // region for initializing registers\n    uint8_t overflow_pad[OVERFLOW_PAD_SIZE];   // zero-initialized region for accidental overflows\n} __attribute__((packed)) actor_data_t;\n\n// =================================================================================================\n// Sandbox code layout\n// =================================================================================================\ntypedef struct {\n    uint8_t section[MAX_EXPANDED_SECTION_SIZE];\n    uint8_t macros[MAX_EXPANDED_MACROS_SIZE];\n} __attribute__((packed)) actor_code_t;\n\n// =================================================================================================\n// sandbox_t\n// =================================================================================================\ntypedef struct {\n    actor_data_t *data;\n    actor_code_t *code;\n    util_t *util;\n} sandbox_t;\n\n// =================================================================================================\n// Sandbox manager interface\n// =================================================================================================\nextern sandbox_t *sandbox;\n\nint get_sandbox_size_pages(void);\n\nint set_sandbox_page_tables(void);\nvoid restore_orig_sandbox_page_tables(void);\n\nvoid set_faulty_page_permissions(void);\nvoid restore_faulty_page_permissions(void);\n\nint allocate_sandbox(void);\nvoid reset_code_area(void);\n\nint init_sandbox_manager(void);\nvoid free_sandbox_manager(void);\n\n#endif // _SANDBOX_MANAGER_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/shortcuts.h",
    "content": "/// File: Header for common macros\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef KM_SHORTCUTS_H\n#define KM_SHORTCUTS_H\n\n#include \"hardware_desc.h\"\n#include <asm/io.h>\n#include <linux/kernel.h>\n#include <linux/mm.h>\n#include <linux/slab.h>    // kfree, kmalloc\n#include <linux/vmalloc.h> // vfree, vmalloc\n\n#ifdef ARCH_X86_64\n#include <../arch/x86/include/asm/desc.h>\n#endif\n\n// =================================================================================================\n// Strings and assembly\n// =================================================================================================\n#define STRINGIFY(...) #__VA_ARGS__\n\n#define xstr(s) _str(s)\n#define _str(s) str(s)\n#define str(s)  #s\n\n// clang-format off\n#define asm_volatile_intel(ASM)                                                                    \\\n    asm volatile(\"\\n.intel_syntax noprefix\\n\"                                                      \\\n                    ASM                                                                            \\\n                 \".att_syntax noprefix\\n\")\n// clang-format on\n\n// =================================================================================================\n// MSR access\n// =================================================================================================\n#ifdef ARCH_X86_64\n// Kernel 6.16+ changed native_write_msr signature from (msr, low, high) to (msr, val)\n#include <linux/version.h>\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 16, 0)\n#define wrmsr64(msr, value) native_write_msr(msr, value)\n#else\n#define wrmsr64(msr, value) native_write_msr(msr, (uint32_t)(value), (uint32_t)((value) >> 32))\n#endif\n#define rdmsr64(msr) native_read_msr(msr)\n#elif defined(ARCH_ARM)\n#define write_msr(NAME, VALUE) asm volatile(\"msr \" NAME \", %0\\n isb\\n\" ::\"r\"(VALUE));\n#define read_msr(NAME, VAR)    asm volatile(\"mrs %0, \" NAME \"\\n isb\\n\" : \"=r\"(VAR));\n#endif\n\n// =================================================================================================\n// Bit manipulation\n// =================================================================================================\n#define BIT_(x) (1ULL << (x))\n\n// =================================================================================================\n// Logging and error handling\n// =================================================================================================\n#define PRINT_ERR(msg, ...)                                                                        \\\n    do {                                                                                           \\\n        printk(KERN_ERR \"[rvzr_executor] \" msg, ##__VA_ARGS__);                                    \\\n    } while (0)\n#define PRINT_ERRS(src, msg, ...)                                                                  \\\n    do {                                                                                           \\\n        printk(KERN_ERR \"[rvzr_executor:\" src \"] \" msg, ##__VA_ARGS__);                            \\\n    } while (0)\n\n#define PRINT_WARN(msg, ...) printk(KERN_WARNING \"[rvzr_executor] \" msg, ##__VA_ARGS__);\n#define PRINT_WARNS(src, msg, ...)                                                                 \\\n    printk(KERN_WARNING \"[rvzr_executor:\" src \"] \" msg, ##__VA_ARGS__);\n\n#define ASSERT(condition, src)                                                                     \\\n    if (!(condition)) {                                                                            \\\n        PRINT_ERRS(src, \"Assertion failed: \" xstr(condition) \"\\n\");                                \\\n        return -EIO;                                                                               \\\n    }\n\n#define ASSERT_MSG(condition, src, msg, ...)                                                       \\\n    if (!(condition)) {                                                                            \\\n        PRINT_ERRS(src, \"Assertion failed: \" xstr(condition) \";\\n\" msg, ##__VA_ARGS__);            \\\n        return -EIO;                                                                               \\\n    }\n\n#define ASSERT_ENULL(condition, src)                                                               \\\n    if (!(condition)) {                                                                            \\\n        PRINT_ERRS(src, \"Assertion failed: \" xstr(condition) \"\\n\");                                \\\n        return NULL;                                                                               \\\n    }\n\n#define ASSERT_MSG_ENULL(condition, src, ...)                                                      \\\n    if (!(condition)) {                                                                            \\\n        PRINT_ERRS(src, \"Assertion failed: \" xstr(condition) \";\" msg, ##__VA_ARGS__);              \\\n        return NULL;                                                                               \\\n    }\n\n#define CHECK_ERR(msg)                                                                             \\\n    if (err) {                                                                                     \\\n        PRINT_ERR(\" Error [\" msg \"]\\n\");                                                           \\\n        return -EIO;                                                                               \\\n    }\n\n#define UNIMPLEMENTED(src)                                                                         \\\n    PRINT_ERRS(src, \"Unimplemented\\n\");                                                            \\\n    return -ENOSYS;\n\n// =================================================================================================\n// Memory management\n// =================================================================================================\n// NOLINTBEGIN(bugprone-macro-parentheses)\n\n#define CHECKED_MALLOC(x)                                                                          \\\n    ({                                                                                             \\\n        void *ptr = kmalloc(x, GFP_KERNEL);                                                        \\\n        if (!ptr) {                                                                                \\\n            PRINT_ERR(\" Error allocating memory\\n\");                                               \\\n            return -ENOMEM;                                                                        \\\n        }                                                                                          \\\n        ptr;                                                                                       \\\n    })\n#define CHECKED_ZALLOC(x)                                                                          \\\n    ({                                                                                             \\\n        void *ptr = kzalloc(x, GFP_KERNEL);                                                        \\\n        if (!ptr) {                                                                                \\\n            PRINT_ERR(\" Error zero-allocating memory\\n\");                                          \\\n            return -ENOMEM;                                                                        \\\n        }                                                                                          \\\n        ptr;                                                                                       \\\n    })\n#define SAFE_FREE(x)                                                                               \\\n    if (x) {                                                                                       \\\n        kfree(x);                                                                                  \\\n        x = NULL;                                                                                  \\\n    }\n\n#define CHECKED_VMALLOC(x)                                                                         \\\n    ({                                                                                             \\\n        void *ptr = vmalloc(x);                                                                    \\\n        if (!ptr) {                                                                                \\\n            PRINT_ERR(\" Error allocating memory\\n\");                                               \\\n            return -ENOMEM;                                                                        \\\n        }                                                                                          \\\n        ptr;                                                                                       \\\n    })\n#define SAFE_VFREE(x)                                                                              \\\n    if (x) {                                                                                       \\\n        vfree(x);                                                                                  \\\n        x = NULL;                                                                                  \\\n    }\n\n#define CHECKED_ALLOC_PAGES(size)                                                                  \\\n    ({                                                                                             \\\n        struct page *ptr = alloc_pages(GFP_KERNEL, get_order(size));                               \\\n        if (!ptr) {                                                                                \\\n            PRINT_ERR(\" Error allocating pages\\n\");                                                \\\n            return -ENOMEM;                                                                        \\\n        }                                                                                          \\\n        ptr;                                                                                       \\\n    })\n\n#define SAFE_PAGES_FREE(x, size)                                                                   \\\n    if (x) {                                                                                       \\\n        __free_pages(x, get_order(size));                                                          \\\n        x = NULL;                                                                                  \\\n    }\n\n// NOLINTEND(bugprone-macro-parentheses)\n\n// =================================================================================================\n// Call sequences\n// =================================================================================================\n#define CALL_16_TIMES(macro, arg, id)                                                              \\\n    macro(arg, id##0) macro(arg, id##1) macro(arg, id##2) macro(arg, id##3) macro(arg, id##4)      \\\n        macro(arg, id##5) macro(arg, id##6) macro(arg, id##7) macro(arg, id##8) macro(arg, id##9)  \\\n            macro(arg, id##a) macro(arg, id##b) macro(arg, id##c) macro(arg, id##d)                \\\n                macro(arg, id##e) macro(arg, id##f)\n#define CALL_256_TIMES(macro, arg)                                                                 \\\n    CALL_16_TIMES(macro, arg, 0)                                                                   \\\n    CALL_16_TIMES(macro, arg, 1)                                                                   \\\n    CALL_16_TIMES(macro, arg, 2)                                                                   \\\n    CALL_16_TIMES(macro, arg, 3)                                                                   \\\n    CALL_16_TIMES(macro, arg, 4)                                                                   \\\n    CALL_16_TIMES(macro, arg, 5)                                                                   \\\n    CALL_16_TIMES(macro, arg, 6)                                                                   \\\n    CALL_16_TIMES(macro, arg, 7)                                                                   \\\n    CALL_16_TIMES(macro, arg, 8)                                                                   \\\n    CALL_16_TIMES(macro, arg, 9)                                                                   \\\n    CALL_16_TIMES(macro, arg, a)                                                                   \\\n    CALL_16_TIMES(macro, arg, b)                                                                   \\\n    CALL_16_TIMES(macro, arg, c)                                                                   \\\n    CALL_16_TIMES(macro, arg, d)                                                                   \\\n    CALL_16_TIMES(macro, arg, e)                                                                   \\\n    CALL_16_TIMES(macro, arg, f)\n\n// =================================================================================================\n// Address translation\n// =================================================================================================\n\nstatic inline uint64_t vmalloc_to_phys(void *hva)\n{\n    struct page *page = vmalloc_to_page(hva);\n    if (!page)\n        return 0;\n    uint64_t hpa = page_to_phys(page);\n    return hpa;\n}\n\nstatic inline void native_page_invalidate(uint64_t hva)\n{\n#ifdef ARCH_X86_64\n    asm volatile(\"invlpg (%0)\" ::\"r\"(hva) : \"memory\");\n#elif defined(ARCH_ARM)\n    hva >>= 12;\n    hva &= 0xfffffffffffULL;\n    asm volatile(\"dsb ishst\\n tlbi vale1is, %0\\n dsb ish\\n\" ::\"r\"(hva) : \"memory\");\n#endif\n}\n\n#endif // KM_SHORTCUTS_H\n"
  },
  {
    "path": "rvzr/executor_km/include/special_registers.h",
    "content": "/// File: Header for msr.c\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _MSR_H_\n#define _MSR_H_\n\n#include <linux/types.h>\n#include \"hardware_desc.h\"\n\n/// @brief Structure to hold the state of special registers (MSRs and other system registers)\n///        that need to be preserved by the kernel module. This ensures that the host system\n///        remains stable despite the potentially unsafe operations performed by the\n///        executor and the sandboxed code.\ntypedef struct {\n#if defined(ARCH_X86_64)\n    uint64_t cr0;\n    uint64_t cr4;\n    uint64_t efer;\n    uint64_t lstar;\n    uint64_t spec_ctrl;\n    uint64_t prefetcher_ctrl;\n    uint64_t syscfg;\n    uint64_t fs_base;\n    uint64_t gs_base;\n    uint64_t gdtr_base;\n    uint16_t gdtr_limit;\n#elif defined(ARCH_ARM)\n    uint64_t spsr_el1;\n    uint64_t sp_el0;\n    uint64_t sp_el1;\n    uint64_t elr_el1;\n#endif\n} __attribute__((packed)) special_registers_t;\n\nextern special_registers_t *orig_special_registers_state;\n\nint set_special_registers(void);\nvoid restore_special_registers(void);\n\nint init_special_register_manager(void);\nvoid free_special_register_manager(void);\n\n\n#endif // _MSR_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/svm.h",
    "content": "/// File: Header for svm.c\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _RVZR_EXECUTOR_SVM_H_\n#define _RVZR_EXECUTOR_SVM_H_\n\n#include <asm/svm.h>\n#include <linux/types.h>\n\n#include \"svm_constants.h\"\n\n// =================================================================================================\n// Virtual Machine Control Block (VMCB) definitions\n#define VMCB_SIZE PAGE_SIZE\n\ntypedef struct {\n    uint32_t intercept_cr;\n    uint32_t intercept_dr;\n    uint32_t intercept_exceptions;\n    uint64_t intercept;\n    uint32_t intercept_ext;\n    uint8_t reserved_1[36];\n    uint16_t pause_filter_thresh;\n    uint16_t pause_filter_count;\n    uint64_t iopm_base_pa;\n    uint64_t msrpm_base_pa;\n    uint64_t tsc_offset;\n    uint32_t asid;\n    uint8_t tlb_ctl;\n    uint8_t reserved_2[3];\n    uint32_t int_ctl;\n    uint8_t int_vector;\n    uint8_t reserved_3[3];\n    uint8_t int_state;\n    uint8_t reserved_4[7];\n    uint64_t exit_code;\n    uint64_t exit_info_1;\n    uint64_t exit_info_2;\n    uint64_t exit_int_info;\n    uint64_t nested_ctl;\n    uint64_t avic_vapic_bar;\n    uint8_t reserved_5[8];\n    uint32_t event_inj;\n    uint32_t event_inj_err;\n    uint64_t nested_cr3;\n    uint64_t virt_ext;\n    uint32_t clean;\n    uint32_t reserved_6;\n    uint64_t next_rip;\n    uint8_t insn_len;\n    uint8_t insn_bytes[15];\n    uint64_t avic_backing_page;\n    uint8_t reserved_7[8];\n    uint64_t avic_logical_id;\n    uint64_t avic_physical_id;\n    uint8_t reserved_8[768];\n} __attribute__((__packed__)) vmcb_control_t;\n\ntypedef struct {\n    uint16_t selector;\n    uint16_t attrib;\n    uint32_t limit;\n    uint64_t base;\n} __attribute__((__packed__)) seg_t;\n\ntypedef struct {\n    seg_t es;\n    seg_t cs;\n    seg_t ss;\n    seg_t ds;\n    seg_t fs;\n    seg_t gs;\n    seg_t gdtr;\n    seg_t ldtr;\n    seg_t idtr;\n    seg_t tr;\n    uint8_t reserved_1[43];\n    uint8_t cpl;\n    uint8_t reserved_2[4];\n    uint64_t efer;\n    uint64_t reserved_2a;\n    uint64_t perf_ctl0;\n    uint64_t perf_ctr0;\n    uint64_t perf_ctl1;\n    uint64_t perf_ctr1;\n    uint64_t perf_ctl2;\n    uint64_t perf_ctr2;\n    uint64_t perf_ctl3;\n    uint64_t perf_ctr3;\n    uint64_t perf_ctl4;\n    uint64_t perf_ctr4;\n    uint64_t perf_ctl5;\n    uint64_t perf_ctr5;\n    uint64_t reserved_3;\n    // uint8_t reserved_3[112];\n    uint64_t cr4;\n    uint64_t cr3;\n    uint64_t cr0;\n    uint64_t dr7;\n    uint64_t dr6;\n    uint64_t rflags;\n    uint64_t rip;\n    uint8_t reserved_4[88];\n    uint64_t rsp;\n    uint8_t reserved_5[24];\n    uint64_t rax;\n    uint64_t star;\n    uint64_t lstar;\n    uint64_t cstar;\n    uint64_t sfmask;\n    uint64_t kernel_gs_base;\n    uint64_t sysenter_cs;\n    uint64_t sysenter_esp;\n    uint64_t sysenter_eip;\n    uint64_t cr2;\n    uint8_t reserved_6[32];\n    uint64_t g_pat;\n    uint64_t dbgctl;\n    uint64_t br_from;\n    uint64_t br_to;\n    uint64_t last_excp_from;\n    uint64_t last_excp_to;\n} __attribute__((__packed__)) vmcb_save_t;\n\ntypedef struct {\n    vmcb_control_t control;\n    vmcb_save_t save;\n} __attribute__((packed)) vmcb_t;\n\n\n// =================================================================================================\n// Module interface\n#define VMCB_RIP_OFFSET offsetof(vmcb_t, save.rip)\n\nextern bool svm_is_on;\nextern uint64_t *vmcb_hpas;\nextern uint64_t *vmcb_hvas;\n\nint svm_check_cpu_compatibility(void);\nint start_svm_operation(void);\nvoid stop_svm_operation(void);\nint store_orig_vmcb_state(void);\nvoid restore_orig_vmcb_state(void);\nint set_vmcb_state(void);\nint print_svm_exit_info(void);\n\nint init_svm(void);\nvoid free_svm(void);\n\n#endif // _RVZR_EXECUTOR_SVM_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/svm_constants.h",
    "content": "/// File: Definitions of constants used by AMD SVM (Secure Virtual Machine) technology\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <asm/msr-index.h>\n\n#ifndef _RVZR_EXECUTOR_SVM_CONSTANTS_H_\n#define _RVZR_EXECUTOR_SVM_CONSTANTS_H_\n\n// =================================================================================================\n// Default values for configuration registers and VMCB fields\n\n// Could be read from cpuid\n#define SVM_MAX_NUM_GUESTS 64 // DO NOT INCREASE without knowing exactly what you are doing\n\n// -------------------------------------------------------------------------------------------------\n// Guest control registers\n#define MUST_SET_BITS_CR0_SVM_GUEST                                                                \\\n    (X86_CR0_PE | X86_CR0_PG | X86_CR0_NE | X86_CR0_WP | X86_CR0_AM | X86_CR0_ET)\n#define MUST_CLEAR_BITS_CR0_SVM_GUEST (X86_CR0_NW | X86_CR0_CD)\n\n#define MUST_SET_BITS_CR4_SVM_GUEST                                                                \\\n    (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_MCE | X86_CR4_PGE | X86_CR4_PCE | X86_CR4_OSFXSR |        \\\n     X86_CR4_OSXMMEXCPT)\n#define MUST_CLEAR_BITS_CR4_SVM_GUEST (X86_CR4_VME)\n\n#define MUST_SET_BITS_EFER_SVM_GUEST   (EFER_SCE | EFER_LME | EFER_LMA | EFER_NX | EFER_SVME)\n#define MUST_CLEAR_BITS_EFER_SVM_GUEST (EFER_LMSLE)\n\n// -------------------------------------------------------------------------------------------------\n// Segment attributes\n#define MUST_SET_BITS_CS_SVM_GUEST                                                                 \\\n    (SVM_SELECTOR_P_MASK | SVM_SELECTOR_L_MASK | SVM_SELECTOR_WRITE_MASK)\n#define MUST_SET_BITS_DS_SVM_GUEST                                                                 \\\n    (SVM_SELECTOR_P_MASK | SVM_SELECTOR_L_MASK | SVM_SELECTOR_WRITE_MASK)\n#define MUST_SET_BITS_SS_SVM_GUEST                                                                 \\\n    (SVM_SELECTOR_P_MASK | SVM_SELECTOR_L_MASK | SVM_SELECTOR_WRITE_MASK)\n\n// -------------------------------------------------------------------------------------------------\n// VMCB control fields\n\n// =================================================================================================\n// Kernel compatibility: Constant definitions that are either missing in the kernel, or are\n// inconsistent between versions\n\n// We define VMCB bits here, as the definitions within the kernel are not stable between versions\n#define VMCB_INTERCEPT_CR0_READ  0\n#define VMCB_INTERCEPT_CR3_READ  3\n#define VMCB_INTERCEPT_CR4_READ  4\n#define VMCB_INTERCEPT_CR8_READ  8\n#define VMCB_INTERCEPT_CR0_WRITE (16 + 0)\n#define VMCB_INTERCEPT_CR3_WRITE (16 + 3)\n#define VMCB_INTERCEPT_CR4_WRITE (16 + 4)\n#define VMCB_INTERCEPT_CR8_WRITE (16 + 8)\n\n#define VMCB_INTERCEPT_DR0_READ  0\n#define VMCB_INTERCEPT_DR1_READ  1\n#define VMCB_INTERCEPT_DR2_READ  2\n#define VMCB_INTERCEPT_DR3_READ  3\n#define VMCB_INTERCEPT_DR4_READ  4\n#define VMCB_INTERCEPT_DR5_READ  5\n#define VMCB_INTERCEPT_DR6_READ  6\n#define VMCB_INTERCEPT_DR7_READ  7\n#define VMCB_INTERCEPT_DR0_WRITE (16 + 0)\n#define VMCB_INTERCEPT_DR1_WRITE (16 + 1)\n#define VMCB_INTERCEPT_DR2_WRITE (16 + 2)\n#define VMCB_INTERCEPT_DR3_WRITE (16 + 3)\n#define VMCB_INTERCEPT_DR4_WRITE (16 + 4)\n#define VMCB_INTERCEPT_DR5_WRITE (16 + 5)\n#define VMCB_INTERCEPT_DR6_WRITE (16 + 6)\n#define VMCB_INTERCEPT_DR7_WRITE (16 + 7)\n\nenum {\n    VMCB_INTERCEPT_INTR,\n    VMCB_INTERCEPT_NMI,\n    VMCB_INTERCEPT_SMI,\n    VMCB_INTERCEPT_INIT,\n    VMCB_INTERCEPT_VINTR,\n    VMCB_INTERCEPT_SELECTIVE_CR0,\n    VMCB_INTERCEPT_STORE_IDTR,\n    VMCB_INTERCEPT_STORE_GDTR,\n    VMCB_INTERCEPT_STORE_LDTR,\n    VMCB_INTERCEPT_STORE_TR,\n    VMCB_INTERCEPT_LOAD_IDTR,\n    VMCB_INTERCEPT_LOAD_GDTR,\n    VMCB_INTERCEPT_LOAD_LDTR,\n    VMCB_INTERCEPT_LOAD_TR,\n    VMCB_INTERCEPT_RDTSC,\n    VMCB_INTERCEPT_RDPMC,\n    VMCB_INTERCEPT_PUSHF,\n    VMCB_INTERCEPT_POPF,\n    VMCB_INTERCEPT_CPUID,\n    VMCB_INTERCEPT_RSM,\n    VMCB_INTERCEPT_IRET,\n    VMCB_INTERCEPT_INTn,\n    VMCB_INTERCEPT_INVD,\n    VMCB_INTERCEPT_PAUSE,\n    VMCB_INTERCEPT_HLT,\n    VMCB_INTERCEPT_INVLPG,\n    VMCB_INTERCEPT_INVLPGA,\n    VMCB_INTERCEPT_IOIO_PROT,\n    VMCB_INTERCEPT_MSR_PROT,\n    VMCB_INTERCEPT_TASK_SWITCH,\n    VMCB_INTERCEPT_FERR_FREEZE,\n    VMCB_INTERCEPT_SHUTDOWN,\n    VMCB_INTERCEPT_VMRUN,\n    VMCB_INTERCEPT_VMMCALL,\n    VMCB_INTERCEPT_VMLOAD,\n    VMCB_INTERCEPT_VMSAVE,\n    VMCB_INTERCEPT_STGI,\n    VMCB_INTERCEPT_CLGI,\n    VMCB_INTERCEPT_SKINIT,\n    VMCB_INTERCEPT_RDTSCP,\n    VMCB_INTERCEPT_ICEBP,\n    VMCB_INTERCEPT_WBINVD,\n    VMCB_INTERCEPT_MONITOR,\n    VMCB_INTERCEPT_MWAIT,\n    VMCB_INTERCEPT_MWAIT_COND,\n    VMCB_INTERCEPT_XSETBV,\n    VMCB_INTERCEPT_RDPRU,\n    VMCB_INTERCEPT_EFER_WRITE,\n};\n\nenum {\n    VMCB_INTERCEPT_ALL_INVLPGB,\n    VMCB_INTERCEPT_INVALID_INVLPGB,\n    VMCB_INTERCEPT_INVPCID,\n    VMCB_INTERCEPT_MCOMMIT,\n    VMCB_INTERCEPT_TLBSYNC,\n    VMCB_INTERCEPT_BUS_LOCK,\n    VMCB_INTERCEPT_HLT_IF_NOT_VINTR,\n};\n\n#endif // _RVZR_EXECUTOR_SVM_CONSTANTS_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/test_case_parser.h",
    "content": "/// File: Header for the test case parser and manager\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _TEST_CASE_PARSER_H_\n#define _TEST_CASE_PARSER_H_\n\n#include \"actor.h\"\n\n#define MAX_SECTIONS            MAX_ACTORS\n#define MAX_SYMBOLS             128\n#define MAX_SECTION_SIZE        4096 // NOTE: must be exactly 1 page to detect sysfs buffering\n#define MAX_LOADED_SECTION_SIZE (4096 * 2)\n#define TC_HEADER_SIZE          (2 * sizeof(uint64_t))\n\ntypedef uint64_t section_size_t;\ntypedef uint64_t section_metadata_reserved_t;\ntypedef uint64_t section_id_t;\ntypedef uint64_t symbol_offset_t;\ntypedef uint64_t symbol_id_t;\ntypedef uint64_t symbol_args_t;\n\ntypedef struct {\n    actor_id_t owner;\n    section_size_t size;\n    section_metadata_reserved_t reserved;\n} tc_section_metadata_entry_t;\n\ntypedef struct {\n    char code[MAX_SECTION_SIZE];\n} tc_section_t;\n\ntypedef struct {\n    actor_id_t owner;\n    symbol_offset_t offset;\n    symbol_id_t id;\n    symbol_args_t args;\n} tc_symbol_entry_t;\n\ntypedef struct {\n    bool includes_vm_actors;\n    bool includes_user_actors;\n    bool has_explicit_fault_handler;\n} tc_features_t;\n\ntypedef struct {\n    tc_features_t features;\n    size_t actor_table_size;\n    size_t symbol_table_size;\n    size_t metadata_size;\n    size_t sections_size;\n    actor_metadata_t *actor_table;\n    tc_symbol_entry_t *symbol_table;\n    tc_section_metadata_entry_t *metadata;\n    tc_section_t *sections;\n} test_case_t;\n\nextern test_case_t *test_case;\n\nssize_t parse_test_case_buffer(const char *buf, size_t count, bool *finished);\nbool tc_parsing_completed(void);\nint init_test_case_parser(void);\nvoid free_test_case_parser(void);\n\n#endif // _TEST_CASE_PARSER_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/vmx.h",
    "content": "/// File: Header for vmx.c\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _RVZR_EXECUTOR_VMX_H_\n#define _RVZR_EXECUTOR_VMX_H_\n\n#include <asm/vmx.h>\n#include <linux/types.h>\n\n// =================================================================================================\n// Kernel compatibility\n#ifdef FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX\n#define FEATURE_VMX_ENABLED_OUTSIDE_SMX FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX\n#elif defined(FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX)\n#define FEATURE_VMX_ENABLED_OUTSIDE_SMX FEATURE_CONTROL_VMXON_ENABLED_OUTSIDE_SMX\n#else\n#error \"FEATURE_VMX_ENABLED_OUTSIDE_SMX not defined\"\n#endif\n\n#ifdef FEAT_CTL_LOCKED\n#define FEATURE_CTL_LOCKED FEAT_CTL_LOCKED\n#elif defined(FEATURE_CONTROL_LOCKED)\n#define FEATURE_CTL_LOCKED FEATURE_CONTROL_LOCKED\n#else\n#error \"FEATURE_CTL_LOCKED not defined\"\n#endif\n\n#ifdef MSR_IA32_FEAT_CTL\n#define MSR_FEATURE_CONTROL MSR_IA32_FEAT_CTL\n#elif defined(MSR_IA32_FEATURE_CONTROL)\n#define MSR_FEATURE_CONTROL MSR_IA32_FEATURE_CONTROL\n#else\n#error \"MSR_FEATURE_CONTROL not defined\"\n#endif\n\n#ifndef SECONDARY_EXEC_XSAVES\n#define SECONDARY_EXEC_XSAVES SECONDARY_EXEC_ENABLE_XSAVES\n#endif\n\n// =================================================================================================\n// Host VMX data structures\n#define VMXON_SIZE 4096 // 4KB, as defined in SDM \"Enabling and Entering VMX Operation\"\n#define VMCS_SIZE  4096 // 4KB, as defined in SDM \"Format of the VMCS Region\"\n\ntypedef struct {\n    uint32_t revision_id : 30;\n    uint32_t reserved_31 : 1;\n    uint8_t data[VMXON_SIZE - 4];\n} __attribute__((packed)) vmxon_region_t;\n\ntypedef struct {\n    uint32_t revision_id : 30;\n    uint32_t reserved_31 : 1;\n    uint32_t abort_indicator;\n    uint8_t data[VMCS_SIZE - 8];\n} __attribute__((packed)) vmcs_t;\n\n// =================================================================================================\n// Module interface\nextern bool vmx_is_on;\nextern uint64_t *vmcs_hpas;\n\nint vmx_check_cpu_compatibility(void);\nint start_vmx_operation(void);\nvoid stop_vmx_operation(void);\nint store_orig_vmcs_state(void);\nvoid restore_orig_vmcs_state(void);\nint set_vmcs_state(void);\nint print_vmx_exit_info(void);\n\nint init_vmx(void);\nvoid free_vmx(void);\n\n#endif // _RVZR_EXECUTOR_VMX_H_\n"
  },
  {
    "path": "rvzr/executor_km/include/vmx_config.h",
    "content": "/// File: Configuration constants for VMX\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef _VMX_CONFIG_H_\n#define _VMX_CONFIG_H_\n\n#include <asm/vmx.h>\n\n// Could be read from cpuid\n#define VMX_MAX_NUM_GUESTS 64 // DO NOT INCREASE without knowing exactly what you are doing\n\n// Constants missing in (some versions of) Linux\n#ifndef CPU_BASED_ACTIVATE_TERTIARY_CONTROLS\n#define CPU_BASED_ACTIVATE_TERTIARY_CONTROLS (1ULL << 17)\n#endif\n#ifndef SECONDARY_EXEC_RDTSCP\n#define SECONDARY_EXEC_RDTSCP (1ULL << 3)\n#endif\n#define SECONDARY_EXEC_EPT_VIOLATION_CAUSES_VE (1ULL << 18)\n#define SECONDARY_EXEC_PASID_TRANSLATION       (1ULL << 21)\n#define SECONDARY_EXEC_SUBPAGE_WRITE_PERM      (1ULL << 23)\n#define SECONDARY_EXEC_ENABLE_PCONFIG          (1ULL << 27)\n#define SECONDARY_EXEC_ENABLE_ENCLV_EXITING    (1ULL << 28)\n\n#define VM_EXIT_UINV               (1ULL << 19)\n#define VM_ENTRY_CET               (1ULL << 20)\n#define VM_ENTRY_LOAD_IA32_LBR_CTL (1ULL << 21)\n#define VM_ENTRY_LOAD_IA32_PKRS    (1ULL << 22)\n\n// ----------------------------------------------------------------------------------------------\n// Guest control registers\n#define MUST_SET_BITS_CR0_VMX_GUEST                                                                    \\\n    (X86_CR0_PE | X86_CR0_PG | X86_CR0_NE | X86_CR0_WP | X86_CR0_AM | X86_CR0_ET)\n#define MUST_CLEAR_BITS_CR0_VMX_GUEST (X86_CR0_NW | X86_CR0_CD)\n\n#define MUST_SET_BITS_CR4_VMX_GUEST                                                                    \\\n    (X86_CR4_PSE | X86_CR4_PAE | X86_CR4_MCE | X86_CR4_PGE | X86_CR4_PCE | X86_CR4_OSFXSR |        \\\n     X86_CR4_OSXMMEXCPT | X86_CR4_VMXE | X86_CR4_PCIDE)\n#define MUST_CLEAR_BITS_CR4_VMX_GUEST                                                                  \\\n    (X86_CR4_VME | X86_CR4_PVI | X86_CR4_TSD | X86_CR4_UMIP | X86_CR4_SMXE | X86_CR4_FSGSBASE |    \\\n     X86_CR4_OSXSAVE)\n\n// ----------------------------------------------------------------------------------------------\n// VMCS control fields\n\n// Table 25-5. Definitions of Pin-Based VM-Execution Controls\n// IMPORTANT: never combine setting of PIN_BASED_EXT_INTR_MASK and VM_EXIT_ACK_INTR_ON_EXIT\n//            (i.e., at least one must be disabled); otherwise, interrupts lead to system crash\n#define MUST_SET_PIN_BASED_VM_EXEC_CONTROL                                                         \\\n    (PIN_BASED_NMI_EXITING | PIN_BASED_VIRTUAL_NMIS | PIN_BASED_VMX_PREEMPTION_TIMER)\n#define MUST_CLEAR_PIN_BASED_VM_EXEC_CONTROL (PIN_BASED_EXT_INTR_MASK | PIN_BASED_POSTED_INTR)\n\n// Table 25-6. Definitions of Primary Processor-Based VM-Execution Controls\n// DO NOT add CPU_BASED_RDPMC_EXITING because we may need it if guest primes or probes\n#define MUST_SET_PRIMARY_VM_EXEC_CONTROL                                                           \\\n    (CPU_BASED_INTR_WINDOW_EXITING | CPU_BASED_HLT_EXITING | CPU_BASED_INVLPG_EXITING |            \\\n     CPU_BASED_MWAIT_EXITING | CPU_BASED_CR3_LOAD_EXITING | CPU_BASED_CR3_STORE_EXITING |          \\\n     CPU_BASED_CR8_LOAD_EXITING | CPU_BASED_CR8_STORE_EXITING | CPU_BASED_MOV_DR_EXITING |         \\\n     CPU_BASED_UNCOND_IO_EXITING | CPU_BASED_MONITOR_EXITING | CPU_BASED_PAUSE_EXITING |           \\\n     CPU_BASED_ACTIVATE_SECONDARY_CONTROLS | CPU_BASED_NMI_WINDOW_EXITING)\n#define MUST_CLEAR_PRIMARY_VM_EXEC_CONTROL                                                         \\\n    (CPU_BASED_USE_TSC_OFFSETTING | CPU_BASED_RDPMC_EXITING | CPU_BASED_RDTSC_EXITING |            \\\n     CPU_BASED_ACTIVATE_TERTIARY_CONTROLS | CPU_BASED_TPR_SHADOW | CPU_BASED_USE_IO_BITMAPS |      \\\n     CPU_BASED_MONITOR_TRAP_FLAG | CPU_BASED_USE_MSR_BITMAPS)\n\n// Table 25-7. Definitions of Secondary Processor-Based VM-Execution Controls\n#define MUST_SET_SECONDARY_VM_EXEC_CONTROL                                                         \\\n    (SECONDARY_EXEC_ENABLE_EPT | SECONDARY_EXEC_DESC | SECONDARY_EXEC_WBINVD_EXITING |             \\\n     SECONDARY_EXEC_ENABLE_INVPCID | SECONDARY_EXEC_RDRAND_EXITING |                               \\\n     SECONDARY_EXEC_RDSEED_EXITING)\n#define MUST_CLEAR_SECONDARY_VM_EXEC_CONTROL                                                       \\\n    (SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | SECONDARY_EXEC_RDTSCP |                             \\\n     SECONDARY_EXEC_VIRTUALIZE_X2APIC_MODE | SECONDARY_EXEC_ENABLE_VPID |                          \\\n     SECONDARY_EXEC_UNRESTRICTED_GUEST | SECONDARY_EXEC_APIC_REGISTER_VIRT |                       \\\n     SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | SECONDARY_EXEC_ENABLE_VMFUNC |                         \\\n     SECONDARY_EXEC_ENCLS_EXITING | SECONDARY_EXEC_ENABLE_PML |                                    \\\n     SECONDARY_EXEC_EPT_VIOLATION_CAUSES_VE | SECONDARY_EXEC_PT_CONCEAL_VMX |                      \\\n     SECONDARY_EXEC_XSAVES | SECONDARY_EXEC_PASID_TRANSLATION |                                    \\\n     SECONDARY_EXEC_MODE_BASED_EPT_EXEC | SECONDARY_EXEC_SUBPAGE_WRITE_PERM |                      \\\n     SECONDARY_EXEC_PT_USE_GPA | SECONDARY_EXEC_TSC_SCALING |                                      \\\n     SECONDARY_EXEC_ENABLE_USR_WAIT_PAUSE | SECONDARY_EXEC_ENABLE_PCONFIG |                        \\\n     SECONDARY_EXEC_ENABLE_ENCLV_EXITING | SECONDARY_EXEC_SHADOW_VMCS)\n\n#ifdef VMBUILD\n#undef MUST_SET_SECONDARY_VM_EXEC_CONTROL\n#define MUST_SET_SECONDARY_VM_EXEC_CONTROL                                                         \\\n    (SECONDARY_EXEC_ENABLE_EPT | SECONDARY_EXEC_DESC | SECONDARY_EXEC_WBINVD_EXITING |             \\\n     SECONDARY_EXEC_RDRAND_EXITING | SECONDARY_EXEC_RDSEED_EXITING)\n#endif\n\n// Misc.\n#define DEFAULT_EXCEPTION_BITMAP 0xFFFFFFFF // all exceptions are redirected to host\n\n// Exit/entry controls\n#define MUST_SET_EXIT_CTRL (VM_EXIT_SAVE_DEBUG_CONTROLS | VM_EXIT_HOST_ADDR_SPACE_SIZE)\n#define MUST_CLEAR_EXIT_CTRL                                                                       \\\n    (VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL | VM_EXIT_SAVE_IA32_PAT | VM_EXIT_LOAD_IA32_PAT |          \\\n     VM_EXIT_SAVE_IA32_EFER | VM_EXIT_LOAD_IA32_EFER | VM_EXIT_SAVE_VMX_PREEMPTION_TIMER |         \\\n     VM_EXIT_CLEAR_BNDCFGS | VM_EXIT_PT_CONCEAL_PIP | VM_EXIT_CLEAR_IA32_RTIT_CTL |                \\\n     VM_EXIT_ACK_INTR_ON_EXIT)\n\n#define MUST_SET_ENTRY_CTRL (VM_ENTRY_LOAD_DEBUG_CONTROLS | VM_ENTRY_IA32E_MODE)\n#define MUST_CLEAR_ENTRY_CTRL                                                                      \\\n    (VM_ENTRY_SMM | VM_ENTRY_DEACT_DUAL_MONITOR | VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL |            \\\n     VM_ENTRY_LOAD_IA32_PAT | VM_ENTRY_LOAD_IA32_EFER | VM_ENTRY_LOAD_BNDCFGS |                    \\\n     VM_ENTRY_PT_CONCEAL_PIP | VM_ENTRY_LOAD_IA32_RTIT_CTL | VM_EXIT_UINV | VM_ENTRY_CET |         \\\n     VM_ENTRY_LOAD_IA32_LBR_CTL | VM_ENTRY_LOAD_IA32_PKRS)\n\n#endif // _VMX_CONFIG_H_\n"
  },
  {
    "path": "rvzr/executor_km/input_parser.c",
    "content": "/// File:\n///   - Parsing inputs\n///   - Management of input-related data structures\n///   - Accessors to the input data\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <linux/slab.h> // PAGE_SIZE\n\n#include \"actor.h\"\n#include \"input_parser.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n\ninput_batch_t *inputs = NULL; // global\nsize_t n_inputs = 0;          // global\n\n// =================================================================================================\n// State machine for input acquisition\n// =================================================================================================\nstatic bool is_receiving_inputs = false;\nstatic uint64_t cursor = 0;\nstatic size_t highest_n_actors = 0;\nstatic size_t highest_n_inputs = 0;\nstatic input_fragment_metadata_entry_t *allocated_metadata;\nstatic input_fragment_t *allocated_data;\n\n/// Initialize the state machine\n///\nstatic int start_batch_input_parsing(const char *buf)\n{\n    int ret = 0;\n\n    // Restart parsing\n    cursor = 0;\n\n    // Create a new batch\n    SAFE_FREE(inputs);\n    inputs = CHECKED_MALLOC(sizeof(input_batch_t));\n\n    // Get the number the number of actors\n    // (here, we just check that it matches the previous value from test_case.c)\n    uint64_t new_n_actors = ((uint64_t *)buf)[0];\n    ASSERT_MSG(new_n_actors == n_actors, \"start_batch_input_parsing\",\n               \"Mismatch in n_actors;\"\n               \" Either inputs were loaded befor the test case,\\n\"\n               \"or the declared n_actors does not match \"\n               \"(n_actors = %lu, new_n_actors = %llu)\\n\",\n               n_actors, new_n_actors);\n    ret += 8;\n\n    // Get the number of inputs\n    uint64_t new_n_inputs = ((uint64_t *)buf)[1];\n    ASSERT(new_n_inputs != 0, \"start_batch_input_parsing\");\n    ASSERT_MSG((int)new_n_inputs <= MAX_INPUTS, \"start_batch_input_parsing\",\n               \"n_inputs (%llu) > MAX_INPUTS (%u)\\n\", new_n_inputs, MAX_INPUTS);\n    ret += 8;\n\n    // Store object sizes\n    //       Note: do not multiply by the number of inputs per actor for metadata,\n    //       because we keep the same metadata for each run\n    inputs->metadata_size = new_n_actors * sizeof(input_fragment_metadata_entry_t);\n    inputs->data_size = new_n_actors * new_n_inputs * sizeof(input_fragment_t);\n\n    // If the number of actors or the number of inputs has increased, we need to re-allocate\n    if (new_n_actors > highest_n_actors || new_n_inputs > highest_n_inputs || !allocated_metadata ||\n        !allocated_data) {\n        SAFE_FREE(allocated_metadata);\n        SAFE_VFREE(allocated_data);\n        allocated_metadata = CHECKED_MALLOC(inputs->metadata_size);\n        allocated_data = CHECKED_VMALLOC(inputs->data_size);\n        highest_n_actors = new_n_actors;\n        highest_n_inputs = new_n_inputs;\n    }\n\n    // Update globals\n    inputs->metadata = allocated_metadata;\n    inputs->data = allocated_data;\n    n_inputs = new_n_inputs;\n    // note: n_actors is not updated here; test_case.c is responsible for that\n\n    ASSERT(ret < PAGE_SIZE, \"start_batch_input_parsing\");\n    return ret;\n}\n\n/// Parse the inputs sent via sysfs in RDBF format\n/// (see docs/devel/binary-formats.md for the format description)\n///\nssize_t parse_input_buffer(const char *buf, size_t count, bool *finished)\n{\n    ssize_t consumed_bytes = 0;\n    ssize_t byte_id = 0;\n\n    if (!is_receiving_inputs) // Starting a a new batch\n    {\n        // Consume the fixed-size part of the batch\n        // We assume that this part is small enough to fit into the minimum buffer size,\n        // thus it does not require multiple calls to this function\n        consumed_bytes = start_batch_input_parsing(buf);\n        cursor += consumed_bytes;\n        if (consumed_bytes <= 0)\n            return -1;\n\n        is_receiving_inputs = true;\n    } else if (cursor < BATCH_HEADER_SIZE + inputs->metadata_size) // Parsing metadata\n    {\n        size_t metadata_cursor = cursor - BATCH_HEADER_SIZE;\n        size_t end = inputs->metadata_size;\n        for (; metadata_cursor < end && byte_id < count;) {\n            ((char *)inputs->metadata)[metadata_cursor] = buf[byte_id];\n            byte_id++;\n            metadata_cursor++;\n        }\n        cursor = metadata_cursor + BATCH_HEADER_SIZE;\n        consumed_bytes = byte_id;\n    } else // Parsing data\n    {\n        // FIXME: this implementation is not optimal performance-wise,\n        // because it will copy the unused data between fragment_size and FRAGMENT_SIZE_ALIGNED\n        // See Flavien's implementation for a better one.\n        size_t data_cursor = cursor - inputs->metadata_size - BATCH_HEADER_SIZE;\n        size_t end = inputs->data_size;\n        for (; data_cursor < end && byte_id < count;) {\n            ((char *)inputs->data)[data_cursor] = buf[byte_id];\n            byte_id++;\n            data_cursor++;\n        }\n        cursor = data_cursor + inputs->metadata_size + BATCH_HEADER_SIZE;\n        consumed_bytes = byte_id;\n    }\n\n    // Check whether we are done\n    size_t data_end = BATCH_HEADER_SIZE + inputs->metadata_size + inputs->data_size;\n    if (cursor >= data_end) {\n        is_receiving_inputs = false;\n        *finished = true;\n    }\n    // printk(KERN_ERR \"parse_input_buffer: consumed_bytes = %lu; count = %lu; cursor = %llu; end =\n    // \"\n    //                 \"%lu; finished = %d\\n\",\n    //        consumed_bytes, count, cursor, data_end, *finished);\n    return consumed_bytes;\n}\n\n// =================================================================================================\n// Misc. functions\n// =================================================================================================\n\n/// @brief Get the input fragment from the dynamic array\n/// @param actor_id: 0 is guest 0, 1 is guest 1, and host is last\n/// @param input_id\n/// @return The input fragment for input_id of actor_id\ninput_fragment_t *get_input_fragment(uint64_t input_id, uint64_t actor_id)\n{\n    ASSERT_ENULL(inputs != NULL, \"get_input_fragment\");\n    if (actor_id >= n_actors) {\n        PRINT_ERRS(\"get_input_fragment\", \"actor_id (%llu) >= n_actors (%lu)\\n\", actor_id, n_actors);\n        return NULL;\n    }\n    if (input_id >= n_inputs) {\n        PRINT_ERRS(\"get_input_fragment\", \"input_id (%llu) >= n_inputs (%lu)\\n\", input_id, n_inputs);\n        return NULL;\n    }\n\n    return &inputs->data[(actor_id * n_inputs) + input_id];\n}\n\n/// @brief Unsafe version of get_input_fragment\n/// @param input_id\n/// @param actor_id\n/// @return\ninput_fragment_t *get_input_fragment_unsafe(uint64_t input_id, uint64_t actor_id)\n{\n    return &inputs->data[(input_id * n_actors) + actor_id];\n}\n\n/// Getter for is_receiving_inputs\n///\nbool input_parsing_completed(void) { return !is_receiving_inputs; }\n\n// =================================================================================================\nint init_input_parser(void)\n{\n    is_receiving_inputs = false;\n    cursor = 0;\n    n_inputs = 0;\n    inputs = CHECKED_MALLOC(sizeof(input_batch_t));\n    allocated_data = CHECKED_VMALLOC(sizeof(input_fragment_t));\n    allocated_metadata = CHECKED_MALLOC(sizeof(input_fragment_metadata_entry_t));\n    inputs->data_size = 0;\n    inputs->metadata_size = 0;\n    inputs->data = allocated_data;\n    inputs->metadata = allocated_metadata;\n    return 0;\n}\n\nvoid free_input_parser(void)\n{\n    SAFE_FREE(inputs);\n    SAFE_FREE(allocated_metadata);\n    SAFE_VFREE(allocated_data);\n}\n"
  },
  {
    "path": "rvzr/executor_km/macro_expansion.c",
    "content": "/// File: Expansion of macros in the test case; used primarily by code_loader.c\n///       This file contains architecture-independent code for expanding macros in the test case.\n///       For concrete architecture-specific implementations of macros, see <arch>/macros.c.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"hardware_desc.h\"\n\n#include \"asm_snippets.h\"\n#include \"fault_handler.h\"\n#include \"macro_expansion.h\"\n#include \"main.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n#include \"test_case_parser.h\"\n\n// Max sizes for sanity checks\n#define MAX_MACRO_START_OFFSET 0x100\n#define MAX_MACRO_LENGTH       0x800\n\nstatic size_t main_prologue_size = 0;\n\n// =================================================================================================\n// Helper functions\n// =================================================================================================\n/// @brief Setter/getter for the module variable main_prologue_size\n///        This interface is necessary because the main section does not set from offset zero,\n///        and instead starts from a hardcoded prologue. To take this offset into account,\n///        Code Loader passes the size of the prologue to the Macros Loader, and then\n///        arch-specific macros can query this size to calculate the correct function address.\n/// @param size\nvoid set_main_prologue_size(size_t size) { main_prologue_size = size; }\nsize_t get_main_prologue_size(void) { return main_prologue_size; }\n\n/// @brief Determine the macro subtype from the macro ID and current configuration\n/// @param macro_id ID of the macro\n/// @return Pointer to the macro descriptor\nstatic macro_descr_t *get_macro_subtype_from_id(uint64_t macro_id)\n{\n    // determine macro subtype\n    macro_subtype_e macro_subtype = TYPE_UNDEFINED;\n    switch (macro_id) {\n    case MACRO_MEASUREMENT_START:\n        switch (measurement_mode) {\n        case PRIME_PROBE:\n            macro_subtype = TYPE_PRIME;\n            break;\n        case FAST_PRIME_PROBE:\n            macro_subtype = TYPE_FAST_PRIME;\n            break;\n        case PARTIAL_PRIME_PROBE:\n            macro_subtype = TYPE_PARTIAL_PRIME;\n            break;\n        case FAST_PARTIAL_PRIME_PROBE:\n            macro_subtype = TYPE_FAST_PARTIAL_PRIME;\n            break;\n        case FLUSH_RELOAD:\n            macro_subtype = TYPE_FLUSH;\n            break;\n        case EVICT_RELOAD:\n            macro_subtype = TYPE_EVICT;\n            break;\n        case TSC:\n            macro_subtype = TYPE_TSC_START;\n            break;\n        default:\n            PRINT_ERRS(\"get_macro_subtype_from_id\", \"misconfigured measurement_mode\\n\");\n            return NULL;\n        }\n        break;\n    case MACRO_FAULT_HANDLER_WITH_MEASUREMENT:\n        switch (measurement_mode) {\n        case PRIME_PROBE:\n        case FAST_PRIME_PROBE:\n        case PARTIAL_PRIME_PROBE:\n        case FAST_PARTIAL_PRIME_PROBE:\n            macro_subtype = TYPE_FAULT_AND_PROBE;\n            break;\n        case FLUSH_RELOAD:\n        case EVICT_RELOAD:\n            macro_subtype = TYPE_FAULT_AND_RELOAD;\n            break;\n        case TSC:\n            macro_subtype = TYPE_FAULT_AND_TSC_END;\n            break;\n        default:\n            PRINT_ERRS(\"get_macro_subtype_from_id\", \"misconfigured measurement_mode\\n\");\n            return NULL;\n        }\n        break;\n    case MACRO_MEASUREMENT_END:\n        switch (measurement_mode) {\n        case PRIME_PROBE:\n        case FAST_PRIME_PROBE:\n        case PARTIAL_PRIME_PROBE:\n        case FAST_PARTIAL_PRIME_PROBE:\n            macro_subtype = TYPE_PROBE;\n            break;\n        case FLUSH_RELOAD:\n        case EVICT_RELOAD:\n            macro_subtype = TYPE_RELOAD;\n            break;\n        case TSC:\n            macro_subtype = TYPE_TSC_END;\n            break;\n        default:\n            PRINT_ERRS(\"get_macro_subtype_from_id\", \"misconfigured measurement_mode\\n\");\n            return NULL;\n        }\n        break;\n    case MACRO_SWITCH_K2U:\n        macro_subtype = TYPE_SWITCH_K2U;\n        break;\n    case MACRO_SWITCH_U2K:\n        macro_subtype = TYPE_SWITCH_U2K;\n        break;\n    case MACRO_SWITCH_H2G:\n        macro_subtype = TYPE_SWITCH_H2G;\n        break;\n    case MACRO_SWITCH_G2H:\n        macro_subtype = TYPE_SWITCH_G2H;\n        break;\n    case MACRO_SET_H2G_TARGET:\n        macro_subtype = TYPE_SET_H2G_TARGET;\n        break;\n    case MACRO_SET_G2H_TARGET:\n        macro_subtype = TYPE_SET_G2H_TARGET;\n        break;\n    case MACRO_FAULT_HANDLER:\n        macro_subtype = TYPE_FAULT_HANDLER;\n        break;\n    case MACRO_SWITCH:\n        macro_subtype = TYPE_SWITCH;\n        break;\n    case MACRO_SET_K2U_TARGET:\n        macro_subtype = TYPE_SET_K2U_TARGET;\n        break;\n    case MACRO_SET_U2K_TARGET:\n        macro_subtype = TYPE_SET_U2K_TARGET;\n        break;\n    case MACRO_LANDING_K2U:\n        macro_subtype = TYPE_LANDING_K2U;\n        break;\n    case MACRO_LANDING_U2K:\n        macro_subtype = TYPE_LANDING_U2K;\n        break;\n    case MACRO_LANDING_H2G:\n        macro_subtype = TYPE_LANDING_H2G;\n        break;\n    case MACRO_LANDING_G2H:\n        macro_subtype = TYPE_LANDING_G2H;\n        break;\n    case MACRO_SET_DATA_PERMISSIONS:\n        macro_subtype = TYPE_SET_DATA_PERMISSIONS;\n        break;\n    default:\n        PRINT_ERRS(\"get_macro_subtype_from_id\", \"macro_id %llu is not valid\\n\", macro_id);\n        return NULL;\n    }\n\n    macro_descr_t *descr = &macro_descriptors[macro_subtype];\n    if (descr->start == NULL && descr->body == NULL) {\n        PRINT_ERRS(\"get_macro_subtype_from_id\", \"macro_id %llu is not implemented\\n\", macro_id);\n        return NULL;\n    }\n    return descr;\n}\n\n/// @brief Check if the given pointer points to a token marking the start of a macro\n/// @param ptr\n/// @return True if the pointer points to the start of a macro, false otherwise\nstatic inline bool is_macro_start(uint8_t *ptr)\n{\n    return (ptr)[7] == ((MACRO_START >> 56) & 0xFF) && (ptr)[6] == ((MACRO_START >> 48) & 0xFF) &&\n           (ptr)[5] == ((MACRO_START >> 40) & 0xFF) && (ptr)[4] == ((MACRO_START >> 32) & 0xFF) &&\n           (ptr)[3] == ((MACRO_START >> 24) & 0xFF) && (ptr)[2] == ((MACRO_START >> 16) & 0xFF) &&\n           (ptr)[1] == ((MACRO_START >> 8) & 0xFF) && (ptr)[0] == ((MACRO_START) & 0xFF);\n}\n\n/// @brief Check if the given pointer points to a token marking the end of a macro\n/// @param ptr\n/// @return True if the pointer points to the end of a macro, false otherwise\nstatic inline bool is_macro_end(uint8_t *ptr)\n{\n    return (ptr)[7] == ((MACRO_END >> 56) & 0xFF) && (ptr)[6] == ((MACRO_END >> 48) & 0xFF) &&\n           (ptr)[5] == ((MACRO_END >> 40) & 0xFF) && (ptr)[4] == ((MACRO_END >> 32) & 0xFF) &&\n           (ptr)[3] == ((MACRO_END >> 24) & 0xFF) && (ptr)[2] == ((MACRO_END >> 16) & 0xFF) &&\n           (ptr)[1] == ((MACRO_END >> 8) & 0xFF) && (ptr)[0] == ((MACRO_END) & 0xFF);\n}\n\n/// @brief Replace the NOP at the given location with a relative jump to the expanded macro\n///        and add a fence after the jump to prevent straight-line speculation\n/// @param dest Destination buffer\n/// @param target Target address for the jump\n/// @return Size of the added code, in bytes\nstatic inline uint64_t insert_relative_jmp_n_fence(uint8_t *dest, int32_t target)\n{\n    uint64_t cursor = 0;\n\n#if defined(ARCH_X86_64)\n    const int jmp_opcode_size = 5;\n    target -= jmp_opcode_size;\n\n    // jmp *target\n    dest[cursor++] = 0xe9; // start of the jump opcode\n    *((uint32_t *)&dest[cursor]) = target;\n    cursor += 4;\n\n    // lfence\n    dest[cursor++] = 0x0f;\n    dest[cursor++] = 0xae;\n    dest[cursor++] = 0xe8;\n#elif defined(ARCH_ARM)\n    // offsets in ARM are in dwords\n    target = target / 4;\n\n    // the target for a jump is a 26-bit signed offset from the current PC\n    int target_sign = target < 0 ? 1 : 0;\n    ASSERT(target < 0x02000000 && target >= -0x02000000, \"insert_relative_jmp_n_fence\");\n    target = (target & 0x3FFFFFF) | (target_sign << 25);\n\n    // b *target\n    *((uint32_t *)&dest[cursor]) = 0x14000000; // start of the jump opcode\n    *((uint32_t *)&dest[cursor]) |= target;\n    cursor += 4;\n\n    // isb\n    *((uint32_t *)&dest[cursor]) = 0xd5033fdf;\n    cursor += 4;\n\n    // dsb SY\n    *((uint32_t *)&dest[cursor]) = 0xd5033f9f;\n    cursor += 4;\n\n#endif\n\n    return cursor;\n}\n\n// =================================================================================================\n// Macro expansion logic\n// =================================================================================================\n\n/// @brief Dynamically generate the configurable part of the macro\n/// @param[in] descr Pointer to the macro descriptor\n/// @param[in] args Compressed representation of the macro arguments, as received from the test case\n///            symbol table\n/// @param[in] owner ID of the actor owning the macro\n/// @param[out] dest Pointer to the destination buffer\n/// @return Size of the added code, in bytes\nstatic uint64_t inject_macro_configurable_part(macro_descr_t *descr, uint64_t args, uint64_t owner,\n                                               uint8_t *dest)\n{\n    // Extract the macro arguments\n    macro_args_t args_struct = {\n        .arg1 = (args >> 0x00) & 0xFFFF,\n        .arg2 = (args >> 0x10) & 0xFFFF,\n        .arg3 = (args >> 0x20) & 0xFFFF,\n        .arg4 = (args >> 0x30) & 0xFFFF,\n        .owner = owner,\n    };\n\n    // Generate the macro start code\n    size_t cursor = descr->start(args_struct, dest);\n    return cursor;\n}\n\n/// @brief Inject the static part of the macro into destination\n/// @param[in] descr Pointer to the macro descriptor\n/// @param[out] dest Pointer to the destination buffer\n/// @return Size of the added code, in bytes\nstatic uint64_t inject_macro_static_part(macro_descr_t *descr, uint8_t *dest)\n{\n    // Get pointers to the start and the end of the static part of the macro\n    uint8_t *macro_wrapper_start = (uint8_t *)descr->body;\n    uint8_t *macro_start = macro_wrapper_start;\n    while (!is_macro_start(macro_start)) {\n        macro_start++;\n        ASSERT(macro_start - macro_wrapper_start < MAX_MACRO_START_OFFSET, \"get_macro_ptr\");\n    }\n    macro_start += MACRO_START_TOKEN_LENGTH;\n\n    uint8_t *macro_end = macro_start;\n    while (!is_macro_end(macro_end)) {\n        macro_end++;\n        ASSERT(macro_end - macro_start < MAX_MACRO_LENGTH, \"get_macro_ptr\");\n    }\n    if (macro_end - macro_start == 0)\n        return 0;\n\n    // Copy the static part of the macro\n    size_t size = macro_end - macro_start;\n    memcpy(dest, macro_start, size);\n    return size;\n}\n\n/// @brief Expand a macro into the destination buffer (macro_dest) and replace the nop at\n///        jmp_location with a relative jump to the expanded macro\n/// @param macro Macro to expand\n/// @param[in] dest Destination address for placing the JMP instruction\n/// @param[in] macro_dest Destination buffer for the expanded macro\n/// @param[out] macro_size Size of the expanded macro\n/// @return 0 on success, -1 on failure\nint expand_macro(tc_symbol_entry_t *macro, uint8_t *code_dest, uint8_t *macro_dest,\n                 size_t *macro_size)\n{\n    uint64_t code_cursor = 0;\n    uint64_t macro_cursor = 0;\n\n    // Get the macro type\n    symbol_id_t type_id = macro->id;\n    ASSERT(type_id != 0, \"expand_macro\");\n\n    // Get the macro descriptor\n    macro_descr_t *descr = get_macro_subtype_from_id(type_id);\n    ASSERT(descr != NULL, \"expand_macro\");\n\n    // Code area: Replace the NOP with a relative jump to the expanded macro + fence\n    int32_t target = (int32_t)(&macro_dest[macro_cursor] - code_dest);\n    code_cursor += insert_relative_jmp_n_fence(&code_dest[code_cursor], target);\n\n    // Macro area: Inject the configurable part of the macro\n    if (descr->start != NULL) {\n        macro_cursor += inject_macro_configurable_part(descr, macro->args, macro->owner,\n                                                       &macro_dest[macro_cursor]);\n    }\n    ASSERT(macro_cursor >= 0, \"expand_macro\");\n\n    // Macro area: Inject the static part of the macro\n    if (descr->body != NULL) {\n        macro_cursor += inject_macro_static_part(descr, &macro_dest[macro_cursor]);\n    }\n    ASSERT(macro_cursor >= 0, \"expand_macro\");\n\n    // Macro area: Insert a relative jump backwards\n    target = (int32_t)(&code_dest[code_cursor] - &macro_dest[macro_cursor]);\n    macro_cursor += insert_relative_jmp_n_fence(&macro_dest[macro_cursor], target);\n\n    *macro_size = macro_cursor;\n    return 0;\n}\n"
  },
  {
    "path": "rvzr/executor_km/main.c",
    "content": "/// File: Kernel module interface\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n// clang-format off\n#include <linux/kernel.h>\n#include <linux/init.h>\n#include <linux/module.h>\n#include <linux/sysfs.h>\n#include <linux/version.h>\n#include <linux/kobject.h>\n#include <asm/processor.h>\n// clang-format on\n\n#include \"hardware_desc.h\"\n\n#include \"actor.h\"\n#include \"code_loader.h\"\n#include \"data_loader.h\"\n#include \"hardware_desc.h\"\n#include \"input_parser.h\"\n#include \"macro_expansion.h\"\n#include \"main.h\"\n#include \"measurement.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n#include \"test_case_parser.h\"\n\n#include \"fault_handler.h\"\n#include \"page_tables_common.h\"\n#include \"page_tables_guest.h\"\n#include \"page_tables_host.h\"\n#include \"perf_counters.h\"\n#include \"special_registers.h\"\n\n// ISA-specific includes\n#if defined(ARCH_X86_64)\n#include \"svm.h\"\n#include \"vmx.h\"\n#elif defined(ARCH_ARM)\n#include <asm/cpufeature.h>\n#endif\n\n// =================================================================================================\n// Kernel compatibility handling\n\n// Version-dependent includes\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 6)\n#ifdef ARCH_X86_64\n#include <../arch/x86/include/asm/io.h>\n#endif\n#endif\n\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)\n#define KPROBE_LOOKUP 1\n#include <linux/kprobes.h>\nstatic struct kprobe kp = {.symbol_name = \"kallsyms_lookup_name\"};\n#endif\n\n#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 12, 0)\n#include <asm/cacheflush.h>\n#endif\n\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)\n#include <linux/kallsyms.h>\nint (*set_memory_x)(unsigned long, int) = 0;\nint (*set_memory_nx)(unsigned long, int) = 0;\nstruct mm_struct init_mm = {0};\n#else\n#include <linux/set_memory.h>\n#endif\n\n// Version-dependent definitions\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)\n#define bin_attr_t const struct bin_attribute\n#else\n#define bin_attr_t struct bin_attribute\n#endif\n\n// =================================================================================================\n// Global Variables\nbool quick_and_dirty_mode = false;\n\nlong uarch_reset_rounds = UARCH_RESET_ROUNDS_DEFAULT;\nbool enable_ssbp_patch = SSBP_PATCH_DEFAULT;\nbool enable_prefetchers = PREFETCHER_DEFAULT;\nchar pre_run_flush = PRE_RUN_FLUSH_DEFAULT;\nbool enable_hpa_gpa_collisions = HPA_GPA_COLLISIONS_DEFAULT;\nmeasurement_mode_e measurement_mode = MEASUREMENT_MODE_DEFAULT;\nbool dbg_gpr_mode = DBG_GPR_MODE_DEFAULT;\n\ncpuinfo_t *cpuinfo = NULL;\n\n// =================================================================================================\n// Local declarations and definitions\n#define SYSFS_DIRNAME \"rvzr_executor\"\nstatic struct kobject *kobj_interface;\n\nunsigned inputs_top = 0;\nbool inputs_ready = false;\nbool tc_ready = false;\n\nbool unfinished_call = false;\n\n// =================================================================================================\n// SysFS interface to the module\n\n/* warning! need write-all permission so overriding check */\n#undef VERIFY_OCTAL_PERMISSIONS\n#define VERIFY_OCTAL_PERMISSIONS(perms) (perms)\n\n/// Reading hardware traces and performance counters\n///\nstatic ssize_t trace_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);\nstatic struct kobj_attribute trace_attribute = __ATTR(trace, 0664, trace_show, NULL);\n\n/// Loading a test case\n///\nstatic ssize_t test_case_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,\n                               size_t count);\nstatic struct kobj_attribute test_case_attribute = __ATTR(test_case, 0666, NULL, test_case_store);\n\nstatic ssize_t test_case_bin_read(struct file *file, struct kobject *kobj, bin_attr_t *bin_attr,\n                                  char *to, loff_t pos, size_t count);\nstatic struct bin_attribute test_case_bin_attribute = __BIN_ATTR_RO(test_case_bin, 0);\n\n/// Loading inputs\n///\nstatic ssize_t inputs_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,\n                            size_t count);\nstatic ssize_t inputs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);\nstatic struct kobj_attribute inputs_attribute = __ATTR(inputs, 0666, inputs_show, inputs_store);\n\n/// Setting the number of warm up rounds\n///\nstatic ssize_t warmups_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);\nstatic ssize_t warmups_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,\n                             size_t count);\nstatic struct kobj_attribute warmups_attribute = __ATTR(warmups, 0666, warmups_show, warmups_store);\n\n/// Getting the data base address\n///\nstatic ssize_t print_data_base_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);\nstatic struct kobj_attribute print_data_base_attribute =\n    __ATTR(print_data_base, 0664, print_data_base_show, NULL);\n\n/// Getting the base address of the memory region where the test case is loaded\n///\nstatic ssize_t print_code_base_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);\nstatic struct kobj_attribute print_code_base_attribute =\n    __ATTR(print_code_base, 0664, print_code_base_show, NULL);\n\n/// Control SSBP patch\n///\nstatic ssize_t enable_ssbp_patch_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                       const char *buf, size_t count);\nstatic struct kobj_attribute enable_ssbp_patch_attribute =\n    __ATTR(enable_ssbp_patch, 0666, NULL, enable_ssbp_patch_store);\n\n/// Control prefetchers\n///\nstatic ssize_t enable_prefetcher_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                       const char *buf, size_t count);\nstatic struct kobj_attribute enable_prefetcher_attribute =\n    __ATTR(enable_prefetcher, 0666, NULL, enable_prefetcher_store);\n\n/// Control flushing\n///\nstatic ssize_t enable_pre_run_flush_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                          const char *buf, size_t count);\nstatic struct kobj_attribute enable_pre_run_flush_attribute =\n    __ATTR(enable_pre_run_flush, 0666, NULL, enable_pre_run_flush_store);\n\n// Control virtual memory mapping\nstatic ssize_t enable_hpa_gpa_collisions_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                               const char *buf, size_t count);\nstatic struct kobj_attribute enable_hpa_gpa_collisions_attribute =\n    __ATTR(enable_hpa_gpa_collisions, 0666, NULL, enable_hpa_gpa_collisions_store);\n\n/// Measurement template selector\n///\nstatic ssize_t measurement_mode_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                      const char *buf, size_t count);\nstatic struct kobj_attribute measurement_mode_attribute =\n    __ATTR(measurement_mode, 0666, NULL, measurement_mode_store);\n\n/// Q&D mode selector\n///\nstatic ssize_t enable_quick_and_dirty_mode(struct kobject *kobj, struct kobj_attribute *attr,\n                                           const char *buf, size_t count);\nstatic struct kobj_attribute enable_quick_and_dirty_mode_attribute =\n    __ATTR(enable_quick_and_dirty_mode, 0666, NULL, enable_quick_and_dirty_mode);\n\n/// Setting which faults should be handled within the test case\n///\nstatic ssize_t handled_faults_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);\nstatic ssize_t handled_faults_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                    const char *buf, size_t count);\nstatic struct kobj_attribute handled_faults_attribute =\n    __ATTR(handled_faults, 0666, handled_faults_show, handled_faults_store);\n\n/// Debug GPR mode selector\n///\nstatic ssize_t enable_dbg_gpr_mode(struct kobject *kobj, struct kobj_attribute *attr,\n                                   const char *buf, size_t count);\nstatic struct kobj_attribute enable_dbg_gpr_mode_attribute =\n    __ATTR(enable_dbg_gpr_mode, 0666, NULL, enable_dbg_gpr_mode);\n\n/// Debugging interface\n///\nstatic ssize_t dbg_dump_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf);\nstatic struct kobj_attribute dbg_dump_attribute = __ATTR(dbg_dump_mode, 0666, dbg_dump_show, NULL);\n\nstatic ssize_t dbg_guest_page_tables_show(struct kobject *kobj, struct kobj_attribute *attr,\n                                          char *buf);\nstatic struct kobj_attribute dbg_guest_page_tables_attribute =\n    __ATTR(dbg_guest_page_tables, 0666, dbg_guest_page_tables_show, NULL);\n\nstatic struct attribute *sysfs_attributes[] = {\n    &trace_attribute.attr,\n    &test_case_attribute.attr,\n    &inputs_attribute.attr,\n    &warmups_attribute.attr,\n    &print_data_base_attribute.attr,\n    &print_code_base_attribute.attr,\n    &enable_ssbp_patch_attribute.attr,\n    &enable_prefetcher_attribute.attr,\n    &enable_pre_run_flush_attribute.attr,\n    &measurement_mode_attribute.attr,\n    &enable_quick_and_dirty_mode_attribute.attr,\n    &enable_dbg_gpr_mode_attribute.attr,\n    &handled_faults_attribute.attr,\n    &dbg_dump_attribute.attr,\n    &dbg_guest_page_tables_attribute.attr,\n    &enable_hpa_gpa_collisions_attribute.attr,\n    NULL, /* need to NULL terminate the list of attributes */\n};\n\nstatic struct bin_attribute *bin_sysfs_attributes[] = {\n    &test_case_bin_attribute, //\n    NULL,                     /* need to NULL terminate the list of attributes */\n};\n\n// =================================================================================================\n// Implementation of the sysfs attributes\n\nint next_measurement_id = -1;\nstatic ssize_t trace_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)\n{\n    int count = 0;\n    int retval = 0;\n\n    ASSERT(measurements, \"trace_show\");\n    ASSERT(input_parsing_completed(), \"trace_show\");\n    ASSERT(tc_parsing_completed(), \"trace_show\");\n\n    // start a new measurement?\n    unfinished_call = true;\n    if (next_measurement_id < 0) {\n        int err = trace_test_case();\n        if (err)\n            return -EIO;\n\n        // start printing the results\n        next_measurement_id = n_inputs - 1;\n    }\n    unfinished_call = false;\n\n    // print the results, but make sure we can continue later if we run out of space in buf\n    for (; next_measurement_id >= 0; next_measurement_id--) {\n        // check if the output buffer still has space\n        if (count >= (4096 - 128))\n            return count; // we will continue in the next call of this function\n\n        measurement_t m = measurements[next_measurement_id];\n        retval =\n            sprintf(&buf[count], \"%llu,%llu,%llu,%llu,%llu,%llu\\n\", m.htrace[0], m.pfc_reading[0],\n                    m.pfc_reading[1], m.pfc_reading[2], m.pfc_reading[3], m.pfc_reading[4]);\n        if (!retval)\n            return -1;\n        count += retval;\n    }\n    count += sprintf(&buf[count], \"done\\n\");\n    return count;\n}\n\n/// @brief Check if the parsed test case is compatible with the current CPU\n/// @param void\n/// @return 0 if the test case is compatible, -1 otherwise\nstatic int check_test_case_compat(void)\n{\n    int err = 0;\n\n#ifdef ARCH_X86_64\n    if (test_case->features.includes_user_actors) {\n#ifndef FORCE_SMAP_OFF\n        // ensure that SMAP and SMEP are disabled\n        uint64_t cr4 = __read_cr4();\n        ASSERT(!(__read_cr4() & (X86_CR4_SMAP | X86_CR4_SMEP)), \"test_case_store\");\n#endif\n    }\n    if (test_case->features.includes_vm_actors) {\n        if (cpuinfo->x86_vendor == X86_VENDOR_INTEL) {\n            err = vmx_check_cpu_compatibility();\n        } else if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n            err = svm_check_cpu_compatibility();\n        }\n        CHECK_ERR(\"vm_check_cpu_compatibility\");\n    }\n#endif\n\n    return err;\n}\n\nstatic ssize_t test_case_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,\n                               size_t count)\n{\n    int err = 0;\n    tc_ready = false;\n\n    bool finished = false;\n    ssize_t consumed_bytes = parse_test_case_buffer(buf, count, &finished);\n    if (!finished) {\n        return consumed_bytes;\n    }\n\n    // check if the given test case can be executed on this CPU\n    err = check_test_case_compat();\n    CHECK_ERR(\"check_test_case_compat\");\n\n    // prepare sandboxes\n    err = allocate_sandbox();\n    CHECK_ERR(\"allocate_sandbox\");\n\n    err = load_sandbox_code();\n    CHECK_ERR(\"load_sandbox_code\");\n\n    next_measurement_id = -1;\n    tc_ready = true;\n    return consumed_bytes;\n}\n\nstatic ssize_t test_case_bin_read(struct file *file, struct kobject *kobj, bin_attr_t *bin_attr,\n                                  char *to, loff_t pos, size_t count)\n{\n    loff_t max_pos = n_actors * sizeof(actor_code_t);\n    if (pos > max_pos)\n        return 0;\n\n    loff_t chunk_end = pos + PAGE_SIZE;\n    if (chunk_end > max_pos)\n        chunk_end = max_pos;\n    count = chunk_end - pos;\n    memcpy(to, &loaded_test_case_entry[pos], count);\n    return count;\n}\n\nstatic ssize_t inputs_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,\n                            size_t count)\n{\n    bool finished = false;\n    ssize_t consumed_bytes = parse_input_buffer(buf, count, &finished);\n    inputs_ready = false;\n\n    if (finished) {\n        inputs_ready = true;\n    }\n    return consumed_bytes;\n}\n\nstatic ssize_t inputs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)\n{\n    // FIXME: not implemented yet. See Flavien's branch for a reference implementation\n    return sprintf(buf, \"%d\\n\", inputs_ready);\n}\n\nstatic ssize_t warmups_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)\n{\n    return sprintf(buf, \"%ld\\n\", uarch_reset_rounds);\n}\n\nstatic ssize_t warmups_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf,\n                             size_t count)\n{\n    sscanf(buf, \"%ld\", &uarch_reset_rounds);\n    return count;\n}\n\nstatic ssize_t print_data_base_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)\n{\n    return sprintf(buf, \"%llx\\n\", (long long unsigned)&sandbox->data[0]);\n}\n\nstatic ssize_t print_code_base_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)\n{\n    return sprintf(buf, \"%llx\\n\", (long long unsigned)loaded_test_case_entry);\n}\n\nstatic ssize_t enable_ssbp_patch_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                       const char *buf, size_t count)\n{\n    unsigned value = 0;\n    sscanf(buf, \"%u\", &value);\n    enable_ssbp_patch = (value == 0) ? false : true;\n    return count;\n}\n\nstatic ssize_t enable_prefetcher_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                       const char *buf, size_t count)\n{\n    unsigned value = 0;\n    sscanf(buf, \"%u\", &value);\n    enable_prefetchers = (value == 0) ? false : true;\n    return count;\n}\n\nstatic ssize_t enable_pre_run_flush_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                          const char *buf, size_t count)\n{\n    unsigned value = 0;\n    sscanf(buf, \"%u\", &value);\n    pre_run_flush = (value == 0) ? 0 : 1;\n    return count;\n}\n\nstatic ssize_t enable_hpa_gpa_collisions_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                               const char *buf, size_t count)\n{\n    unsigned value = 0;\n    sscanf(buf, \"%u\", &value);\n    enable_hpa_gpa_collisions = (value == 0) ? false : true;\n    return count;\n}\n\nstatic ssize_t measurement_mode_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                      const char *buf, size_t count)\n{\n    switch (buf[0]) {\n    case 'P':\n        if (buf[1] == '+')\n            measurement_mode = PRIME_PROBE;\n        else\n            measurement_mode = PARTIAL_PRIME_PROBE;\n        break;\n    case 'F':\n        measurement_mode = FLUSH_RELOAD;\n        break;\n    case 'E':\n        measurement_mode = EVICT_RELOAD;\n        break;\n    case 'T':\n        measurement_mode = TSC;\n        break;\n    default:\n        PRINT_ERRS(\"measurement_mode_store\", \"Invalid measurement mode\\n\");\n        return -1;\n    }\n\n    quick_and_dirty_mode = false; // updating the measurement mode resets the Q&D mode\n    return count;\n}\n\nstatic ssize_t enable_quick_and_dirty_mode(struct kobject *kobj, struct kobj_attribute *attr,\n                                           const char *buf, size_t count)\n{\n\n    unsigned value = 0;\n    sscanf(buf, \"%u\", &value);\n    if (value == 1 && quick_and_dirty_mode == false) {\n        quick_and_dirty_mode = true;\n        switch (measurement_mode) {\n        case PRIME_PROBE:\n            measurement_mode = FAST_PRIME_PROBE;\n            break;\n        case PARTIAL_PRIME_PROBE:\n            measurement_mode = FAST_PARTIAL_PRIME_PROBE;\n            break;\n        default:\n            break;\n        }\n    } else if (value == 0 && quick_and_dirty_mode == true) {\n        quick_and_dirty_mode = false;\n        switch (measurement_mode) {\n        case FAST_PRIME_PROBE:\n            measurement_mode = PRIME_PROBE;\n            break;\n        case FAST_PARTIAL_PRIME_PROBE:\n            measurement_mode = PARTIAL_PRIME_PROBE;\n            break;\n        default:\n            break;\n        }\n    }\n    return count;\n}\n\nstatic ssize_t enable_dbg_gpr_mode(struct kobject *kobj, struct kobj_attribute *attr,\n                                   const char *buf, size_t count)\n{\n    unsigned value = 0;\n    sscanf(buf, \"%u\", &value);\n    dbg_gpr_mode = (value == 0) ? false : true;\n    return count;\n}\n\nstatic ssize_t handled_faults_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)\n{\n    return sprintf(buf, \"0x%llx\\n\", (unsigned long long)handled_faults);\n}\n\nstatic ssize_t handled_faults_store(struct kobject *kobj, struct kobj_attribute *attr,\n                                    const char *buf, size_t count)\n{\n    unsigned long long value;\n    if (sscanf(buf, \"%lld\", &value) != 1)\n        return -EINVAL;\n\n    handled_faults = value | HANDLED_FAULTS_DEFAULT;\n    return count;\n}\n\n/// Dump all global variables\n///\nstatic ssize_t dbg_dump_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)\n{\n    int len = 0;\n    len += sprintf(&buf[len], \"n_actors: %lu\\n\", n_actors);\n    len += sprintf(&buf[len], \"test_case: 0x%llx\\n\", (uint64_t)test_case);\n    len += sprintf(&buf[len], \"loaded_test_case_entry: 0x%llx\\n\", (uint64_t)loaded_test_case_entry);\n    len += sprintf(&buf[len], \"measurements: 0x%llx\\n\", (uint64_t)measurements);\n    len += sprintf(&buf[len], \"n_inputs: %lu\\n\", n_inputs);\n    len += sprintf(&buf[len], \"inputs: %llx\\n\", (uint64_t)inputs);\n    if (inputs) {\n        len += sprintf(&buf[len], \"inputs->metadata: %llx\\n\", (uint64_t)inputs->metadata);\n        len += sprintf(&buf[len], \"inputs->data: %llx\\n\", (uint64_t)inputs->data);\n    }\n    len += sprintf(&buf[len], \"sandbox: %llx\\n\", (uint64_t)sandbox);\n    len += sprintf(&buf[len], \"fault_handler: %llx\\n\", (uint64_t)fault_handler);\n    len += sprintf(&buf[len], \"handled_faults: %u\\n\", handled_faults);\n    len += sprintf(&buf[len], \"quick_and_dirty_mode: %d\\n\", quick_and_dirty_mode);\n    len += sprintf(&buf[len], \"uarch_reset_rounds: %ld\\n\", uarch_reset_rounds);\n    len += sprintf(&buf[len], \"enable_ssbp_patch: %d\\n\", enable_ssbp_patch);\n    len += sprintf(&buf[len], \"enable_prefetchers: %d\\n\", enable_prefetchers);\n    len += sprintf(&buf[len], \"pre_run_flush: %d\\n\", pre_run_flush);\n    return len;\n}\n\n/// Dump guest page tables into the kernel log\nstatic ssize_t dbg_guest_page_tables_show(struct kobject *kobj, struct kobj_attribute *attr,\n                                          char *buf)\n{\n    if (n_actors < 2)\n        return sprintf(buf, \"No actors to print tables for\\n\");\n\n    int err = dbg_dump_guest_page_tables(1);\n    if (err)\n        return err;\n    err = dbg_dump_ept(1);\n    if (err)\n        return err;\n    return sprintf(buf, \"done (see dmesg)\\n\");\n}\n\n// =================================================================================================\n// Initialization and Memory Management\n// =================================================================================================\n\n/// @brief Get symbols for missing kernel functions\n/// @param void\n/// @return void\nstatic inline void _get_required_kernel_functions(void)\n{\n#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)\n#ifdef KPROBE_LOOKUP\n    typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);\n    kallsyms_lookup_name_t kallsyms_lookup_name;\n    register_kprobe(&kp);\n    kallsyms_lookup_name = (kallsyms_lookup_name_t)kp.addr;\n    unregister_kprobe(&kp);\n#endif // KPROBE_LOOKUP\n    set_memory_x = (void *)kallsyms_lookup_name(\"set_memory_x\");\n    set_memory_nx = (void *)kallsyms_lookup_name(\"set_memory_nx\");\n    init_mm = *(struct mm_struct *)kallsyms_lookup_name(\"init_mm\");\n#endif // LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0)\n}\n\n/// @brief Get a description of the CPU\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic inline cpuinfo_t *get_cpuinfo(void)\n{\n#if defined(ARCH_X86_64)\n    return &cpu_data(0);\n#elif defined(ARCH_ARM)\n    cpuinfo_t *cpuinfo = kmalloc(sizeof(cpuinfo_t), GFP_KERNEL);\n    if (!cpuinfo) {\n        return NULL;\n    }\n\n    uint64_t midr_el1 = 0;\n    asm volatile(\"MRS %0, MIDR_EL1\" : \"=r\"(midr_el1));\n    cpuinfo->implementer = (midr_el1 >> 24) & 0xFF;\n    cpuinfo->variant = (midr_el1 >> 20) & 0xF;\n    cpuinfo->architecture = (midr_el1 >> 16) & 0xF;\n    cpuinfo->part = (midr_el1 >> 4) & 0xFFF;\n    cpuinfo->revision = midr_el1 & 0xF;\n\n    return cpuinfo;\n#endif\n}\n\n/// @brief Check if the CPU supports the required features\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic int check_cpu_compat(void)\n{\n#if defined(ARCH_X86_64)\n    // Check CPU vendor\n    if (cpuinfo->x86_vendor != X86_VENDOR_INTEL && cpuinfo->x86_vendor != X86_VENDOR_AMD) {\n        printk(KERN_ERR \"ERROR: rvzr_executor: This CPU vendor is not supported\\n\");\n        return -1;\n    }\n\n    // Check that the CPU supports the required features\n    if (!cpu_has(cpuinfo, X86_FEATURE_AVX) || !cpu_has(cpuinfo, X86_FEATURE_MMX)) {\n        printk(KERN_ERR \"ERROR: rvzr_executor: Executor KM requires AVX\\n\");\n        return -1;\n    }\n\n    // Check memory configuration\n    unsigned int phys_addr_width = cpuinfo->x86_phys_bits;\n    if (phys_addr_width != PHYSICAL_WIDTH) {\n        printk(KERN_ERR \"rvzr_executor: ERROR: The width of physical addresses is %d instead of \"\n                        \"expected %d\\n\",\n               phys_addr_width, PHYSICAL_WIDTH);\n        return -1;\n    }\n#elif defined(ARCH_ARM)\n    // Nothing so far\n#endif\n    return 0;\n}\n\nstatic int __init executor_init(void)\n{\n    // Get CPU information and store in a global variable for future references\n    cpuinfo = get_cpuinfo();\n    if (!cpuinfo) {\n        printk(KERN_ERR \"rvzr_executor: Failed to get CPU information\\n\");\n        return -ENOMEM;\n    }\n\n    // Check if the CPU supports the required features\n    if (check_cpu_compat() != 0) {\n        return -1;\n    }\n\n    // Make sure that we have all requirements\n    _get_required_kernel_functions();\n\n    // Initialize modules\n    int err = 0;\n    err |= init_measurements();\n    err |= init_sandbox_manager();\n    err |= init_code_loader();\n    err |= init_data_loader();\n    err |= init_input_parser();\n    err |= init_test_case_parser();\n    err |= init_fault_handler();\n    err |= init_page_table_manager();\n    err |= init_perf_counters();\n    err |= init_special_register_manager();\n\n#if VENDOR_ID == VENDOR_INTEL_\n    err |= init_vmx();\n#elif VENDOR_ID == VENDOR_AMD_\n    err |= init_svm();\n#endif\n    CHECK_ERR(\"executor_init\");\n\n    // Create a pseudo file system interface\n    kobj_interface = kobject_create_and_add(SYSFS_DIRNAME, kernel_kobj->parent);\n    if (!kobj_interface) {\n        printk(KERN_ERR \"rvzr_executor: Failed to create a sysfs directory for x86-executor\\n\");\n        return -ENOMEM;\n    }\n\n    // Create the files associated with this kobject\n    // int retval = sysfs_create_group(kobj_interface, &attr_group);\n    int i = 0;\n    struct attribute *attr;\n    for (attr = sysfs_attributes[i]; !err; i++) {\n        attr = sysfs_attributes[i];\n        if (attr == NULL)\n            break;\n\n        err = sysfs_create_file(kobj_interface, attr);\n    }\n    if (err != 0) {\n        printk(KERN_ERR \"rvzr_executor: Failed to create a sysfs group\\n\");\n        kobject_put(kobj_interface);\n        return err;\n    }\n\n    // Create binary attributes (used for passing large amounts of data)\n    i = 0;\n    struct bin_attribute *bin_attr;\n    for (bin_attr = bin_sysfs_attributes[i]; !err; i++) {\n        bin_attr = bin_sysfs_attributes[i];\n        if (bin_attr == NULL)\n            break;\n\n        err = sysfs_create_bin_file(kobj_interface, bin_attr);\n    }\n    if (err != 0) {\n        printk(KERN_ERR \"rvzr_executor: Failed to create a binary sysfs files\\n\");\n        kobject_put(kobj_interface);\n        return err;\n    }\n\n    return 0;\n}\n\nstatic void __exit executor_exit(void)\n{\n    if (unfinished_call) {\n        PRINT_ERR(\"CRITICAL ERROR: executor crashed while handling a sysfs call\\n\"\n                  \"Removing the module is no longer safe as it may lead to system blocking\\n\"\n                  \"Reboot to remove the module\\n\");\n        return;\n    }\n\n    free_measurements();\n    free_sandbox_manager();\n    free_code_loader();\n    free_data_loader();\n    free_input_parser();\n    free_test_case_parser();\n    free_fault_handler();\n    free_page_table_manager();\n    free_perf_counters();\n    free_special_register_manager();\n\n#if VENDOR_ID == VENDOR_INTEL_\n    free_vmx();\n#elif VENDOR_ID == VENDOR_AMD_\n    free_svm();\n#endif\n\n#if defined(ARCH_ARM)\n    if (cpuinfo)\n        kfree(cpuinfo);\n#endif\n\n    if (kobj_interface)\n        kobject_put(kobj_interface);\n}\n\nmodule_init(executor_init);\nmodule_exit(executor_exit);\nMODULE_LICENSE(\"Dual MIT/GPL\");\nMODULE_AUTHOR(\"Oleksii Oleksenko\");\n"
  },
  {
    "path": "rvzr/executor_km/measurement.c",
    "content": "/// File:\n///  - Test case execution\n///  - Ensuring an isolated environment\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"hardware_desc.h\"\n#include <asm/processor.h>\n\n#include \"code_loader.h\"\n#include \"data_loader.h\"\n#include \"input_parser.h\"\n#include \"main.h\"\n#include \"measurement.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n#include \"test_case_parser.h\"\n\n#include \"fault_handler.h\"\n#include \"page_tables_guest.h\"\n#include \"page_tables_host.h\"\n#include \"perf_counters.h\"\n#include \"special_registers.h\"\n\n#ifdef ARCH_X86_64\n#include <asm/msr-index.h>\n#include <asm/msr.h>\n#include <asm/spec-ctrl.h>\n\n#include \"svm.h\"\n#include \"vmx.h\"\n#elif defined(ARCH_ARM)\n\n#endif\n\nmeasurement_t *measurements = NULL; // global\n\nint run_experiment_outer(void); // inline asm label defined in <arch>/fault_handler.c\n\n// =================================================================================================\n// Local shortcut functions\n// =================================================================================================\n\n/// @brief Flushes the microarchitectural state\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic inline int uarch_flush(void)\n{\n#if VENDOR_ID == VENDOR_INTEL_ // Intel\n    static const u16 ds = __KERNEL_DS;\n    asm volatile(\"verw %[ds]\" : : [ds] \"m\"(ds) : \"cc\");\n#ifndef VMBUILD\n    wrmsr64(MSR_IA32_FLUSH_CMD, L1D_FLUSH);\n#endif\n    asm volatile(\"wbinvd\\n\" : : :);\n    asm volatile(\"lfence\\n\" : : :);\n#elif VENDOR_ID == VENDOR_AMD_ // AMD\n    asm volatile(\"wbinvd\\n\" : : :);\n    asm volatile(\"lfence\\n\" : : :);\n    // TBD\n#endif\n    return 0;\n}\n\n/// @brief Check if entry page of the test case is valid (present and executable)\n/// @param void\n/// @return 0 if the entry page is valid, -1 otherwise\nstatic int check_test_case_entry(void)\n{\n    pte_t *tc_pte = get_pte((uint64_t)loaded_test_case_entry);\n    if (!tc_pte || !pte_present(*tc_pte)) {\n        return -1;\n    }\n#ifdef ARCH_X86_64\n    if (!pte_exec(*tc_pte)) {\n        return -1;\n    }\n#endif\n\n    return 0;\n}\n\n/// @brief Checks the measurement status for corruption\n/// @param status The measurement status structure to check\n/// @return 0 on valid (non-corrupted) measurement, -1 on corrupted measurement\nstatic int check_measurement_status(measurement_status_t *status)\n{\n    if (status->measurement_state != STATUS_ENDED) {\n        switch (status->measurement_state) {\n        case STATUS_UNINITIALIZED:\n            PRINT_WARNS(\"run_experiment\",\n                        \"Corrupted measurement: measurement_start macro was not executed, state=%d\",\n                        status->measurement_state);\n            break;\n        case STATUS_STARTED:\n            PRINT_WARNS(\"run_experiment\",\n                        \"Corrupted measurement: measurement_end macro was not executed, state=%d\",\n                        status->measurement_state);\n            break;\n        default:\n            PRINT_WARNS(\"run_experiment\", \"Corrupted measurement: unknown state, state=%d\",\n                        status->measurement_state);\n        }\n        return -1;\n    }\n\n    if (status->smi_count != 0) {\n        PRINT_WARNS(\"run_experiment\", \"Corrupted measurement: SMI detected, count=%d\",\n                    status->smi_count);\n        return -1;\n    }\n\n    return 0;\n}\n\n/// @brief Check if the executor is ready to start measurements, and perform the necessary\n///        setup of the CPU to ensure that the test case can be executed. Note that this function\n///        only partially configures the CPU, and more will be done in set_execution_environment\n/// @param irq_flags The flags to store the interrupt state\n/// @return 0 on success, -1 on failure\nstatic int pre_run(unsigned long *irq_flags)\n{\n    int err = 0;\n\n    // check that all main data structures were allocated\n    ASSERT(loaded_test_case_entry, \"trace_test_case\");\n    ASSERT(check_test_case_entry() == 0, \"trace_test_case\");\n    ASSERT(inputs, \"trace_test_case\");\n    ASSERT(inputs->metadata, \"trace_test_case\");\n    ASSERT(inputs->data, \"trace_test_case\");\n\n    // Configure performance counters\n    err |= pfc_configure();\n    CHECK_ERR(\"trace_test_case:pfc_configure\");\n\n    // Enable FPU - just in case, we might use it within the test case\n#ifdef ARCH_X86_64\n    kernel_fpu_begin();\n#endif\n\n    // Prevent preemption\n    get_cpu();\n\n    unsigned long flags;\n    raw_local_irq_save(flags);\n    *irq_flags = flags;\n\n    return err;\n}\n\n/// @brief Cleanup after the test case execution by undoing the changes made in pre_run\n/// @param irq_flags The flags to restore the interrupt state\n/// @return void\nstatic inline void post_run(unsigned long *irq_flags)\n{\n#if VENDOR_ID == VENDOR_AMD_\n    asm volatile(\"stgi\\n\"); // enable interrupts in case they were disabled\n#endif\n    unsigned long flags = *irq_flags;\n    raw_local_irq_restore(flags);\n\n    put_cpu();\n\n#ifdef ARCH_X86_64\n    kernel_fpu_end();\n#endif\n}\n\n// =================================================================================================\n// CPU state management\n// =================================================================================================\n/// @brief Stores the current state of the CPU and re-configures it for the test case execution\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic int set_execution_environment(void)\n{\n    int err = 0;\n    err = set_special_registers();\n    CHECK_ERR(\"set_execution_environment:set_special_registers\");\n\n    // If necessary, enable VM operation\n#ifdef ARCH_X86_64\n    if (test_case->features.includes_vm_actors) {\n        if (cpuinfo->x86_vendor == X86_VENDOR_INTEL) {\n            err = start_vmx_operation();\n            CHECK_ERR(\"set_execution_environment:start_vmx_operation\");\n\n            err = store_orig_vmcs_state();\n            CHECK_ERR(\"set_execution_environment:store_orig_vmcs_state\");\n\n            err = set_vmcs_state();\n            CHECK_ERR(\"set_execution_environment:set_vmcs_state\");\n        } else if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n            err = start_svm_operation();\n            CHECK_ERR(\"set_execution_environment:start_svm_operation\");\n\n            err = store_orig_vmcb_state();\n            CHECK_ERR(\"set_execution_environment:store_orig_vmcb_state\");\n\n            err = set_vmcb_state();\n            CHECK_ERR(\"set_execution_environment:set_vmcb_state\");\n        }\n    }\n#endif\n    return 0;\n}\n\n/// @brief Restores the CPU state to the state before the test case execution. This function is\n/// written in a fail-safe manner, so that it can be called in fault handlers.\n/// @param void\nvoid recover_orig_state(void)\n{\n    // restore VMX state\n#ifdef ARCH_X86_64\n    if (test_case->features.includes_vm_actors) {\n        if (cpuinfo->x86_vendor == X86_VENDOR_INTEL) {\n            // if (vmx_is_on)\n            //     print_vmx_exit_info(); // uncomment to debug VMX exits\n            restore_orig_vmcs_state();\n            stop_vmx_operation();\n        } else if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n            // if (svm_is_on)\n            //     print_svm_exit_info(); // uncomment to debug SVM exits\n            restore_orig_vmcb_state();\n            stop_svm_operation();\n        }\n    }\n#endif\n\n    restore_faulty_page_permissions();\n    restore_special_registers();\n    restore_orig_sandbox_page_tables();\n}\n\n// =================================================================================================\n// Measurement loop: trace_test_case -> run_experiment_outer -> run_experiment\n// =================================================================================================\n\n/// @brief Run a complete measurement experiment: setup the execution environment and execute\n///        the loaded test case for each inputs, storing the resulting hardware traces and PFC\n///        readings in the global `measurements` array\n/// @param void\n/// @return 0 on success, -1 on error\nint run_experiment(void)\n{\n    int err = 0;\n\n    // allocate and map memory for the test case\n    err = set_sandbox_page_tables();\n    if (err)\n        goto cleanup;\n\n    // configure the CPU (and anything else necessary) to prepare for the test case execution\n    err = set_execution_environment();\n    if (err)\n        goto cleanup;\n\n    // Zero-initialize the region of memory used by Prime+Probe\n    if (!quick_and_dirty_mode)\n        memset(&sandbox->util->l1d_priming_area[0], 0, L1D_PRIMING_AREA_SIZE * sizeof(char));\n\n    // Try to reset the uarch state\n    // (we do it here because from this point on the execution is expected to be deterministic\n    // and depend solely on the test case and the input to it)\n    if (pre_run_flush == 1 && !quick_and_dirty_mode)\n        uarch_flush();\n\n    long rounds = (long)n_inputs;\n    for (long i = -uarch_reset_rounds; i < rounds; i++) {\n        // ignore \"warm-up\" runs (i<0)uarch_reset_rounds\n        long i_ = (i < 0) ? 0 : i;\n\n        // Prepare sandbox\n        load_sandbox_data(i_);\n        set_faulty_page_permissions();\n\n        // Catch all exceptions\n        set_inner_fault_handlers();\n\n        // Execute\n        char *main_data = &sandbox->data[0].main_area[0];\n        err = ((int (*)(char *))loaded_test_case_entry)(main_data);\n\n        // Restore the original fault handlers and sandbox state\n        unset_inner_fault_handlers();\n        restore_faulty_page_permissions();\n        if (err) // Note: this check HAS to be after IDT/PT reset to avoid corrupting system state\n            goto cleanup;\n\n        // Store the measurement\n        measurement_t result = sandbox->util->vars.latest_measurement;\n        measurements[i_].htrace[0] = result.htrace[0];\n        memcpy(measurements[i_].pfc_reading, result.pfc_reading, sizeof(uint64_t) * NUM_PFC);\n\n        // Post-process the measurement\n        // (only in normal, non-debug non-warmup runs)\n        if (i >= 0 && !dbg_gpr_mode) {\n            // Check for measurement corruption\n            if (check_measurement_status(&result.status) != 0)\n                // Note: we intentionally do not set the `err` variable upon corruption, because\n                // corruptions are expected to happen every once in a while because of SMIs,\n                // and thus we want to handle them gracefully\n                goto cleanup;\n\n            // If the measurement is valid, set the upper bit of htrace\n            // to distinguish correct htraces from corrupted ones\n            measurements[i_].htrace[0] |= 1ULL << 63;\n        }\n    }\n\ncleanup:\n    if (err)\n        measurements[0].htrace[0] = 0; // communicate the error up to executor.py\n    recover_orig_state();\n    CHECK_ERR(\"run_experiment:cleanup\");\n    return err;\n}\n\n/// @brief The outermost wrapper for the test case execution. Sets up performance counters,\n///        configures the CPU, disables interrupts, and calls enter_unsafe_bubble\n/// @param void\n/// @return 0 on success, -1 on failure\nint trace_test_case(void)\n{\n    int err = 0;\n    unsigned long irq_flags = 0;\n\n    err = alloc_measurements();\n    CHECK_ERR(\"alloc_measurements\");\n\n    err = pre_run(&irq_flags);\n    CHECK_ERR(\"trace_test_case:pre_run\");\n\n    if (n_inputs) {\n        err |= run_experiment_outer();\n    }\n\n    post_run(&irq_flags);\n    CHECK_ERR(\"trace_test_case:cleanup\");\n\n    return err;\n}\n\n// =================================================================================================\n// Constructor and destructor + initialization\n// =================================================================================================\nint alloc_measurements(void)\n{\n    static int old_n_inputs = 0;\n    if (n_inputs <= old_n_inputs)\n        return 0;\n    old_n_inputs = n_inputs;\n\n    SAFE_VFREE(measurements);\n    measurements = CHECKED_VMALLOC(n_inputs * sizeof(measurement_t));\n    memset(measurements, 0, n_inputs * sizeof(measurement_t));\n    return 0;\n}\n\nint init_measurements(void)\n{\n    measurements = CHECKED_VMALLOC(sizeof(measurement_t));\n    return 0;\n}\n\n/// Destructor for the measurement module\n///\nvoid free_measurements(void) { SAFE_VFREE(measurements); }\n"
  },
  {
    "path": "rvzr/executor_km/page_tables_host.c",
    "content": "/// File:\n///  - Page Table management\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <linux/kernel.h>\n#include <linux/mm.h>\n\n#include \"actor.h\"\n#include \"hardware_desc.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n\n#include \"page_tables_common.h\"\n#include \"page_tables_host.h\"\n\nsandbox_pteps_t *sandbox_pteps;\n\nstatic sandbox_ptes_t *orig_ptes;\nstatic pte_t_ *faulty_ptes = NULL;\n\nextern struct mm_struct init_mm;\n\npte_t *get_pte(uint64_t hva)\n{\n    // Make sure we are in vmalloc area\n    if (!is_vmalloc_addr((void *)hva) && !virt_addr_valid((void *)hva)) {\n        PRINT_ERR(\"get_pte: address not in vmalloc or kmalloc area\");\n        return NULL;\n    }\n\n    // Do a page walk\n    pgd_t *pgdp = pgd_offset_k(hva);\n    pgd_t pgd = READ_ONCE(*pgdp);\n    if (pgd_none(pgd)) {\n        PRINT_ERR(\"get_pte: pgd_none\");\n        return NULL;\n    }\n\n    p4d_t *p4dp = p4d_offset(pgdp, hva);\n    p4d_t p4d = READ_ONCE(*p4dp);\n    if (p4d_none(p4d)) {\n        PRINT_ERR(\"get_pte: p4d_none\");\n        return NULL;\n    }\n\n    pud_t *pudp = pud_offset(p4dp, hva);\n    pud_t pud = READ_ONCE(*pudp);\n    if (pud_none(pud)) {\n        PRINT_ERR(\"get_pte: pud_none\");\n        return NULL;\n    }\n    if (pud_bad(pud)) {\n        PRINT_ERR(\"get_pte: pud_bad\");\n        return NULL;\n    }\n\n    pmd_t *pmdp = pmd_offset(pudp, hva);\n    pmd_t pmd = READ_ONCE(*pmdp);\n    if (pmd_none(pmd)) {\n        PRINT_ERR(\"get_pte: pmd_none\");\n        return NULL;\n    }\n    if (pmd_bad(pmd)) {\n        PRINT_ERR(\"get_pte: pmd_bad\");\n        return NULL;\n    }\n\n    pte_t *pte = pte_offset_kernel(pmdp, hva);\n    ASSERT_ENULL(pte_present(*pte), \"get_pte\");\n\n    return pte;\n}\n\n// =================================================================================================\n// Manipulation of Host Page Tables\n// =================================================================================================\n/// @brief Cache the PTE pointers for all sandbox pages.\n/// @param void\n/// @return 0 on success, -1 on failure\nint cache_host_pteps(void)\n{\n    ASSERT(sandbox_pteps != NULL, \"cache_host_pteps\");\n    ASSERT(sandbox != NULL, \"cache_host_pteps\");\n\n    static int old_n_actors = 1;\n    if (n_actors > old_n_actors) {\n        SAFE_FREE(sandbox_pteps->data_pteps);\n        SAFE_FREE(sandbox_pteps->code_pteps);\n        sandbox_pteps->data_pteps =\n            CHECKED_ZALLOC(N_DATA_PAGES_PER_ACTOR * n_actors * sizeof(pte_t_ *));\n        sandbox_pteps->code_pteps =\n            CHECKED_ZALLOC(N_CODE_PAGES_PER_ACTOR * n_actors * sizeof(pte_t_ *));\n    }\n    old_n_actors = n_actors;\n\n    // cache the PTE pointers for the util pages\n    for (int i = 0; i < N_UTIL_PAGES; i++) {\n        uint64_t va = (uint64_t)sandbox->util + i * PAGE_SIZE;\n        pte_t *ptep = get_pte(va);\n        ASSERT(ptep != NULL, \"cache_host_pteps\");\n        sandbox_pteps->util_pteps[i] = (pte_t_ *)&ptep->pte;\n    }\n\n    // cache the PTE pointers for the code and data pages of the sandbox\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        // cache the PTE pointers for the data pages of the actor\n        for (int i = 0; i < N_DATA_PAGES_PER_ACTOR; i++) {\n            uint64_t va = ((uint64_t)&sandbox->data[actor_id]) + i * PAGE_SIZE;\n            pte_t *ptep = get_pte(va);\n            ASSERT(ptep != NULL, \"cache_host_pteps\");\n            sandbox_pteps->data_pteps[actor_id * N_DATA_PAGES_PER_ACTOR + i] = (pte_t_ *)&ptep->pte;\n        }\n        // cache the PTE pointers for the code pages of the actor\n        for (int i = 0; i < N_CODE_PAGES_PER_ACTOR; i++) {\n            uint64_t va = ((uint64_t)&sandbox->code[actor_id]) + i * PAGE_SIZE;\n            pte_t *ptep = get_pte(va);\n            ASSERT(ptep != NULL, \"cache_host_pteps\");\n            sandbox_pteps->code_pteps[actor_id * N_CODE_PAGES_PER_ACTOR + i] = (pte_t_ *)&ptep->pte;\n        }\n    }\n    return 0;\n}\n\n/// @brief Preserve the original PTEs for all sandbox pages.\n/// @param void\n/// @return 0 on success, -1 on failure\nint store_orig_host_permissions(void)\n{\n    ASSERT(sandbox_pteps->util_pteps[0] != NULL, \"store_orig_host_permissions\");\n    ASSERT(sandbox_pteps->data_pteps[0] != NULL, \"store_orig_host_permissions\");\n    ASSERT(sandbox_pteps->code_pteps[0] != NULL, \"store_orig_host_permissions\");\n\n    static int old_n_actors = 1;\n    if (n_actors > old_n_actors) {\n        SAFE_FREE(orig_ptes->data_ptes);\n        SAFE_FREE(orig_ptes->code_ptes);\n        orig_ptes->data_ptes = CHECKED_ZALLOC(N_DATA_PAGES_PER_ACTOR * n_actors * sizeof(pte_t_));\n        orig_ptes->code_ptes = CHECKED_ZALLOC(N_CODE_PAGES_PER_ACTOR * n_actors * sizeof(pte_t_));\n\n        SAFE_FREE(faulty_ptes);\n        faulty_ptes = CHECKED_ZALLOC(sizeof(pte_t_) * n_actors);\n    }\n    old_n_actors = n_actors;\n\n    // save the original PTEs for the util pages\n    for (int i = 0; i < N_UTIL_PAGES; i++) {\n        orig_ptes->util_ptes[i] = *sandbox_pteps->util_pteps[i];\n    }\n\n    // save the original PTEs for the code and data pages of the sandbox\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        // save the original PTEs for the data pages of the actor\n        for (int i = 0; i < N_DATA_PAGES_PER_ACTOR; i++) {\n            int page_id = actor_id * N_DATA_PAGES_PER_ACTOR + i;\n            orig_ptes->data_ptes[page_id] = *sandbox_pteps->data_pteps[page_id];\n        }\n        // save the original PTEs for the code pages of the actor\n        for (int i = 0; i < N_CODE_PAGES_PER_ACTOR; i++) {\n            int page_id = actor_id * N_CODE_PAGES_PER_ACTOR + i;\n            orig_ptes->code_ptes[page_id] = *sandbox_pteps->code_pteps[page_id];\n        }\n    }\n    return 0;\n}\n\n/// @brief A shortcut to restore the original PTEs for a single page.\n/// @param ptep\n/// @param old_pte\n/// @param vaddr\nstatic void restore_pte(pte_t_ *ptep, pte_t_ old_pte, uint64_t vaddr)\n{\n    uint64_t curr_pte_val = *(uint64_t *)ptep;\n    uint64_t old_pte_val = *(uint64_t *)&old_pte;\n\n    if (curr_pte_val != old_pte_val) {\n        *ptep = old_pte;\n        native_page_invalidate(vaddr);\n    }\n}\n\n/// @brief Restore the original PTEs for all sandbox pages.\n/// @param void\n/// @return\nint restore_orig_host_permissions(void)\n{\n    ASSERT(sandbox_pteps->util_pteps[0] != NULL, \"restore_orig_host_permissions\");\n    ASSERT(sandbox_pteps->data_pteps[0] != NULL, \"restore_orig_host_permissions\");\n    ASSERT(sandbox_pteps->code_pteps[0] != NULL, \"restore_orig_host_permissions\");\n\n    // restore the original PTEs for the util pages\n    for (int i = 0; i < N_UTIL_PAGES; i++) {\n        restore_pte(sandbox_pteps->util_pteps[i], orig_ptes->util_ptes[i],\n                    (uint64_t)sandbox->util + i * PAGE_SIZE);\n    }\n\n    // restore the original PTEs for the code and data pages of the sandbox\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        // restore the original PTEs for the data pages of the actor\n        for (int i = 0; i < N_DATA_PAGES_PER_ACTOR; i++) {\n            int page_id = actor_id * N_DATA_PAGES_PER_ACTOR + i;\n            restore_pte(sandbox_pteps->data_pteps[page_id], orig_ptes->data_ptes[page_id],\n                        (uint64_t)&sandbox->data[actor_id] + i * PAGE_SIZE);\n        }\n        // restore the original PTEs for the code pages of the actor\n        for (int i = 0; i < N_CODE_PAGES_PER_ACTOR; i++) {\n            int page_id = actor_id * N_CODE_PAGES_PER_ACTOR + i;\n            restore_pte(sandbox_pteps->code_pteps[page_id], orig_ptes->code_ptes[page_id],\n                        (uint64_t)&sandbox->code[actor_id] + i * PAGE_SIZE);\n        }\n    }\n    return 0;\n}\n\n/// @brief Configures the page table entries for those sandbox pages that are mapped into\n/// user-type actors\n/// @param void\n/// @return 0 on success, -1 on failure\nint set_user_pages(void)\n{\n    ASSERT(sandbox_pteps->util_pteps[0] != NULL, \"restore_orig_host_permissions\");\n    ASSERT(sandbox_pteps->data_pteps[0] != NULL, \"restore_orig_host_permissions\");\n    ASSERT(sandbox_pteps->code_pteps[0] != NULL, \"restore_orig_host_permissions\");\n\n    // enable user access to util pages so that the actors can store measurement results\n    for (int i = 0; i < N_UTIL_PAGES; i++) {\n        set_user_bit(sandbox_pteps->util_pteps[i]);\n        native_page_invalidate((uint64_t)sandbox->util + i * PAGE_SIZE);\n    }\n\n    // enable user access to code and data pages of the sandbox that belong to user actors\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        // skip non-user actors\n        actor_metadata_t *actor = &actors[actor_id];\n        if (actor->pl != PL_USER) {\n            continue;\n        }\n\n        // configure PTEs for each area of the actor sandbox\n        for (int i = 0; i < N_DATA_PAGES_PER_ACTOR; i++) {\n            int page_id = actor_id * N_DATA_PAGES_PER_ACTOR + i;\n            set_user_bit(sandbox_pteps->data_pteps[page_id]);\n            native_page_invalidate((uint64_t)&sandbox->data[actor_id] + i * PAGE_SIZE);\n        }\n        for (int i = 0; i < N_CODE_PAGES_PER_ACTOR; i++) {\n            int page_id = actor_id * N_CODE_PAGES_PER_ACTOR + i;\n            set_user_bit(sandbox_pteps->code_pteps[page_id]);\n            native_page_invalidate((uint64_t)&sandbox->code[actor_id] + i * PAGE_SIZE);\n        }\n    }\n\n    return 0;\n}\n\n/// @brief Fast modification of the faulty page host PTE; sets the permissions according to\n/// actor_t->data_permissions\n/// @param void\nvoid set_faulty_page_host_permissions(void)\n{\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        uint64_t pte_mask = actors[actor_id].data_permissions;\n        uint64_t mask_set = pte_mask & MODIFIABLE_PTE_BITS;\n        uint64_t mask_clear = pte_mask | ~MODIFIABLE_PTE_BITS;\n\n        int page_id = actor_id * N_DATA_PAGES_PER_ACTOR + FAULTY_PAGE_ID;\n        pte_t_ *ptep = sandbox_pteps->data_pteps[page_id];\n        faulty_ptes[actor_id] = *ptep;\n        uint64_t org_value = *(uint64_t *)ptep;\n        uint64_t pte = (org_value | mask_set) & mask_clear;\n        // PRINT_ERR(\"set_faulty_page_host_permissions: actor %d, pte 0x%llx -> 0x%llx\", actor_id,\n        //   org_value, pte);\n\n        if (pte != org_value) {\n            *(uint64_t *)ptep = pte;\n            native_page_invalidate((uint64_t)&sandbox->data[actor_id] + FAULTY_PAGE_ID * PAGE_SIZE);\n        }\n    }\n}\n\n/// @brief Fast recovery of original permissions of the faulty page host PTE\n/// @param void\nvoid restore_faulty_page_host_permissions(void)\n{\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        int page_id = actor_id * N_DATA_PAGES_PER_ACTOR + FAULTY_PAGE_ID;\n        *sandbox_pteps->data_pteps[page_id] = faulty_ptes[actor_id];\n        native_page_invalidate((uint64_t)&sandbox->data[actor_id] + FAULTY_PAGE_ID * PAGE_SIZE);\n    }\n}\n\n// =================================================================================================\nint init_page_table_manager(void)\n{\n    orig_ptes = CHECKED_ZALLOC(sizeof(sandbox_ptes_t));\n    orig_ptes->data_ptes = CHECKED_ZALLOC(N_DATA_PAGES_PER_ACTOR * sizeof(pte_t));\n    orig_ptes->code_ptes = CHECKED_ZALLOC(N_CODE_PAGES_PER_ACTOR * sizeof(pte_t));\n    orig_ptes->util_ptes = CHECKED_ZALLOC(N_UTIL_PAGES * sizeof(pte_t));\n\n    sandbox_pteps = CHECKED_ZALLOC(sizeof(sandbox_pteps_t));\n    sandbox_pteps->data_pteps = CHECKED_ZALLOC(N_DATA_PAGES_PER_ACTOR * sizeof(pte_t *));\n    sandbox_pteps->code_pteps = CHECKED_ZALLOC(N_CODE_PAGES_PER_ACTOR * sizeof(pte_t *));\n    sandbox_pteps->util_pteps = CHECKED_ZALLOC(N_UTIL_PAGES * sizeof(pte_t *));\n\n    faulty_ptes = (pte_t_ *)CHECKED_ZALLOC(sizeof(pte_t_));\n    return 0;\n}\n\nvoid free_page_table_manager(void)\n{\n    SAFE_FREE(sandbox_pteps->data_pteps);\n    SAFE_FREE(sandbox_pteps->code_pteps);\n    SAFE_FREE(sandbox_pteps->util_pteps);\n    SAFE_FREE(sandbox_pteps);\n\n    SAFE_FREE(orig_ptes->data_ptes);\n    SAFE_FREE(orig_ptes->code_ptes);\n    SAFE_FREE(orig_ptes->util_ptes);\n    SAFE_FREE(orig_ptes);\n\n    SAFE_FREE(faulty_ptes);\n}\n"
  },
  {
    "path": "rvzr/executor_km/readme.md",
    "content": "# Install\n\nSee https://microsoft.github.io/side-channel-fuzzer/quick-start/ or `README.md` in the project root.\n\n# Using the executor\n\nUse the Revizor CLI (`revizor.py`).\nThis executor is not meant to be used standalone.\n\nOn your own peril, you could try using it directly, through the `/sys/rvzr_executor/` pseudo file system.\nYou can find an example of how to use it in `rvzr/tests/x86_tests/kernel_module.bats`.\nBut I promise you, there will come a point when your machine will crash or hang.\nBetter not.\n"
  },
  {
    "path": "rvzr/executor_km/sandbox_manager.c",
    "content": "/// File: Sandbox memory management\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"hardware_desc.h\"\n\n#include \"actor.h\"\n#include \"code_loader.h\" // loaded_test_case_entry\n#include \"main.h\"        // set_memory_x, set_memory_nx\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n#include \"test_case_parser.h\"\n\n#include \"page_tables_guest.h\"\n#include \"page_tables_host.h\"\n\nsandbox_t *sandbox = NULL; // global\n\n// Util+Data allocation state (alloc_pages + vmap)\nstatic struct {\n    void *vaddr_unaligned;    // vmap'd virtual address (unaligned)\n    void *vaddr_aligned;      // aligned to 2-page boundary\n    struct page **page_array; // array of page pointers for vmap\n    int num_pages;            // number of pages allocated\n} util_data = {NULL, NULL, NULL, 0};\n\nstatic void *code = NULL;\nstatic size_t old_x_size = 0;\n\n/// @brief Free util_data allocation (vmap + physical pages)\nstatic void safe_free_util_data(void)\n{\n    if (util_data.vaddr_unaligned) {\n        vunmap(util_data.vaddr_unaligned);\n        util_data.vaddr_unaligned = NULL;\n        util_data.vaddr_aligned = NULL;\n    }\n    if (util_data.page_array) {\n        int order = get_order(util_data.num_pages * PAGE_SIZE);\n        __free_pages(util_data.page_array[0], order);\n        kfree(util_data.page_array);\n        util_data.page_array = NULL;\n    }\n}\n\n/// @brief Free code allocation (vmalloc)\nstatic void safe_free_code(void)\n{\n    if (code) {\n        set_memory_nx((unsigned long)code, old_x_size);\n        SAFE_VFREE(code);\n        loaded_test_case_entry = NULL;\n    }\n}\n\n/// @brief Initialize sandbox pointers after allocation\n/// @return 0 on success, -ENOMEM on failure\nstatic int init_sandbox_pointers(void)\n{\n    if (!sandbox) {\n        sandbox = CHECKED_MALLOC(sizeof(sandbox_t));\n    }\n    sandbox->data = (actor_data_t *)((unsigned long)util_data.vaddr_aligned + sizeof(util_t));\n    sandbox->code = (actor_code_t *)code;\n    sandbox->util = (util_t *)util_data.vaddr_aligned;\n    loaded_test_case_entry = code;\n    return 0;\n}\n\n/// @brief Allocate memory for the Util and Data areas of the sandbox\n/// @details\n/// Constraints:\n/// 1. Physical Continuity - Prime+Probe attacks require contiguous physical pages for PIPT caches\n/// 2. 4KB Page Tables - Executor must manipulate individual PTEs (impossible with huge pages)\n/// 3. 8KB Alignment - Memory must be aligned to 2-page boundary\n///\n/// Solution: alloc_pages() + vmap()\n/// - cannot use kmalloc: physically contiguous BUT uses huge pages in direct mapping\n/// - cannot use vmalloc: uses 4KB PTEs BUT not physically contiguous\n/// - solution -> alloc_pages + vmap: physically contiguous AND creates new 4KB page tables\n///\n/// @param n_actors Number of actors\n/// @return 0 on success, -ENOMEM on failure\nstatic int allocate_util_and_data(size_t n_actors)\n{\n    safe_free_util_data();\n\n    // calculate required memory sizes\n    const size_t util_mem_size = sizeof(util_t);\n    const size_t data_mem_size = n_actors * sizeof(actor_data_t);\n    const size_t mem_size = util_mem_size + data_mem_size;\n    size_t alloc_size = mem_size + 0x1000; // add 4KB to ensure we can align to 8KB boundary\n    util_data.num_pages = (alloc_size + PAGE_SIZE - 1) / PAGE_SIZE;\n    int order = get_order(alloc_size);\n\n    // allocate physical pages\n    struct page *page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);\n    if (!page) {\n        PRINT_ERR(\"Error allocating util_and_data pages\\n\");\n        return -ENOMEM;\n    }\n\n    // map the pages into kernel virtual address space\n    util_data.page_array = kmalloc(util_data.num_pages * sizeof(struct page *), GFP_KERNEL);\n    if (!util_data.page_array) {\n        __free_pages(page, order);\n        PRINT_ERR(\"Error allocating page array\\n\");\n        return -ENOMEM;\n    }\n\n    for (int i = 0; i < util_data.num_pages; i++) {\n        util_data.page_array[i] = page + i;\n    }\n\n    util_data.vaddr_unaligned =\n        vmap(util_data.page_array, util_data.num_pages, VM_MAP, PAGE_KERNEL);\n    if (!util_data.vaddr_unaligned) {\n        kfree(util_data.page_array);\n        __free_pages(page, order);\n        util_data.page_array = NULL;\n        PRINT_ERR(\"Error mapping util_and_data pages\\n\");\n        return -ENOMEM;\n    }\n\n    // Align to 2-page (8KB) boundary\n    unsigned long addr = (unsigned long)util_data.vaddr_unaligned;\n    util_data.vaddr_aligned = (void *)ALIGN(addr, 0x2000);\n\n    return 0;\n}\n\n/// @brief Allocate memory for the Code area of the sandbox\n/// @details\n/// Uses vmalloc (physical continuity not required). Provides 4KB page tables for PTE\n/// manipulation and executable memory support via set_memory_x().\n/// @param n_actors Number of actors (each gets its own code area)\n/// @return 0 on success, error code on failure\nstatic int allocate_code(size_t n_actors)\n{\n    safe_free_code();\n\n    code = CHECKED_VMALLOC(n_actors * sizeof(actor_code_t));\n    reset_code_area();\n\n    size_t code_size = n_actors * sizeof(actor_code_t);\n    old_x_size = DIV_ROUND_UP(code_size, PAGE_SIZE);\n    set_memory_x((unsigned long)code, old_x_size);\n\n    return 0;\n}\n\n/// @brief Clears out the code area from previous executions and fills the area with NOPs\n/// @param void\n/// @return void\nvoid reset_code_area(void)\n{\n    // fill the code area with NOPs\n#if defined(ARCH_X86_64)\n    memset(code, 0x90, sizeof(actor_code_t) * n_actors);\n#elif defined(ARCH_ARM)\n    for (int i = 0; i < n_actors * sizeof(actor_code_t) / 4; i += 1)\n        ((uint32_t *)code)[i] = 0xd503201f;\n#endif\n\n    // initialize the main section with a single ret instruction\n#if defined(ARCH_X86_64)\n    ((uint8_t *)code)[0] = '\\xC3';\n#elif defined(ARCH_ARM)\n    ((uint32_t *)code)[0] = 0xd65f03c0;\n#endif\n}\n\nint allocate_sandbox(void)\n{\n    int err = 0;\n    static int old_n_actors = 1;\n\n    // Allocate sandbox in host memory\n    if (old_n_actors < n_actors) {\n        err = allocate_util_and_data(n_actors);\n        CHECK_ERR(\"allocate_util_and_data\");\n\n        err = allocate_code(n_actors);\n        CHECK_ERR(\"allocate_code\");\n\n        err = init_sandbox_pointers();\n        CHECK_ERR(\"init_sandbox_pointers\");\n    }\n\n    // Make sure that everything is property initialized\n    memset(util_data.vaddr_aligned, 0, sizeof(util_t) + n_actors * sizeof(actor_data_t));\n\n    err = cache_host_pteps();\n    CHECK_ERR(\"cache_host_pteps\");\n\n    // when necessary, map the sandbox into guest memory and allocate VM management data structures\n    if (test_case->features.includes_vm_actors) {\n        err = allocate_guest_page_tables();\n        CHECK_ERR(\"allocate_guest_page_tables\");\n\n        err = map_sandbox_to_guest_memory();\n        CHECK_ERR(\"map_sandbox_to_guest_memory\");\n    }\n    old_n_actors = n_actors;\n\n    return err;\n}\n\n/// @brief Returns the number of pages allocated for the sandbox, including util area, code and data\n/// @param void\n/// @return number of pages; -1 on error\nint get_sandbox_size_pages(void)\n{\n    if (!sandbox)\n        return -1;\n\n    return DIV_ROUND_UP(sizeof(util_t), PAGE_SIZE) +\n           DIV_ROUND_UP(sizeof(actor_data_t) * n_actors, PAGE_SIZE) +\n           DIV_ROUND_UP(sizeof(actor_code_t) * n_actors, PAGE_SIZE);\n}\n\n/// @brief Sets PTE values for the sandbox based on the current test case configuration\n/// @param void\n/// @return 0 on success; -1 on error\nint set_sandbox_page_tables(void)\n{\n    int err = store_orig_host_permissions();\n    CHECK_ERR(\"store_orig_host_permissions\");\n\n    if (test_case->features.includes_user_actors) {\n        err = set_user_pages();\n        CHECK_ERR(\"set_user_pages\");\n    }\n    return 0;\n}\n\nvoid restore_orig_sandbox_page_tables(void) { restore_orig_host_permissions(); }\n\n/// @brief Fast modification of the faulty page PTE; sets the permissions according to\n/// actor_t->data_permissions\nvoid set_faulty_page_permissions(void)\n{\n    set_faulty_page_host_permissions();\n    set_faulty_page_guest_permissions();\n    set_faulty_page_ept_permissions();\n}\n\n/// @brief Fast recovery of original permissions of the faulty page PTE\nvoid restore_faulty_page_permissions(void)\n{\n    restore_faulty_page_host_permissions();\n    restore_faulty_page_guest_permissions();\n    restore_faulty_page_ept_permissions();\n}\n\n// =================================================================================================\nint init_sandbox_manager(void)\n{\n    int err = allocate_util_and_data(1);\n    CHECK_ERR(\"allocate_util_and_data\");\n\n    err = allocate_code(1);\n    CHECK_ERR(\"allocate_code\");\n\n    err = init_sandbox_pointers();\n    CHECK_ERR(\"init_sandbox_pointers\");\n\n    // ensure that the main_area of the first actor is aligned as expected\n    int offset = (unsigned long)sandbox->data[0].main_area % 0x2000;\n    ASSERT(offset == 0, \"init_sandbox_manager\");\n\n    // self-test: To enable offset-based accesses in assembly code, we have to hardcode\n    //            the layout of the data structures in sandbox_constants.h;\n    //            This naturally creates a risk of mismatches, so we perform sanity checks here\n    //            to ensure that the layout is as expected.\n    util_t *util = sandbox->util;\n    ASSERT(&util->l1d_priming_area[0] - (uint8_t *)util == L1D_PRIMING_OFFSET, \"init_sandbox\");\n    ASSERT((uint8_t *)&util->vars.stored_rsp - (uint8_t *)util == STORED_RSP_OFFSET,\n           \"init_sandbox\");\n    ASSERT((uint8_t *)&util->vars.latest_measurement - (uint8_t *)util == MEASUREMENT_OFFSET,\n           \"init_sandbox\");\n    actor_data_t *data = &sandbox->data[0];\n    ASSERT(&data->main_area[0] - (uint8_t *)util == UTIL_REL_TO_MAIN, \"init_sandbox\");\n    ASSERT(&data->main_area[0] - &data->macro_stack[64] == MACRO_STACK_TOP_OFFSET, \"init_sandbox\");\n    ASSERT(&data->faulty_area[0] - &data->main_area[0] == FAULTY_AREA_OFFSET, \"init_sandbox\");\n    ASSERT(&data->reg_init_area[0] - &data->main_area[0] == REG_INIT_OFFSET, \"init_sandbox\");\n    ASSERT(&data->overflow_pad[0] - &data->main_area[0] == OVERFLOW_PAD_OFFSET, \"init_sandbox\");\n    ASSERT(sizeof(measurement_t) == MEASUREMENT_SIZE, \"init_sandbox\");\n\n    return 0;\n}\n\nvoid free_sandbox_manager(void)\n{\n    safe_free_util_data();\n    safe_free_code();\n    free_guest_page_tables();\n}\n"
  },
  {
    "path": "rvzr/executor_km/test_case_parser.c",
    "content": "/// File:\n///   - Parsing of test cases in RCBF format (see docs/devel/binary-formats.md)\n///   - Management of TC-related data structures\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"test_case_parser.h\"\n#include \"macro_expansion.h\"\n#include \"main.h\"\n#include \"shortcuts.h\"\n\ntest_case_t *test_case = NULL;   // global\nactor_metadata_t *actors = NULL; // global\nsize_t n_actors = 1;             // global\n\nstatic size_t n_symbols;\n\nstatic int new_test_case(test_case_t **test_case_p);\n\n// =================================================================================================\n// State machine for test case loading\n// =================================================================================================\nstatic bool _is_receiving_test_case = false;\nstatic uint64_t _cursor = 0;\nstatic size_t highest_n_actors = 0;\nstatic size_t highest_n_symbols = 0;\nstatic actor_metadata_t *_allocated_actor_table;\nstatic tc_symbol_entry_t *_allocated_symbol_table;\nstatic tc_section_metadata_entry_t *_allocated_metadata;\nstatic tc_section_t *_allocated_data;\n\n/// @brief Initialize the state machine\n/// @param buf A pointer to the buffer containing (a portion of) the test case\n/// @return Error code; 0 if successful\nstatic int __batch_tc_parsing_start(const char *buf)\n{\n    int ret = 0;\n\n    // Restart parsing\n    _cursor = 0;\n\n    // Create a new batch\n    SAFE_FREE(test_case);\n    if (new_test_case(&test_case) != 0) {\n        PRINT_ERRS(\"__batch_tc_parsing_start\", \"Failed to create test case\\n\");\n        return -ENOMEM;\n    }\n\n    // Get the number the number of actors\n    uint64_t new_n_actors = ((uint64_t *)buf)[0];\n    ASSERT(new_n_actors > 0, \"__batch_tc_parsing_start\");\n    ret += 8;\n\n    // Get the number of symbols\n    uint64_t new_n_symbols = ((uint64_t *)buf)[1];\n    ASSERT_MSG(new_n_symbols <= MAX_SYMBOLS, \"__batch_tc_parsing_start\",\n               \"n_symbols (%llu) > MAX_SYMBOLS (%u)\\n\", new_n_symbols, MAX_SYMBOLS);\n    ret += 8;\n\n    // Store object sizes\n    test_case->actor_table_size = new_n_actors * sizeof(actor_metadata_t);\n    test_case->symbol_table_size = new_n_symbols * sizeof(tc_symbol_entry_t);\n    test_case->metadata_size = new_n_actors * sizeof(tc_section_metadata_entry_t);\n    test_case->sections_size = new_n_actors * sizeof(tc_section_t);\n\n    // Allocate memory for the test case\n    if (new_n_symbols > highest_n_symbols || !_allocated_symbol_table) {\n        SAFE_FREE(_allocated_symbol_table);\n        // +1 to have a valid allocation if the test case is empty\n        _allocated_symbol_table = CHECKED_MALLOC(test_case->symbol_table_size + 1);\n        highest_n_symbols = new_n_symbols;\n    }\n    if (new_n_actors > highest_n_actors || !_allocated_data) {\n        SAFE_FREE(_allocated_actor_table);\n        SAFE_FREE(_allocated_metadata);\n        SAFE_VFREE(_allocated_data);\n        _allocated_actor_table = CHECKED_MALLOC(test_case->actor_table_size);\n        _allocated_metadata = CHECKED_MALLOC(test_case->metadata_size);\n        _allocated_data = CHECKED_VMALLOC(test_case->sections_size);\n        highest_n_actors = new_n_actors;\n    }\n\n    // Reset the allocated memory\n    memset(_allocated_actor_table, 0, highest_n_actors * sizeof(actor_metadata_t));\n    memset(_allocated_symbol_table, 0, highest_n_symbols * sizeof(tc_symbol_entry_t));\n    memset(_allocated_metadata, 0, highest_n_actors * sizeof(tc_section_metadata_entry_t));\n    memset(_allocated_data, 0, highest_n_actors * sizeof(tc_section_t));\n\n    test_case->actor_table = _allocated_actor_table;\n    test_case->symbol_table = _allocated_symbol_table;\n    test_case->metadata = _allocated_metadata;\n    test_case->sections = _allocated_data;\n\n    // set globals\n    n_symbols = new_n_symbols;\n    n_actors = new_n_actors;\n    actors = test_case->actor_table;\n\n    ASSERT(ret < PAGE_SIZE, \"__batch_tc_parsing_start\");\n    return ret;\n}\n\n/// @brief Finalize parsing:\n///        - do sanity checks\n///        - set test case features\n///        - type-check actor switch targets\n/// @param void\n/// @return Error code; 0 if successful\nstatic int __batch_tc_parsing_end(void)\n{\n    // Make sure that macros in the symbol table are ordered by owner and offset;\n    // the symbol table contains measurement start/end; and contains the main function at offset 0\n    bool macros_ordered = true;\n    bool has_start, has_end = false;\n    bool has_main = false;\n    tc_symbol_entry_t *prev_e = NULL;\n    for (tc_symbol_entry_t *e = test_case->symbol_table; e < test_case->symbol_table + n_symbols;\n         e++) {\n        // check for start, end, and main\n        if (e->id == MACRO_MEASUREMENT_START)\n            has_start = true;\n        if (e->id == MACRO_MEASUREMENT_END)\n            has_end = true;\n        if (e->owner == 0 && e->offset == 0)\n            has_main = true;\n\n        // check ordering\n        if (prev_e && e->id != NONMACRO_FUNCTION && prev_e->id != NONMACRO_FUNCTION) {\n            if (e->owner < prev_e->owner)\n                macros_ordered = false;\n            if (e->owner == prev_e->owner && e->offset < prev_e->offset)\n                macros_ordered = false;\n        }\n\n        // check targets\n        if (e->id == MACRO_SET_K2U_TARGET)\n            ASSERT((actors[e->args & 0xFF].pl == PL_USER), \"__batch_tc_parsing_end\");\n        if (e->id == MACRO_SET_U2K_TARGET)\n            ASSERT((actors[e->args & 0xFF].pl == PL_KERNEL), \"__batch_tc_parsing_end\");\n        if (e->id == MACRO_SET_H2G_TARGET)\n            ASSERT((actors[e->args & 0xFF].mode == MODE_GUEST), \"__batch_tc_parsing_end\");\n        if (e->id == MACRO_SET_G2H_TARGET)\n            ASSERT((actors[e->args & 0xFF].mode == MODE_HOST), \"__batch_tc_parsing_end\");\n\n        prev_e = e;\n    }\n    if (!macros_ordered) {\n        PRINT_ERRS(\"__batch_tc_parsing_end\", \"Macros in the symbol table are not ordered\\n\");\n        return -1;\n    }\n    if (!has_start || !has_end) {\n        PRINT_ERRS(\"__batch_tc_parsing_end\", \"Symbol table does not contain measurement \"\n                                             \"start/end\\n\");\n        return -1;\n    }\n    if (!has_main) {\n        PRINT_ERRS(\"__batch_tc_parsing_end\", \"Symbol table does not contain main function\\n\");\n        return -1;\n    }\n\n    // Set test case features\n    for (int i = 0; i < n_actors; i++) {\n        if (actors[i].mode == MODE_GUEST) {\n            test_case->features.includes_vm_actors = true;\n            break;\n        }\n        if (actors[i].pl == PL_USER) {\n            test_case->features.includes_user_actors = true;\n            break;\n        }\n    }\n\n    bool fault_handler_found = false;\n    for (tc_symbol_entry_t *e = test_case->symbol_table; e < test_case->symbol_table + n_symbols;\n         e++) {\n        if (e->id == MACRO_FAULT_HANDLER) {\n            fault_handler_found = true;\n            break;\n        }\n    }\n    test_case->features.has_explicit_fault_handler = fault_handler_found;\n    return 0;\n}\n\n/// Parse the test case sent via sysfs in the RCBF format\n/// (see docs/devel/binary-formats.md for details)\n///\nssize_t parse_test_case_buffer(const char *buf, size_t count, bool *finished)\n{\n    ASSERT(*finished == false, \"parse_test_case_buffer\");\n\n    static size_t curr_section_id = 0;\n    static size_t curr_section_start = 0;\n    static size_t curr_section_end = 0;\n    ssize_t consumed_bytes = 0;\n    ssize_t byte_id = 0;\n\n    int actor_table_end = TC_HEADER_SIZE + test_case->actor_table_size;\n    int symbol_table_end = actor_table_end + test_case->symbol_table_size;\n    int metadata_end = symbol_table_end + test_case->metadata_size;\n\n    if (!_is_receiving_test_case) // Starting a a new batch\n    {\n        consumed_bytes = __batch_tc_parsing_start(buf);\n        if (consumed_bytes != TC_HEADER_SIZE) {\n            PRINT_ERRS(\"parse_test_case_buffer\", \"Error parsing header\\n\");\n            return -1;\n        }\n\n        _cursor += consumed_bytes;\n        _is_receiving_test_case = true;\n    } else if (_cursor < actor_table_end) // Parsing actor table\n    {\n        size_t at_cursor = _cursor - TC_HEADER_SIZE;\n        for (; at_cursor < test_case->actor_table_size && byte_id < count;) {\n            ((char *)test_case->actor_table)[at_cursor] = buf[byte_id];\n            byte_id++;\n            at_cursor++;\n        }\n        _cursor = at_cursor + TC_HEADER_SIZE;\n        consumed_bytes = byte_id;\n    } else if (_cursor < symbol_table_end) // Parsing symbol table\n    {\n        size_t st_cursor = _cursor - actor_table_end;\n        for (; st_cursor < test_case->symbol_table_size && byte_id < count;) {\n            ((char *)test_case->symbol_table)[st_cursor] = buf[byte_id];\n            byte_id++;\n            st_cursor++;\n        }\n        _cursor = st_cursor + actor_table_end;\n        consumed_bytes = byte_id;\n    } else if (_cursor < metadata_end) // Parsing metadata\n    {\n        size_t metadata_cursor = _cursor - symbol_table_end;\n        for (; metadata_cursor < test_case->metadata_size && byte_id < count;) {\n            ((char *)test_case->metadata)[metadata_cursor] = buf[byte_id];\n            byte_id++;\n            metadata_cursor++;\n        }\n        _cursor = metadata_cursor + symbol_table_end;\n        consumed_bytes = byte_id;\n    } else // Parsing data\n    {\n        if (curr_section_id == 0) {\n            curr_section_start = metadata_end;\n            curr_section_end = metadata_end + test_case->metadata[0].size;\n        }\n        // Check that the section is not too large\n        if (test_case->metadata[curr_section_id].size > MAX_SECTION_SIZE) {\n            PRINT_ERRS(\"parse_test_case_buffer\", \"Section size exceeds MAX_SECTION_SIZE\\n\");\n            _is_receiving_test_case = false;\n            return -1;\n        }\n        // printk(KERN_ERR \"parse_test_case_buffer: curr_section_start = %lu; curr_section_end =\n        // \"\n        //                 \"%lu; curr_section_id = %lu\\n\",\n        //        curr_section_start, curr_section_end, curr_section_id);\n\n        size_t func_cursor = _cursor - curr_section_start;\n        bool func_finished = false;\n        for (; byte_id < count;) {\n            test_case->sections[curr_section_id].code[func_cursor] = buf[byte_id];\n            byte_id++;\n            func_cursor++;\n            if (func_cursor >= test_case->metadata[curr_section_id].size) {\n                func_finished = true;\n                break;\n            }\n        }\n        _cursor = func_cursor + curr_section_start;\n        consumed_bytes = byte_id;\n\n        if (func_finished) {\n            curr_section_id++;\n            curr_section_start = curr_section_end;\n            curr_section_end = curr_section_end + test_case->metadata[curr_section_id].size;\n        }\n    }\n\n    // Check whether we are done\n    if (curr_section_id >= n_actors) {\n        curr_section_id = 0;\n        curr_section_start = 0;\n        curr_section_end = 0;\n\n        _is_receiving_test_case = false;\n        *finished = true;\n\n        if (__batch_tc_parsing_end())\n            return -1;\n\n        ASSERT_MSG(consumed_bytes == count, \"parse_test_case_buffer\",\n                   \"consumed_bytes (%lu) != count (%lu)\\n\", consumed_bytes, count);\n    }\n    // printk(KERN_ERR \"parse_test_case_buffer: consumed_bytes = %lu; count = %lu; _cursor =\n    // %llu, \" \"fid: %ld, finished: %d\\n\",\n    //    consumed_bytes, count, _cursor, curr_section_id, *finished);\n\n    return consumed_bytes;\n}\n\n/// Getter for _is_receiving_test_case\n///\nbool tc_parsing_completed(void) { return !_is_receiving_test_case; }\n\n// =================================================================================================\n\n/// @brief Helper function to initialize a new test case with default values\n/// @param test_case_p\n/// @return 0 on success; -ENOMEM on error\nstatic int new_test_case(test_case_t **test_case_p)\n{\n    test_case_t *tc = CHECKED_MALLOC(sizeof(test_case_t));\n    memset(tc, 0, sizeof(test_case_t)); // zero out just in case\n\n    tc->actor_table_size = sizeof(actor_metadata_t);\n    tc->symbol_table_size = 0;\n    tc->metadata_size = sizeof(tc_section_metadata_entry_t);\n    tc->sections_size = sizeof(tc_section_t);\n    tc->actor_table = _allocated_actor_table;\n    tc->symbol_table = _allocated_symbol_table;\n    tc->metadata = _allocated_metadata;\n    tc->sections = _allocated_data;\n\n    tc->features.includes_vm_actors = false;\n    tc->features.includes_user_actors = false;\n    tc->features.has_explicit_fault_handler = false;\n\n    *test_case_p = tc;\n    return 0;\n}\n\nint init_test_case_parser(void)\n{\n    // locals\n    n_symbols = 0;\n    _is_receiving_test_case = false;\n    _cursor = 0;\n    _allocated_actor_table = CHECKED_MALLOC(sizeof(actor_metadata_t));\n    _allocated_symbol_table = CHECKED_MALLOC(1);\n    _allocated_metadata = CHECKED_MALLOC(sizeof(tc_section_metadata_entry_t));\n    _allocated_data = CHECKED_VMALLOC(sizeof(tc_section_t));\n\n    // Dummy test case\n    if (new_test_case(&test_case) != 0) {\n        PRINT_ERRS(\"init_test_case_parser\", \"Failed to create test case\\n\");\n        return -ENOMEM;\n    }\n    actors = test_case->actor_table;\n    return 0;\n}\n\nvoid free_test_case_parser(void)\n{\n    SAFE_FREE(test_case);\n    SAFE_FREE(_allocated_actor_table);\n    SAFE_FREE(_allocated_symbol_table);\n    SAFE_FREE(_allocated_metadata);\n    SAFE_VFREE(_allocated_data);\n    actors = NULL;\n}\n"
  },
  {
    "path": "rvzr/executor_km/x86/asm_snippets.h",
    "content": "/// File: Building blocks for creating macros; x86-64\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef X86_ASM_SNIPPETS_H_\n#define X86_ASM_SNIPPETS_H_\n// clang-format off\n\n#include \"hardware_desc.h\"\n#include \"measurement.h\"\n#include \"registers.h\"\n#include <asm/msr-index.h>\n\n#ifndef VENDOR_ID\n#error \"VENDOR_ID is not defined! Make sure to include this header late enough.\"\n#endif\n\n/// State machine of the tracing process\n#define SET_SR_STARTED()       \"mov \"STATUS_REGISTER_8\", \"xstr(STATUS_STARTED)\" \\n\"\n#define SET_SR_ENDED()         \"mov \"STATUS_REGISTER_8\", \"xstr(STATUS_ENDED)\" \\n\"\n\n\n/// Accessors to MSRs\n///\n// clobber: rax, rcx, rdx\n#define READ_MSR_START(ID, DEST)                          \\\n        \"mov rcx, \"ID\"                           \\n\"      \\\n        \"lfence; rdmsr; lfence                   \\n\"      \\\n        \"shl rdx, 32; or rdx, rax                \\n\"      \\\n        \"sub \"DEST\", rdx                         \\n\"\n\n// clobber: rax, rcx, rdx\n#define READ_MSR_END(ID, DEST)                            \\\n        \"mov rcx, \"ID\"                           \\n\"      \\\n        \"lfence; rdmsr; lfence                   \\n\"      \\\n        \"shl rdx, 32; or rdx, rax                \\n\"      \\\n        \"add \"DEST\", rdx                         \\n\"\n\n\n/// Accessors to Performance Counters\n///\n// clobber: rax, rcx, rdx\n#define READ_ONE_PFC(ID) \\\n        \"mov rcx, \"ID\" \\n\"      \\\n        \"lfence; rdpmc; lfence \\n\" \\\n        \"shl rdx, 32; or rdx, rax \\n\"\n\n// clobber: rax, rcx, rdx\n#define READ_PFC_START() \\\n        READ_ONE_PFC(\"1\") \\\n        \"sub \"PFC0\", rdx \\n\" \\\n        READ_ONE_PFC(\"2\") \\\n        \"sub \"PFC1\", rdx \\n\" \\\n        READ_ONE_PFC(\"3\") \\\n        \"sub \"PFC2\", rdx \\n\"\n\n// clobber: rax, rcx, rdx\n#define READ_PFC_END() \\\n        READ_ONE_PFC(\"1\") \\\n        \"add \"PFC0\", rdx \\n\" \\\n        READ_ONE_PFC(\"2\") \\\n        \"add \"PFC1\", rdx \\n\" \\\n        READ_ONE_PFC(\"3\") \\\n        \"add \"PFC2\", rdx \\n\"\n\n\n/// Detection of System Management Interrupts (SMIs)\n///\n\n/// @brief Clear the upper 32 bits of the STATUS_REGISTER\n#define CLEAR_SMI_STATUS() \\\n   \"mov \"STATUS_REGISTER_32\", \"STATUS_REGISTER_32\" \\n\"\n\n#if VENDOR_ID == VENDOR_INTEL_\n/// @brief Start monitoring SMIs by reading the current value of the SMI counter (MSR 0x34)\n///        and storing it in the STATUS_REGISTER[63:32]\n///  clobber: rax, rcx, rdx\n#define READ_SMI_START()               \\\n    \"mov rcx, \"xstr(MSR_SMI_COUNT)\"\\n\" \\\n    \"lfence; rdmsr; lfence         \\n\" \\\n    \"mov rcx, 0                    \\n\" \\\n    \"sub ecx, eax                  \\n\" \\\n    \"shl rcx, 32                   \\n\" \\\n    CLEAR_SMI_STATUS()                 \\\n    \"or \"STATUS_REGISTER\", rcx     \\n\"\n\n/// @brief End monitoring SMIs by reading the current value of the SMI counter (MSR 0x34)\n///        and storing the difference between the current and the previous value\n///        in the STATUS_REGISTER[31:0]\n/// clobber: rax, rcx, rdx\n#define READ_SMI_END()                 \\\n    \"mov rcx, \"xstr(MSR_SMI_COUNT)\"\\n\" \\\n    \"lfence; rdmsr; lfence         \\n\" \\\n    \"mov rcx, \"STATUS_REGISTER\"    \\n\" \\\n    \"shr rcx, 32                   \\n\" \\\n    \"add ecx, eax                  \\n\" \\\n    \"shl rcx, 32                   \\n\" \\\n    CLEAR_SMI_STATUS()                 \\\n    \"or \"STATUS_REGISTER\", rcx     \\n\"\n#elif VENDOR_ID == VENDOR_AMD_\n/// @brief Start monitoring SMIs by reading the current value of the SMI counter (PMU ID 5)\n///        and storing it in the STATUS_REGISTER[63:32]\n///  clobber: rax, rcx, rdx\n#define READ_SMI_START()            \\\n    \"mov rcx, 5                 \\n\" \\\n    \"lfence; rdpmc; lfence      \\n\" \\\n    \"mov rcx, 0                 \\n\" \\\n    \"sub ecx, eax               \\n\" \\\n    \"shl rcx, 32                \\n\" \\\n    CLEAR_SMI_STATUS()              \\\n    \"or \"STATUS_REGISTER\", rcx  \\n\"\n\n/// @brief End monitoring SMIs by reading the current value of the SMI counter (PMU ID 5)\n///        and storing the difference between the current and the previous value\n///        in the STATUS_REGISTER[31:0]\n/// clobber: rax, rcx, rdx\n#define READ_SMI_END()              \\\n    \"mov rcx, 5                 \\n\" \\\n    \"lfence; rdpmc; lfence      \\n\" \\\n    \"mov rcx, \"STATUS_REGISTER\" \\n\" \\\n    \"shr rcx, 32                \\n\" \\\n    \"add ecx, eax               \\n\" \\\n    \"shl rcx, 32                \\n\" \\\n    CLEAR_SMI_STATUS()              \\\n    \"or \"STATUS_REGISTER\", rcx  \\n\"\n\n#endif\n\n\n/// A sequence of instructions that attempts to set the pipeline to a uniform state,\n/// regardless of the code that was executed before it. The idea is that if we execute\n/// a whole bunch fences, it will give time for the uops that are currently in\n/// the reservation station to get executed, and thus ensure that the test case\n/// starts with an empty-ish pipeline\n/// clobber: none\n#define PIPELINE_RESET() asm volatile(\"\"\\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\" \\\n    \"lfence; lfence; lfence; lfence; lfence \\n\");\n\n/// Register Loading\n#if VENDOR_ID == 1 // Intel\n#define SET_REGISTER_FROM_INPUT()\\\n    asm volatile(\"\\n.intel_syntax noprefix\\n\" \\\n    \"lea rsp, [\"MEMORY_BASE_REG\" + \"xstr(REG_INIT_OFFSET)\"]\\n\" \\\n    \"pop rax \\n\" \\\n    \"pop rbx \\n\" \\\n    \"pop rcx \\n\" \\\n    \"pop rdx \\n\" \\\n    \"pop rsi \\n\" \\\n    \"pop rdi \\n\" \\\n    \"popfq \\n\" \\\n    \"lea rsp, [\"MEMORY_BASE_REG\" + \"xstr(LOCAL_RSP_OFFSET)\"]\\n\" \\\n    \"mov rbp, rsp \\n\" \\\n    \".att_syntax noprefix\");\n\n#elif VENDOR_ID == 2 // AMD\n#define SET_REGISTER_FROM_INPUT()\\\n    asm volatile(\"\\n.intel_syntax noprefix\\n\" \\\n    \"lea rsp, [\"MEMORY_BASE_REG\" + \"xstr(REG_INIT_OFFSET)\"]\\n\" \\\n    \"pop rax \\n\" \\\n    \"pop rbx \\n\" \\\n    \"pop rcx \\n\" \\\n    \"pop rdx \\n\" \\\n    \"pop rsi \\n\" \\\n    \"pop rdi \\n\" \\\n    \"popfq \\n\" \\\n    \"lea rsp, [\"MEMORY_BASE_REG\" + \"xstr(LOCAL_RSP_OFFSET)\"]\\n\" \\\n    \"mov rbp, rsp \\n\" \\\n    \".att_syntax noprefix\");\n#endif\n\n// =================================================================================================\n// L1D Prime+Probe\n// =================================================================================================\n// TODO: generate this code dynamically\n#if L1D_ASSOCIATIVITY == 2\n#define PRIME_ONE_SET(BASE, OFFSET, TMP)                 \\\n        \"mov \"TMP\", \"OFFSET\"                ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\"]        ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 4096] ; mfence \\n\"\n\n#define PROBE_ONE_SET(BASE, OFFSET)                  \\\n        \"mov rax, \"OFFSET\"                       \\n\" \\\n        \"add rax, [\"BASE\" + rax]        ; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 4096] ; mfence \\n\"\n\n#elif L1D_ASSOCIATIVITY == 4\n#define PRIME_ONE_SET(BASE, OFFSET, TMP)                 \\\n        \"mov \"TMP\", \"OFFSET\"                ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\"]        ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 4096] ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 8192] ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 12288]; mfence \\n\"\n\n#define PROBE_ONE_SET(BASE, OFFSET)                  \\\n        \"mov rax, \"OFFSET\"                       \\n\" \\\n        \"add rax, [\"BASE\" + rax]        ; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 4096] ; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 8192] ; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 12288]; mfence \\n\"\n\n#elif L1D_ASSOCIATIVITY == 8\n#define PRIME_ONE_SET(BASE, OFFSET, TMP)                 \\\n        \"mov \"TMP\", \"OFFSET\"                ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\"]        ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 4096] ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 8192] ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 12288]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 16384]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 20480]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 24576]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 28672]; mfence \\n\"\n\n#define PROBE_ONE_SET(BASE, OFFSET)                  \\\n        \"mov rax, \"OFFSET\"                       \\n\" \\\n        \"add rax, [\"BASE\" + rax]        ; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 4096] ; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 8192] ; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 12288]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 16384]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 20480]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 24576]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 28672]; mfence \\n\"\n\n#elif L1D_ASSOCIATIVITY == 12\n#define PRIME_ONE_SET(BASE, OFFSET, TMP)                 \\\n        \"mov \"TMP\", \"OFFSET\"                ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\"]        ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 4096] ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 8192] ; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 12288]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 16384]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 20480]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 24576]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 28672]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 32768]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 36864]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 40960]; mfence \\n\" \\\n        \"add \"TMP\", [\"BASE\" + \"TMP\" + 45056]; mfence \\n\"\n\n#define PROBE_ONE_SET(BASE, OFFSET)                  \\\n        \"mov rax, \"OFFSET\"                       \\n\" \\\n        \"add rax, [\"BASE\" + rax]        ; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 4096] ; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 8192] ; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 12288]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 16384]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 20480]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 24576]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 28672]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 32768]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 36864]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 40960]; mfence \\n\" \\\n        \"add rax, [\"BASE\" + rax + 45056]; mfence \\n\"\n\n#else\n#error \"Unexpected associativity\"\n#endif\n\n// clobber: none\n#define PRIME(BASE, OFFSET, TMP, COUNTER, REPS)                 \\\n        \"mfence                                             \\n\" \\\n        \"mov \"COUNTER\", \"REPS\"                              \\n\" \\\n        \"   1: mov \"OFFSET\", 0                              \\n\" \\\n        \"       2: lfence                                   \\n\" \\\n                PRIME_ONE_SET(BASE, OFFSET, TMP)                \\\n        \"       add \"OFFSET\", 64                            \\n\" \\\n        \"   cmp \"OFFSET\", 4096; jl 2b                       \\n\" \\\n        \"dec \"COUNTER\"; jnz 1b                              \\n\" \\\n        \"mfence;                                            \\n\"\n\n\n// clobber: rax, rcx, rdx\n#define PROBE_INTEL(BASE, OFFSET, TMP, DEST)            \\\n        \"xor \"DEST\", \"DEST\"                         \\n\" \\\n        \"xor \"OFFSET\", \"OFFSET\"                     \\n\" \\\n        \"1: lfence                                  \\n\" \\\n        \"   xor \"TMP\", \"TMP\"                        \\n\" \\\n            READ_ONE_PFC(\"0\")                           \\\n        \"   sub \"TMP\", rdx                          \\n\" \\\n            PROBE_ONE_SET(BASE, OFFSET)                 \\\n            READ_ONE_PFC(\"0\")                           \\\n        \"   add \"TMP\", rdx                          \\n\" \\\n        \"   cmp \"TMP\", \"xstr(L1D_ASSOCIATIVITY)\"    \\n\" \\\n        \"   jl 2f                                   \\n\" \\\n        \"      shl \"DEST\", 1                        \\n\" \\\n        \"      jmp 3f                               \\n\" \\\n        \"   2:                                      \\n\" \\\n        \"      shl \"DEST\", 1                        \\n\" \\\n        \"      or \"DEST\", 1                         \\n\" \\\n        \"   3:                                      \\n\" \\\n        \"   add \"OFFSET\", 64                        \\n\" \\\n        \"cmp \"OFFSET\", 4096; jl 1b                  \\n\"\n\n// clobber: rax, rcx, rdx\n#define PROBE_AMD(BASE, OFFSET, TMP, DEST)              \\\n        \"xor \"DEST\", \"DEST\"                         \\n\" \\\n        \"xor \"OFFSET\", \"OFFSET\"                     \\n\" \\\n        \"1: lfence                                  \\n\" \\\n        \"   xor \"TMP\", \"TMP\"                        \\n\" \\\n            READ_ONE_PFC(\"0\")                           \\\n        \"   sub \"TMP\", rdx                          \\n\" \\\n            PROBE_ONE_SET(BASE, OFFSET)                 \\\n            READ_ONE_PFC(\"0\")                           \\\n        \"   add \"TMP\", rdx                          \\n\" \\\n        \"   cmp \"TMP\", 0; jg 2f                     \\n\" \\\n        \"      shl \"DEST\", 1                        \\n\" \\\n        \"      jmp 3f                               \\n\" \\\n        \"   2:                                      \\n\" \\\n        \"      shl \"DEST\", 1                        \\n\" \\\n        \"      or \"DEST\", 1                         \\n\" \\\n        \"   3:                                      \\n\" \\\n        \"   add \"OFFSET\", 64                        \\n\" \\\n        \"cmp \"OFFSET\", 4096; jl 1b                  \\n\"\n\n#if VENDOR_ID == 1\n#define PROBE(BASE, OFFSET, TMP, DEST) PROBE_INTEL(BASE, OFFSET, TMP, DEST)\n#elif VENDOR_ID == 2\n#define PROBE(BASE, OFFSET, TMP, DEST) PROBE_AMD(BASE, OFFSET, TMP, DEST)\n#endif\n\n// =================================================================================================\n// Partial Prime+Probe (P+P applied to a subset of L1D instead the whole cache)\n// =================================================================================================\n#define PRIME_PARTIAL(BASE, OFFSET, TMP, COUNTER, REPS)         \\\n        \"mfence                                             \\n\" \\\n        \"mov \"COUNTER\", \"REPS\"                              \\n\" \\\n        \"   1: mov \"OFFSET\", 0                              \\n\" \\\n        \"       2: lfence                                   \\n\" \\\n                PRIME_ONE_SET(BASE, OFFSET, TMP)                \\\n        \"       add \"OFFSET\", 64                            \\n\" \\\n        \"   cmp \"OFFSET\", 3840; jl 2b                       \\n\" \\\n        \"dec \"COUNTER\"; jnz 1b                              \\n\" \\\n        \"mfence;                                            \\n\"\n\n// =================================================================================================\n// L1D Flush+Reload\n// =================================================================================================\n\n// clobber: none\n#define FLUSH(BASE, OFFSET) \\\n        \"mfence                                     \\n\" \\\n        \"mov \"OFFSET\", 0                            \\n\" \\\n        \"1: lfence                                  \\n\" \\\n        \"   clflush qword ptr [\"BASE\" + \"OFFSET\"]   \\n\" \\\n        \"   add \"OFFSET\", 64                        \\n\" \\\n        \"cmp \"OFFSET\", 4096; jl 1b                  \\n\" \\\n        \"mfence                                     \\n\"\n\n// clobber: rax, rcx, rdx\n#define RELOAD_INTEL(BASE, OFFSET, TMP, DEST)           \\\n        \"xor \"DEST\", \"DEST\"                         \\n\" \\\n        \"xor \"OFFSET\", \"OFFSET\"                     \\n\" \\\n        \"1:                                         \\n\" \\\n        \"   xor \"TMP\", \"TMP\"                        \\n\" \\\n            READ_ONE_PFC(\"0\")                           \\\n        \"   sub \"TMP\", rdx                          \\n\" \\\n        \"   mov rax, qword ptr [\"BASE\" + \"OFFSET\"]  \\n\" \\\n            READ_ONE_PFC(\"0\")                           \\\n        \"   add \"TMP\", rdx                          \\n\" \\\n        \"   cmp \"TMP\", 0; jne 2f                    \\n\" \\\n        \"      shl \"DEST\", 1                        \\n\" \\\n        \"      jmp 3f                               \\n\" \\\n        \"   2:                                      \\n\" \\\n        \"      shl \"DEST\", 1                        \\n\" \\\n        \"      or \"DEST\", 1                         \\n\" \\\n        \"   3:                                      \\n\" \\\n        \"   add \"OFFSET\", 64                        \\n\" \\\n        \"cmp \"OFFSET\", 4096; jl 1b                  \\n\"\n\n// clobber: rax, rcx, rdx\n#define RELOAD_AMD(BASE, OFFSET, TMP, DEST)             \\\n        \"xor \"DEST\", \"DEST\"                         \\n\" \\\n        \"xor \"OFFSET\", \"OFFSET\"                     \\n\" \\\n        \"1:                                         \\n\" \\\n        \"   xor \"TMP\", \"TMP\"                        \\n\" \\\n            READ_ONE_PFC(\"0\")                           \\\n        \"   sub \"TMP\", rdx                          \\n\" \\\n        \"   mov rax, qword ptr [\"BASE\" + \"OFFSET\"]  \\n\" \\\n            READ_ONE_PFC(\"0\")                           \\\n        \"   add \"TMP\", rdx                          \\n\" \\\n        \"   cmp \"TMP\", 0; je 2f                     \\n\" \\\n        \"      shl \"DEST\", 1                        \\n\" \\\n        \"      jmp 3f                               \\n\" \\\n        \"   2:                                      \\n\" \\\n        \"      shl \"DEST\", 1                        \\n\" \\\n        \"      or \"DEST\", 1                         \\n\" \\\n        \"   3:                                      \\n\" \\\n        \"   add \"OFFSET\", 64                        \\n\" \\\n        \"cmp \"OFFSET\", 4096; jl 1b                  \\n\"\n\n#if VENDOR_ID == 1\n#define RELOAD(BASE, OFFSET, TMP, DEST) RELOAD_INTEL(BASE, OFFSET, TMP, DEST)\n#elif VENDOR_ID == 2\n#define RELOAD(BASE, OFFSET, TMP, DEST) RELOAD_AMD(BASE, OFFSET, TMP, DEST)\n#endif\n\n// =================================================================================================\n// Macro stack management\n// =================================================================================================\n/// @brief A sequence of instructions that switches the stack pointer to the macro stack\n///        and pushes the flags and registers RAX, RBX, RCX, RDX\n#define MACRO_PROLOGUE()                                                                           \\\n    \"mov qword ptr [\"MEMORY_BASE_REG\" - \" xstr(MACRO_STACK_TOP_OFFSET) \" - 8], rsp\\n\"                            \\\n    \"lea rsp, [\"MEMORY_BASE_REG\" - \" xstr(MACRO_STACK_TOP_OFFSET) \" - 8]\\n\"                                      \\\n    \"push rax\\n\"                                                                                   \\\n    \"push rbx\\n\"                                                                                   \\\n    \"push rcx\\n\"                                                                                   \\\n    \"push rdx\\n\"                                                                                   \\\n    \"pushf\\n\"\n\n/// @brief A sequence of instructions that pops the flags and registers RDX, RCX, RBX, RAX, RSP\n///        and overwrites the popped memory addresses with zeros\n#define MACRO_EPILOGUE()                                                                           \\\n    \"popf\\n\"                                                                                       \\\n    \"pop rdx\\n\"                                                                                    \\\n    \"pop rcx\\n\"                                                                                    \\\n    \"pop rbx\\n\"                                                                                    \\\n    \"pop rax\\n\"                                                                                    \\\n    \"mov qword ptr [rsp - 0x08], 0 \\n\"                                                             \\\n    \"mov qword ptr [rsp - 0x10], 0 \\n\"                                                             \\\n    \"mov qword ptr [rsp - 0x18], 0 \\n\"                                                             \\\n    \"mov qword ptr [rsp - 0x20], 0 \\n\"                                                             \\\n    \"mov qword ptr [rsp - 0x28], 0 \\n\"                                                             \\\n    \"pop rsp\\n\"\n\n// clang-format on\n#endif // X86_ASM_SNIPPETS_H_\n"
  },
  {
    "path": "rvzr/executor_km/x86/entry_exit_points.h",
    "content": "/// File: Multiple variants of test case entry and exit points, for x86-64 architecture\n///      used exclusively by code_loader.c\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n// -----------------------------------------------------------------------------------------------\n// Note on registers.\n// Some of the registers are reserved for a specific purpose and should never be overwritten.\n// See ./docs/registers.md and registers.h for more information.\n\n#ifndef RVZR_ENTRY_EXIT_H\n#define RVZR_ENTRY_EXIT_H\n\n#include \"hardware_desc.h\"\n\n#include \"asm_snippets.h\"\n#include \"registers.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n\n#define TEMPLATE_START                     0x0fff379000000000\n#define TEMPLATE_INSERT_TC                 0x0fff2f9000000000\n#define TEMPLATE_DEFAULT_EXCEPTION_LANDING 0x0fff479000000000\n#define TEMPLATE_END                       0x0fff279000000000\n#define TEMPLATE_MARKER_SIZE               8\n\n// clang-format off\nstatic inline void prologue(void)\n{\n    // As we don't use a compiler to track clobbering,\n    // we have to save the callee-saved regs\n    asm_volatile_intel(\n        \"push rbx\\n\"\n        \"push rbp\\n\"\n        \"push r10\\n\"\n        \"push r11\\n\"\n        \"push r12\\n\"\n        \"push r13\\n\"\n        \"push r14\\n\"\n        \"push r15\\n\"\n        \"pushfq\\n\"\n\n        // MEMORY_BASE_REG = main_area of actor 0\n        // (passed in rdi, the first argument of measurement_code)\n        \"mov \"MEMORY_BASE_REG\", rdi\\n\"\n\n        // UTIL_BASE_REG = sandbox->util\n        \"lea \"UTIL_BASE_REG\", [\"MEMORY_BASE_REG\" - \"xstr(UTIL_REL_TO_MAIN)\"]\\n\"\n\n        // sandbox->util->stored_rsp = rsp\n        \"mov qword ptr [\"UTIL_BASE_REG\" + \"xstr(STORED_RSP_OFFSET)\"], rsp\\n\"\n\n        // clear the rest of the registers\n        \"mov rax, 0\\n\"\n        \"mov rbx, 0\\n\"\n        \"mov rcx, 0\\n\"\n        \"mov rdx, 0\\n\"\n        \"mov rsi, 0\\n\"\n        \"mov rdi, 0\\n\"\n        \"mov r8,  0\\n\"\n        \"mov r9,  0\\n\"\n        \"mov r10, 0\\n\"\n        \"mov r11, 0\\n\"\n        \"mov r12, 0\\n\"\n        \"mov r13, 0\\n\"\n\n        // initialize special registers\n        \"mov \"HTRACE_REGISTER\", 0\\n\"\n        \"mov \"STATUS_REGISTER\", \"xstr(STATUS_UNINITIALIZED)\"\\n\"\n\n        \"mov rbp, rsp\\n\"\n        \"sub rsp, 0x1000\\n\"\n\n        // start monitoring interrupts\n        READ_SMI_START()\n    );\n\n}\n\nstatic inline void epilogue(void)\n{\n    asm_volatile_intel(\n        // rbx <- SMI counter\n        READ_SMI_END()\n\n        // rax <- &latest_measurement\n        \"lea rax, [\"UTIL_BASE_REG\" + \"xstr(MEASUREMENT_OFFSET)\"]\\n\"\n\n        // Store the results\n        \"mov qword ptr [rax + 0x00], \"HTRACE_REGISTER\" \\n\"  // HTrace\n        \"mov qword ptr [rax + 0x08], r10 \\n\"                // PFC0\n        \"mov qword ptr [rax + 0x10], r9 \\n\"                 // PFC1\n        \"mov qword ptr [rax + 0x18], r8 \\n\"                 // PFC2\n        \"mov qword ptr [rax + 0x20], 0 \\n\"                  // PFC3 (unused)\n        \"mov qword ptr [rax + 0x28], 0 \\n\"                  // PFC4 (unused)\n        \"mov qword ptr [rax + 0x30], \"STATUS_REGISTER\" \\n\"  // Measurement status\n\n        // rsp = sandbox->util->stored_rsp\n        \"mov rsp, qword ptr [\"UTIL_BASE_REG\" + \"xstr(STORED_RSP_OFFSET)\"]\\n\"\n\n        // restore registers\n        \"popfq\\n\"\n        \"pop r15\\n\"\n        \"pop r14\\n\"\n        \"pop r13\\n\"\n        \"pop r12\\n\"\n        \"pop r11\\n\"\n        \"pop r10\\n\"\n        \"pop rbp\\n\"\n        \"pop rbx\\n\"\n\n        // return 0\n        \"mov rax, 0\\n\"\n        \"ret\\n\"\n        \"int3\\n\" // Silences objtool warnings about no int3 after ret\n    );\n}\n\nstatic inline void epilogue_dbg_gpr(void)\n{\n    asm_volatile_intel(\n        // r14 <- &latest_measurement\n        // clobber r14; not in use anymore\n        \"lea r14, [\"UTIL_BASE_REG\" + \"xstr(MEASUREMENT_OFFSET)\"]\\n\"\n\n        // Store the results\n        \"mov qword ptr [r14 + 0x00], rax\\n\"\n        \"mov qword ptr [r14 + 0x08], rbx\\n\"\n        \"mov qword ptr [r14 + 0x10], rcx\\n\"\n        \"mov qword ptr [r14 + 0x18], rdx\\n\"\n        \"mov qword ptr [r14 + 0x20], rsi\\n\"\n        \"mov qword ptr [r14 + 0x28], rdi\\n\"\n        \"mov qword ptr [r14 + 0x30], \"STATUS_REGISTER\"\\n\"\n\n        // rsp = sandbox->util->stored_rsp\n        \"mov rsp, qword ptr [\"UTIL_BASE_REG\" + \"xstr(STORED_RSP_OFFSET)\"]\\n\"\n\n        // restore registers\n        \"popfq\\n\"\n        \"pop r15\\n\"\n        \"pop r14\\n\"\n        \"pop r13\\n\"\n        \"pop r12\\n\"\n        \"pop r11\\n\"\n        \"pop r10\\n\"\n        \"pop rbp\\n\"\n        \"pop rbx\\n\"\n\n        // return 0\n        \"mov rax, 0\\n\"\n        \"ret\\n\"\n        \"int3\\n\" // Silences objtool warnings about no int3 after ret\n    );\n}\n// clang-format on\n\nstatic void main_segment_template(void)\n{\n    asm volatile(\".quad \" xstr(TEMPLATE_START));\n    prologue();\n\n    SET_REGISTER_FROM_INPUT();\n    PIPELINE_RESET();\n\n    // test case placeholder\n    asm volatile(\"\\nlfence\\n\");\n    asm volatile(\".quad \" xstr(TEMPLATE_INSERT_TC) \"\\n\");\n    asm volatile(\"\\nmfence\\n\");\n\n    // fault handler\n    asm_volatile_intel(\"\"\n                       \"jmp 1f\\n\"\n                       \".quad \" xstr(TEMPLATE_DEFAULT_EXCEPTION_LANDING) \"\\n\"\n                                                                         \"1:nop; nop; nop\\n\");\n\n    epilogue();\n    asm volatile(\".quad \" xstr(TEMPLATE_END));\n}\n\nstatic void main_segment_template_dbg_gpr(void)\n{\n    asm volatile(\".quad \" xstr(TEMPLATE_START));\n    prologue();\n\n    SET_REGISTER_FROM_INPUT();\n    PIPELINE_RESET();\n\n    // test case placeholder\n    asm volatile(\"\\nlfence\\n\");\n    asm volatile(\".quad \" xstr(TEMPLATE_INSERT_TC) \"\\n\");\n    asm volatile(\"\\nmfence\\n\");\n\n    asm_volatile_intel(\"\"\n                       \"jmp 1f\\n\"\n                       \".quad \" xstr(TEMPLATE_DEFAULT_EXCEPTION_LANDING) \"\\n\"\n                                                                         \"1:nop; nop; nop\\n\");\n\n    epilogue_dbg_gpr();\n    asm volatile(\".quad \" xstr(TEMPLATE_END));\n}\n\n#endif // RVZR_ENTRY_EXIT_H\n"
  },
  {
    "path": "rvzr/executor_km/x86/fault_handlers.S",
    "content": "// File: Low-level exception handling code for x86-64\n//\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n.intel_syntax noprefix\n\n#include \"sandbox_constants.h\"\n#include \"registers.h\"\n\n.extern _printk\n.extern set_outer_fault_handlers\n.extern unset_outer_fault_handlers\n.extern run_experiment\n.extern recover_orig_state\n\n\n// =================================================================================================\n// Global variables\n// =================================================================================================\n.data\n\n.global is_nested_fault\nis_nested_fault:\n.quad 0\n\nfault_recovery_sp:\n.quad 0\n\n.Lrecovery_triggered_msg:\n.asciz\t\"\\0013[rvzr_executor:run_experiment_outer]\\n\\\nERROR: Recovery from fault triggered during run_experiment\\n\"\n.align\n\n.Lunreachable_msg:\n.asciz\t\"\\0013[rvzr_executor:rvzr_executor]\\n\\\nERROR: unreachable code\\n\"\n.align\n\n.Ltest_case_handler_msg:\n.asciz\t\"\\0013[rvzr_executor:test_case_handler]\\n\\\nERROR: Unhandled fault in the test case: \\n\\\n    0 [Exception ID]:\\t0x%llx\\n\\\n    1 [Error code]:\\t0x%llx\\n\\\n    2 [RIP]:\\t0x%llx\\n\\\n    3 [CS]:\\t0x%llx\\n\\\n    4 [RFLAGS]:\\t0x%llx\\n\\\n    5 [RSP]:\\t0x%llx\\n\\\nContext:\\n\\\n    R14: \\t0x%llx\\n\\\n    R15: \\t0x%llx\\n\"\n.align\n\n.Lbubble_handler_msg:\n.asciz\t\"\\0013[rvzr_executor:bubble_handler]\\n\\\nERROR: Unexpected fault in run_experiment: \\n\\\n    0 [Exception ID]:\\t0x%llx\\n\\\n    1 [Error code]:\\t0x%llx\\n\\\n    2 [RIP]:\\t0x%llx\\n\\\n    3 [CS]:\\t0x%llx\\n\\\n    4 [RFLAGS]:\\t0x%llx\\n\\\n    5 [RSP]:\\t0x%llx\\n\\\nContext:\\n\\\n    run_experiment:\\t\\t0x%llx\\n\\\n    run_experiment_outer:\\t0x%llx\\n\"\n.align\n\n.Lnmi_handler_msg:\n.asciz\t\"\\0013[rvzr_executor:nmi_handler]\\n\\\nERROR: Unhandled NMI occurred during run_experiment\\n\"\n.align\n\n.Lnested_fault_msg:\n.asciz\t\"\\0013[rvzr_executor:nested_fault]\\n\\\nERROR: Nested fault detected\\n\"\n.align\n\n// =================================================================================================\n// Macros\n// =================================================================================================\n.macro unreachable\n    lea rdi, .Lunreachable_msg\n    mov rsi, rcx\n    call _printk\n    hlt\n.endm\n\n// Generate entry stub for exception WITHOUT error code\n.macro entry_stub_no_err name, id\n.global \\name\\()_\\id\n.balign 16\n\\name\\()_\\id:\n    push 0                // push dummy error code for stack normalization\n    mov r13, 0x\\id        // set exception ID in r13\n    jmp \\name             // jump to main handler\n.endm\n\n// Generate entry stub for exception WITH error code\n.macro entry_stub_err name, id\n.global \\name\\()_\\id\n.balign 16\n\\name\\()_\\id:\n    mov r13, 0x\\id        // set exception ID in r13 (error code already on stack)\n    jmp \\name             // jump to main handler\n.endm\n\n// C preprocessor macro: Helper to repeat a macro 16 times\n#define CALL_16_TIMES(macro, arg, id)                                                              \\\n    macro arg, id##0; macro arg, id##1; macro arg, id##2; macro arg, id##3;                        \\\n    macro arg, id##4; macro arg, id##5; macro arg, id##6; macro arg, id##7;                        \\\n    macro arg, id##8; macro arg, id##9; macro arg, id##a; macro arg, id##b;                        \\\n    macro arg, id##c; macro arg, id##d; macro arg, id##e; macro arg, id##f;\n\n\n// =================================================================================================\n// Handlers\n// =================================================================================================\n.text\n\n/// @brief The default handler for interrupts/exceptions that occur during the test case execution.\n/// @param r13: error code\n.global test_case_handler\n.balign 4096\ntest_case_handler:\n    // just in case, disable interrupts\n    cli\n\n    // check for nested faults\n    lea rax, is_nested_fault\n    cmp qword ptr [rax], 0\n    jne .run_experiment_recovery  // nested fault detected -> go to recovery\n    mov qword ptr [rax], 1        // no nested fault -> set the flag\n\n    // Print error info:\n    // Note: this function is called by the generated entry stubs (see entry_stub_* below),\n    //       which set r13 to the exception ID\n    //       and ensure that the stack layout is:\n    //          - error code\n    //          - RIP\n    //          - CS\n    //          - RFLAGS\n    //          - RSP\n    lea rdi, .Ltest_case_handler_msg  // format string\n    mov rsi, r13                      // arg 1: Exception ID\n    pop rdx                           // arg 2: Error code\n    pop rcx                           // arg 3: RIP\n    pop r8                            // arg 4: CS\n    pop r9                            // arg 5: RFLAGS\n    // arg 6+ are passed on stack\n    pop rax\n    push r15                          // arg 8: R15\n    push r14                          // arg 7: R14\n    push rax                          // arg 6: RSP\n    xor eax, eax   // no vector registers used\n    call _printk\n\n    // ensure that the CPU state is properly restored before we exit\n    call recover_orig_state\n\n    jmp .run_experiment_recovery\n\n\n/// @brief The default handler for interrupts/exceptions that occur inside the code called by\n///        run_experiment_outer but outside of the test case itself.\n.global bubble_handler\n.balign 4096\nbubble_handler:\n    // just in case, disable interrupts\n    cli\n\n    // check for nested faults\n    lea rax, is_nested_fault\n    cmp qword ptr [rax], 0\n    jne .run_experiment_recovery  // nested fault detected -> go to recovery\n    mov qword ptr [rax], 1        // no nested fault -> set the flag\n\n    // Print error info:\n    // Note: this function is called by the generated entry stubs (see entry_stub_* below),\n    //       which set r13 to the exception ID\n    //       and ensure that the stack layout is:\n    //          - error code\n    //          - RIP\n    //          - CS\n    //          - RFLAGS\n    //          - RSP\n    lea rdi, .Lbubble_handler_msg     // format string\n    mov rsi, r13                      // arg 1: Exception ID\n    pop rdx                           // arg 2: Error code\n    pop rcx                           // arg 3: RIP\n    pop r8                            // arg 4: CS\n    pop r9                            // arg 5: RFLAGS\n    // arg 6+ are passed on stack\n    pop r10                           // get RSP value\n    lea r11, run_experiment           // get run_experiment address\n    lea r12, run_experiment_outer     // get run_experiment_outer address\n    push r12                          // arg 8: run_experiment_outer\n    push r11                          // arg 7: run_experiment\n    push r10                          // arg 6: RSP\n    xor eax, eax   // no vector registers used\n    call _printk\n    add rsp, 16    // clean up stack (2 arguments)\n\n    // ensure that the CPU state is properly restored before we exit\n    call recover_orig_state\n\n    jmp .run_experiment_recovery\n\n\n/// @brief Universal NMI handler. Used by both Bubble and Test Case IDTs.\n///        Prints a warning message and terminates the measurement.\n///        Returns to the caller of unsafe_bubble_wrapper.\n/// @param void\n.global nmi_handler\n.balign 4096\nnmi_handler:\n    // just in case, disable interrupts\n    cli\n\n    // flag that we had a fault\n    mov qword ptr [is_nested_fault], 1\n\n    // ensure that the stack pointer is valid\n    lea rbx, fault_recovery_sp\n    mov rbx, [rbx]\n    mov rsp, rbx\n\n    // print error message\n    lea rdi, .Lnmi_handler_msg\n    call _printk\n\n    // go into recovery\n    jmp .run_experiment_recovery\n\n\n/// @brief Handler for nested faults.\n///        Note: Revizor currently does not support handling of multiple faults in a single\n///        test case execution. Thus, if a nested fault occurs, we simply print an error message\n///        and terminate the measurement.\n/// @param void\n.global nested_fault_handler\n.balign 4096\nnested_fault_handler:\n    // just in case, disable interrupts\n    cli\n\n    // print error message\n    lea rdi, .Lnested_fault_msg\n    call _printk\n\n    // go into recovery\n    jmp .run_experiment_recovery\n\n\n// =================================================================================================\n// Multi-entry handler generation\n// =================================================================================================\n// This section generates 256 individual entry stubs for each handler (test_case_handler and\n// bubble_handler). Each stub normalizes the stack layout (ensures error code is present) and\n// passes the exception ID in r13 before jumping to the main handler.\n//\n// Exceptions that push error codes: 0x08, 0x0a-0x0e, 0x11, 0x15, 0x1d, 0x1e\n// For exceptions WITHOUT error code, we push a dummy 0 to normalize the stack layout.\n.text\n\n// Generate all 256 entry stubs for test_case_handler\nentry_stub_no_err test_case_handler, 00\nentry_stub_no_err test_case_handler, 01\nentry_stub_no_err test_case_handler, 02\nentry_stub_no_err test_case_handler, 03\nentry_stub_no_err test_case_handler, 04\nentry_stub_no_err test_case_handler, 05\nentry_stub_no_err test_case_handler, 06\nentry_stub_no_err test_case_handler, 07\nentry_stub_err test_case_handler, 08\nentry_stub_no_err test_case_handler, 09\nentry_stub_err test_case_handler, 0a\nentry_stub_err test_case_handler, 0b\nentry_stub_err test_case_handler, 0c\nentry_stub_err test_case_handler, 0d\nentry_stub_err test_case_handler, 0e\nentry_stub_no_err test_case_handler, 0f\nentry_stub_no_err test_case_handler, 10\nentry_stub_err test_case_handler, 11\nentry_stub_no_err test_case_handler, 12\nentry_stub_no_err test_case_handler, 13\nentry_stub_no_err test_case_handler, 14\nentry_stub_err test_case_handler, 15\nentry_stub_no_err test_case_handler, 16\nentry_stub_no_err test_case_handler, 17\nentry_stub_no_err test_case_handler, 18\nentry_stub_no_err test_case_handler, 19\nentry_stub_no_err test_case_handler, 1a\nentry_stub_no_err test_case_handler, 1b\nentry_stub_no_err test_case_handler, 1c\nentry_stub_err test_case_handler, 1d\nentry_stub_err test_case_handler, 1e\nentry_stub_no_err test_case_handler, 1f\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, 2)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, 3)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, 4)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, 5)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, 6)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, 7)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, 8)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, 9)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, a)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, b)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, c)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, d)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, e)\nCALL_16_TIMES(entry_stub_no_err, test_case_handler, f)\n\n// Generate all 256 entry stubs for bubble_handler\nentry_stub_no_err bubble_handler, 00\nentry_stub_no_err bubble_handler, 01\nentry_stub_no_err bubble_handler, 02\nentry_stub_no_err bubble_handler, 03\nentry_stub_no_err bubble_handler, 04\nentry_stub_no_err bubble_handler, 05\nentry_stub_no_err bubble_handler, 06\nentry_stub_no_err bubble_handler, 07\nentry_stub_err bubble_handler, 08\nentry_stub_no_err bubble_handler, 09\nentry_stub_err bubble_handler, 0a\nentry_stub_err bubble_handler, 0b\nentry_stub_err bubble_handler, 0c\nentry_stub_err bubble_handler, 0d\nentry_stub_err bubble_handler, 0e\nentry_stub_no_err bubble_handler, 0f\nentry_stub_no_err bubble_handler, 10\nentry_stub_err bubble_handler, 11\nentry_stub_no_err bubble_handler, 12\nentry_stub_no_err bubble_handler, 13\nentry_stub_no_err bubble_handler, 14\nentry_stub_err bubble_handler, 15\nentry_stub_no_err bubble_handler, 16\nentry_stub_no_err bubble_handler, 17\nentry_stub_no_err bubble_handler, 18\nentry_stub_no_err bubble_handler, 19\nentry_stub_no_err bubble_handler, 1a\nentry_stub_no_err bubble_handler, 1b\nentry_stub_no_err bubble_handler, 1c\nentry_stub_err bubble_handler, 1d\nentry_stub_err bubble_handler, 1e\nentry_stub_no_err bubble_handler, 1f\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, 2)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, 3)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, 4)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, 5)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, 6)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, 7)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, 8)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, 9)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, a)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, b)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, c)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, d)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, e)\nCALL_16_TIMES(entry_stub_no_err, bubble_handler, f)\n\n\n// =================================================================================================\n// run_experiment_outer: Fault-tolerant wrapper for run_experiment\n// =================================================================================================\n.text\n\n/// @brief A wrapper over run_experiment that ensures that any bugs that cause an\n///        exception will be handled gracefully and won't crash the system\n/// @param void\n.global run_experiment_outer\n.balign 4096\nrun_experiment_outer:\n    // A bug in run_experiment may corrupt the CPU state, so we need to save the current state\n    // before calling run_experiment\n    push rbx\n    push rcx\n    push rdx\n    push rsi\n    push rdi\n    push r8\n    push r9\n    push r10\n    push r11\n    push r12\n    push r13\n    push r14\n    push r15\n    push rbp\n    mov rbp, rsp\n\n    // Save the SP into a global variables so that we can recover it after a destructive bug\n    lea rax, fault_recovery_sp\n    mov [rax], rsp\n\n    // Disable interrupts to avoid nested faults (should already be disabled but just in case)\n    cli\n\n    // Set up outer fault handlers\n    call set_outer_fault_handlers\n\n    // Run the measurements\n    call run_experiment\n    jmp .run_experiment_normal_exit\n\n.run_experiment_recovery:\n    // START CRITICAL SECTION\n    // Note: this section is used to recover from bugs in the executor with possible full corruption\n    // of the memory and register state. Thus, the code below has to make as few assumptions\n    // as possible, and also be very careful about memory accesses.\n\n    // Overwrite SP with fault_recovery_sp\n    lea rbx, fault_recovery_sp\n    mov rbx, [rbx]\n    mov rsp, rbx\n\n    // Print an error message\n    lea rdi, .Lrecovery_triggered_msg\n    call _printk\n\n    // Return error code 1\n    mov rax, 1\n\n    jmp .run_experiment_normal_exit\n    // END CRITICAL SECTION\n\n.run_experiment_normal_exit:\n    // preserve err (rax)\n    push rax\n\n    // Restore original fault handlers\n    call unset_outer_fault_handlers\n    pop rax  // restore err\n\n    // Restore the original CPU state\n    pop rbp\n    pop r15\n    pop r14\n    pop r13\n    pop r12\n    pop r11\n    pop r10\n    pop r9\n    pop r8\n    pop rdi\n    pop rsi\n    pop rdx\n    pop rcx\n    pop rbx\n\n    ret\n    unreachable\n"
  },
  {
    "path": "rvzr/executor_km/x86/idt.c",
    "content": "/// File:\n///  - Fault handling and IDT management\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <linux/interrupt.h>\n\n#include \"code_loader.h\"\n#include \"hardware_desc.h\"\n#include \"main.h\"\n#include \"measurement.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n#include \"test_case_parser.h\"\n\n#include \"fault_handler.h\"\n\nuint32_t handled_faults = 0;          // global\nchar *fault_handler = NULL;           // global\nstruct desc_ptr test_case_idtr = {0}; // global\n\nstatic gate_desc *bubble_idt = NULL;\nstatic gate_desc *test_case_idt = NULL;\n\nstatic struct desc_ptr orig_idtr = {0};\nstatic struct desc_ptr bubble_idtr = {0};\n\n// Declarations from fault_handlers.S\nvoid test_case_handler(void);\nvoid bubble_handler(void);\nvoid nmi_handler(void);\nextern uint64_t is_nested_fault;\n\n#define BIT_CHECK(a, b) (!!((a) & (1ULL << (b))))\n\n// =================================================================================================\n// Handler declarations and lists\n// =================================================================================================\n// The 256 entry stubs for each handler are generated in fault_handlers.S using assembly macros.\n// Here we only declare them and create arrays of pointers for IDT initialization.\n\n#define MULTI_ENTRY_HANDLER_DECLARATIONS_ID(name, id) void name##_##id(void);\n#define MULTI_ENTRY_HANDLER_DECLARATIONS(name)                                                     \\\n    CALL_256_TIMES(MULTI_ENTRY_HANDLER_DECLARATIONS_ID, name)\n\n#define MULTI_ENTRY_HANDLER_LIST_ID(name, id) name##_##id,\n#define MULTI_ENTRY_HANDLER_LIST(name)        CALL_256_TIMES(MULTI_ENTRY_HANDLER_LIST_ID, name)\n\nMULTI_ENTRY_HANDLER_DECLARATIONS(test_case_handler);\nstatic void *test_case_handlers[] = {\n    MULTI_ENTRY_HANDLER_LIST(test_case_handler) NULL,\n};\n\nMULTI_ENTRY_HANDLER_DECLARATIONS(bubble_handler);\nstatic void *bubble_handlers[] = {\n    MULTI_ENTRY_HANDLER_LIST(bubble_handler) NULL,\n};\n\n// =================================================================================================\n// IDT management\n// =================================================================================================\ninline static void native_sidt(void *dtr)\n{\n    asm volatile(\"sidt %0\\n mfence\\n\" : \"=m\"(*((struct desc_ptr *)dtr)));\n}\n\ninline static void native_lidt(void *dtr)\n{\n    asm volatile(\"lidt %0\\n mfence\\n\" ::\"m\"(*((struct desc_ptr *)dtr)));\n}\n\nstatic void set_intr_gate_default(gate_desc *idt, int interrupt_id, void *handler)\n{\n    gate_desc desc = {\n        .offset_low = (u16)(unsigned long)handler,\n        .segment = __KERNEL_CS,\n        .bits = (struct idt_bits){.ist = 0, .zero = 0, .type = GATE_INTERRUPT, .dpl = 0, .p = 1},\n        .offset_middle = (u16)((unsigned long)handler >> 16),\n        .offset_high = (u32)((unsigned long)handler >> 32),\n        .reserved = 0,\n    };\n    write_idt_entry(idt, interrupt_id, &desc);\n}\n\nstatic void idt_set_custom_handlers(gate_desc *idt, struct desc_ptr *idtr, void *main_handler,\n                                    void **secondary_handlers)\n{\n    for (int idx = 0; idx < 256; idx++) {\n        if (idx == X86_TRAP_NMI) {\n            set_intr_gate_default(idt, idx, nmi_handler);\n            continue;\n        }\n\n        if (main_handler != NULL && idx < 32 && BIT_CHECK(handled_faults, idx)) {\n            set_intr_gate_default(idt, idx, main_handler);\n            continue;\n        }\n\n        switch (idx) {\n        // if we ever get a machine check exception, the CPU is definitely in a bad state\n        // so we should let OS handle it\n        case X86_TRAP_DF:\n        case X86_TRAP_MC: {\n            // case 22 ... 31: {\n            gate_desc *org_handler = &((gate_desc *)orig_idtr.address)[idx];\n            write_idt_entry(idt, idx, org_handler);\n            break;\n        }\n        default:\n            // all other exceptions are dispatched to the secondary handler\n            set_intr_gate_default(idt, idx, secondary_handlers[idx]);\n            break;\n        }\n    }\n    idtr->address = (unsigned long)idt;\n    idtr->size = (sizeof(gate_desc) * 256) - 1;\n    native_lidt(idtr);\n}\n\nvoid set_outer_fault_handlers(void)\n{\n    native_sidt(&orig_idtr); // preserve original IDT\n    idt_set_custom_handlers(bubble_idt, &bubble_idtr, NULL, bubble_handlers);\n    is_nested_fault = 0;\n}\n\nvoid unset_outer_fault_handlers(void)\n{\n    if (orig_idtr.address != 0) {\n        native_lidt(&orig_idtr); // restore original IDT\n    } else {\n        PRINT_ERR(\"unset_outer_fault_handlers: original IDT is not set\\n\");\n    }\n}\n\nvoid set_inner_fault_handlers(void)\n{\n    idt_set_custom_handlers(test_case_idt, &test_case_idtr, fault_handler, test_case_handlers);\n    is_nested_fault = 0;\n}\n\nvoid unset_inner_fault_handlers(void)\n{\n    if (bubble_idtr.address != 0) {\n        native_lidt(&bubble_idtr); // restore bubble IDT\n    } else {\n        PRINT_ERR(\"unset_inner_fault_handlers: bubble IDT is not set\\n\");\n    }\n}\n\n// =================================================================================================\nint init_fault_handler(void)\n{\n    fault_handler = (void *)test_case_handler;\n\n    bubble_idt = CHECKED_ZALLOC(sizeof(gate_desc) * 256);\n    test_case_idt = CHECKED_ZALLOC(sizeof(gate_desc) * 256);\n    test_case_idtr.address = (unsigned long)test_case_idt;\n    return 0;\n}\n\nvoid free_fault_handler(void)\n{\n    SAFE_FREE(bubble_idt);\n    SAFE_FREE(test_case_idt);\n}\n"
  },
  {
    "path": "rvzr/executor_km/x86/macros.c",
    "content": "/// File: x86 implementation of various macros as well as x86-specific code for\n///       the macro loader (macro_expansion.c)\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"asm_snippets.h\"\n#include \"fault_handler.h\"\n#include \"macro_expansion.h\"\n#include \"main.h\"\n#include \"page_tables_guest.h\"\n#include \"page_tables_host.h\"\n#include \"registers.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n#include \"svm.h\"\n#include \"vmx.h\"\n\nextern uint64_t is_nested_fault; // defined in fault_handlers.S\nvoid nested_fault_handler(void); // defined in fault_handlers.S\n\n// =================================================================================================\n// Convenience shortcuts for writing constants to memory\n// =================================================================================================\n#define APPEND_U8_TO_DEST(value) dest[cursor++] = value;\n\n#define APPEND_U16_TO_DEST(value)                                                                  \\\n    {                                                                                              \\\n        *((uint16_t *)(dest + cursor)) = value;                                                    \\\n        cursor += 2;                                                                               \\\n    }\n\n#define APPEND_U32_TO_DEST(value)                                                                  \\\n    {                                                                                              \\\n        *((uint32_t *)(dest + cursor)) = value;                                                    \\\n        cursor += 4;                                                                               \\\n    }\n\n#define APPEND_U64_TO_DEST(value)                                                                  \\\n    {                                                                                              \\\n        *((uint64_t *)(dest + cursor)) = value;                                                    \\\n        cursor += 8;                                                                               \\\n    }\n\n#define APPEND_BYTES_TO_DEST(...)                                                                  \\\n    {                                                                                              \\\n        static const uint8_t bytes[] = {__VA_ARGS__};                                              \\\n        for (size_t i = 0; i < sizeof(bytes); i++) {                                               \\\n            dest[cursor++] = bytes[i];                                                             \\\n        }                                                                                          \\\n    }\n\n// =================================================================================================\n// Instruction opcodes\n// =================================================================================================\nstatic inline void movabs(uint8_t *dest, size_t *cursor_, uint8_t reg_id, uint64_t value)\n{\n    size_t cursor = *cursor_;\n\n    // REX prefix\n    APPEND_U8_TO_DEST(reg_id >= REX_BOUNDARY ? 0x49 : 0x48);\n\n    // ModRM byte\n    reg_id = reg_id & 0x7;\n    APPEND_U8_TO_DEST(0xb8 + reg_id);\n\n    // Immediate value\n    APPEND_U64_TO_DEST(value);\n    *cursor_ = cursor;\n}\n\n// =================================================================================================\n// Helper functions\n// =================================================================================================\n\n/// @brief Get the address of a function within a section\n/// @param section_id ID of the section\n/// @param function_id ID of the function\n/// @return Virtual address of the function\nstatic uint64_t get_function_addr(uint64_t section_id, uint64_t function_id)\n{\n    uint64_t section_base = 0;\n\n    if (actors[section_id].mode == MODE_HOST) {\n        section_base = (uint64_t)sandbox->code[section_id].section;\n    } else if (actors[section_id].mode == MODE_GUEST) {\n        guest_memory_t *guest_memory = (guest_memory_t *)GUEST_V_MEMORY_START;\n        section_base = (uint64_t)guest_memory->code.section;\n    }\n\n    // The code section of the main actor begins after a hardcoded prologue,\n    // which we need to take into account when calculating the function address\n    if (section_id == 0)\n        section_base += get_main_prologue_size();\n\n    return section_base + test_case->symbol_table[function_id].offset;\n}\n\n/// @brief Insert a sequence of instructions into dest that updates R14 to match\n///        the actor owning section_id\n/// @param section_id ID of the section\n/// @param dest Pointer to the destination of the code sequence\n/// @param cursor Current position in the destination buffer\n/// @return Number of bytes written to the destination buffer\nstatic uint64_t update_r14(uint64_t section_id, uint8_t *dest, uint64_t cursor)\n{\n    uint64_t old_cursor = cursor;\n\n    // calculate the new R14 value\n    uint64_t new_r14 = 0;\n    if (actors[section_id].mode == MODE_HOST) {\n        new_r14 = (uint64_t)sandbox->data[section_id].main_area;\n    } else if (actors[section_id].mode == MODE_GUEST) {\n        guest_memory_t *guest_memory = (guest_memory_t *)GUEST_V_MEMORY_START;\n        new_r14 = (uint64_t)guest_memory->data.main_area;\n    }\n\n    // ASM: movabs r14, new_r14\n    APPEND_BYTES_TO_DEST(0x49, 0xbe);\n    APPEND_U64_TO_DEST(new_r14);\n    return cursor - old_cursor;\n}\n\n/// @brief Insert a sequence of instructions into dest that updates R14 and RSP to match\n///        the actor owning section_id\n/// @param section_id ID of the section\n/// @param dest Pointer to the destination of the code sequence\n/// @param cursor Current position in the destination buffer\n/// @return Number of bytes written to the destination buffer\nstatic uint64_t update_mem_base_and_sp(uint64_t section_id, uint8_t *dest, uint64_t cursor)\n{\n    uint64_t old_cursor = cursor;\n    cursor += update_r14(section_id, dest, cursor);\n\n    // calculate the new RSP value\n    uint64_t new_rsp = 0;\n    if (actors[section_id].mode == MODE_HOST) {\n        new_rsp = (uint64_t)sandbox->data[section_id].main_area + LOCAL_RSP_OFFSET;\n    } else if (actors[section_id].mode == MODE_GUEST) {\n        guest_memory_t *guest_memory = (guest_memory_t *)GUEST_V_MEMORY_START;\n        new_rsp = (uint64_t)guest_memory->data.main_area + LOCAL_RSP_OFFSET;\n    }\n\n    // ASM: movabs rsp, new_rsp\n    APPEND_BYTES_TO_DEST(0x48, 0xbc);\n    APPEND_U64_TO_DEST(new_rsp);\n    return cursor - old_cursor;\n}\n\n/// @brief Insert a sequence of instructions into dest that updates R15 to match the actor\n///        owning section_id\n/// @param section_id ID of the section\n/// @param dest Pointer to the destination of the code sequence\n/// @param cursor Current position in the destination buffer\n/// @return Number of bytes written to the destination buffer\nstatic uint64_t update_r15(uint64_t section_id, uint8_t *dest, uint64_t cursor)\n{\n    uint64_t old_cursor = cursor;\n\n    // calculate the new R15 value\n    uint64_t new_r15 = 0;\n    if (actors[section_id].mode == MODE_HOST) {\n        new_r15 = (uint64_t)sandbox->util;\n    } else if (actors[section_id].mode == MODE_GUEST) {\n        guest_memory_t *guest_memory = (guest_memory_t *)GUEST_V_MEMORY_START;\n        new_r15 = (uint64_t)&guest_memory->util;\n    }\n\n    // ASM: movabs r15, new_r15\n    APPEND_BYTES_TO_DEST(0x49, 0xbf);\n    APPEND_U64_TO_DEST(new_r15);\n    return cursor - old_cursor;\n}\n\n// =================================================================================================\n// Macro implementations\n//\n// Note: A macro consists of two parts: it starts with the dynamically-generated part,\n// and the main body is static.\n// The dynamic part is generated by the start_macro* functions, and the generated code\n// can be configured according to the macro arguments.\n// The body_macro* functions are not configurable, and are copied directly into the test case\n// macro memory.\n// =================================================================================================\n\n// MEASUREMENT_START and MEASUREMENT_END -----------------------------------------------------------\n// Prime+Probe variants\nstatic void __attribute__((noipa)) body_macro_prime(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm_volatile_intel(\"\"                                                //\n                       MACRO_PROLOGUE()                                  //\n                       \"lea rax, [r15 + \" xstr(L1D_PRIMING_OFFSET) \"]\\n\" //\n                       PRIME(\"rax\", \"rbx\", \"rcx\", \"rdx\", \"32\")           //\n                       READ_PFC_START()                                  //\n                       SET_SR_STARTED()                                  //\n                       MACRO_EPILOGUE()                                  //\n                       \"lfence\\n\"                                        //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\nstatic void __attribute__((noipa)) body_macro_fast_prime(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm_volatile_intel(\"\"                                                //\n                       MACRO_PROLOGUE()                                  //\n                       \"lea rax, [r15 + \" xstr(L1D_PRIMING_OFFSET) \"]\\n\" //\n                       PRIME(\"rax\", \"rbx\", \"rcx\", \"rdx\", \"1\")            //\n                       READ_PFC_START()                                  //\n                       SET_SR_STARTED()                                  //\n                       MACRO_EPILOGUE()                                  //\n                       \"lfence\\n\"                                        //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\nstatic void __attribute__((noipa)) body_macro_partial_prime(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm_volatile_intel(\"\"                                                //\n                       MACRO_PROLOGUE()                                  //\n                       \"lea rax, [r15 + \" xstr(L1D_PRIMING_OFFSET) \"]\\n\" //\n                       PRIME_PARTIAL(\"rax\", \"rbx\", \"rcx\", \"rdx\", \"32\")   //\n                       READ_PFC_START()                                  //\n                       SET_SR_STARTED()                                  //\n                       MACRO_EPILOGUE()                                  //\n                       \"lfence\\n\"                                        //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\nstatic void __attribute__((noipa)) body_macro_fast_partial_prime(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm_volatile_intel(\"\"                                                //\n                       MACRO_PROLOGUE()                                  //\n                       \"lea rax, [r15 + \" xstr(L1D_PRIMING_OFFSET) \"]\\n\" //\n                       PRIME_PARTIAL(\"rax\", \"rbx\", \"rcx\", \"rdx\", \"1\")    //\n                       READ_PFC_START()                                  //\n                       SET_SR_STARTED()                                  //\n                       MACRO_EPILOGUE()                                  //\n                       \"lfence\\n\"                                        //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\nstatic void __attribute__((noipa)) body_macro_probe(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    // clang-format off\n    asm_volatile_intel(\"\"\n                       \"cmp \" STATUS_REGISTER_8 \", \"xstr(STATUS_STARTED)\"\\n\"\n                       \"jne 99f\\n\"\n                       MACRO_PROLOGUE()\n                       \"push r15\\n\"\n                       \"lfence\\n\"\n                       READ_PFC_END()\n                       \"lea r15, [r15 + \" xstr(L1D_PRIMING_OFFSET) \"]\\n\"\n                       PROBE(\"r15\", \"rbx\", \"r11\", HTRACE_REGISTER)\n                       \"pop r15\\n\"\n                       \"mov qword ptr [rsp - 8], 0 \\n\"\n                       SET_SR_ENDED()\n                       MACRO_EPILOGUE()\n                       \"99:\\n\"\n    );\n    // clang-format on\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// Flush + Reload and variants\nstatic void __attribute__((noipa)) body_macro_flush(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm_volatile_intel(\"\"                  //\n                       MACRO_PROLOGUE()    //\n                       \"lea rbx, [r14]\\n\"  //\n                       FLUSH(\"rbx\", \"rax\") //\n                       READ_PFC_START()    //\n                       SET_SR_STARTED()    //\n                       MACRO_EPILOGUE()    //\n                       \"lfence\\n\"          //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\nstatic void __attribute__((noipa)) body_macro_reload(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    // clang-format off\n    asm_volatile_intel(\"\"\n                       \"cmp \" STATUS_REGISTER_8 \", \"xstr(STATUS_STARTED)\"\\n\"\n                       \"jne 98f\\n\"\n                       MACRO_PROLOGUE()\n                       \"lfence\\n\"\n                       READ_PFC_END()\n                       RELOAD(\"r14\", \"rbx\", \"r11\", HTRACE_REGISTER)\n                       \"mov rax, 1\\n\"\n                       \"shl rax, 63\\n\"\n                       \"or \" HTRACE_REGISTER \", rax\\n\"\n                       SET_SR_ENDED()\n                       MACRO_EPILOGUE()\n                       \"98:\\n\"\n    );\n    // clang-format on\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// Time stamp counter\nstatic void __attribute__((noipa)) body_macro_tsc_start(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm_volatile_intel(\"\"                                               //\n                       MACRO_PROLOGUE()                                 //\n                       \"lfence; rdtsc; lfence\\n\"                        //\n                       \"shl rdx, 32\\n\"                                  //\n                       \"or rdx, rax\\n\"                                  //\n                       \"xor \" HTRACE_REGISTER \", \" HTRACE_REGISTER \"\\n\" //\n                       \"sub \" HTRACE_REGISTER \", rdx\\n\"                 //\n                       \"lfence\\n\"                                       //\n                       READ_PFC_START()                                 //\n                       SET_SR_STARTED()                                 //\n                       MACRO_EPILOGUE()                                 //\n                       \"lfence\\n\"                                       //\n    );\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\nstatic void __attribute__((noipa)) body_macro_tsc_end(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    // clang-format off\n    asm_volatile_intel(\"\"\n                       \"cmp \" STATUS_REGISTER_8 \", \"xstr(STATUS_STARTED)\"\\n\"\n                       \"jne 97f\\n\"\n                       MACRO_PROLOGUE()\n                       READ_PFC_END()\n                       \"lfence; rdtsc; lfence\\n\"\n                       \"shl rdx, 32\\n\"\n                       \"or rdx, rax\\n\"\n                       \"add \" HTRACE_REGISTER \", rdx\\n\"\n                       SET_SR_ENDED()\n                       MACRO_EPILOGUE()\n                       \"97:\\n\"\n    );\n    // clang-format on\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// FAULT_HANDLER -------------------------------------------------------------------------------\nstatic inline size_t start_macro_fault_handler(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    ASSERT(args.owner == 0, \"inject_macro_configurable_part\");\n\n    // Set new global address to the fault handler\n    fault_handler = (char *)((uint64_t)dest + cursor);\n\n    // Ensure that RSP, R14, and R15 are set to correct values after (potential) actor switch\n    cursor += update_mem_base_and_sp(0, dest, cursor);\n    cursor += update_r15(0, dest, cursor);\n\n    // Check for nested faults; if so, jump to `test_case_handler`\n    uint64_t is_nested_fault_addr = (uint64_t)&is_nested_fault;\n    uint64_t test_case_handler_addr = (uint64_t)nested_fault_handler;\n    //   ASM: movabs TMP_REG, is_nested_fault_addr\n    movabs(dest, &cursor, TMP_REG_ID, is_nested_fault_addr);\n    //   ASM: cmp byte ptr [TMP_REG], 0\n    APPEND_BYTES_TO_DEST(0x41, 0x80, 0x3b, 0x00);\n    //   ASM: je no_nested_fault\n    APPEND_BYTES_TO_DEST(0x74, 0x10);\n    //   ASM: lfence\n    APPEND_BYTES_TO_DEST(0x0f, 0xae, 0xe8);\n    //   ASM: movabs TMP_REG, test_case_handler_addr\n    movabs(dest, &cursor, TMP_REG_ID, test_case_handler_addr);\n    //   ASM: jmp TMP_REG\n    APPEND_BYTES_TO_DEST(0x41, 0xff, 0xe3);\n    //   ASM: no_nested_fault:\n    //   ASM: incb byte ptr [TMP_REG]\n    APPEND_BYTES_TO_DEST(0x41, 0xfe, 0x03);\n    return cursor;\n}\n\n// FAULT_HANDLER_WITH_MEASUREMENT ------------------------------------------------------------------\nstatic inline size_t start_macro_fault_handler_with_measurement(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    cursor += update_r14(args.arg1, dest, cursor);\n    cursor += update_r15(args.arg1, dest, cursor);\n    return cursor;\n}\n\n// MACRO_SWITCH ------------------------------------------------------------------------------------\nstatic inline size_t start_macro_switch(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    // Update RSP and R14 to the addresses within the new actor's memory\n    cursor += update_mem_base_and_sp(args.arg1, dest, cursor);\n\n    // Determine the target address for the switch\n    uint64_t switch_target = get_function_addr(args.arg1, args.arg2);\n    uint32_t relative_offset = switch_target - (uint64_t)dest - cursor - 5;\n\n    // Jump to the target address (in a different actor) via a relative offset\n    // ASM: jmp [RIP + relative_offset]\n    APPEND_BYTES_TO_DEST(0xe9);\n    APPEND_U32_TO_DEST(relative_offset);\n    return cursor;\n}\n\n// MACRO_SET_K2U_TARGET ----------------------------------------------------------------------------\nstatic inline size_t start_macro_set_k2u_target(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n\n    // ASM: movabs r11, function_addr\n    uint64_t function_addr = get_function_addr(args.arg1, args.arg2);\n    APPEND_BYTES_TO_DEST(0x49, 0xbb);\n    APPEND_U64_TO_DEST(function_addr);\n\n    return cursor;\n}\n\n// MACRO_SWITCH_K2U --------------------------------------------------------------------------------\nstatic inline size_t start_macro_switch_k2u(macro_args_t /*args*/, uint8_t * /*dest*/) { return 0; }\n\nstatic void __attribute__((noipa)) body_macro_switch_k2u(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    // clang-format off\n    asm_volatile_intel(\"\"\n                       \"mov rcx, r11\\n\"\n                       \"mov qword ptr [r14 - \" xstr(MACRO_STACK_TOP_OFFSET) \" - 8], rsp\\n\"\n                       \"lea rsp, [r14 - \" xstr(MACRO_STACK_TOP_OFFSET) \" - 8]\\n\"\n                       \"pushfq\\n\"\n                       \"pop r11\\n\"\n                       \"pop rsp\\n\"\n                       \"sysretq\\n\");\n    // clang-format on\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// MACRO_SET_U2K_TARGET ----------------------------------------------------------------------------\nstatic inline size_t start_macro_set_u2k_target(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    uint64_t function_addr = get_function_addr(args.arg1, args.arg2);\n    uint32_t macro_stack_offset = -MACRO_STACK_TOP_OFFSET - 8;\n\n    // ASM: mov [r14 - MACRO_STACK_TOP_OFFSET - 8],rsp\n    APPEND_BYTES_TO_DEST(0x49, 0x89, 0xa6);\n    APPEND_U32_TO_DEST(macro_stack_offset);\n    // ASM: lea rsp,[r14 - MACRO_STACK_TOP_OFFSET - 8]\n    APPEND_BYTES_TO_DEST(0x49, 0x8d, 0xa6);\n    APPEND_U32_TO_DEST(macro_stack_offset);\n    // ASM: push rax\n    APPEND_U8_TO_DEST(0x50);\n    // ASM: push rcx\n    APPEND_U8_TO_DEST(0x51);\n    // ASM: push rdx\n    APPEND_U8_TO_DEST(0x52);\n    // ASM: pushf\n    APPEND_U8_TO_DEST(0x9c);\n    // ASM: movabs rax, function_addr\n    APPEND_BYTES_TO_DEST(0x48, 0xb8);\n    APPEND_U64_TO_DEST(function_addr);\n    // ASM: mov rdx, rax\n    APPEND_BYTES_TO_DEST(0x48, 0x89, 0xc2);\n    // ASM: shr rdx, 0x20\n    APPEND_BYTES_TO_DEST(0x48, 0xc1, 0xea, 0x20);\n    // ASM: movabs rcx, 0xc0000082\n    APPEND_BYTES_TO_DEST(0x48, 0xb9);\n    APPEND_U64_TO_DEST(0xc0000082);\n    // ASM: wrmsr\n    APPEND_BYTES_TO_DEST(0x0f, 0x30);\n    // ASM: popf\n    APPEND_U8_TO_DEST(0x9d);\n    // ASM: mov qword ptr [rsp - 0x08], 0\n    APPEND_BYTES_TO_DEST(0x48, 0xc7, 0x44, 0x24, 0xf8, 0x00, 0x00, 0x00, 0x00);\n    // ASM: pop    rdx\n    APPEND_U8_TO_DEST(0x5a);\n    // ASM: mov qword ptr [rsp - 0x08], 0\n    APPEND_BYTES_TO_DEST(0x48, 0xc7, 0x44, 0x24, 0xf8, 0x00, 0x00, 0x00, 0x00);\n    // ASM: pop    rcx\n    APPEND_U8_TO_DEST(0x59);\n    // ASM: mov qword ptr [rsp - 0x08], 0\n    APPEND_BYTES_TO_DEST(0x48, 0xc7, 0x44, 0x24, 0xf8, 0x00, 0x00, 0x00, 0x00);\n    // ASM: pop    rax\n    APPEND_U8_TO_DEST(0x58);\n    // ASM: mov qword ptr [rsp - 0x08], 0\n    APPEND_BYTES_TO_DEST(0x48, 0xc7, 0x44, 0x24, 0xf8, 0x00, 0x00, 0x00, 0x00);\n    // ASM: pop    rsp\n    APPEND_U8_TO_DEST(0x5c);\n\n    return cursor;\n}\n\n// MACRO_SWITCH_U2K --------------------------------------------------------------------------------\nstatic void __attribute__((noipa)) body_macro_switch_u2k(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n    asm_volatile_intel(\"syscall\\n\");\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// MACRO_SET_H2G_TARGET ----------------------------------------------------------------------------\nstatic inline size_t start_macro_set_h2g_target(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n\n    if (cpuinfo->x86_vendor == X86_VENDOR_INTEL) {\n        uint64_t function_addr = get_function_addr(args.arg1, args.arg2);\n        uint64_t vmcs_hpa_addr = (uint64_t)&vmcs_hpas[args.arg1];\n\n        // ASM: movabs r11, &vmcs_hpa\n        APPEND_BYTES_TO_DEST(0x49, 0xbb);\n        APPEND_U64_TO_DEST(vmcs_hpa_addr);\n        // ASM: vmptrld [r11]\n        APPEND_BYTES_TO_DEST(0x41, 0x0f, 0xc7, 0x33);\n        // ASM: movabs r11, function_addr\n        APPEND_BYTES_TO_DEST(0x49, 0xbb);\n        APPEND_U64_TO_DEST(function_addr);\n\n    } else if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n        uint64_t function_addr = get_function_addr(args.arg1, args.arg2);\n        uint64_t vmcb_hva_addr = (uint64_t)&vmcb_hvas[args.arg1];\n\n        // ASM: movabs r11, &vmcb_hva\n        APPEND_BYTES_TO_DEST(0x49, 0xbb);\n        APPEND_U64_TO_DEST(vmcb_hva_addr);\n        // ASM: mov r11, [r11]\n        APPEND_BYTES_TO_DEST(0x4d, 0x8b, 0x1b);\n        // ASM: add r11, VMCB_RIP_OFFSET\n        APPEND_BYTES_TO_DEST(0x49, 0x81, 0xc3);\n        APPEND_U32_TO_DEST(VMCB_RIP_OFFSET);\n        // ASM: mov dword ptr [r11], function_addr[0:31]\n        APPEND_BYTES_TO_DEST(0x49, 0xc7, 0x03);\n        APPEND_U32_TO_DEST(function_addr & 0xFFFFFFFF);\n        // ASM: add r11, 4\n        APPEND_BYTES_TO_DEST(0x49, 0x83, 0xc3, 0x04);\n        // ASM: mov dword ptr [r11], function_addr[32:63]\n        APPEND_BYTES_TO_DEST(0x49, 0xc7, 0x03);\n        APPEND_U32_TO_DEST((function_addr >> 32) & 0xFFFFFFFF);\n    }\n\n    return cursor;\n}\n\nstatic void __attribute__((noipa)) body_macro_set_h2g_target(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n#if VENDOR_ID == 1\n    asm_volatile_intel(\"\"                      // r11 contains the target address\n                       MACRO_PROLOGUE()        //\n                       \"mov rcx, 0x0000681e\\n\" // GUEST_RIP\n                       \"vmwrite rcx, r11 \\n\"   //\n                       MACRO_EPILOGUE()        //\n    );\n#else\n    // Nothing on AMD\n#endif\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// MACRO_SWITCH_H2G --------------------------------------------------------------------------------\nstatic inline size_t start_macro_switch_h2g(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    if (cpuinfo->x86_vendor == X86_VENDOR_INTEL) {\n        // Nothing for Intel\n    } else if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n        // ASM: movabs rax, &vmcb_hpa\n        APPEND_BYTES_TO_DEST(0x48, 0xb8);\n        APPEND_U64_TO_DEST((uint64_t)&vmcb_hpas[args.arg1]);\n    }\n    return cursor;\n}\n\nstatic void __attribute__((noipa)) body_macro_switch_h2g(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n#if VENDOR_ID == 1\n    asm_volatile_intel(\"vmresume\\n\");\n#else\n    asm_volatile_intel(\"\" // rax contains the current VMCB pointer\n                       \"clgi\\n\"\n                       \"mov rax, qword ptr [rax]\\n\" //\n                       \"vmsave rax\\n\"               //\n                       \"vmrun rax\\n\"                //\n                       \"vmload rax\\n\"\n                       \"mov rax, 0\\n\" //\n                       \"stgi\\n\"       //\n                       \"\");\n#endif\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// MACRO_SET_G2H_TARGET ----------------------------------------------------------------------------\nstatic inline size_t start_macro_set_g2h_target(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    if (cpuinfo->x86_vendor == X86_VENDOR_INTEL) {\n        // ASM: movabs r11, function_addr\n        uint64_t function_addr = get_function_addr(args.arg1, args.arg2);\n        APPEND_BYTES_TO_DEST(0x49, 0xbb);\n        APPEND_U64_TO_DEST(function_addr);\n    } else {\n        // Nothing for AMD\n    }\n    return cursor;\n}\n\nstatic void __attribute__((noipa)) body_macro_set_g2h_target(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n#if VENDOR_ID == VENDOR_INTEL_\n    asm_volatile_intel(\"\"                      // r11 contains the target address\n                       MACRO_PROLOGUE()        //\n                       \"mov rcx, 0x00006c16\\n\" // HOST_RIP\n                       \"vmwrite rcx, r11 \\n\"   //\n                       MACRO_EPILOGUE()        //\n    );\n#else\n    // Nothing on AMD\n#endif\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// MACRO_SWITCH_G2H --------------------------------------------------------------------------------\nstatic void __attribute__((noipa)) body_macro_switch_g2h(void)\n{\n    asm volatile(\".quad \" xstr(MACRO_START));\n#if VENDOR_ID == 1\n    asm_volatile_intel(\"vmcall\\n\");\n#else\n    asm_volatile_intel(\"vmmcall\\n\");\n#endif\n    asm volatile(\".quad \" xstr(MACRO_END));\n}\n\n// MACRO_LANDING_K2U -------------------------------------------------------------------------------\nstatic inline size_t start_macro_landing_k2u(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    cursor += update_mem_base_and_sp(args.owner, dest, cursor);\n    // ASM: movabs rcx, 0  # rcx was corrupted during context switch; set to zero\n    APPEND_BYTES_TO_DEST(0x48, 0xb9);\n    APPEND_U64_TO_DEST(0);\n    return cursor;\n}\n\n// MACRO_LANDING_U2K -------------------------------------------------------------------------------\nstatic inline size_t start_macro_landing_u2k(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    cursor += update_r14(args.owner, dest, cursor);\n    // rsp is automatically restored by syscall instruction\n\n    // ASM: movabs rcx, 0  # rcx was corrupted during context switch; set to zero\n    APPEND_BYTES_TO_DEST(0x48, 0xb9);\n    APPEND_U64_TO_DEST(0);\n\n    return cursor;\n}\n\n// MACRO_LANDING_H2G -------------------------------------------------------------------------------\nstatic inline size_t start_macro_landing_h2g(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    cursor += update_r14(args.owner, dest, cursor);\n    cursor += update_r15(args.owner, dest, cursor);\n\n    if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n        // ASM: mov rax, 0\n        APPEND_BYTES_TO_DEST(0x48, 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00);\n    }\n    return cursor;\n}\n\n// MACRO_LANDING_G2H -------------------------------------------------------------------------------\nstatic inline size_t start_macro_landing_g2h(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    cursor += update_r14(args.owner, dest, cursor);\n    cursor += update_r15(args.owner, dest, cursor);\n\n    if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n        // ASM: mov rax, 0\n        APPEND_BYTES_TO_DEST(0x48, 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00);\n    }\n\n    return cursor;\n}\n\n// MACRO_SET_DATA_PERMISSIONS ----------------------------------------------------------------------\nstatic inline size_t start_macro_set_data_permissions(macro_args_t args, uint8_t *dest)\n{\n    size_t cursor = 0;\n    // get safe bits to set/clear\n    uint16_t mask_set = args.arg2;\n    uint16_t mask_clear = args.arg3;\n\n    // get the target PTE\n    uint64_t actor_id = args.arg1;\n    uint64_t page_id = (actor_id * N_DATA_PAGES_PER_ACTOR) + FAULTY_PAGE_ID;\n    pte_t_ *ptep = sandbox_pteps->data_pteps[page_id];\n    ASSERT(ptep != NULL, \"start_macro_set_data_permissions\");\n\n    uint32_t macro_stack_offset = -MACRO_STACK_TOP_OFFSET - 8;\n\n    // Switch stack\n    // ASM: mov [r14 - MACRO_STACK_TOP_OFFSET - 8],rsp\n    APPEND_BYTES_TO_DEST(0x49, 0x89, 0xa6);\n    APPEND_U32_TO_DEST(macro_stack_offset);\n    // ASM: lea rsp,[r14 - MACRO_STACK_TOP_OFFSET - 8]\n    APPEND_BYTES_TO_DEST(0x49, 0x8d, 0xa6);\n    APPEND_U32_TO_DEST(macro_stack_offset);\n    // ASM: push rax\n    APPEND_U8_TO_DEST(0x50);\n\n    // Get pointer to PTE\n    // ASM: mov rax, ptep\n    APPEND_BYTES_TO_DEST(0x48, 0xb8);\n    APPEND_U64_TO_DEST((uint64_t)ptep);\n\n    // Apply the set and clear masks to the lowest 16 bits of the PTE\n    // note that we leave the remaining bits unchanged because arg2 and arg3 are 16-bit values\n    //   ASM: or qword ptr [r11], mask_set\n    APPEND_BYTES_TO_DEST(0x66, 0x81, 0x08);\n    APPEND_U16_TO_DEST(mask_set);\n    //   ASM: and qword ptr [r11], mask_clear\n    APPEND_BYTES_TO_DEST(0x66, 0x81, 0x20);\n    APPEND_U16_TO_DEST(mask_clear);\n\n    // Restore stack\n    // ASM: pop rax\n    APPEND_U8_TO_DEST(0x58);\n    // ASM: mov qword ptr [rsp - 0x08], 0\n    APPEND_BYTES_TO_DEST(0x48, 0xc7, 0x44, 0x24, 0xf8, 0x00, 0x00, 0x00, 0x00);\n    // ASM: pop rsp\n    APPEND_U8_TO_DEST(0x5c);\n    return cursor;\n}\n\n// =================================================================================================\n// Macro descriptors\n// =================================================================================================\nmacro_descr_t macro_descriptors[] = {\n    [TYPE_UNDEFINED] = {.start = NULL, .body = NULL},\n    [TYPE_PRIME] = {.start = NULL, .body = body_macro_prime},\n    [TYPE_FAST_PRIME] = {.start = NULL, .body = body_macro_fast_prime},\n    [TYPE_PARTIAL_PRIME] = {.start = NULL, .body = body_macro_partial_prime},\n    [TYPE_FAST_PARTIAL_PRIME] = {.start = NULL, .body = body_macro_fast_partial_prime},\n    [TYPE_PROBE] = {.start = NULL, .body = body_macro_probe},\n    [TYPE_FLUSH] = {.start = NULL, .body = body_macro_flush},\n    [TYPE_EVICT] = {.start = NULL, .body = body_macro_prime},\n    [TYPE_RELOAD] = {.start = NULL, .body = body_macro_reload},\n    [TYPE_TSC_START] = {.start = NULL, .body = body_macro_tsc_start},\n    [TYPE_TSC_END] = {.start = NULL, .body = body_macro_tsc_end},\n    [TYPE_FAULT_HANDLER] = {.start = start_macro_fault_handler, .body = NULL},\n    [TYPE_FAULT_AND_PROBE] = {.start = start_macro_fault_handler_with_measurement,\n                              .body = body_macro_probe},\n    [TYPE_FAULT_AND_RELOAD] = {.start = start_macro_fault_handler_with_measurement,\n                               .body = body_macro_reload},\n    [TYPE_FAULT_AND_TSC_END] = {.start = start_macro_fault_handler_with_measurement,\n                                .body = body_macro_tsc_end},\n    [TYPE_SWITCH] = {.start = start_macro_switch, .body = NULL},\n    [TYPE_SET_K2U_TARGET] = {.start = start_macro_set_k2u_target, .body = NULL},\n    [TYPE_SWITCH_K2U] = {.start = start_macro_switch_k2u, .body = body_macro_switch_k2u},\n    [TYPE_SET_U2K_TARGET] = {.start = start_macro_set_u2k_target, .body = NULL},\n    [TYPE_SWITCH_U2K] = {.start = NULL, .body = body_macro_switch_u2k},\n    [TYPE_SET_H2G_TARGET] = {.start = start_macro_set_h2g_target,\n                             .body = body_macro_set_h2g_target},\n    [TYPE_SWITCH_H2G] = {.start = start_macro_switch_h2g, .body = body_macro_switch_h2g},\n    [TYPE_SET_G2H_TARGET] = {.start = start_macro_set_g2h_target,\n                             .body = body_macro_set_g2h_target},\n    [TYPE_SWITCH_G2H] = {.start = NULL, .body = body_macro_switch_g2h},\n    [TYPE_LANDING_K2U] = {.start = start_macro_landing_k2u, .body = NULL},\n    [TYPE_LANDING_U2K] = {.start = start_macro_landing_u2k, .body = NULL},\n    [TYPE_LANDING_H2G] = {.start = start_macro_landing_h2g, .body = NULL},\n    [TYPE_LANDING_G2H] = {.start = start_macro_landing_g2h, .body = NULL},\n    [TYPE_SET_DATA_PERMISSIONS] = {.start = start_macro_set_data_permissions, .body = NULL},\n};\n"
  },
  {
    "path": "rvzr/executor_km/x86/page_tables_guest.c",
    "content": "/// File:\n///  - Guest page table management\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <asm/io.h>\n#include <asm/msr.h>\n\n#include \"actor.h\"\n#include \"hardware_desc.h\"\n#include \"main.h\"\n#include \"sandbox_manager.h\"\n#include \"shortcuts.h\"\n\n#include \"page_tables_common.h\"\n#include \"page_tables_guest.h\"\n\n#define INIT_PTE(PTE, PADDR, P, W, US, PWT, PCD, XD, A)                                            \\\n    {                                                                                              \\\n        (PTE).present = P;                                                                         \\\n        (PTE).write_access = W;                                                                    \\\n        (PTE).user_supervisor = US;                                                                \\\n        (PTE).page_write_through = PWT;                                                            \\\n        (PTE).page_cache_disable = PCD;                                                            \\\n        (PTE).paddr = (PADDR) >> 12;                                                               \\\n        (PTE).execute_disable = XD;                                                                \\\n        (PTE).accessed = A;                                                                        \\\n    }\n\n#if VENDOR_ID == VENDOR_INTEL_\n#define INIT_EPTE(PTE, PADDR, P, W, X, A)                                                          \\\n    {                                                                                              \\\n        (PTE).read_access = P;                                                                     \\\n        (PTE).write_access = W;                                                                    \\\n        (PTE).execute_access = X;                                                                  \\\n        (PTE).paddr = (PADDR) >> 12;                                                               \\\n        (PTE).accessed = A;                                                                        \\\n    }\n#else // AMD\n#define INIT_EPTE(PTE, PADDR, P, W, X, A)                                                          \\\n    {                                                                                              \\\n        (PTE).present = P;                                                                         \\\n        (PTE).write_access = W;                                                                    \\\n        (PTE).user_supervisor = 1;                                                                 \\\n        (PTE).page_write_through = 0;                                                              \\\n        (PTE).page_cache_disable = 0;                                                              \\\n        (PTE).paddr = (PADDR) >> 12;                                                               \\\n        (PTE).execute_disable = X ^ 1;                                                             \\\n        (PTE).accessed = A;                                                                        \\\n    }\n#endif\n\n#define INIT_PTE_DEFAULT(PTE, PADDR)  INIT_PTE(PTE, PADDR, 1, 1, 0, 0, 0, 0, 1)\n#define INIT_EPTE_DEFAULT(PTE, PADDR) INIT_EPTE(PTE, PADDR, 1, 1, 1, 1)\n\n#if VENDOR_ID == VENDOR_INTEL_\n#define EPTE_IS_PRESENT(EPT) EPT.read_access\n#else\n#define EPTE_IS_PRESENT(EPT) EPT.present\n#endif\n\n#if VENDOR_ID == VENDOR_INTEL_\n#define EPTE_IS_EXECUTABLE(EPT) EPT.execute_access\n#else\n#define EPTE_IS_EXECUTABLE(EPT) (EPT.execute_disable ^ 1)\n#endif\n\n#if VENDOR_ID == VENDOR_INTEL_\n#define EPTE_IS_USER_ACCESSIBLE(EPT) EPT.user_ex_access\n#else\n#define EPTE_IS_USER_ACCESSIBLE(EPT) EPT.user_supervisor\n#endif\n\neptp_t *ept_ptr = NULL; // global\n\nstatic actor_page_table_t *allocated_page_tables = NULL;\nstatic actor_ept_t *allocated_extended_page_tables = NULL;\nstatic actor_gdt_t *allocated_guest_gdts = NULL;\nstatic guest_memory_translations_t *guest_memory_translations = NULL;\nstatic uint8_t *vmlaunch_page = NULL;\nstatic pte_t_ *faulty_ptes = NULL;\nstatic epte_t_ *faulty_eptes = NULL;\n\nstatic bool guest_pt_is_set = false;\nstatic bool ept_is_set = false;\n\n// =================================================================================================\n// Helper functions\n// =================================================================================================\n/// @brief Translate a host physical address to a virtual address in high memory.\n/// Note: This function is necessary because kernel does not provide a direct interface to search\n/// for a physical address in page tables (or at least I couldn't find one)\n/// @param hpa Host physical address to translate\n/// @return Host virtual address in high memory\nstatic void *phys_to_vmalloc(uint64_t hpa, int actor_id)\n{\n    hgpa_t *flat_translations = (hgpa_t *)&guest_memory_translations[actor_id];\n    for (int i = 0; i < sizeof(guest_memory_translations_t) / sizeof(hgpa_t); i++) {\n        if (flat_translations[i].hpa == hpa) {\n            return flat_translations[i].hva;\n        }\n    }\n    return 0;\n}\n\nstatic inline bool gpa_is_valid(hgpa_t *translations, uint64_t gpa)\n{\n    for (int i = 0; i < sizeof(guest_memory_translations_t) / sizeof(hgpa_t); i++) {\n        if (translations[i].gpa == gpa) {\n            return true;\n        }\n    }\n    return false;\n}\n\nstatic inline int set_last_pt_level(pte_t_ *pt, hgpa_t *translation, uint64_t paddr, uint64_t vaddr)\n{\n    size_t pt_index = PT_INDEX(vaddr);\n    ASSERT(pt[pt_index].present == 0, \"set_last_pt_level\");\n    INIT_PTE_DEFAULT(pt[pt_index], paddr);\n    pt[pt_index].dirty = 1;\n\n    translation->gpa = paddr;\n    translation->gva = (void *)vaddr;\n    return 0;\n}\n\nstatic inline int set_ept_entry(actor_ept_t *actor_ept_base, hgpa_t *translation, uint64_t l3_hpa,\n                                uint64_t l2_hpa, uint64_t l1_hpa, void *hva)\n{\n    // get the addresses to map\n    uint64_t gpa = translation->gpa;\n    uint64_t hpa = vmalloc_to_phys(hva);\n\n    // check for collisions\n    // (the way we allocate page could, with very low likelihood, cause a collision)\n    ASSERT(actor_ept_base->l1[PT_INDEX(gpa)].paddr == 0, \"set_extended_page_tables\");\n    ASSERT(hpa, \"set_extended_page_tables\");\n\n    translation->hpa = hpa;\n    translation->hva = hva;\n\n    // set all page table levels\n    INIT_EPTE_DEFAULT(actor_ept_base->l4[PML4_INDEX(gpa)], l3_hpa);\n    INIT_EPTE_DEFAULT(actor_ept_base->l3[PDPT_INDEX(gpa)], l2_hpa);\n    INIT_EPTE_DEFAULT(actor_ept_base->l2[PDT_INDEX(gpa)], l1_hpa);\n    INIT_EPTE_DEFAULT(actor_ept_base->l1[PT_INDEX(gpa)], hpa);\n\n    // set additional properties for the last level\n    actor_ept_base->l1[PT_INDEX(gpa)].dirty = 1;\n\n#if VENDOR_ID == VENDOR_INTEL_\n    actor_ept_base->l1[PT_INDEX(gpa)].ept_mem_type = 6;\n    actor_ept_base->l1[PT_INDEX(gpa)].ignore_pat = 1;\n#else\n    actor_ept_base->l1[PT_INDEX(gpa)].page_attribute_table = 1;\n#endif\n\n    return 0;\n}\n\n// =================================================================================================\n// Page table management interface\n// =================================================================================================\n\n/// @brief Set the guest page tables for all guest actors according to the layout defined in\n/// guest_memory_t (see guest_page_tables.h), with the base address GUEST_MEMORY_START\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic int set_guest_page_tables(void)\n{\n    int err = 0;\n    uint64_t vaddr = 0;\n    uint64_t paddr = 0;\n\n    static size_t old_n_actors = 0;\n    if (n_actors > old_n_actors) {\n        SAFE_FREE(faulty_ptes);\n        SAFE_FREE(faulty_eptes);\n        faulty_ptes = (pte_t_ *)CHECKED_ZALLOC(sizeof(pte_t_) * n_actors);\n        faulty_eptes = (epte_t_ *)CHECKED_ZALLOC(sizeof(epte_t_) * n_actors);\n    }\n    old_n_actors = n_actors;\n\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        // skip non-guest actors\n        actor_metadata_t *actor = &actors[actor_id];\n        if (actor->mode != MODE_GUEST) {\n            continue;\n        }\n\n        // get a type that represents the guest memory\n        guest_memory_t *guest_v_memory = (guest_memory_t *)(GUEST_V_MEMORY_START);\n        guest_memory_t *guest_p_memory = (guest_memory_t *)(GUEST_P_MEMORY_START);\n        guest_memory_translations_t *translations = &guest_memory_translations[actor_id];\n\n        // Set the first three levels of the page table\n        // For convenience, we set GPA of the page tables to the same value as their GVA\n        // Also, since the actor's sandbox is fairly small, the first three levels are identical\n        // for all addresses within the actor memory\n        actor_page_table_t *page_table = &allocated_page_tables[actor_id];\n        actor_page_table_t *page_table_gpa = &guest_p_memory->guest_page_tables;\n        translations->guest_page_tables[3].gpa = (uint64_t)&page_table_gpa->l4;\n\n        size_t l4_index = PML4_INDEX(GUEST_V_MEMORY_START);\n        uint64_t l3_gpa = (uint64_t)&page_table_gpa->l3;\n        INIT_PTE_DEFAULT(page_table->l4[l4_index], l3_gpa);\n        translations->guest_page_tables[2].gpa = l3_gpa;\n\n        size_t l3_index = PDPT_INDEX(GUEST_V_MEMORY_START);\n        uint64_t l2_gpa = (uint64_t)&page_table_gpa->l2;\n        INIT_PTE_DEFAULT(page_table->l3[l3_index], l2_gpa);\n        translations->guest_page_tables[1].gpa = l2_gpa;\n\n        size_t l2_index = PDT_INDEX(GUEST_V_MEMORY_START);\n        uint64_t l1_gpa = (uint64_t)&page_table_gpa->l1;\n        INIT_PTE_DEFAULT(page_table->l2[l2_index], l1_gpa);\n        translations->guest_page_tables[0].gpa = l1_gpa;\n\n        // set the last level of the page table for each area of the actor sandbox\n        for (int i = 0; i < sizeof(util_t); i += 4096) {\n            vaddr = ((uint64_t)&guest_v_memory->util) + i;\n            paddr = ((uint64_t)&guest_p_memory->util) + i;\n            err = set_last_pt_level(page_table->l1, &translations->util[i / 4096], paddr, vaddr);\n            CHECK_ERR(\"set_guest_page_tables\");\n        }\n        for (int i = 0; i < sizeof(actor_data_t); i += 4096) {\n            uint64_t vaddr = ((uint64_t)&guest_v_memory->data) + i;\n            if (enable_hpa_gpa_collisions) {\n                uint64_t aliased_vaddr = ((uint64_t)&sandbox->data[0]) + i;\n                paddr = vmalloc_to_phys((void *)aliased_vaddr);\n            } else {\n                paddr = ((uint64_t)&guest_p_memory->data) + i;\n            }\n            err = set_last_pt_level(page_table->l1, &translations->data[i / 4096], paddr, vaddr);\n            CHECK_ERR(\"set_guest_page_tables\");\n        }\n        for (int i = 0; i < sizeof(actor_code_t); i += 4096) {\n            vaddr = ((uint64_t)&guest_v_memory->code) + i;\n            if (enable_hpa_gpa_collisions) {\n                uint64_t aliased_vaddr = ((uint64_t)&sandbox->code[0]) + i;\n                paddr = vmalloc_to_phys((void *)aliased_vaddr);\n            } else {\n                paddr = ((uint64_t)&guest_p_memory->code) + i;\n            }\n            err = set_last_pt_level(page_table->l1, &translations->code[i / 4096], paddr, vaddr);\n            CHECK_ERR(\"set_guest_page_tables\");\n        }\n        { // GDT (indentation is for readability)\n            vaddr = (uint64_t)&guest_v_memory->gdt;\n            paddr = (uint64_t)&guest_p_memory->gdt;\n            err = set_last_pt_level(page_table->l1, &translations->gdt[0], paddr, vaddr);\n            CHECK_ERR(\"set_guest_page_tables\");\n        }\n        { // VMLAUNCH page (indentation is for readability)\n            vaddr = (uint64_t)&guest_v_memory->vmlaunch_page[0];\n            paddr = (uint64_t)&guest_p_memory->vmlaunch_page[0];\n            err = set_last_pt_level(page_table->l1, &translations->vmlaunch_page[0], paddr, vaddr);\n            CHECK_ERR(\"set_guest_page_tables\");\n        }\n    }\n\n    guest_pt_is_set = true;\n    return 0;\n}\n\n/// @brief Map sandbox_t from host memory into the guest memory of each guest actor, according to\n/// the layout defined in guest_memory_t (see page_tables_guest.h), with the base address equal to\n/// GUEST_MEMORY_START\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic int set_extended_page_tables(void)\n{\n    int err = 0;\n\n    ASSERT(actors != NULL, \"set_extended_page_tables\");\n    ASSERT(sandbox != NULL, \"set_extended_page_tables\");\n    ASSERT(guest_pt_is_set, \"set_extended_page_tables\");\n\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        // skip non-guest actors\n        actor_metadata_t *actor = &actors[actor_id];\n        if (actor->mode != MODE_GUEST) {\n            continue;\n        }\n        actor_ept_t *ept_base = &allocated_extended_page_tables[actor_id];\n        guest_memory_translations_t *translations = &guest_memory_translations[actor_id];\n\n        // get addresses of the last three levels\n        uint64_t l3_hpa = vmalloc_to_phys((void *)ept_base->l3);\n        uint64_t l2_hpa = vmalloc_to_phys((void *)ept_base->l2);\n        uint64_t l1_hpa = vmalloc_to_phys((void *)ept_base->l1);\n\n        // map util_t into guest memory (the same phys range for all actors, i.e., shared)\n        for (int i = 0; i < sizeof(util_t) / PAGE_SIZE; i += 1) {\n            void *hva = (void *)&sandbox->util[0] + (i * PAGE_SIZE);\n            err = set_ept_entry(ept_base, &translations->util[i], l3_hpa, l2_hpa, l1_hpa, hva);\n            CHECK_ERR(\"set_extended_page_tables\");\n        }\n\n        // map actor_data_t, actor_code_t, and GDT into guest memory (each actor has its own)\n        for (int i = 0; i < sizeof(actor_data_t) / PAGE_SIZE; i += 1) {\n            void *hva = (void *)&sandbox->data[actor_id] + (i * PAGE_SIZE);\n            err = set_ept_entry(ept_base, &translations->data[i], l3_hpa, l2_hpa, l1_hpa, hva);\n            CHECK_ERR(\"set_extended_page_tables\");\n        }\n        for (int i = 0; i < sizeof(actor_code_t) / PAGE_SIZE; i += 1) {\n            void *hva = (void *)&sandbox->code[actor_id] + (i * PAGE_SIZE);\n            err = set_ept_entry(ept_base, &translations->code[i], l3_hpa, l2_hpa, l1_hpa, hva);\n            CHECK_ERR(\"set_extended_page_tables\");\n        }\n        { // indent for readability\n            void *hva = (void *)&allocated_guest_gdts[actor_id];\n            err = set_ept_entry(ept_base, &translations->gdt[0], l3_hpa, l2_hpa, l1_hpa, hva);\n            CHECK_ERR(\"set_extended_page_tables\");\n        }\n        { // indent for readability\n            void *hva = (void *)&vmlaunch_page[0];\n            err = set_ept_entry(ept_base, &translations->vmlaunch_page[0], l3_hpa, l2_hpa, l1_hpa,\n                                hva);\n            CHECK_ERR(\"set_extended_page_tables\");\n        }\n\n        // map guest page tables\n        for (int i = 0; i < sizeof(actor_page_table_t) / PAGE_SIZE; i += 1) {\n            void *hva = (void *)&allocated_page_tables[actor_id] + (i * PAGE_SIZE);\n            err = set_ept_entry(ept_base, &translations->guest_page_tables[i], l3_hpa, l2_hpa,\n                                l1_hpa, hva);\n            CHECK_ERR(\"set_extended_page_tables\");\n        }\n    }\n\n    ept_is_set = true;\n    return 0;\n}\n\n/// @brief Store a pointer to the EPT of actor 1 (default) in ept_ptr after updating extended page\n/// tables\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic int update_eptp(void)\n{\n    ASSERT(ept_is_set, \"update_eptp\");\n    SAFE_FREE(ept_ptr);\n    ept_ptr = CHECKED_ZALLOC(sizeof(eptp_t) * n_actors);\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        actor_ept_t *actor_ept_base = &allocated_extended_page_tables[actor_id];\n        ept_ptr[actor_id].memory_type = VMX_BASIC_MEM_TYPE_WB;\n        ept_ptr[actor_id].page_walk_length = 3;\n        ept_ptr[actor_id].ad_enabled = 1; // native_read_msr(MSR_IA32_VMX_EPT_VPID_CAP) &0x00200000;\n        ept_ptr[actor_id].superv_sdw_stack = 0;\n        ept_ptr[actor_id].paddr = vmalloc_to_phys(actor_ept_base->l4) >> 12;\n    }\n\n    return 0;\n}\n\nint map_sandbox_to_guest_memory(void)\n{\n    int err = 0;\n    ASSERT(allocated_page_tables != NULL, \"map_sandbox_to_guest_memory\");\n    ASSERT(allocated_extended_page_tables != NULL, \"map_sandbox_to_guest_memory\");\n    ASSERT(allocated_guest_gdts != NULL, \"map_sandbox_to_guest_memory\");\n\n    err = set_guest_page_tables();\n    CHECK_ERR(\"set_guest_page_tables\");\n\n    err = set_extended_page_tables();\n    CHECK_ERR(\"set_extended_page_tables\");\n\n    err = update_eptp();\n    CHECK_ERR(\"update_eptp\");\n\n    return 0;\n}\n\n/// @brief Set permissions on the faulty page based on the actor's metadata (for each actor)\n/// @param void\nvoid set_faulty_page_guest_permissions(void)\n{\n    guest_memory_t *guest_v_memory = (guest_memory_t *)(GUEST_V_MEMORY_START);\n    uint64_t vaddr = ((uint64_t)&guest_v_memory->data.faulty_area[0]);\n    size_t index = PT_INDEX(vaddr);\n\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        actor_metadata_t *actor = &actors[actor_id];\n        if (actor->mode != MODE_GUEST)\n            continue;\n\n        uint64_t pte_mask = actor->data_permissions;\n        uint64_t mask_set = pte_mask & MODIFIABLE_PTE_BITS;\n        uint64_t mask_clear = pte_mask | ~MODIFIABLE_PTE_BITS;\n\n        pte_t_ *ptep = &allocated_page_tables[actor_id].l1[index];\n        faulty_ptes[actor_id] = *ptep;\n\n        uint64_t org_pte = *(uint64_t *)ptep;\n        uint64_t pte = (org_pte | mask_set) & mask_clear;\n        if (pte != org_pte) {\n            *(uint64_t *)ptep = pte;\n            // native_page_invalidate(vaddr);\n        }\n    }\n}\n\nvoid restore_faulty_page_guest_permissions(void)\n{\n    guest_memory_t *guest_v_memory = (guest_memory_t *)(GUEST_V_MEMORY_START);\n    uint64_t vaddr = ((uint64_t)&guest_v_memory->data.faulty_area[0]);\n    size_t index = PT_INDEX(vaddr);\n\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        actor_metadata_t *actor = &actors[actor_id];\n        if (actor->mode != MODE_GUEST)\n            continue;\n\n        allocated_page_tables[actor_id].l1[index] = faulty_ptes[actor_id];\n    }\n}\n\n/// @brief Set EPT permissions on the faulty page based on the actor's metadata (for each actor)\n/// @param void\nvoid set_faulty_page_ept_permissions(void)\n{\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        actor_metadata_t *actor = &actors[actor_id];\n        if (actor->mode != MODE_GUEST)\n            continue;\n\n        guest_memory_translations_t *translations = &guest_memory_translations[actor_id];\n        uint64_t gpa = translations->data[FAULTY_PAGE_ID].gpa;\n        size_t index = PT_INDEX(gpa);\n\n        uint64_t pte_mask = actor->data_ept_properties;\n        uint64_t mask_set = pte_mask & MODIFIABLE_EPTE_BITS;\n        uint64_t mask_clear = pte_mask | ~MODIFIABLE_EPTE_BITS;\n\n        epte_t_ *ptep = &allocated_extended_page_tables[actor_id].l1[index];\n        faulty_eptes[actor_id] = *ptep;\n\n        uint64_t org_pte = *(uint64_t *)ptep;\n        uint64_t pte = (org_pte | mask_set) & mask_clear;\n        if (pte != org_pte) {\n            *(uint64_t *)ptep = pte;\n            // native_page_invalidate(vaddr);\n        }\n    }\n}\n\nvoid restore_faulty_page_ept_permissions(void)\n{\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        actor_metadata_t *actor = &actors[actor_id];\n        if (actor->mode != MODE_GUEST)\n            continue;\n\n        guest_memory_translations_t *translations = &guest_memory_translations[actor_id];\n        uint64_t gpa = translations->data[FAULTY_PAGE_ID].gpa;\n        size_t index = PT_INDEX(gpa);\n\n        allocated_extended_page_tables[actor_id].l1[index] = faulty_eptes[actor_id];\n    }\n}\n\n// =================================================================================================\n// Debugging Interfaces\n// =================================================================================================\n\n/// @brief Dump the guest page tables for a given actor\n/// @param actor_id\n/// @return 0 on success, -1 on failure\nint dbg_dump_guest_page_tables(int actor_id)\n{\n    // NOTE: the implementation below traverses page tables as if they were unbounded (i.e.,\n    //   contained an unlimited number of PTEs, PDs, etc). This is not the case for the guest page\n    //   tables as we have only one PT per actor. However, this implementation is more future-proof,\n    //   so we have a traditional page walk, and just check the number of entries with asserts\n    printk(KERN_INFO \"------- Page table dump for actor %d ---------------\\n\", actor_id);\n    actor_page_table_t *page_table = &allocated_page_tables[actor_id];\n    guest_memory_translations_t *translations = &guest_memory_translations[actor_id];\n\n    // L4 traversal\n    pml4e_t *l4 = page_table->l4;\n    for (uint64_t curr_l4_id = 0; curr_l4_id < ENTRIES_PER_PAGE; curr_l4_id += 1) {\n        pml4e_t l4e = l4[curr_l4_id]; // current L4 entry\n        if (!l4e.present)\n            continue;\n        // we allocate memory such that only the first L4 entry is used\n        ASSERT(curr_l4_id == 0, \"dbg_dump_guest_page_tables\");\n\n        uint64_t l3_gpa = ((uint64_t)l4e.paddr << 12);\n        ASSERT_MSG(l3_gpa == translations->guest_page_tables[2].gpa, \"dbg_dump_guest_page_tables\",\n                   \"0x%llx != 0x%llx\\n\", l3_gpa, translations->guest_page_tables[2].gpa);\n        pdpte_t *l3 = (pdpte_t *)translations->guest_page_tables[2].hva;\n\n        // L3 traversal\n        for (uint64_t curr_l3_id = 0; curr_l3_id < ENTRIES_PER_PAGE; curr_l3_id += 1) {\n            pdpte_t l3e = l3[curr_l3_id]; // current L3 entry\n            if (!l3e.present)\n                continue;\n            // we allocate memory such that only the first L3 entry is used\n            ASSERT(curr_l3_id == 0, \"dbg_dump_guest_page_tables\");\n\n            uint64_t l2_gpa = ((uint64_t)l3e.paddr << 12);\n            ASSERT(l2_gpa == translations->guest_page_tables[1].gpa, \"dbg_dump_guest_page_tables\");\n            pdte_t *l2 = (pdte_t *)translations->guest_page_tables[1].hva;\n\n            // L2 traversal\n            for (uint64_t curr_l2_id = 0; curr_l2_id < ENTRIES_PER_PAGE; curr_l2_id += 1) {\n                pdte_t l2e = l2[curr_l2_id]; // current L2 entry\n                if (!l2e.present)\n                    continue;\n                ASSERT(curr_l2_id == 0, \"dbg_dump_guest_page_tables\");\n\n                uint64_t l1_gpa = ((uint64_t)l2e.paddr << 12);\n                ASSERT(l1_gpa == translations->guest_page_tables[0].gpa,\n                       \"dbg_dump_guest_page_tables\");\n                pte_t_ *l1 = (pte_t_ *)translations->guest_page_tables[0].hva;\n\n                // L1 traversal\n                for (uint64_t curr_l1_id = 0; curr_l1_id < ENTRIES_PER_PAGE; curr_l1_id += 1) {\n                    pte_t_ l1e = l1[curr_l1_id];\n                    if (!l1e.present)\n                        continue;\n                    uint64_t paddr = ((uint64_t)l1e.paddr << 12);\n                    uint64_t vaddr = (curr_l4_id << PML4_SHIFT) | (curr_l3_id << PDPT_SHIFT) |\n                                     (curr_l2_id << PDT_SHIFT) | (curr_l1_id << PT_SHIFT);\n                    char p = l1e.present ? 'P' : '-';\n                    char w = l1e.write_access ? 'W' : '-';\n                    char us = l1e.user_supervisor ? 'U' : '-';\n                    char pwt = l1e.page_write_through ? 'T' : '-';\n                    char pcd = l1e.page_cache_disable ? 'C' : '-';\n                    char a = l1e.accessed ? 'A' : '-';\n                    char d = l1e.dirty ? 'D' : '-';\n                    char pat = l1e.page_attribute_table ? 'T' : '-';\n                    char g = l1e.global_page ? 'G' : '-';\n                    char x = l1e.execute_disable ? '-' : 'X';\n                    printk(KERN_INFO \"V: 0x%-16llx -> P: 0x%-16llx; %c%c%c%c%c%c%c%c%c%c\\n\", vaddr,\n                           paddr, p, w, us, pwt, pcd, a, d, pat, g, x);\n                }\n            }\n        }\n    }\n    return 0;\n}\n\nint dbg_dump_ept(int actor_id)\n{\n    printk(KERN_INFO \"------- EPT dump -----------------------------------\\n\");\n    actor_ept_t *actor_ept_base = &allocated_extended_page_tables[actor_id];\n\n    // L4 traversal\n    epml4e_t *l4 = actor_ept_base->l4;\n    for (uint64_t curr_l4_id = 0; curr_l4_id < ENTRIES_PER_PAGE; curr_l4_id += 1) {\n        epml4e_t l4e = l4[curr_l4_id];\n        if (!EPTE_IS_PRESENT(l4e))\n            continue;\n        uint64_t l3_hpa = ((uint64_t)l4e.paddr << 12);\n        epdpte_t *l3 = actor_ept_base->l3;\n        ASSERT((l3_hpa & ~0xFFF) == vmalloc_to_phys(l3), \"dbg_dump_ept\");\n\n        // L3 traversal\n        for (uint64_t curr_l3_id = 0; curr_l3_id < ENTRIES_PER_PAGE; curr_l3_id += 1) {\n            epdpte_t l3e = l3[curr_l3_id];\n            if (!EPTE_IS_PRESENT(l3e))\n                continue;\n            uint64_t l2_hpa = ((uint64_t)l3e.paddr << 12);\n            epdte_t *l2 = actor_ept_base->l2;\n            ASSERT((l2_hpa & ~0xFFF) == vmalloc_to_phys(l2), \"dbg_dump_ept\");\n\n            // L2 traversal\n            for (uint64_t curr_l2_id = 0; curr_l2_id < ENTRIES_PER_PAGE; curr_l2_id += 1) {\n                epdte_t l2e = l2[curr_l2_id];\n                if (!EPTE_IS_PRESENT(l2e))\n                    continue;\n                uint64_t l1_hpa = ((uint64_t)l2e.paddr << 12);\n                epte_t_ *l1 = actor_ept_base->l1;\n                ASSERT((l1_hpa & ~0xFFF) == vmalloc_to_phys(l1), \"dbg_dump_ept\");\n\n                // L1 traversal\n                for (uint64_t curr_l1_id = 0; curr_l1_id < ENTRIES_PER_PAGE; curr_l1_id += 1) {\n                    epte_t_ l1e = l1[curr_l1_id];\n                    if (!EPTE_IS_PRESENT(l1e))\n                        continue;\n                    uint64_t gpa = (curr_l4_id << PML4_SHIFT) | (curr_l3_id << PDPT_SHIFT) |\n                                   (curr_l2_id << PDT_SHIFT) | (curr_l1_id << PT_SHIFT);\n\n                    // if HPA-GPA collisions are enabled, we will have multiple translations per\n                    // physical address; hence, filter out the unused GPAs\n                    if (enable_hpa_gpa_collisions &&\n                        !gpa_is_valid((hgpa_t *)&guest_memory_translations[actor_id], gpa)) {\n                        continue;\n                    }\n\n                    uint64_t hpa = ((uint64_t)l1e.paddr << 12);\n                    void *hva = phys_to_vmalloc(hpa, actor_id);\n                    char r = EPTE_IS_PRESENT(l1e) ? 'R' : '-';\n                    char w = l1e.write_access ? 'W' : '-';\n                    char x = EPTE_IS_EXECUTABLE(l1e) ? 'X' : '-';\n                    char a = l1e.accessed ? 'A' : '-';\n                    char d = l1e.dirty ? 'D' : '-';\n                    char us = EPTE_IS_USER_ACCESSIBLE(l1e) ? 'U' : '-';\n                    printk(KERN_INFO\n                           \"GP: 0x%-16llx -> HP: 0x%-16llx (HV: 0x%-16llx); %c%c%c%c%c%c\\n\",\n                           gpa, hpa, (uint64_t)hva, r, w, x, a, d, us);\n                }\n            }\n        }\n    }\n    return 0;\n}\n\n// =================================================================================================\nint allocate_guest_page_tables()\n{\n    ASSERT(n_actors < 64, \"allocate_guest_page_tables\");\n\n    static size_t old_n_actors = 0;\n    if (n_actors <= old_n_actors) {\n        memset(allocated_page_tables, 0, n_actors * sizeof(actor_page_table_t));\n        memset(allocated_extended_page_tables, 0, n_actors * sizeof(actor_ept_t));\n        memset(allocated_guest_gdts, 0, n_actors * sizeof(actor_gdt_t));\n        memset(guest_memory_translations, 0, n_actors * sizeof(guest_memory_translations_t));\n        return 0;\n    }\n    old_n_actors = n_actors;\n    SAFE_VFREE(allocated_page_tables);\n    SAFE_VFREE(allocated_extended_page_tables);\n    SAFE_VFREE(allocated_guest_gdts);\n    SAFE_FREE(guest_memory_translations);\n    SAFE_FREE(vmlaunch_page);\n\n    // Guest page tables\n    allocated_page_tables =\n        (actor_page_table_t *)CHECKED_VMALLOC(n_actors * sizeof(actor_page_table_t));\n    memset(allocated_page_tables, 0, n_actors * sizeof(actor_page_table_t));\n\n    // EPTs\n    allocated_extended_page_tables = (actor_ept_t *)CHECKED_VMALLOC(n_actors * sizeof(actor_ept_t));\n    memset(allocated_extended_page_tables, 0, n_actors * sizeof(actor_ept_t));\n\n    allocated_guest_gdts = CHECKED_VMALLOC(n_actors * sizeof(actor_gdt_t));\n\n    // Fast translations\n    guest_memory_translations = CHECKED_ZALLOC(n_actors * sizeof(guest_memory_translations_t));\n\n    // A page with a single VMCALL instruction; used to put the VM into launched state\n    vmlaunch_page = CHECKED_ZALLOC(PAGE_SIZE);\n    vmlaunch_page[0] = 0x0f;\n    vmlaunch_page[1] = 0x01;\n    vmlaunch_page[2] = 0xc1;\n\n    faulty_ptes = (pte_t_ *)CHECKED_ZALLOC(sizeof(pte_t_));\n    faulty_eptes = (epte_t_ *)CHECKED_ZALLOC(sizeof(epte_t_));\n\n    guest_pt_is_set = false;\n    ept_is_set = false;\n    return 0;\n}\n\nvoid free_guest_page_tables(void)\n{\n    SAFE_VFREE(allocated_page_tables);\n    SAFE_VFREE(allocated_extended_page_tables);\n    SAFE_VFREE(allocated_guest_gdts);\n    SAFE_FREE(guest_memory_translations);\n    SAFE_FREE(ept_ptr);\n    SAFE_FREE(vmlaunch_page);\n    SAFE_FREE(faulty_ptes);\n    SAFE_FREE(faulty_eptes);\n}\n"
  },
  {
    "path": "rvzr/executor_km/x86/perf_counters.c",
    "content": "/// File: Configuration and use of performance counters\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <asm/msr-index.h>\n#include <linux/kernel.h>\n#include <linux/types.h>\n\n#include \"main.h\"\n#include \"shortcuts.h\"\n\n#include \"perf_counters.h\"\n\nstruct pfc_config {\n    unsigned long evt_num;\n    unsigned long umask;\n    unsigned long cmask;\n    unsigned int any;\n    unsigned int edge;\n    unsigned int inv;\n};\n\ntypedef enum {\n    L1_HITS = 0,\n    UOPS_ISSUED_ANY = 1,\n    UOPS_RETIRED_ANY = 2,\n    MISPREDICTION_RECOVERY_CYCLES = 3,\n    HW_INTERRUPTS_RECEIVED = 4,\n    SMI_INTERRUPTS_RECEIVED = 5,\n    DECODE_REDIRECTS = 6\n} pfc_name_e;\n\nstatic int get_pfc_config_by_name(pfc_name_e pfc_name, struct pfc_config *config)\n{\n    uint64_t family = cpuinfo->x86;\n    uint64_t model = cpuinfo->x86_model;\n\n    // most commonly, the fields cmask, any, edge, and inv are set to 0\n    config->cmask = 0;\n    config->any = 0;\n    config->edge = 0;\n    config->inv = 0;\n\n    // Intel PMU\n    if (cpuinfo->x86_vendor == X86_VENDOR_INTEL) {\n        switch (pfc_name) {\n        case L1_HITS:\n            //   MEM_LOAD_RETIRED.L1_HIT: Counts retired load instructions with at least one uop\n            //   that hit in the L1 data cache. This event includes all SW prefetches and lock\n            //   instructions regardless of the data source.\n            config->evt_num = 0xd1;\n            config->umask = 0x01;\n            break;\n        case UOPS_ISSUED_ANY:\n            //   UOPS_ISSUED.ANY: Counts the number of uops that the Resource Allocation Table (RAT)\n            //   issues to the Reservation Station (RS).\n            if (model == 0xBA || model == 0xB7 || model == 0xBF || model == 0x97 || model == 0x9A) {\n                config->evt_num = 0xAE;\n                config->umask = 0x01;\n            } else {\n                config->evt_num = 0x0E;\n                config->umask = 0x01;\n            }\n            break;\n        case UOPS_RETIRED_ANY:\n            //   UOPS_RETIRED.RETIRE_SLOTS: Counts the retirement slots used.\n            config->evt_num = 0xC2;\n            config->umask = 0x02;\n            break;\n        case MISPREDICTION_RECOVERY_CYCLES:\n            //   INT_MISC.CLEAR_RESTEER_CYCLES: Cycles the issue-stage is waiting for front-end to\n            //   fetch from resteered path following branch misprediction or machine clear events.\n            if (model == 0xBA || model == 0xB7 || model == 0xBF || model == 0x97 || model == 0x9A) {\n                config->evt_num = 0xAD;\n                config->umask = 0x80;\n            } else {\n                config->evt_num = 0x0D;\n                config->umask = 0x01;\n            }\n            break;\n        case HW_INTERRUPTS_RECEIVED:\n            //   HW_INTERRUPTS.RECEIVED: Counts the number of hardware interruptions received by the\n            //   processor.\n            config->evt_num = 0xCB;\n            config->umask = 0x01;\n            break;\n        default:\n            return -1;\n        }\n        return 0;\n    }\n\n    // AMD PMU\n    if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n        switch (pfc_name) {\n        case L1_HITS:\n            switch (family) {\n            case 0x1a:\n            case 0x19:\n                // Any Data Cache Fills by Data Source\n                config->evt_num = 0x44;\n                config->umask = 0xff;\n                break;\n            default:\n                config->evt_num = 0x43;\n                config->umask = 0xff;\n            }\n            break;\n        case UOPS_ISSUED_ANY:\n            // Dispatched ops\n            switch (family) {\n            case 0x17:\n                // there's no reliable counter of dispatched ops on this family (at least that\n                // I know of), so we use a dummy counter that always returns zero; this way,\n                // we effectively disable the speculation filter\n                config->evt_num = 0x00;\n                config->umask = 0x00;\n                break;\n            default:\n                config->evt_num = 0xAB;\n                config->umask = 0xff;\n            }\n            break;\n        case UOPS_RETIRED_ANY:\n            // Retired ops\n            config->evt_num = 0xC1;\n            config->umask = 0x00;\n            break;\n        case MISPREDICTION_RECOVERY_CYCLES:\n            // Decode redirects\n            config->evt_num = 0x91;\n            config->umask = 0x00;\n            break;\n        case SMI_INTERRUPTS_RECEIVED:\n            // SMI monitoring\n            config->evt_num = 0x2c;\n            config->umask = 0x00;\n            break;\n        default:\n            return -1;\n        }\n        return 0;\n    }\n\n    // unsupported vendor\n    return -1;\n}\n\n/// @brief  Clears the programmable performance counters and writes the\n///         configurations to the corresponding MSRs.\n/// @param  void\n/// @return 0 on success, -1 on failure\nstatic int pfc_write(unsigned int id, struct pfc_config *config, unsigned int usr, unsigned int os)\n{\n    uint64_t perf_configuration = 0;\n#if VENDOR_ID == 1\n    uint64_t global_ctrl = native_read_msr(MSR_CORE_PERF_GLOBAL_CTRL);\n    global_ctrl |= ((uint64_t)7 << 32) | 15;\n    wrmsr64(MSR_CORE_PERF_GLOBAL_CTRL, global_ctrl);\n\n    perf_configuration = native_read_msr(MSR_P6_EVNTSEL0 + id);\n\n    // disable the counter\n    perf_configuration &= ~(((uint64_t)1 << 32) - 1);\n    wrmsr64(MSR_P6_EVNTSEL0 + id, perf_configuration);\n\n    // clear\n    wrmsr64(MSR_IA32_PERFCTR0 + id, 0ULL);\n\n    perf_configuration |= ((config->cmask & 0xFF) << 24);\n    perf_configuration |= (config->inv << 23);\n    perf_configuration |= (1ULL << 22);\n    perf_configuration |= (config->any << 21);\n    perf_configuration |= (config->edge << 18);\n    perf_configuration |= (os << 17);\n    perf_configuration |= (usr << 16);\n    perf_configuration |= ((config->umask & 0xFF) << 8);\n    perf_configuration |= (config->evt_num & 0xFF);\n    wrmsr64(MSR_P6_EVNTSEL0 + id, perf_configuration);\n#elif VENDOR_ID == 2\n    perf_configuration = 0;\n    perf_configuration |= ((config->evt_num) & 0xF00) << 24;\n    perf_configuration |= (config->evt_num) & 0xFF;\n    perf_configuration |= ((config->umask) & 0xFF) << 8;\n    perf_configuration |= ((config->cmask) & 0x7F) << 24;\n    perf_configuration |= (config->inv << 23);\n    perf_configuration |= (1ULL << 22);\n    perf_configuration |= (config->edge << 18);\n    perf_configuration |= (os << 17);\n    perf_configuration |= (usr << 16);\n    wrmsr64(MSR_F15H_PERF_CTL + 2 * id, perf_configuration);\n#endif\n    return 0;\n}\n\nint pfc_configure(void)\n{\n    int err = 0;\n    struct pfc_config config = {0};\n\n    // Configure PMU\n    // #0:  Htrace collection\n    err |= get_pfc_config_by_name(L1_HITS, &config);\n    CHECK_ERR(\"pfc_configure\");\n    err |= pfc_write(0, &config, 1, 1);\n    CHECK_ERR(\"pfc_configure\");\n\n    // #1: Fuzzing feedback\n    err |= get_pfc_config_by_name(UOPS_ISSUED_ANY, &config);\n    CHECK_ERR(\"pfc_configure\");\n    err |= pfc_write(1, &config, 1, 1);\n    CHECK_ERR(\"pfc_configure\");\n\n    // #2: Fuzzing feeback\n    err |= get_pfc_config_by_name(UOPS_RETIRED_ANY, &config);\n    CHECK_ERR(\"pfc_configure\");\n    err |= pfc_write(2, &config, 1, 1);\n    CHECK_ERR(\"pfc_configure\");\n\n    // #3: Fuzzing feedback\n    err |= get_pfc_config_by_name(MISPREDICTION_RECOVERY_CYCLES, &config);\n    CHECK_ERR(\"pfc_configure\");\n    err |= pfc_write(3, &config, 1, 1);\n    CHECK_ERR(\"pfc_configure\");\n\n    // #4: Interrupt detection\n    if (cpuinfo->x86_vendor == X86_VENDOR_INTEL) {\n        err |= get_pfc_config_by_name(HW_INTERRUPTS_RECEIVED, &config);\n        CHECK_ERR(\"pfc_configure\");\n        err |= pfc_write(4, &config, 1, 1);\n        CHECK_ERR(\"pfc_configure\");\n    }\n\n    // #5: SMI monitoring\n    if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n        err |= get_pfc_config_by_name(SMI_INTERRUPTS_RECEIVED, &config);\n        CHECK_ERR(\"pfc_configure\");\n        err |= pfc_write(5, &config, 1, 1);\n        CHECK_ERR(\"pfc_configure\");\n    }\n\n    return err;\n}\n\n// =================================================================================================\nint init_perf_counters(void) { return 0; }\nvoid free_perf_counters(void) {}\n"
  },
  {
    "path": "rvzr/executor_km/x86/registers.h",
    "content": "/// File: Symbolic names for pre-allocated registers; x86-64 version\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef X86_REGISTERS_H_\n#define X86_REGISTERS_H_\n\n// Register IDs\n#define RAX_REG_ID 0x0\n#define RCX_REG_ID 0x1\n#define RDX_REG_ID 0x2\n#define RBX_REG_ID 0x3\n#define RSP_REG_ID 0x4\n#define RBP_REG_ID 0x5\n#define RSI_REG_ID 0x6\n#define RDI_REG_ID 0x7\n\n#define REX_BOUNDARY 0x8\n#define R8_REG_ID    0x8\n#define R9_REG_ID    0x9\n#define R10_REG_ID   0xa\n#define R11_REG_ID   0xb\n#define R12_REG_ID   0xc\n#define R13_REG_ID   0xd\n#define R14_REG_ID   0xe\n#define R15_REG_ID   0xf\n\n/// Reserved registers\n#define STATUS_REGISTER    \"r12\"\n#define STATUS_REGISTER_32 \"r12d\"\n#define STATUS_REGISTER_8  \"r12b\"\n\n#define HTRACE_REGISTER \"r13\"\n#define MEMORY_BASE_REG \"r14\"\n#define UTIL_BASE_REG   \"r15\"\n\n#define TMP_REG    \"r11\" // temporary register for various uses\n#define TMP_REG_ID (R11_REG_ID)\n\n/// Performance counter registers\n#define PFC0 \"r10\"\n#define PFC1 \"r9\"\n#define PFC2 \"r8\"\n\n#endif // X86_REGISTERS_H_\n"
  },
  {
    "path": "rvzr/executor_km/x86/special_registers.c",
    "content": "/// File:\n///  - Management of model-specific registers (MSRs)\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <asm/msr-index.h>\n\n#include \"fault_handler.h\"\n#include \"main.h\"\n#include \"shortcuts.h\"\n#include \"special_registers.h\"\n#include \"test_case_parser.h\"\n\nspecial_registers_t *orig_special_registers_state = NULL; // global\n\n// =================================================================================================\n// Local shortcuts to read/write special registers\n// =================================================================================================\n/// Note: we intentionally don't use the native_read/write_cr0/4 functions here for long-term\n/// stability, because their signatures may change between kernel versions\n\nstatic inline unsigned long _read_cr0(void)\n{\n    unsigned long val = 0;\n    asm volatile(\"mov %%cr0, %0\\n\" : \"=r\"(val));\n    return val;\n}\n\nstatic inline void _write_cr0(unsigned long val) { asm volatile(\"mov %0, %%cr0\\n\" : : \"r\"(val)); }\n\nstatic inline unsigned long _read_cr4(void)\n{\n    unsigned long val = 0;\n    asm volatile(\"mov %%cr4, %0\\n\" : \"=r\"(val));\n    return val;\n}\n\nstatic inline void _write_cr4(unsigned long val) { asm volatile(\"mov %0, %%cr4\\n\" : : \"r\"(val)); }\n\n// =================================================================================================\n// Private implementation of special register management\n// =================================================================================================\n\nstatic int store_orig_msr_state(void);\n\nstatic int set_msrs_for_user_actors(void)\n{\n#ifdef FORCE_SMAP_OFF\n    uint64_t cr4 = _read_cr4();\n    cr4 &= ~(X86_CR4_SMAP | X86_CR4_SMEP);\n    asm volatile(\"mov %0, %%cr4\" : : \"r\"(cr4)); // use asm to bypass checks\n#endif\n    // set default syscall entry point\n    wrmsr64(MSR_LSTAR, (uint64_t)fault_handler);\n\n    return 0;\n}\n\n/// @brief Configure MSRs to enable VMX operation\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic int set_msrs_for_vmx(void)\n{\n    uint64_t cr4 = _read_cr4();\n    uint64_t cr0 = _read_cr0();\n\n    // Ensure bits in CR0 and CR4 are valid in VMX operation:\n    // - Bit X is 1 in _FIXED0: bit X is fixed to 1 in CRx.\n    // - Bit X is 0 in _FIXED1: bit X is fixed to 0 in CRx.\n    // (source: SDM, 24.8 \"restrictions on VMX operation\")\n    cr0 &= rdmsr64(MSR_IA32_VMX_CR0_FIXED1);\n    cr0 |= rdmsr64(MSR_IA32_VMX_CR0_FIXED0);\n    cr4 &= rdmsr64(MSR_IA32_VMX_CR4_FIXED1);\n    cr4 |= rdmsr64(MSR_IA32_VMX_CR4_FIXED0);\n    _write_cr0(cr0);\n\n    // Enable VMX operation:\n    // (source: SDM, 24.7 \"Enabling and entering VMX operation\")\n    // - CR4.VMXE = 1\n    cr4 |= X86_CR4_VMXE;\n    _write_cr4(cr4);\n\n    return 0;\n}\n\n/// @brief Configure MSRs to enable SVM operation\n/// @param void\n/// @return 0 on success, -1 on failure\nstatic int set_msrs_for_svm(void)\n{\n    // Ensure SVM is not disabled in BIOS\n    uint64_t vm_cr = rdmsr64(MSR_VM_CR);\n    ASSERT((vm_cr & (1 << 4)) == 0, \"set_msrs_for_svm\");\n\n    // Enable SVM operation\n    uint64_t efer = rdmsr64(MSR_EFER);\n    if (!(efer & EFER_SVME)) {\n        efer |= EFER_SVME;\n        wrmsr64(MSR_EFER, efer);\n    }\n\n    return 0;\n}\n\nstatic int get_ssbp_patch_msr_ctrls(uint64_t *msr_id, uint64_t *msr_mask)\n{\n    if (cpu_has(cpuinfo, X86_FEATURE_MSR_SPEC_CTRL)) {\n        *msr_id = MSR_IA32_SPEC_CTRL;\n        *msr_mask = SPEC_CTRL_SSBD;\n    } else if (cpu_has(cpuinfo, X86_FEATURE_VIRT_SSBD)) {\n        *msr_id = MSR_AMD64_VIRT_SPEC_CTRL;\n        *msr_mask = SPEC_CTRL_SSBD;\n    } else if (cpu_has(cpuinfo, X86_FEATURE_LS_CFG_SSBD)) {\n        *msr_id = MSR_AMD64_LS_CFG;\n        switch (cpuinfo->x86) {\n        case 0x15:\n            *msr_mask = 1ULL << 54;\n            break;\n        case 0x16:\n            *msr_mask = 1ULL << 33;\n            break;\n        case 0x17:\n            *msr_mask = 1ULL << 10;\n            break;\n        default:\n            PRINT_ERR(\"ERROR: Unable to patch SSBD on this CPU; unexpected CPU model\\n\");\n            return -1;\n        }\n    } else {\n        PRINT_ERR(\"ERROR: Unable to patch SSBD on this CPU; no known patch\\n\");\n        return -1;\n    }\n    return 0;\n}\n\nstatic int get_prefetcher_msr_ctrls(uint64_t *msr_id, uint64_t *msr_mask)\n{\n    if (cpuinfo->x86_vendor == X86_VENDOR_INTEL) {\n        *msr_id = MSR_MISC_FEATURE_CONTROL;\n        switch (cpuinfo->x86_model) {\n        case 0x97:\n        case 0x9a:\n        case 0xba:\n        case 0xb7:\n        case 0xbf:\n            *msr_mask = 0b101111;\n            break;\n        default:\n            *msr_mask = 0b1111;\n            break;\n        }\n    } else if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n        switch (cpuinfo->x86) {\n        case 0x19:\n            *msr_id = 0xc0000108;\n            *msr_mask = 0b101111;\n            break;\n        default:\n            *msr_id = MSR_AMD64_DC_CFG;\n            *msr_mask = (1 << 13) | (1 << 15);\n            break;\n        }\n    }\n    return 0;\n}\n\nstatic int apply_msr_mask(uint64_t msr_id, uint64_t msr_mask, bool enable)\n{\n    uint64_t msr_value = rdmsr64(msr_id);\n    if (enable) {\n        msr_value |= msr_mask;\n    } else {\n        msr_value &= ~msr_mask;\n    }\n    wrmsr64(msr_id, msr_value);\n    if (rdmsr64(msr_id) != msr_value) {\n        PRINT_ERR(\"ERROR: Not able to set MSR 0x%llx\\n\", msr_id);\n        return -1;\n    }\n    return 0;\n}\n\n// =================================================================================================\n// Public interface to special register management\n// =================================================================================================\n\nint set_special_registers(void)\n{\n    int err = 0;\n    uint64_t msr_id = 0, msr_mask = 0;\n\n    err = store_orig_msr_state();\n    CHECK_ERR(\"store_orig_msr_state\");\n\n#ifndef VMBUILD\n    // Speculative Store Bypass (SSBP) patch\n    err = get_ssbp_patch_msr_ctrls(&msr_id, &msr_mask);\n    orig_special_registers_state->spec_ctrl = rdmsr64(msr_id);\n    CHECK_ERR(\"set_enable_ssbp_patch\");\n    err = apply_msr_mask(msr_id, msr_mask, enable_ssbp_patch);\n    CHECK_ERR(\"set_enable_ssbp_patch\");\n\n    // Prefetcher control\n    err = get_prefetcher_msr_ctrls(&msr_id, &msr_mask);\n    orig_special_registers_state->prefetcher_ctrl = rdmsr64(msr_id);\n    CHECK_ERR(\"set_disable_prefetchers\");\n    err = apply_msr_mask(msr_id, msr_mask, !enable_prefetchers); // the mask is\n    CHECK_ERR(\"set_disable_prefetchers\");\n#endif\n\n    // CR0\n    uint64_t cr0 = _read_cr0();\n    cr0 &= ~X86_CR0_CD; // enable caching; required for collecting traces\n    _write_cr0(cr0);\n\n    // CR4\n    uint64_t cr4 = _read_cr4();\n    cr4 |= X86_CR4_PCE; // enable performance counters\n    _write_cr4(cr4);\n\n    if (test_case->features.includes_user_actors) {\n        err = set_msrs_for_user_actors();\n        CHECK_ERR(\"set_msrs_for_user_actors\");\n    }\n\n    if (test_case->features.includes_vm_actors) {\n        if (cpuinfo->x86_vendor == X86_VENDOR_INTEL) {\n            err = set_msrs_for_vmx();\n        } else if (cpuinfo->x86_vendor == X86_VENDOR_AMD) {\n            err = set_msrs_for_svm();\n        }\n        CHECK_ERR(\"set_msrs_for_vm_actors\");\n    }\n\n    return 0;\n}\n\nstatic int store_orig_msr_state(void)\n{\n    orig_special_registers_state->cr0 = _read_cr0();\n    orig_special_registers_state->cr4 = _read_cr4();\n    orig_special_registers_state->lstar = rdmsr64(MSR_LSTAR);\n    orig_special_registers_state->efer = rdmsr64(MSR_EFER);\n    orig_special_registers_state->fs_base = rdmsr64(MSR_FS_BASE);\n    orig_special_registers_state->gs_base = rdmsr64(MSR_GS_BASE);\n\n    struct desc_ptr gdtr;\n    asm volatile(\"sgdt %0\" : \"=m\"(gdtr));\n    orig_special_registers_state->gdtr_base = gdtr.address;\n    orig_special_registers_state->gdtr_limit = gdtr.size;\n\n#if VENDOR_ID == VENDOR_AMD_ // AMD\n    orig_special_registers_state->syscfg = rdmsr64(MSR_SYSCFG);\n#endif\n    return 0;\n}\n\nvoid restore_special_registers(void)\n{\n    uint64_t msr_id = 0, msr_mask = 0;\n\n    // note: the if-zero statements are necessary because the MSR initialization might have failed\n    // midway through the process, in which case the MSR state was only partially initialized\n\n    if (orig_special_registers_state->cr0 != 0)\n        _write_cr0(orig_special_registers_state->cr0);\n\n    if (orig_special_registers_state->cr4 != 0)\n        _write_cr4(orig_special_registers_state->cr4);\n\n    if (orig_special_registers_state->efer != 0)\n        wrmsr64(MSR_EFER, orig_special_registers_state->efer);\n\n    if (orig_special_registers_state->lstar != 0)\n        wrmsr64(MSR_LSTAR, orig_special_registers_state->lstar);\n\n    if (orig_special_registers_state->spec_ctrl != 0) {\n        get_ssbp_patch_msr_ctrls(&msr_id, &msr_mask);\n        wrmsr64(msr_id, orig_special_registers_state->spec_ctrl);\n    }\n\n    if (orig_special_registers_state->prefetcher_ctrl != 0) {\n        get_prefetcher_msr_ctrls(&msr_id, &msr_mask);\n        wrmsr64(msr_id, orig_special_registers_state->prefetcher_ctrl);\n    }\n\n    if (orig_special_registers_state->fs_base != 0) {\n        wrmsr64(MSR_FS_BASE, orig_special_registers_state->fs_base);\n    }\n\n    if (orig_special_registers_state->gs_base != 0) {\n        wrmsr64(MSR_GS_BASE, orig_special_registers_state->gs_base);\n    }\n\n    if (orig_special_registers_state->gdtr_base != 0) {\n        struct desc_ptr gdtr = {.address = orig_special_registers_state->gdtr_base,\n                                .size = orig_special_registers_state->gdtr_limit};\n        asm volatile(\"lgdt %0\" : : \"m\"(gdtr));\n    }\n\n#if VENDOR_ID == VENDOR_AMD_ // AMD\n    if (orig_special_registers_state->syscfg != 0)\n        wrmsr64(MSR_SYSCFG, orig_special_registers_state->syscfg);\n#endif\n\n    memset(orig_special_registers_state, 0, sizeof(special_registers_t));\n}\n\n// =================================================================================================\nint init_special_register_manager(void)\n{\n    orig_special_registers_state = CHECKED_ZALLOC(sizeof(special_registers_t));\n    return 0;\n}\n\nvoid free_special_register_manager(void) { SAFE_FREE(orig_special_registers_state); }\n"
  },
  {
    "path": "rvzr/executor_km/x86/svm.c",
    "content": "/// File: Configuration and use of AMD SVM\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <linux/types.h>\n\n#include \"actor.h\"\n#include \"shortcuts.h\"\n\n#include \"fault_handler.h\"\n#include \"hardware_desc.h\"\n#include \"main.h\"\n#include \"page_tables_guest.h\"\n#include \"special_registers.h\"\n#include \"svm.h\"\n#include \"svm_constants.h\"\n\nbool svm_is_on = false; // global\nuint64_t *vmcb_hpas;    // global\nuint64_t *vmcb_hvas;    // global\n\nstatic struct page *host_ssa_page = NULL;\nstatic char *host_ssa_hva = NULL;\nstatic uint64_t orig_host_ssa_hpa = 0;\n\nstatic struct page *vmcb_pages = NULL;\n\nstatic void *iopm_hva = NULL;\nstatic uint64_t iopm_hpa = 0;\n\nstatic void *msrpm_hva = NULL;\nstatic uint64_t msrpm_hpa = 0;\n\nstatic int set_vmcb_guest_state(vmcb_t *vmcb_hva);\nstatic int set_vmcb_control(vmcb_t *vmcb_hva, uint64_t actor_id);\n\n// =================================================================================================\n// Helper functions\n// =================================================================================================\n#define _BITU(x) (1U << (x))\n\n/// @brief Initialize a segment\n/// See arch/x86/svm.c for original implementation\n/// @param seg The segment to initialize\ninline static void init_seg(seg_t *seg, uint16_t selector, uint64_t base, uint32_t limit,\n                            uint16_t attrib)\n{\n    seg->selector = selector;\n    seg->attrib = attrib;\n    seg->limit = limit;\n    seg->base = base;\n}\n\n/// @brief Initialize a system segment\n/// See arch/x86/svm.c for original implementation\n/// @param seg The segment to initialize\n/// @param type Segment attributes\nstatic void init_sys_seg(seg_t *seg, uint32_t type)\n{\n    seg->selector = 0;\n    seg->attrib = SVM_SELECTOR_P_MASK | type;\n    seg->limit = 0xffff;\n    seg->base = 0;\n}\n\n// =================================================================================================\n// SVM management interface\n// (functions exposed to the rest of the executor)\n// =================================================================================================\n\n/// @brief Check whether the target CPU is compatible with our implementation of SVM management\n/// @return 0 is compatible, -1 otherwise\nint svm_check_cpu_compatibility(void)\n{\n    ASSERT_MSG(cpu_has(cpuinfo, X86_FEATURE_SVM), \"svm_check_cpu_compatibility\",\n               \"SVM is not supported on this CPU\");\n\n    // Control registers\n    uint64_t cr0 = read_cr0();\n    uint64_t cr4 = __read_cr4();\n    uint64_t efer = rdmsr64(MSR_EFER);\n    ASSERT((cr0 & X86_CR0_CD) == 0, \"set_vmcb_guest_state\");\n    ASSERT((cr0 & X86_CR0_NW) == 0, \"set_vmcb_guest_state\");\n    ASSERT((cr0 & X86_CR0_PE) != 0, \"set_vmcb_guest_state\");\n    ASSERT((cr0 & X86_CR0_PG) != 0, \"set_vmcb_guest_state\");\n    ASSERT((cr4 & X86_CR4_PAE) != 0, \"set_vmcb_guest_state\");\n    ASSERT((efer & EFER_LME) != 0, \"set_vmcb_guest_state\");\n    ASSERT((efer & EFER_LMA) != 0, \"set_vmcb_guest_state\");\n\n    // SNP is not supported\n    uint64_t syscfg = rdmsr64(MSR_SYSCFG);\n    ASSERT((syscfg & _BITULL(24)) == 0, \"set_vmcb_guest_state\");\n\n    return 0;\n}\n\n/// @brief Enable SVM operation\n/// @return 0 on success, negative error code on failure\nint start_svm_operation(void)\n{\n    // Note that EFER.SVME is already set in special_registers.c\n\n    // Store the original Host State Save Area\n    orig_host_ssa_hpa = rdmsr64(MSR_VM_HSAVE_PA);\n\n    // Prepare Host State Save Area\n    memset(host_ssa_hva, 0, PAGE_SIZE);\n    wrmsr64(MSR_VM_HSAVE_PA, page_to_pfn(host_ssa_page) << PAGE_SHIFT);\n    ((uint64_t *)host_ssa_hva)[0] = 0x42;\n\n    svm_is_on = true;\n\n    return 0;\n}\n\n/// @brief Disable SVM operation\n/// Should never fail as this function can be used in exception handlers;\n/// instead, it will print warning upon error.\n/// @return void\nvoid stop_svm_operation(void)\n{\n    // Restore the original Host State Save Area\n    wrmsr64(MSR_VM_HSAVE_PA, orig_host_ssa_hpa);\n\n    svm_is_on = false;\n}\n\n/// @brief Restore the VMCB state that was active when we started\n/// @param void\n/// @return 0 on success, negative error code on failure\nint store_orig_vmcb_state(void) { return 0; }\n\n/// @brief Restore the VMCB state that was active when we started\n/// Should never fail as this function can be used in exception handlers;\n/// instead, it prints warnings upon errors.\n/// @return void\nvoid restore_orig_vmcb_state(void) {}\n\n/// @brief Configure VMCBs for all guest actors\n/// @param void\n/// @return 0 on success, negative error code on failure\nint set_vmcb_state(void)\n{\n    int err = 0;\n\n    // initialize VMCBs for all guest actors\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        // skip non-guest actors\n        actor_metadata_t *actor = &actors[actor_id];\n        if (actor->mode != MODE_GUEST)\n            continue;\n\n        struct page *vmcb_page = &vmcb_pages[actor_id];\n        vmcb_t *vmcb_hva = page_address(vmcb_page);\n        vmcb_hvas[actor_id] = (uint64_t)vmcb_hva;\n        vmcb_hpas[actor_id] = page_to_pfn(vmcb_page) << PAGE_SHIFT;\n\n        ASSERT(vmcb_hpas[actor_id] != 0, \"set_vmcb_state\");\n        ASSERT((vmcb_hpas[actor_id] & 0xFFF) == 0, \"set_vmcb_state\");\n\n        // reset VMCB\n        memset(vmcb_hva, 0, VMCB_SIZE);\n\n        // set VMCB fields\n        err = set_vmcb_guest_state(vmcb_hva);\n        CHECK_ERR(\"set_vmcb_state\");\n\n        err = set_vmcb_control(vmcb_hva, actor_id);\n        CHECK_ERR(\"set_vmcb_state\");\n    }\n\n    return 0;\n}\n\nstatic int set_vmcb_guest_state(vmcb_t *vmcb_hva)\n{\n    int err = 0;\n    vmcb_save_t *save = &vmcb_hva->save;\n    guest_memory_t *guest_v_memory = (guest_memory_t *)(GUEST_V_MEMORY_START);\n    guest_memory_t *guest_p_memory = (guest_memory_t *)(GUEST_P_MEMORY_START);\n\n    // - Control registers\n    save->cr0 = (read_cr0() | MUST_SET_BITS_CR0_SVM_GUEST) & ~MUST_CLEAR_BITS_CR0_SVM_GUEST;\n    save->cr3 = (uint64_t)&guest_p_memory->guest_page_tables.l4[0];\n    save->cr4 = (__read_cr4() | MUST_SET_BITS_CR4_SVM_GUEST) & ~MUST_CLEAR_BITS_CR4_SVM_GUEST;\n    save->efer =\n        (rdmsr64(MSR_EFER) | MUST_SET_BITS_EFER_SVM_GUEST) & ~MUST_CLEAR_BITS_EFER_SVM_GUEST;\n\n    // - Debug registers\n    save->dr7 = 0x400;\n    save->dr6 = 0;\n\n    // - GPRs\n    save->rip = (uint64_t)&guest_v_memory->code.section[0];\n    save->rsp = (uint64_t)&guest_v_memory->data.main_area[LOCAL_RSP_OFFSET];\n    save->rflags = X86_EFLAGS_FIXED;\n    save->rax = 0;\n\n    // - Segment registers (values mainly based on https://www.sandpile.org/x86/initial.htm)\n    init_seg(&save->cs, 0x10, 0, 0xffffffff, MUST_SET_BITS_CS_SVM_GUEST);\n    init_seg(&save->ss, 0x20, 0, 0xffffffff, MUST_SET_BITS_SS_SVM_GUEST);\n    init_seg(&save->ds, 0, 0, 0xffffffff, MUST_SET_BITS_DS_SVM_GUEST);\n    init_seg(&save->es, 0, 0, 0xffffffff, 0);\n    init_seg(&save->fs, 0, 0, 0xffffffff, 0);\n    init_seg(&save->gs, 0, 0, 0xffffffff, 0);\n\n    init_sys_seg(&save->ldtr, 2);\n    init_sys_seg(&save->tr, 3);\n\n    // - GDTR and IDTR (left empty for the time being; attempt to use will cause VM exit)\n    save->gdtr.base = (uint64_t)&guest_v_memory->gdt;\n    save->gdtr.limit = 0xffff;\n    save->idtr.base = 0;\n    save->idtr.limit = 0xffff;\n\n    // MSRs\n    save->dbgctl = 0;\n    save->sysenter_cs = 0x10;\n    // save->sysenter_cs = rdmsr64(MSR_IA32_SYSENTER_CS);\n    save->sysenter_esp = (uint64_t)&guest_v_memory->data.main_area[LOCAL_RSP_OFFSET];\n    // save->sysenter_esp = rdmsr64(MSR_IA32_SYSENTER_ESP);\n    save->sysenter_eip = (uint64_t)&guest_v_memory->code.section[0];\n    // save->sysenter_eip = rdmsr64(MSR_IA32_SYSENTER_EIP);\n\n    save->kernel_gs_base = rdmsr64(MSR_KERNEL_GS_BASE);\n    save->star = rdmsr64(MSR_STAR);\n    save->lstar = rdmsr64(MSR_LSTAR);\n    save->cstar = rdmsr64(MSR_CSTAR);\n    save->sfmask = rdmsr64(MSR_SYSCALL_MASK);\n\n    // Performance counters\n    save->perf_ctl0 = rdmsr64(MSR_F15H_PERF_CTL0);\n    save->perf_ctr0 = rdmsr64(MSR_F15H_PERF_CTR0);\n    save->perf_ctl1 = rdmsr64(MSR_F15H_PERF_CTL1);\n    save->perf_ctr1 = rdmsr64(MSR_F15H_PERF_CTR1);\n    save->perf_ctl2 = rdmsr64(MSR_F15H_PERF_CTL2);\n    save->perf_ctr2 = rdmsr64(MSR_F15H_PERF_CTR2);\n    save->perf_ctl3 = rdmsr64(MSR_F15H_PERF_CTL3);\n    save->perf_ctr3 = rdmsr64(MSR_F15H_PERF_CTR3);\n\n    // Privilege level\n    save->cpl = 0;\n\n    // PAT\n    uint64_t pat = 0;\n    for (int i = 0; i < 8; i++) {\n        pat |= (uint64_t)0x06 << (i * 8);\n    }\n    save->g_pat = pat;\n    return err;\n}\n\nstatic int set_vmcb_control(vmcb_t *vmcb_hva, uint64_t actor_id)\n{\n    int err = 0;\n    vmcb_control_t *ctrl = &vmcb_hva->control;\n\n    ctrl->intercept_cr |= _BITU(VMCB_INTERCEPT_CR0_READ);\n    ctrl->intercept_cr |= _BITU(VMCB_INTERCEPT_CR3_READ);\n    ctrl->intercept_cr |= _BITU(VMCB_INTERCEPT_CR4_READ);\n    ctrl->intercept_cr |= _BITU(VMCB_INTERCEPT_CR8_READ);\n    ctrl->intercept_cr |= _BITU(VMCB_INTERCEPT_CR0_WRITE);\n    ctrl->intercept_cr |= _BITU(VMCB_INTERCEPT_CR3_WRITE);\n    ctrl->intercept_cr |= _BITU(VMCB_INTERCEPT_CR4_WRITE);\n    ctrl->intercept_cr |= _BITU(VMCB_INTERCEPT_CR8_WRITE);\n\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR0_READ);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR1_READ);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR2_READ);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR3_READ);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR4_READ);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR5_READ);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR6_READ);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR7_READ);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR0_WRITE);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR1_WRITE);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR2_WRITE);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR3_WRITE);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR4_WRITE);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR5_WRITE);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR6_WRITE);\n    ctrl->intercept_dr |= _BITU(VMCB_INTERCEPT_DR7_WRITE);\n\n    ctrl->intercept_exceptions = 0XFFFFFFFF;\n\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_INTR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_NMI);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_SMI);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_INIT);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_VINTR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_SELECTIVE_CR0);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_STORE_IDTR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_STORE_GDTR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_STORE_LDTR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_STORE_TR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_LOAD_IDTR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_LOAD_GDTR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_LOAD_LDTR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_LOAD_TR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_CPUID);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_RSM);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_IRET);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_INTn);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_INVD);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_PAUSE);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_HLT);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_INVLPG);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_INVLPGA);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_IOIO_PROT);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_MSR_PROT);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_TASK_SWITCH);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_FERR_FREEZE);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_SHUTDOWN);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_VMRUN);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_VMMCALL);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_VMLOAD);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_VMSAVE);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_STGI);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_CLGI);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_SKINIT);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_ICEBP);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_WBINVD);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_MONITOR);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_MWAIT);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_MWAIT_COND);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_XSETBV);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_RDPRU);\n    ctrl->intercept |= _BITULL(VMCB_INTERCEPT_EFER_WRITE);\n    // DO NOT SET the following bits! Required for htrace collection\n    // ctrl->intercept |= _BITULL(VMCB_INTERCEPT_PUSHF);\n    // ctrl->intercept |= _BITULL(VMCB_INTERCEPT_POPF);\n    // ctrl->intercept |= _BITULL(VMCB_INTERCEPT_RDTSC);\n    // ctrl->intercept |= _BITULL(VMCB_INTERCEPT_RDPMC);\n    // ctrl->intercept |= _BITULL(VMCB_INTERCEPT_RDTSCP);\n\n    ctrl->intercept_ext |= _BITULL(VMCB_INTERCEPT_ALL_INVLPGB);\n    ctrl->intercept_ext |= _BITULL(VMCB_INTERCEPT_INVPCID);\n    ctrl->intercept_ext |= _BITULL(VMCB_INTERCEPT_MCOMMIT);\n    ctrl->intercept_ext |= _BITULL(VMCB_INTERCEPT_TLBSYNC);\n    ctrl->intercept_ext |= _BITULL(VMCB_INTERCEPT_BUS_LOCK);\n\n    ctrl->pause_filter_count = 0;\n    ctrl->pause_filter_thresh = 0;\n\n    ctrl->iopm_base_pa = iopm_hpa;\n    ASSERT(ctrl->iopm_base_pa < MAX_PHYSICAL_ADDRESS, \"set_vmcb_control\");\n\n    ctrl->msrpm_base_pa = msrpm_hpa;\n    ASSERT(ctrl->msrpm_base_pa < MAX_PHYSICAL_ADDRESS, \"set_vmcb_control\");\n\n    ctrl->tsc_offset = 0;\n\n    ctrl->asid = (uint32_t)actor_id;\n\n    ctrl->tlb_ctl = 0;\n    ctrl->int_ctl = V_INTR_MASKING_MASK;\n    ctrl->int_vector = 0;\n    ctrl->int_state = 0;\n\n    ctrl->nested_ctl |= SVM_NESTED_CTL_NP_ENABLE;\n    ctrl->nested_ctl |= _BITULL(6); // Read-only guest page tables\n\n    ctrl->nested_cr3 = (((uint64_t)ept_ptr[actor_id].paddr) << 12);\n    ASSERT(ctrl->nested_cr3 < MAX_PHYSICAL_ADDRESS, \"set_vmcb_control\");\n\n    ctrl->exit_code = 0x42;\n\n    ctrl->clean = 0;\n\n    return err;\n}\n\n/// @brief Print information about the last VM exit\n/// @param void\n/// @return 0 on success, negative error code on failure\nint print_svm_exit_info(void)\n{\n    int err = 0;\n\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        // skip non-guest actors\n        actor_metadata_t *actor = &actors[actor_id];\n        if (actor->mode != MODE_GUEST)\n            continue;\n\n        struct page *vmcb_page = &vmcb_pages[actor_id];\n        vmcb_t *vmcb_hva = page_address(vmcb_page);\n\n        uint64_t exitcode = vmcb_hva->control.exit_code;\n        uint64_t exitinfo1 = vmcb_hva->control.exit_info_1;\n        uint64_t exitinfo2 = vmcb_hva->control.exit_info_2;\n        uint64_t exitintinfo = vmcb_hva->control.exit_int_info;\n\n        // print exit information\n        printk(\n            KERN_ERR\n            \"VMCB[%d]: exitcode=0x%llx, exitinfo1=0x%llx, exitinfo2=0x%llx, exitintinfo=0x%llx\\n\",\n            actor_id, exitcode, exitinfo1, exitinfo2, exitintinfo);\n        printk(KERN_ERR \"insn_len=0x%x, insn_bytes=0x%llx\\n\", vmcb_hva->control.insn_len,\n               *(uint64_t *)(&vmcb_hva->control.insn_bytes[0]));\n    }\n\n    return err;\n}\n\n// =================================================================================================\nint init_svm(void)\n{\n    int err = 0;\n\n    // VMCBs\n    vmcb_pages = CHECKED_ALLOC_PAGES(SVM_MAX_NUM_GUESTS * VMCB_SIZE);\n    vmcb_hpas = CHECKED_ZALLOC(SVM_MAX_NUM_GUESTS * sizeof(uint64_t));\n    vmcb_hvas = CHECKED_ZALLOC(SVM_MAX_NUM_GUESTS * sizeof(uint64_t));\n\n    // host state save area\n    host_ssa_page = alloc_page(GFP_KERNEL);\n    if (!host_ssa_page)\n        return -ENOMEM;\n    host_ssa_hva = page_address(host_ssa_page);\n\n    // IOPM\n    struct page *iopm_pages = alloc_pages(GFP_KERNEL, 2);\n    if (!iopm_pages)\n        return -ENOMEM;\n    iopm_hva = page_address(iopm_pages);\n    memset(iopm_hva, 0xff, PAGE_SIZE * 4);\n    iopm_hpa = page_to_pfn(iopm_pages) << PAGE_SHIFT;\n\n    // MSRPM\n    struct page *msrpm_pages = alloc_pages(GFP_KERNEL, 1);\n    if (!msrpm_pages)\n        return -ENOMEM;\n    msrpm_hva = page_address(msrpm_pages);\n    memset(msrpm_hva, 0xff, PAGE_SIZE * 2);\n    msrpm_hpa = page_to_pfn(msrpm_pages) << PAGE_SHIFT;\n\n    return err;\n}\n\nvoid free_svm(void)\n{\n    SAFE_PAGES_FREE(vmcb_pages, SVM_MAX_NUM_GUESTS * VMCB_SIZE);\n    SAFE_FREE(vmcb_hpas);\n    SAFE_FREE(vmcb_hvas);\n\n    if (host_ssa_page) {\n        __free_page(host_ssa_page);\n        host_ssa_page = NULL;\n        host_ssa_hva = NULL;\n    }\n\n    if (iopm_hva) {\n        __free_pages(virt_to_page(iopm_hva), 2);\n        iopm_hva = NULL;\n        iopm_hpa = 0;\n    }\n\n    if (msrpm_hva) {\n        __free_pages(virt_to_page(msrpm_hva), 1);\n        msrpm_hva = NULL;\n        msrpm_hpa = 0;\n    }\n}\n"
  },
  {
    "path": "rvzr/executor_km/x86/vmx.c",
    "content": "/// File: Configuration and use of Intel VMX\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <asm/io.h>\n#include <asm/msr-index.h>\n#include <asm/processor-flags.h>\n#include <asm/tlbflush.h>\n#include <linux/types.h>\n\n#include \"actor.h\"\n#include \"shortcuts.h\"\n\n#include \"fault_handler.h\"\n#include \"main.h\"\n#include \"page_tables_guest.h\"\n#include \"special_registers.h\"\n#include \"vmx.h\"\n#include \"vmx_config.h\"\n\n// NOLINTBEGIN(readability-function-size)\n// NOLINTBEGIN(readability-function-cognitive-complexity)\n// Justification: these functions directly follow VMX implementation steps as per Intel SDM;\n// therefore, they are allowed to be complex\n\n#define CHECK_VMFAIL(src)                                                                          \\\n    ASSERT(err_inv == 0, src);                                                                     \\\n    ASSERT(err_val == 0, src);\n\nbool vmx_is_on = false;     // global\nuint64_t *vmcs_hpas = NULL; // global\n\nstatic bool orig_vmxon_state = false;\nstatic uint64_t orig_vmcs_ptr = 0;\n\nstatic void *vmxon_page_hva = NULL;\nstatic uint64_t vmxon_page_hpa = 0;\n\nstatic vmcs_t *vmcss = NULL;\n\nstatic uint64_t supported_vmcs_pin_ctrl = 0;\nstatic uint64_t supported_vmcs_primary_ctrl = 0;\nstatic uint64_t supported_vmcs_secondary_ctrl = 0;\n\nstatic int set_vmcs_guest_state(void);\nstatic int set_vmcs_host_state(void);\nstatic int set_vmcs_exec_control(int actor_id);\nstatic int set_vmcs_exit_control(void);\nstatic int set_vmcs_entry_control(void);\nstatic int make_vmcs_launched(int actor_id);\nstatic void print_vmlaunch_error_info(int err_inv, int err_val, int actor_id);\n\n// =================================================================================================\n// Error decoding\n// =================================================================================================\nstatic const char *vmx_instruction_error_to_str[] = {\n    \"Unknown error: 0\",\n    \"VMXERR_VMCALL_IN_VMX_ROOT_OPERATION\",\n    \"VMXERR_VMCLEAR_INVALID_ADDRESS\",\n    \"VMXERR_VMCLEAR_VMXON_POINTER\",\n    \"VMXERR_VMLAUNCH_NONCLEAR_VMCS\",\n    \"VMXERR_VMRESUME_NONLAUNCHED_VMCS\",\n    \"VMXERR_VMRESUME_AFTER_VMXOFF\",\n    \"VMXERR_ENTRY_INVALID_CONTROL_FIELD\",\n    \"VMXERR_ENTRY_INVALID_HOST_STATE_FIELD\",\n    \"VMXERR_VMPTRLD_INVALID_ADDRESS\",\n    \"VMXERR_VMPTRLD_VMXON_POINTER\",\n    \"VMXERR_VMPTRLD_INCORRECT_VMCS_REVISION_ID\",\n    \"VMXERR_UNSUPPORTED_VMCS_COMPONENT\",\n    \"VMXERR_VMWRITE_READ_ONLY_VMCS_COMPONENT\",\n    \"VMXERR_VMXON_IN_VMX_ROOT_OPERATION\",\n    \"VMXERR_ENTRY_INVALID_EXECUTIVE_VMCS_POINTER\",\n    \"VMXERR_ENTRY_NONLAUNCHED_EXECUTIVE_VMCS\",\n    \"VMXERR_ENTRY_EXECUTIVE_VMCS_POINTER_NOT_VMXON_POINTER\",\n    \"VMXERR_VMCALL_NONCLEAR_VMCS\",\n    \"VMXERR_VMCALL_INVALID_VM_EXIT_CONTROL_FIELDS\",\n    \"VMXERR_VMCALL_INCORRECT_MSEG_REVISION_ID\",\n    \"VMXERR_VMXOFF_UNDER_DUAL_MONITOR_TREATMENT_OF_SMIS_AND_SMM\",\n    \"VMXERR_VMCALL_INVALID_SMM_MONITOR_FEATURES\",\n    \"VMXERR_ENTRY_INVALID_VM_EXECUTION_CONTROL_FIELDS_IN_EXECUTIVE_VMCS\",\n    \"VMXERR_ENTRY_EVENTS_BLOCKED_BY_MOV_SS\",\n    \"VMXERR_INVALID_OPERAND_TO_INVEPT_INVVPID\",\n    NULL};\n\ntypedef struct {\n    uint16_t basic_exit_reason;\n    const char *str;\n} vmx_basic_exit_reason_t;\n\nstatic vmx_basic_exit_reason_t vmx_basic_exit_reason_to_str[] = {VMX_EXIT_REASONS, {0, NULL}};\n\n// =================================================================================================\n// Helper functions\n// =================================================================================================\n/// @brief Runs VMXON and indicates whether it failed\n/// @param err_inv Set if VMXOFF failed due to VMfailInvalid\n/// @param err_val Set if VMXOFF failed due to VMfailValid\nstatic inline void vmxon(uint64_t phys, uint8_t *err_inv, uint8_t *err_val)\n{\n    uint8_t inv = 0, val = 0;\n    __asm__ __volatile__(\"vmxon %[pa]; setc %[inval]; setz %[val]\\n\"\n                         : [val] \"=rm\"(val), [inval] \"=rm\"(inv)\n                         : [pa] \"m\"(phys)\n                         : \"cc\", \"memory\");\n    *err_inv = inv;\n    *err_val = val;\n}\n\n/// @brief Runs VMXOFF and indicates whether it failed\n/// @param err_inv Set if VMXOFF failed due to VMfailInvalid\n/// @param err_val Set if VMXOFF failed due to VMfailValid\nstatic inline void vmxoff(uint8_t *err_inv, uint8_t *err_val)\n{\n    uint8_t inv = 0, val = 0;\n    __asm__ __volatile__(\"vmxoff; setc %[inval]; setz %[val]\\n\"\n                         : [val] \"=rm\"(val), [inval] \"=rm\"(inv)\n                         :\n                         : \"cc\", \"memory\");\n    *err_inv = inv;\n    *err_val = val;\n}\n\nstatic inline void vmptrst(uint64_t *dest, uint8_t *err_inv, uint8_t *err_val)\n{\n    uint64_t tmp = 0;\n    uint8_t inv = 0, val = 0;\n    __asm__ __volatile__(\"vmptrst %[tmp]; setc %[inval]; setz %[val]\\n\"\n                         : [tmp] \"=m\"(tmp), [val] \"=rm\"(val), [inval] \"=rm\"(inv)\n                         :\n                         : \"cc\", \"memory\");\n    *dest = tmp;\n    *err_inv = inv;\n    *err_val = val;\n}\n\nstatic inline void vmptrld(uint64_t vmcs_hpa, uint8_t *err_inv, uint8_t *err_val)\n{\n    uint8_t inv = 0, val = 0;\n    __asm__ __volatile__(\"vmptrld %[pa]; setc %[inval]; setz %[val]\\n\"\n                         : [val] \"=rm\"(val), [inval] \"=rm\"(inv)\n                         : [pa] \"m\"(vmcs_hpa)\n                         : \"cc\", \"memory\");\n    *err_inv = inv;\n    *err_val = val;\n}\n\nstatic inline void vmclear(uint64_t vmcs_hpa, uint8_t *err_inv, uint8_t *err_val)\n{\n    uint8_t inv = 0, val = 0;\n    __asm__ __volatile__(\"vmclear %[pa]; setc %[inval]; setz %[val]\\n\"\n                         : [val] \"=rm\"(val), [inval] \"=rm\"(inv)\n                         : [pa] \"m\"(vmcs_hpa)\n                         : \"cc\", \"memory\");\n    *err_inv = inv;\n    *err_val = val;\n}\n\nstatic inline void vmread(uint64_t field, uint64_t *dest, uint8_t *err_inv, uint8_t *err_val)\n{\n    uint8_t inv = 0, val = 0;\n    uint64_t dest_local = 0;\n    __asm__ __volatile__(\"vmread %[field], %[dest]; setc %[inval]; setz %[val]\\n\"\n                         : [dest] \"=rm\"(dest_local), [val] \"=rm\"(val), [inval] \"=rm\"(inv)\n                         : [field] \"r\"(field)\n                         : \"cc\", \"memory\");\n    *err_inv = inv;\n    *err_val = val;\n    *dest = dest_local;\n}\n\nstatic inline void vmwrite(uint64_t field, uint64_t value, uint8_t *err_inv, uint8_t *err_val)\n{\n    uint8_t inv = 0, valid = 0;\n    __asm__ __volatile__(\"vmwrite %[value], %[field]; setc %[inval]; setz %[valid]\\n\"\n                         : [valid] \"=rm\"(valid), [inval] \"=rm\"(inv)\n                         : [field] \"r\"(field), [value] \"rm\"(value)\n                         : \"cc\", \"memory\");\n    *err_inv = inv;\n    *err_val = valid;\n}\n#define CHECKED_VMWRITE(field, value)                                                              \\\n    {                                                                                              \\\n        vmwrite(field, value, &err_inv, &err_val);                                                 \\\n        CHECK_VMFAIL(\"CHECKED_VMWRITE\");                                                           \\\n    }\n\nstatic int check_vmx_controls(uint32_t options, uint32_t msr)\n{\n    uint64_t msr_value = rdmsr64(msr);\n    uint32_t mask_low = msr_value & 0xFFFFFFFF; // 1 low bits indicate must-one\n    uint32_t mask_high = msr_value >> 32;       // zero high bits indicate must-zero\n\n    if ((~options & mask_low) || (options & ~mask_high)) {\n        PRINT_ERR(\"VMX MSR 0x%x: bits not supported (value 0x%x, mask l-0x%x h-0x%x)\\n\", msr,\n                  options, mask_low, mask_high);\n        return -1;\n    }\n\n    return 0;\n}\n\n#define VMWRITE_GUEST_SEGMENT(segment, selector, base, limit, ar)                                  \\\n    {                                                                                              \\\n        CHECKED_VMWRITE(GUEST_##segment##_SELECTOR, selector);                                     \\\n        CHECKED_VMWRITE(GUEST_##segment##_BASE, base);                                             \\\n        CHECKED_VMWRITE(GUEST_##segment##_LIMIT, limit);                                           \\\n        CHECKED_VMWRITE(GUEST_##segment##_AR_BYTES, ar);                                           \\\n    }\n\n// =================================================================================================\n// VMX management interface\n// (functions exposed to the rest of the executor)\n// =================================================================================================\n\n/// @brief Check whether the target CPU is compatible with our implementation of VMX management\n/// @return 0 is compatible, -1 otherwise\nint vmx_check_cpu_compatibility(void)\n{\n    uint64_t msr_value = 0;\n\n    // Check if VMX is supported\n    ASSERT_MSG(cpu_has(cpuinfo, X86_FEATURE_VMX), \"vmx_check_cpu_compatibility\",\n               \"VMX is not supported on this CPU\");\n\n    // Control registers\n    uint64_t cr0 = read_cr0();\n    uint64_t cr4 = __read_cr4();\n    uint64_t efer = rdmsr64(MSR_EFER);\n    ASSERT((cr0 & X86_CR0_PE) != 0, \"set_vmcs_guest_state\");\n    ASSERT((cr0 & X86_CR0_PG) != 0, \"set_vmcs_guest_state\");\n    ASSERT((cr4 & X86_CR4_PAE) != 0, \"set_vmcs_guest_state\");\n    ASSERT((efer & EFER_LME) != 0, \"set_vmcs_guest_state\");\n    ASSERT((efer & EFER_LMA) != 0, \"set_vmcs_guest_state\");\n\n    // True controls are usable\n    msr_value = rdmsr64(MSR_IA32_VMX_BASIC);\n    ASSERT((msr_value & VMX_BASIC_TRUE_CTLS) != 0, \"vmx_check_cpu_compatibility\");\n\n    // Pin-based controls\n    supported_vmcs_pin_ctrl = rdmsr64(MSR_IA32_VMX_TRUE_PINBASED_CTLS);\n    ASSERT((supported_vmcs_pin_ctrl & MUST_CLEAR_PIN_BASED_VM_EXEC_CONTROL) == 0,\n           \"vmx_check_cpu_compatibility\");\n\n    // Primary processor-based controls\n    supported_vmcs_primary_ctrl = rdmsr64(MSR_IA32_VMX_TRUE_PROCBASED_CTLS);\n    ASSERT((supported_vmcs_primary_ctrl & MUST_CLEAR_PRIMARY_VM_EXEC_CONTROL) == 0,\n           \"vmx_check_cpu_compatibility\");\n\n    // Secondary\n    supported_vmcs_secondary_ctrl = rdmsr64(MSR_IA32_VMX_PROCBASED_CTLS2);\n    ASSERT((supported_vmcs_secondary_ctrl & MUST_CLEAR_SECONDARY_VM_EXEC_CONTROL) == 0,\n           \"vmx_check_cpu_compatibility\");\n\n    // Exit/entry\n    msr_value = rdmsr64(MSR_IA32_VMX_TRUE_EXIT_CTLS);\n    ASSERT((msr_value & MUST_CLEAR_EXIT_CTRL) == 0, \"vmx_check_cpu_compatibility\");\n    msr_value = rdmsr64(MSR_IA32_VMX_TRUE_ENTRY_CTLS);\n    ASSERT((msr_value & MUST_CLEAR_ENTRY_CTRL) == 0, \"vmx_check_cpu_compatibility\");\n\n    return 0;\n}\n\n/// @brief Enable VMX operation and do VMXON\n/// @return 0 on success, negative error code on failure\nint start_vmx_operation(void)\n{\n    uint8_t err_inv = 0, err_val = 0;\n\n    orig_vmxon_state = ((orig_special_registers_state->cr4 & X86_CR4_VMXE) != 0);\n    unsigned long cr4 = __read_cr4();\n    unsigned long cr0 = read_cr0();\n\n    if (!orig_vmxon_state) {\n        // Note: registers are already configured in special_registers.c:set_msrs_for_vmx\n\n        // Check SDM 24.8 \"restrictions on VMX operation\" and 24.7 \"Enabling and entering VMX\"\n        ASSERT(((cr0 & rdmsr64(MSR_IA32_VMX_CR0_FIXED1)) | rdmsr64(MSR_IA32_VMX_CR0_FIXED0)) == cr0,\n               \"start_vmx_operation\");\n        ASSERT((cr4 | X86_CR4_VMXE) == cr4, \"start_vmx_operation\");\n\n        // Configure IA32_FEATURE_CONTROL MSR to allow VMXON\n        //   Bit 0: Lock bit. If clear, VMXON causes a #GP.\n        //   Bit 2: Enables VMXON outside of SMX operation. If clear, VMXON\n        //          outside of SMX causes a #GP.\n        uint64_t feature_control = rdmsr64(MSR_FEATURE_CONTROL);\n        uint64_t required = FEATURE_VMX_ENABLED_OUTSIDE_SMX | FEATURE_CTL_LOCKED;\n        if ((feature_control & required) != required)\n            wrmsr64(MSR_FEATURE_CONTROL, feature_control | required);\n\n        // Prepare VMXON region:\n        // (source: SDM, 25.11.5 VMXON Region)\n        // - Write the revision identifier into bits 30:0, and clear bit 31\n        memset(vmxon_page_hva, 0, VMXON_SIZE);\n        ((vmxon_region_t *)vmxon_page_hva)->revision_id = rdmsr64(MSR_IA32_VMX_BASIC);\n        ((vmxon_region_t *)vmxon_page_hva)->reserved_31 = 0;\n\n        // Run VMXON\n        vmxon(vmxon_page_hpa, &err_inv, &err_val);\n        CHECK_VMFAIL(\"vmx_start_operation\");\n    }\n\n    vmx_is_on = true;\n    return 0;\n}\n\n/// @brief Disable VMX operation and do VMXOFF\n/// Should never fail as this function can be used in exception handlers;\n/// instead, it will print warning upon error.\n/// @return void\nvoid stop_vmx_operation(void)\n{\n    // PRINT_ERR(\"Stopping VMX operation\\n\");\n    uint8_t err_inv = 0, err_val = 0;\n\n    // Run VMXOFF\n    if (vmx_is_on && !orig_vmxon_state) {\n        // Flush all EPT TLB entries before vmxoff to ensure no stale EPT translations remain\n        uint64_t invept_desc[2] = {0, 0};\n        asm volatile(\"invept %0, %1\" : : \"m\"(invept_desc), \"r\"(2ULL) : \"cc\", \"memory\");\n\n        vmxoff(&err_inv, &err_val);\n        orig_vmxon_state = false;\n    }\n\n    vmx_is_on = false;\n    if (err_inv || err_val)\n        PRINT_ERRS(\"stop_vmx_operation\", \"Exited with VMfailInvalid=%d, VMfailValid=%d\\n\", err_inv,\n                   err_val);\n}\n\n/// @brief Restore the VMCS state that was active when we started\n/// @param void\n/// @return 0 on success, negative error code on failure\nint store_orig_vmcs_state(void)\n{\n    if (!orig_vmxon_state)\n        return 0; // VMX was not in use when we started; nothing to store\n\n    uint8_t err_inv = 0, err_val = 0;\n    vmptrst(&orig_vmcs_ptr, &err_inv, &err_val);\n    CHECK_VMFAIL(\"store_orig_vmcs_state\");\n    return 0;\n}\n\n/// @brief Restore the VMCS state that was active when we started\n/// Should never fail as this function can be used in exception handlers;\n/// instead, it prints warnings upon errors.\n/// @return void\nvoid restore_orig_vmcs_state(void)\n{\n    uint8_t err_inv = 0, err_val = 0;\n    if (!orig_vmxon_state || orig_vmcs_ptr == 0xFFFFFFFFFFFFFFFF)\n        return;\n\n    if (!vmx_is_on) {\n        PRINT_ERR(\"ERROR: attempting to restore VMX state while VMX is not on\\n\");\n        return;\n    }\n\n    if (!orig_vmcs_ptr) {\n        PRINT_ERR(\"ERROR: attempting to restore VMX state but no state was stored\\n\");\n        return;\n    }\n\n    vmptrld(orig_vmcs_ptr, &err_inv, &err_val);\n    if (err_inv || err_val)\n        PRINT_ERRS(\"restore_orig_vmcs_state\", \"Exited with VMfailInvalid=%d, VMfailValid=%d\\n\",\n                   err_inv, err_val);\n}\n\nint set_vmcs_state(void)\n{\n    int err = 0;\n    uint8_t err_inv = 0, err_val = 0;\n\n    // if necessary, allocate additional memory for VMCSs\n    ASSERT(n_actors <= MAX_ACTORS, \"set_vmcs_state:n_actors exceeds MAX_ACTORS\");\n    static unsigned old_n_actors = 0;\n    if (n_actors > old_n_actors) {\n        SAFE_VFREE(vmcss);\n        vmcss = CHECKED_VMALLOC(n_actors * VMCS_SIZE);\n    }\n    old_n_actors = n_actors;\n\n    // initialize VMCSs for all guest actors\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        // skip non-guest actors\n        actor_metadata_t *actor = &actors[actor_id];\n        if (actor->mode != MODE_GUEST)\n            continue;\n\n        vmcs_t *vmcs_hva = &vmcss[actor_id];\n        uint64_t vmcs_hpa = vmalloc_to_phys(vmcs_hva);\n        ASSERT(vmcs_hpa != 0, \"set_vmcs_state:vmalloc_to_phys\");\n        vmcs_hpas[actor_id] = vmcs_hpa;\n\n        // initialize VMCS revision identifier\n        memset(vmcs_hva, 0, VMCS_SIZE);\n        vmcs_hva->revision_id = rdmsr64(MSR_IA32_VMX_BASIC);\n        vmcs_hva->abort_indicator = 0;\n\n        // load VMCS\n        vmclear(vmcs_hpa, &err_inv, &err_val);\n        CHECK_VMFAIL(\"set_vmcs_state:vmclear\");\n\n        vmptrld(vmcs_hpa, &err_inv, &err_val);\n        CHECK_VMFAIL(\"set_vmcs_state:vmptrld\");\n\n        // set VMCS fields\n        err = set_vmcs_guest_state();\n        CHECK_ERR(\"set_vmcs_guest_state\");\n\n        err = set_vmcs_host_state();\n        CHECK_ERR(\"set_vmcs_host_state\");\n\n        err = set_vmcs_exec_control(actor_id);\n        CHECK_ERR(\"set_vmcs_exec_control\");\n\n        err = set_vmcs_exit_control();\n        CHECK_ERR(\"set_vmcs_exit_control\");\n\n        err = set_vmcs_entry_control();\n        CHECK_ERR(\"set_vmcs_entry_control\");\n\n        err = make_vmcs_launched(actor_id);\n        CHECK_ERR(\"set_vmcs_state:make_vmcs_launched\");\n    }\n\n    return 0;\n}\n\nstatic int set_vmcs_guest_state(void)\n{\n    uint8_t err_inv = 0, err_val = 0;\n    guest_memory_t *guest_v_memory = (guest_memory_t *)(GUEST_V_MEMORY_START);\n    guest_memory_t *guest_p_memory = (guest_memory_t *)(GUEST_P_MEMORY_START);\n\n    // SDM 25.4 Guest-State Area\n    // - Control registers\n    uint64_t cr0 = (read_cr0() | MUST_SET_BITS_CR0_VMX_GUEST) & ~MUST_CLEAR_BITS_CR0_VMX_GUEST;\n    uint64_t cr4 = (__read_cr4() | MUST_SET_BITS_CR4_VMX_GUEST) & ~MUST_CLEAR_BITS_CR4_VMX_GUEST;\n    CHECKED_VMWRITE(GUEST_CR0, cr0);\n    CHECKED_VMWRITE(GUEST_CR3, (uint64_t)&guest_p_memory->guest_page_tables.l4[0]);\n    CHECKED_VMWRITE(GUEST_CR4, cr4);\n\n    // - Debug register\n    CHECKED_VMWRITE(GUEST_DR7, 0x400);\n\n    // - RSP, RIP, and RFLAGS\n    // (see also make_vmcs_launched)\n    CHECKED_VMWRITE(GUEST_RSP, (uint64_t)&guest_v_memory->data.main_area[LOCAL_RSP_OFFSET]);\n    CHECKED_VMWRITE(GUEST_RIP, (uint64_t)&guest_v_memory->vmlaunch_page[0]);\n    CHECKED_VMWRITE(GUEST_RFLAGS, (X86_EFLAGS_FIXED));\n\n    // - Segments (values mainly based on https://www.sandpile.org/x86/initial.htm)\n    VMWRITE_GUEST_SEGMENT(CS, 0x10, 0, 0xFFFF, 0xa09B);\n    VMWRITE_GUEST_SEGMENT(SS, 0x20, 0, 0xFFFF, 0xc093);\n    VMWRITE_GUEST_SEGMENT(DS, 0, 0, 0xFFFF, 0x10000); // 0xc093\n    VMWRITE_GUEST_SEGMENT(ES, 0, 0, 0xFFFF, 0x10000);\n    VMWRITE_GUEST_SEGMENT(FS, 0, 0, 0xFFFF, 0x10000);\n    VMWRITE_GUEST_SEGMENT(GS, 0, 0, 0xFFFF, 0x10000);\n    VMWRITE_GUEST_SEGMENT(LDTR, 0, 0, 0xFFFF, 0x10000); // 0xc082);\n    VMWRITE_GUEST_SEGMENT(TR, 0, 0, 0xFFFF, 0x8b);\n\n    // - GDTR and IDTR (left empty for the time being; attempt to use will cause VM exit)\n    CHECKED_VMWRITE(GUEST_GDTR_BASE, (uint64_t)&guest_v_memory->gdt);\n    CHECKED_VMWRITE(GUEST_GDTR_LIMIT, 0xFFFF);\n    CHECKED_VMWRITE(GUEST_IDTR_BASE, 0);\n    CHECKED_VMWRITE(GUEST_IDTR_LIMIT, 0xFFFF);\n\n    // - MSRs\n    CHECKED_VMWRITE(GUEST_IA32_DEBUGCTL, 0);\n    CHECKED_VMWRITE(GUEST_SYSENTER_CS, 0x10);\n    CHECKED_VMWRITE(GUEST_SYSENTER_ESP,\n                    (uint64_t)&guest_v_memory->data.main_area[LOCAL_RSP_OFFSET]);\n    CHECKED_VMWRITE(GUEST_SYSENTER_EIP, (uint64_t)&guest_v_memory->code.section[0]);\n\n    ASSERT((VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL & MUST_CLEAR_ENTRY_CTRL) != 0,\n           \"set_vmcs_guest_state\");\n    ASSERT((VM_ENTRY_LOAD_IA32_PAT & MUST_CLEAR_ENTRY_CTRL) != 0, \"set_vmcs_guest_state\");\n    ASSERT((VM_ENTRY_LOAD_IA32_EFER & MUST_CLEAR_ENTRY_CTRL) != 0, \"set_vmcs_guest_state\");\n\n    // SDM 25.4.2 Guest Non-Register State\n    CHECKED_VMWRITE(GUEST_ACTIVITY_STATE, 0);\n    CHECKED_VMWRITE(GUEST_INTERRUPTIBILITY_INFO, 0b1000); // block NMI\n    CHECKED_VMWRITE(GUEST_PENDING_DBG_EXCEPTIONS, 0);\n    CHECKED_VMWRITE(VMCS_LINK_POINTER, -1LL);\n    CHECKED_VMWRITE(VMX_PREEMPTION_TIMER_VALUE, 0xFFFF); // FIXME: make configurable\n\n    return 0;\n}\n\nstatic int set_vmcs_host_state(void)\n{\n    uint8_t err_inv = 0, err_val = 0;\n\n    // get TR, GDTR, IDTR and LDTR bases (will be necessary later, in several places)\n    uint64_t tr = 0, ldtr = 0;\n    struct desc_ptr gdtr, idtr;\n    asm volatile(\"str %[tr]\\n\"\n                 \"sgdt %[gdtr]\\n\"\n                 \"sidt %[idtr]\\n\"\n                 \"sldt %[ldtr]\\n\"\n                 : [tr] \"=r\"(tr), [gdtr] \"=m\"(gdtr), [idtr] \"=m\"(idtr), [ldtr] \"=r\"(ldtr)\n                 :\n                 : \"memory\");\n    struct ldttss_desc *tr_register = (struct ldttss_desc *)(gdtr.address + tr);\n    uint64_t tr_base = ((uint64_t)tr_register->base0 | ((tr_register->base1) << 16) |\n                        ((tr_register->base2) << 24) | ((uint64_t)tr_register->base3 << 32));\n\n    // SDM 25.5 Host-State Area\n    // - Control registers\n    CHECKED_VMWRITE(HOST_CR0, read_cr0());\n    CHECKED_VMWRITE(HOST_CR3, __read_cr3());\n    CHECKED_VMWRITE(HOST_CR4, __read_cr4());\n\n    // - RSP and RIP\n    // set later (make_vmcs_launched)\n\n    // - Segment selectors\n    CHECKED_VMWRITE(HOST_CS_SELECTOR, __KERNEL_CS);\n    CHECKED_VMWRITE(HOST_SS_SELECTOR, __KERNEL_DS);\n    CHECKED_VMWRITE(HOST_DS_SELECTOR, 0);\n    CHECKED_VMWRITE(HOST_ES_SELECTOR, 0);\n    CHECKED_VMWRITE(HOST_FS_SELECTOR, 0);\n    CHECKED_VMWRITE(HOST_GS_SELECTOR, 0);\n    CHECKED_VMWRITE(HOST_TR_SELECTOR, tr);\n\n    // - Segment bases\n    CHECKED_VMWRITE(HOST_FS_BASE, rdmsr64(MSR_FS_BASE));\n    CHECKED_VMWRITE(HOST_GS_BASE, rdmsr64(MSR_GS_BASE));\n    CHECKED_VMWRITE(HOST_TR_BASE, tr_base);\n    CHECKED_VMWRITE(HOST_GDTR_BASE, gdtr.address);\n    CHECKED_VMWRITE(HOST_IDTR_BASE, test_case_idtr.address);\n\n    // - MSRs\n    CHECKED_VMWRITE(HOST_IA32_SYSENTER_CS, rdmsr64(MSR_IA32_SYSENTER_CS));\n    CHECKED_VMWRITE(HOST_IA32_SYSENTER_ESP, rdmsr64(MSR_IA32_SYSENTER_ESP));\n    CHECKED_VMWRITE(HOST_IA32_SYSENTER_EIP, rdmsr64(MSR_IA32_SYSENTER_EIP));\n    CHECKED_VMWRITE(HOST_IA32_EFER, rdmsr64(MSR_EFER));\n\n    ASSERT((VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL & MUST_CLEAR_EXIT_CTRL) != 0, \"set_vmcs_host_state\");\n    ASSERT((VM_EXIT_LOAD_IA32_PAT & MUST_CLEAR_EXIT_CTRL) != 0, \"set_vmcs_host_state\");\n    return 0;\n}\n\nstatic int set_vmcs_exec_control(int actor_id)\n{\n    // int err = 0;\n    uint8_t err_inv = 0, err_val = 0;\n\n    // SDM 25.6.1 Pin-Based VM-Execution Controls\n    uint32_t pin_based_vm_exec_control = MUST_SET_PIN_BASED_VM_EXEC_CONTROL |\n                                         (rdmsr64(MSR_IA32_VMX_TRUE_PINBASED_CTLS) & 0xFFFFFFFFULL);\n    if (check_vmx_controls(pin_based_vm_exec_control, MSR_IA32_VMX_TRUE_PINBASED_CTLS))\n        return -1;\n    CHECKED_VMWRITE(PIN_BASED_VM_EXEC_CONTROL, pin_based_vm_exec_control);\n\n    // SDM 25.6.2 Processor-Based VM-Execution Controls\n    // - primary\n    uint32_t primary_vm_exec_control = MUST_SET_PRIMARY_VM_EXEC_CONTROL |\n                                       (rdmsr64(MSR_IA32_VMX_TRUE_PROCBASED_CTLS) & 0xFFFFFFFFULL);\n    if (check_vmx_controls(primary_vm_exec_control, MSR_IA32_VMX_TRUE_PROCBASED_CTLS))\n        return -1;\n    CHECKED_VMWRITE(CPU_BASED_VM_EXEC_CONTROL, primary_vm_exec_control);\n\n    // - secondary\n    uint32_t secondary_vm_exec_control = MUST_SET_SECONDARY_VM_EXEC_CONTROL |\n                                         (rdmsr64(MSR_IA32_VMX_PROCBASED_CTLS2) & 0xFFFFFFFFULL);\n    if (check_vmx_controls(secondary_vm_exec_control, MSR_IA32_VMX_PROCBASED_CTLS2))\n        return -1;\n    CHECKED_VMWRITE(SECONDARY_VM_EXEC_CONTROL, secondary_vm_exec_control);\n\n    // SDM 25.6.3 Exception Bitmap\n    CHECKED_VMWRITE(EXCEPTION_BITMAP, DEFAULT_EXCEPTION_BITMAP);\n\n    // SDM 25.6.4 I/O-Bitmap Addresses\n    ASSERT((CPU_BASED_USE_IO_BITMAPS & primary_vm_exec_control) == 0, \"set_vmcs_exec_control\");\n\n    // SDM 25.6.5 Time-Stamp Counter Offset and Multiplier\n    ASSERT((CPU_BASED_USE_TSC_OFFSETTING & primary_vm_exec_control) == 0, \"set_vmcs_exec_control\");\n\n    // SDM 25.6.6 Guest/Host Masks and Read Shadows for CR0 and CR4\n    uint64_t cr0 = read_cr0();\n    uint64_t cr4 = __read_cr4();\n    CHECKED_VMWRITE(CR0_GUEST_HOST_MASK, cr0);\n    CHECKED_VMWRITE(CR4_GUEST_HOST_MASK, cr4);\n    CHECKED_VMWRITE(CR0_READ_SHADOW, cr0);\n    CHECKED_VMWRITE(CR4_READ_SHADOW, cr4);\n\n    // SDM 25.6.7 CR3-Target Controls\n    CHECKED_VMWRITE(CR3_TARGET_COUNT, 0);\n\n    // SDM 25.6.8 Controls for APIC Virtualization\n    ASSERT((SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES & secondary_vm_exec_control) == 0,\n           \"set_vmcs_exec_control\");\n\n    // SDM 25.6.9 MSR-Bitmap Address\n    ASSERT((CPU_BASED_USE_MSR_BITMAPS & primary_vm_exec_control) == 0, \"set_vmcs_exec_control\");\n\n    // SDM 25.6.10 Executive-VMCS Pointer\n    // (no idea, leaving it blank for the time being)\n\n    // SDM 25.6.11 Extended-Page-Table Pointer (EPTP)\n    CHECKED_VMWRITE(EPT_POINTER, ((uint64_t *)ept_ptr)[actor_id]);\n\n    // SDM 25.6.12 Virtual-Processor Identifier (VPID)\n    ASSERT((SECONDARY_EXEC_ENABLE_VPID & secondary_vm_exec_control) == 0, \"set_vmcs_exec_control\");\n\n    // SDM 25.6.13 Controls for PAUSE-Loop Exiting\n    // not implemented\n\n    // SDM 25.6.14 VM-Functions\n    ASSERT((SECONDARY_EXEC_ENABLE_VMFUNC & secondary_vm_exec_control) == 0,\n           \"set_vmcs_exec_control\");\n\n    // SDM 25.6.15 VMCS Shadowing Bitmap Addresses\n    ASSERT((SECONDARY_EXEC_SHADOW_VMCS & secondary_vm_exec_control) == 0, \"set_vmcs_exec_control\");\n\n    // SDM 25.6.16 ENCLS-Exiting Bitmap\n    if (supported_vmcs_secondary_ctrl & SECONDARY_EXEC_ENCLS_EXITING) {\n        ASSERT((SECONDARY_EXEC_ENCLS_EXITING & secondary_vm_exec_control) != 0,\n               \"set_vmcs_exec_control\");\n        CHECKED_VMWRITE(ENCLS_EXITING_BITMAP, 0x0FFFFFFFFFFFFFFFULL);\n    }\n\n    // Misc. features (25.6.14--23) are disabled\n    return 0;\n}\n\nstatic int set_vmcs_exit_control(void)\n{\n    uint8_t err_inv = 0, err_val = 0;\n\n    uint64_t exit_ctls =\n        MUST_SET_EXIT_CTRL | (rdmsr64(MSR_IA32_VMX_TRUE_EXIT_CTLS) & 0xFFFFFFFFULL);\n    if (check_vmx_controls(exit_ctls, MSR_IA32_VMX_TRUE_EXIT_CTLS))\n        return -1;\n    CHECKED_VMWRITE(VM_EXIT_CONTROLS, exit_ctls);\n\n    // SDM 25.7.2 VM-Exit Controls for MSRs\n    CHECKED_VMWRITE(VM_EXIT_MSR_STORE_COUNT, 0);\n    CHECKED_VMWRITE(VM_EXIT_MSR_LOAD_COUNT, 0);\n    return 0;\n}\n\nstatic int set_vmcs_entry_control(void)\n{\n    uint8_t err_inv = 0, err_val = 0;\n\n    uint64_t entry_ctls =\n        MUST_SET_ENTRY_CTRL | (rdmsr64(MSR_IA32_VMX_TRUE_ENTRY_CTLS) & 0xFFFFFFFFULL);\n    if (check_vmx_controls(entry_ctls, MSR_IA32_VMX_TRUE_ENTRY_CTLS))\n        return -1;\n    CHECKED_VMWRITE(VM_ENTRY_CONTROLS, entry_ctls);\n\n    // SDM 25.8.2 VM-Entry Controls for MSRs\n    CHECKED_VMWRITE(VM_ENTRY_MSR_LOAD_COUNT, 0);\n\n    // SDM 25.8.3 VM-Entry Controls for Event Injection\n    CHECKED_VMWRITE(VM_ENTRY_INTR_INFO_FIELD, 0);\n\n    return 0;\n}\n\nstatic int make_vmcs_launched(int actor_id)\n{\n    uint8_t err_inv = 0, err_val = 0;\n\n    // 1. Load VMCS\n    uint64_t vmcs_hpa = vmcs_hpas[actor_id];\n    vmptrld(vmcs_hpa, &err_inv, &err_val);\n    CHECK_VMFAIL(\"make_vmcs_launched:vmptrld\");\n\n    // 2. Launch VM\n    //\n    // Note 1: HOST_RIP and HOST_RSP must be set in assembly to capture the correct\n    // return address and stack pointer for VM exit. After VM exit, guest may have\n    // modified any general-purpose registers, so we clobber all caller-saved regs.\n    //\n    // Note 2: If vmlaunch succeeds, it transfers control to guest and the setc/setz\n    // instructions are skipped. On VM exit, we return to label 1 with err flags = 0.\n    // If vmlaunch fails, setc/setz execute and we jump to label 1 with error flags set.\n    asm volatile(\"\"\n                 \"xor %[inval], %[inval]\\n\"\n                 \"xor %[val], %[val]\\n\"\n                 \"lea 1f(%%rip), %%rax\\n\"\n                 \"mov %[host_rip], %%rcx\\n\"\n                 \"vmwrite %%rax, %%rcx\\n\"\n                 \"mov %%rsp, %%rax\\n\"\n                 \"mov %[host_rsp], %%rcx\\n\"\n                 \"vmwrite %%rax, %%rcx\\n\"\n                 \"vmlaunch\\n\"\n                 \"setc %[inval]\\n\"\n                 \"setz %[val]\\n\"\n                 \"1:\\n\"\n                 : [val] \"+rm\"(err_val), [inval] \"+rm\"(err_inv)\n                 : [host_rip] \"i\"((uint64_t)HOST_RIP), [host_rsp] \"i\"((uint64_t)HOST_RSP)\n                 : \"cc\", \"memory\", \"rax\", \"rcx\", \"rdx\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\");\n\n    // 3. If vmlaunch failed, print error info\n    if (err_inv || err_val) {\n        print_vmlaunch_error_info(err_inv, err_val, actor_id);\n    }\n    CHECK_VMFAIL(\"make_vmcs_launched:vmlaunch\");\n\n    // 4. Check that the launch was successful (abort indicator check)\n    ASSERT(vmcss[actor_id].abort_indicator == 0, \"make_vmcs_launched:abort_indicator\");\n    uint64_t exit_reason = 0;\n    vmread(VM_EXIT_REASON, &exit_reason, &err_inv, &err_val);\n    CHECK_VMFAIL(\"make_vmcs_launched:VM_EXIT_REASON\");\n    // Expected exit reasons after initial vmlaunch: VMCALL (guest code) or timeout\n    ASSERT((exit_reason == EXIT_REASON_VMCALL || exit_reason == EXIT_REASON_PREEMPTION_TIMER),\n           \"make_vmcs_launched:unexpected exit reason\");\n\n    // 5. Finalize VMCS fields\n    guest_memory_t *guest_v_memory = (guest_memory_t *)(GUEST_V_MEMORY_START);\n    CHECKED_VMWRITE(GUEST_RIP, (uint64_t)&guest_v_memory->code.section[0]);\n    CHECKED_VMWRITE(GUEST_RSP, (uint64_t)&guest_v_memory->data.main_area[LOCAL_RSP_OFFSET]);\n    CHECKED_VMWRITE(HOST_RIP, (uint64_t)fault_handler);\n    CHECKED_VMWRITE(HOST_RSP, (uint64_t)&sandbox->data[0].main_area[LOCAL_RSP_OFFSET]);\n\n    return 0;\n}\n\nstatic void print_vmlaunch_error_info(int err_inv, int err_val, int actor_id)\n{\n    PRINT_ERR(\"vmlaunch failed: VMfailInvalid=%d, VMfailValid=%d\\n\", err_inv, err_val);\n    if (err_val) {\n        uint64_t instr_error = 0;\n        uint8_t tmp_inv = 0, tmp_val = 0;\n        vmread(VM_INSTRUCTION_ERROR, &instr_error, &tmp_inv, &tmp_val);\n        PRINT_ERR(\"VM_INSTRUCTION_ERROR: %llu\\n\", instr_error);\n        if (instr_error > 0 && instr_error < 26)\n            PRINT_ERR(\"  decoded: %s\\n\", vmx_instruction_error_to_str[instr_error]);\n    }\n    PRINT_ERR(\"VMCS abort indicator: %d\\n\", vmcss[actor_id].abort_indicator);\n}\n\nint print_vmx_exit_info(void)\n{\n    uint8_t err_inv = 0, err_val = 0;\n    uint64_t value = 0;\n\n    // Abort reasons\n    PRINT_ERR(\"VMX Abort indicators:\\n\");\n    for (int actor_id = 0; actor_id < n_actors; actor_id++) {\n        if (actors[actor_id].mode == MODE_GUEST)\n            PRINT_ERR(\"  actor 0x%x: %d\\n\", actor_id, vmcss[actor_id].abort_indicator);\n    }\n\n    // VM exit reason\n    PRINT_ERR(\"VMXC exit info:\\n\");\n    vmread(VM_EXIT_REASON, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:VM_EXIT_REASON\");\n    PRINT_ERR(\"  VM exit reason: 0x%llx\\n\", value);\n    if (value != 0) {\n        uint16_t basic_reason = value & 0xFFFF;\n        char *exit_type = NULL;\n        if (value & (1ULL << 31))\n            exit_type = \"entry\";\n        else\n            exit_type = \"exit\";\n\n        for (int i = 0; vmx_basic_exit_reason_to_str[i].str != NULL; i++) {\n            if (basic_reason == vmx_basic_exit_reason_to_str[i].basic_exit_reason) {\n                PRINT_ERR(\"    decoded: %s [%s]\\n\", vmx_basic_exit_reason_to_str[i].str, exit_type);\n                break;\n            }\n        }\n    }\n\n    vmread(EXIT_QUALIFICATION, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:EXIT_QUALIFICATION\");\n    PRINT_ERR(\"  Exit qualification: 0x%llx\\n\", value);\n\n    vmread(GUEST_LINEAR_ADDRESS, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:GUEST_LINEAR_ADDRESS\");\n    PRINT_ERR(\"  Guest linear address: 0x%llx\\n\", value);\n\n    vmread(GUEST_PHYSICAL_ADDRESS, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:GUEST_PHYSICAL_ADDRESS\");\n    PRINT_ERR(\"  Guest physical address: 0x%llx\\n\", value);\n\n    vmread(VM_EXIT_INTR_INFO, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:VM_EXIT_INTR_INFO\");\n    PRINT_ERR(\"  VM exit interrupt info: 0x%llx\\n\", value);\n\n    vmread(VM_EXIT_INTR_ERROR_CODE, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:VM_EXIT_INTR_ERROR_CODE\");\n    PRINT_ERR(\"  VM exit interrupt error code: 0x%llx\\n\", value);\n\n    vmread(IDT_VECTORING_INFO_FIELD, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:IDT_VECTORING_INFO_FIELD\");\n    PRINT_ERR(\"  IDT vectoring info field: 0x%llx\\n\", value);\n\n    vmread(IDT_VECTORING_ERROR_CODE, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:IDT_VECTORING_ERROR_CODE\");\n    PRINT_ERR(\"  IDT vectoring error code: 0x%llx\\n\", value);\n\n    vmread(VM_EXIT_INSTRUCTION_LEN, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:VM_EXIT_INSTRUCTION_LEN\");\n    PRINT_ERR(\"  VM exit instruction length: 0x%llx\\n\", value);\n\n    vmread(VMX_INSTRUCTION_INFO, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:VMX_INSTRUCTION_INFO\");\n    PRINT_ERR(\"  VM exit instruction info: 0x%llx\\n\", value);\n\n    vmread(VM_INSTRUCTION_ERROR, &value, &err_inv, &err_val);\n    CHECK_VMFAIL(\"print_vmx_exit_info:VM_INSTRUCTION_ERROR\");\n    PRINT_ERR(\"  VM exit instruction error: 0x%llx\\n\", value);\n    if (value > 0 && value < 22)\n        PRINT_ERR(\"    decoded: %s\\n\", vmx_instruction_error_to_str[value]);\n\n    return 0;\n}\n\n// =================================================================================================\nint init_vmx(void)\n{\n    int err = 0;\n\n    // check that the hw-specific region sizes match our constants\n    size_t vmxon_size = (rdmsr64(MSR_IA32_VMX_BASIC) >> 32) & 0xFFF;\n    ASSERT(vmxon_size <= VMXON_SIZE, \"init_vmx\");\n\n    // VMX host data structures\n    vmxon_page_hva = CHECKED_ZALLOC(VMXON_SIZE);\n    vmxon_page_hpa = virt_to_phys(vmxon_page_hva);\n    ASSERT((vmxon_page_hpa & 0xFFF) == 0, \"init_vmx\"); // VMXON region must be 4KB-aligned\n\n    // VMCS\n    vmcss = CHECKED_VMALLOC(VMCS_SIZE);\n    vmcs_hpas = CHECKED_ZALLOC(sizeof(uint64_t) * MAX_ACTORS);\n\n    return err;\n}\n\nvoid free_vmx(void)\n{\n    SAFE_FREE(vmxon_page_hva);\n    SAFE_VFREE(vmcss);\n    SAFE_FREE(vmcs_hpas);\n}\n\n// NOLINTEND(readability-function-cognitive-complexity)\n// NOLINTEND(readability-function-size)\n"
  },
  {
    "path": "rvzr/factory.py",
    "content": "\"\"\"\nFile: Configuration factory; constructs objects based on the configuration options.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom typing import Dict, Type, List, TYPE_CHECKING, Any, Optional, Union\n\nfrom . import data_generator, analyser, executor, fuzzer, model, elf_parser\nfrom .model_unicorn import tracer, speculator_abc, speculators_basic, \\\n    speculators_fault, speculators_vs, interpreter, model as uc_model\nfrom .model_dynamorio import model as dr_model\nfrom .postprocessing.minimizer import Minimizer\n\nfrom .arch.x86 import asm_parser as x86_asm_parser, \\\n    executor as x86_executor, fuzzer as x86_fuzzer, generator as x86_generator, \\\n    target_desc as x86_target_desc, get_spec as x86_get_spec\nfrom .arch.arm64 import asm_parser as arm64_asm_parser, \\\n    executor as arm64_executor, fuzzer as arm64_fuzzer, generator as arm64_generator, \\\n    target_desc as arm64_target_desc, get_spec as arm64_get_spec\nfrom .config import CONF, ConfigException\n\nif TYPE_CHECKING:\n    from .isa_spec import InstructionSet\n    from .target_desc import TargetDesc\n    from .code_generator import CodeGenerator\n    from .asm_parser import AsmParser\n    from .sandbox import BaseAddrTuple\n\n\nclass FactoryException(SystemExit):\n    \"\"\" Exception raised by the factory functions \"\"\"\n\n    def __init__(self, options: Dict[str, Type[Any]], key: str, conf_option_name: str) -> None:\n        super().__init__(\n            f\"ERROR: unknown value `{key}` of `{conf_option_name}` configuration option.\\n\"\n            \"  Available options are:\\n  - \" + \"\\n  - \".join(options.keys()))\n\n\n# ==================================================================================================\n# Common enumerations\n# ==================================================================================================\n_TARGET_DESC: Dict[str, Type[TargetDesc]] = {\n    \"x86-64\": x86_target_desc.X86TargetDesc,\n    \"arm64\": arm64_target_desc.ARM64TargetDesc,\n}\n\n\n# ==================================================================================================\n# Fuzzer Construction\n# ==================================================================================================\ndef get_fuzzer(instruction_set_path: str, working_directory: str, existing_test_case: str,\n               input_paths: Optional[List[str]]) -> fuzzer.Fuzzer:\n    \"\"\" Construct a fuzzer based on the configuration options in the CONF object. \"\"\"\n\n    if CONF.fuzzer == \"architectural\":\n        if CONF.instruction_set == \"x86-64\":\n            return x86_fuzzer.X86ArchitecturalFuzzer(instruction_set_path, working_directory,\n                                                     existing_test_case, input_paths)\n        if CONF.instruction_set == \"arm64\":\n            return arm64_fuzzer.ARM64ArchitecturalFuzzer(instruction_set_path, working_directory,\n                                                         existing_test_case, input_paths)\n        raise ConfigException(\"ERROR: unknown value of `instruction_set` configuration option\")\n    if CONF.fuzzer == \"archdiff\":\n        if CONF.instruction_set == \"x86-64\":\n            return x86_fuzzer.X86ArchDiffFuzzer(instruction_set_path, working_directory,\n                                                existing_test_case, input_paths)\n        if CONF.instruction_set == \"arm64\":\n            return arm64_fuzzer.ARM64ArchDiffFuzzer(instruction_set_path, working_directory,\n                                                    existing_test_case, input_paths)\n        raise ConfigException(\"ERROR: unknown value of `instruction_set` configuration option\")\n    if CONF.fuzzer == \"basic\":\n        if CONF.instruction_set == \"x86-64\":\n            return x86_fuzzer.X86Fuzzer(instruction_set_path, working_directory, existing_test_case,\n                                        input_paths)\n        if CONF.instruction_set == \"arm64\":\n            return arm64_fuzzer.ARM64Fuzzer(instruction_set_path, working_directory,\n                                            existing_test_case, input_paths)\n        raise ConfigException(\"ERROR: unknown value of `instruction_set` configuration option\")\n    raise ConfigException(\"ERROR: unknown value of `fuzzer` configuration option\")\n\n\n# ==================================================================================================\n# Executor Construction\n# ==================================================================================================\n_EXECUTORS = {\n    'x86-64-intel': x86_executor.X86IntelExecutor,\n    'x86-64-amd': x86_executor.X86AMDExecutor,\n    'arm64': arm64_executor.ARM64Executor,\n}\n\n\ndef get_executor(enable_mismatch_check_mode: bool = False) -> executor.Executor:\n    \"\"\" Construct an executor based on the configuration options in the CONF object. \"\"\"\n    key: str = CONF.executor\n    if key not in _EXECUTORS:\n        raise FactoryException(_EXECUTORS, key, \"executor\")\n    return _EXECUTORS[key](enable_mismatch_check_mode)\n\n\n# ==================================================================================================\n# Model Construction\n# ==================================================================================================\n_TRACERS: Dict[str, Type[tracer.UnicornTracer]] = {\n    \"none\": tracer.NoneTracer,\n    \"l1d\": tracer.L1DTracer,\n    \"pc\": tracer.PCTracer,\n    \"memory\": tracer.MemoryTracer,\n    \"ct\": tracer.CTTracer,\n    \"loads+stores+pc\": tracer.CTTracer,\n    \"ct-nonspecstore\": tracer.CTNonSpecStoreTracer,\n    \"arch\": tracer.ArchTracer,\n    \"tct\": tracer.TruncatedCTTracer,\n    \"tcto\": tracer.TruncatedCTWithOverflowsTracer,\n    \"ct-ni\": tracer.ActorNITracer,\n}\n\n_SPECULATORS_GENERIC: Dict[str, Type[speculator_abc.UnicornSpeculator]] = {\n    \"seq\": speculators_basic.SeqSpeculator,\n    \"no_speculation\": speculators_basic.SeqSpeculator,\n    \"bpas\": speculators_basic.StoreBpasSpeculator,\n    \"cond-bpas\": speculators_basic.X86CondBpasSpeculator,\n    \"seq-assist\": speculators_fault.SequentialAssistSpeculator,\n}\n\n_SPECULATORS_X86: Dict[str, Type[speculator_abc.UnicornSpeculator]] = {\n    **_SPECULATORS_GENERIC,\n    \"cond\": speculators_basic.X86CondSpeculator,\n    \"conditional_br_misprediction\": speculators_basic.X86CondSpeculator,\n    \"delayed-exception-handling\": speculators_fault.X86UnicornDEH,\n    \"nullinj-fault\": speculators_fault.X86UnicornNull,\n    \"nullinj-assist\": speculators_fault.X86UnicornNullAssist,\n    \"meltdown\": speculators_fault.X86Meltdown,\n    \"noncanonical\": speculators_fault.X86NonCanonicalAddress,\n    \"vspec-ops-div\": speculators_vs.VspecDIVSpeculator,\n    \"vspec-ops-memory-faults\": speculators_vs.VspecMemoryFaultsSpeculator,\n    \"vspec-ops-memory-assists\": speculators_vs.VspecMemoryAssistsSpeculator,\n    \"vspec-ops-gp\": speculators_vs.VspecGPSpeculator,\n    \"vspec-all-div\": speculators_vs.VspecAllDIVSpeculator,\n    \"vspec-all-memory-faults\": speculators_vs.VspecAllMemoryFaultsSpeculator,\n    \"vspec-all-memory-assists\": speculators_vs.VspecAllMemoryAssistsSpeculator,\n}\n\n_SPECULATORS_ARM64: Dict[str, Type[speculator_abc.UnicornSpeculator]] = {\n    **_SPECULATORS_GENERIC,\n    \"cond\": speculators_basic.ARM64CondSpeculator,\n    \"conditional_br_misprediction\": speculators_basic.ARM64CondSpeculator,\n    \"delayed-exception-handling\": speculators_fault.ARMUnicornDEH,\n}\n\n\ndef _get_exec_clause_name() -> str:\n    \"\"\" Determine the name of the execution clause based on the configuration options \"\"\"\n    if \"cond\" in CONF.contract_execution_clause and \"bpas\" in CONF.contract_execution_clause:\n        clause_name = \"cond-bpas\"\n    elif \"conditional_br_misprediction\" in CONF.contract_execution_clause and \\\n            \"nullinj-fault\" in CONF.contract_execution_clause:\n        clause_name = \"cond-nullinj-fault\"\n    elif len(CONF.contract_execution_clause) == 1:\n        clause_name = CONF.contract_execution_clause[0]\n    else:\n        raise ConfigException(\n            \"ERROR: unknown value of `contract_execution_clause` configuration option\")\n    return clause_name\n\n\ndef _get_x86_unicorn_model(bases: BaseAddrTuple, obs_clause_name: str, exec_clause_name: str,\n                           enable_mismatch_check_mode: bool) -> model.Model:\n    target_desc = _TARGET_DESC[CONF.instruction_set]()\n    tracer_cls = _TRACERS[obs_clause_name]\n    speculator_cls = _SPECULATORS_X86[exec_clause_name]\n    interpreter_cls = interpreter.X86ExtraInterpreter\n    model_ = uc_model.X86UnicornModel(bases, target_desc, speculator_cls, tracer_cls,\n                                      interpreter_cls, enable_mismatch_check_mode)\n    return model_\n\n\ndef _get_arm64_unicorn_model(bases: BaseAddrTuple, obs_clause_name: str, exec_clause_name: str,\n                             enable_mismatch_check_mode: bool) -> model.Model:\n    target_desc = _TARGET_DESC[CONF.instruction_set]()\n    tracer_cls = _TRACERS[obs_clause_name]\n    speculator_cls = _SPECULATORS_ARM64[exec_clause_name]\n    interpreter_cls = interpreter.ARMExtraInterpreter\n    model_ = uc_model.ARM64UnicornModel(bases, target_desc, speculator_cls, tracer_cls,\n                                        interpreter_cls, enable_mismatch_check_mode)\n    return model_\n\n\ndef _get_dr_model(bases: BaseAddrTuple, obs_clause_name: str, exec_clause_name: str,\n                  enable_mismatch_check_mode: bool) -> model.Model:\n    # DR backend is not implemented in python, so we have to call its API\n    # to check if the contract is supported\n    obs_clauses = dr_model.DynamoRIOModel.get_supported_obs_clauses()\n    exec_clauses = dr_model.DynamoRIOModel.get_supported_exec_clauses()\n\n    if obs_clause_name not in obs_clauses:\n        raise ConfigException(f\"ERROR: unsupported observation clause `{obs_clause_name}`.\\n\"\n                              f\"  Available options are:\\n  - \" + \"\\n  - \".join(obs_clauses))\n    if exec_clause_name not in exec_clauses:\n        raise ConfigException(f\"ERROR: unsupported execution clause `{exec_clause_name}`.\\n\"\n                              f\"  Available options are:\\n  - \" + \"\\n  - \".join(exec_clauses))\n    model_ = dr_model.DynamoRIOModel(bases, enable_mismatch_check_mode=enable_mismatch_check_mode)\n    model_.configure_clauses(obs_clause_name, exec_clause_name)\n    return model_\n\n\ndef get_model(bases: BaseAddrTuple, enable_mismatch_check_mode: bool = False) -> model.Model:\n    \"\"\" Construct a model based on the configuration options in the CONF object. \"\"\"\n    obs_clause_name = CONF.contract_observation_clause\n    exec_clause_name = _get_exec_clause_name()\n\n    if CONF.instruction_set == \"x86-64\":\n        if CONF.model_backend == \"unicorn\":\n            return _get_x86_unicorn_model(bases, obs_clause_name, exec_clause_name,\n                                          enable_mismatch_check_mode)\n        if CONF.model_backend == \"dynamorio\":\n            return _get_dr_model(bases, obs_clause_name, exec_clause_name,\n                                 enable_mismatch_check_mode)\n        if CONF.model_backend == \"dummy\":\n            return model.DummyModel(bases, enable_mismatch_check_mode)\n\n        raise ConfigException(\"ERROR: unknown value of `model_backend` configuration option\")\n\n    if CONF.instruction_set == \"arm64\":\n        if CONF.model_backend == \"unicorn\":\n            return _get_arm64_unicorn_model(bases, obs_clause_name, exec_clause_name,\n                                            enable_mismatch_check_mode)\n        if CONF.model_backend == \"dynamorio\":\n            raise ConfigException(\"ERROR: DynamoRIO backend is not supported for ARM64\")\n        if CONF.model_backend == \"dummy\":\n            return model.DummyModel(bases, enable_mismatch_check_mode)\n\n        raise ConfigException(\"ERROR: unknown value of `model_backend` configuration option\")\n\n    raise ConfigException(\"ERROR: unknown value of `instruction_set` configuration option\")\n\n\n# ==================================================================================================\n# Program Generator Construction and Related Classes\n# ==================================================================================================\n_GENERATORS: Dict[str, Type[CodeGenerator]] = {\n    \"x86-64\": x86_generator.X86Generator,\n    \"arm64\": arm64_generator.ARM64Generator,\n}\n\n_ASM_PARSERS: Dict[str, Type[AsmParser]] = {\n    'x86-64': x86_asm_parser.X86AsmParser,\n    'arm64': arm64_asm_parser.ARM64AsmParser,\n}\n\n_ELF_PARSERS: Dict[str, Type[elf_parser.ELFParser]] = {\n    'x86-64': elf_parser.ELFParser,\n    'arm64': elf_parser.ELFParser,\n}\n\n\ndef get_program_generator(seed: int, instruction_set: InstructionSet) -> CodeGenerator:\n    \"\"\"\n    Produce a ProgramGenerator object based on the configuration options in the CONF object.\n    \"\"\"\n    key: str = CONF.instruction_set\n    target_desc = _TARGET_DESC[key]()\n    elf_parser_ = _ELF_PARSERS[key](target_desc)\n    asm_parser = _ASM_PARSERS[key](instruction_set, target_desc)\n    generator = _GENERATORS[key](seed, instruction_set, target_desc, asm_parser, elf_parser_)\n    return generator\n\n\ndef get_asm_parser(instruction_set: InstructionSet) -> AsmParser:\n    \"\"\" Produce an AsmParser object based on the configuration options in the CONF object. \"\"\"\n    key: str = CONF.instruction_set\n    target_desc = _TARGET_DESC[key]()\n    asm_parser = _ASM_PARSERS[key](instruction_set, target_desc)\n    return asm_parser\n\n\ndef get_elf_parser() -> elf_parser.ELFParser:\n    \"\"\" Produce an ELFParser object based on the configuration options in the CONF object. \"\"\"\n    key: str = CONF.instruction_set\n    target_desc = _TARGET_DESC[key]()\n    elf_parser_ = _ELF_PARSERS[key](target_desc)\n    return elf_parser_\n\n\n# ==================================================================================================\n# Input Data Generator Construction\n# ==================================================================================================\n_DATA_GENERATORS: Dict[str, Type[data_generator.DataGenerator]] = {\n    'random': data_generator.DataGenerator,\n}\n\n\ndef get_data_generator(seed: int) -> data_generator.DataGenerator:\n    \"\"\" Produce an DataGenerator object based on the configuration options in the CONF object. \"\"\"\n    key: str = CONF.data_generator\n    if key not in _DATA_GENERATORS:\n        raise FactoryException(_DATA_GENERATORS, key, \"data_generator\")\n    return _DATA_GENERATORS[key](seed)\n\n\n# ==================================================================================================\n# Analyser Construction\n# ==================================================================================================\n_ANALYZERS: Dict[str, Type[analyser.Analyser]] = {\n    'bitmaps': analyser.MergedBitmapAnalyser,\n    'sets': analyser.SetAnalyser,\n    'mwu': analyser.MWUAnalyser,\n    'chi2': analyser.ChiSquaredAnalyser,\n}\n\n\ndef get_analyser() -> analyser.Analyser:\n    \"\"\" Construct an analyser based on the configuration options in the CONF object. \"\"\"\n    key: str = CONF.analyser\n    if key not in _ANALYZERS:\n        raise FactoryException(_ANALYZERS, key, \"analyser\")\n    return _ANALYZERS[key]()\n\n\n# ==================================================================================================\n# Minimizer Construction\n# ==================================================================================================\n_MINIMIZERS: Dict[str, Type[Minimizer]] = {\n    'violation': Minimizer,\n}\n\n\ndef get_minimizer(fuzzer_: fuzzer.Fuzzer, instruction_set: InstructionSet) -> Minimizer:\n    \"\"\" Construct a minimizer based on the configuration options in the CONF object. \"\"\"\n    key: str = \"violation\"  # expansion point for future; currently hardcoded\n    if key not in _MINIMIZERS:\n        raise FactoryException(_MINIMIZERS, key, \"minimizer\")\n    return _MINIMIZERS[key](fuzzer_, instruction_set)\n\n\n# ==================================================================================================\n# Spec Downloader Construction\n# ==================================================================================================\nDownloader = Union[x86_get_spec.Downloader, arm64_get_spec.Downloader]\n\n_SPEC_DOWNLOADERS: Dict[str, Type[Downloader]] = {\n    'x86-64': x86_get_spec.Downloader,\n    'arm64': arm64_get_spec.Downloader,\n}\n\n\ndef get_downloader(arch: str, extensions: List[str], out_file: str) -> Downloader:\n    \"\"\" Construct a class that downloads an ISA spec for the given architecture. \"\"\"\n    key: str = arch\n    if key not in _SPEC_DOWNLOADERS:\n        raise FactoryException(_SPEC_DOWNLOADERS, key, \"downloader\")\n    return _SPEC_DOWNLOADERS[key](extensions, out_file)\n"
  },
  {
    "path": "rvzr/fuzzer.py",
    "content": "\"\"\"\nFile: Fuzzing Orchestration\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# pylint: disable=too-many-instance-attributes\n\nfrom __future__ import annotations\n\nimport shutil\nimport os\nimport tempfile\nfrom pathlib import Path\nfrom datetime import datetime\nfrom typing import TYPE_CHECKING, Optional, List, Callable, Literal, Final\nfrom typing_extensions import assert_never\nimport numpy as np\n\nfrom . import factory\n\nfrom .traces import HTrace, CTrace, Violation, RawHTraceSample, ArrayOfSamples, CTraceEntry, \\\n    TraceBundle\nfrom .tc_components.actor import ActorMode\nfrom .tc_components.test_case_code import TestCaseProgram\nfrom .tc_components.test_case_data import InputData\nfrom .isa_spec import InstructionSet\nfrom .analyser import Analyser\nfrom .config import CONF\nfrom .stats import FuzzingStats\nfrom .logs import FuzzLogger, warning, update_logging_after_config_change\n\nif TYPE_CHECKING:\n    from .code_generator import CodeGenerator\n    from .data_generator import DataGenerator\n    from .asm_parser import AsmParser\n    from .elf_parser import ELFParser\n    from .model import Model\n    from .executor import Executor\n\nFuzzingMode = Literal[\"random\", \"template\", \"asm\"]\nRoundStage = Literal[\"fast\", \"nesting\", \"taint_mistake\", \"priming\", \"noise\", \"arch_mismatch\",\n                     \"priming_large\"]\n\nSTAT = FuzzingStats()\n\n\n# ==================================================================================================\n# Private: Round Manager\n# ==================================================================================================\nclass _RoundState:\n    \"\"\"\n    Collection of configuration options for various modules\n    in Revizor that are used in a fuzzing round, and that get updated\n    as the round progresses.\n    \"\"\"\n    executor_n_reps: int\n    \"\"\" The number of repetitions to be used by the executor \"\"\"\n\n    _start_nesting: int\n    max_nesting: int\n    model_nesting: int\n    \"\"\" Nesting level to be used by the model \"\"\"\n\n    enable_fast_contract_tracing: bool\n    \"\"\" Whether to use the fast boosting feature in the _boost_inputs function \"\"\"\n\n    enable_priming: bool\n    \"\"\" Whether to use the priming stage of the fuzzing round \"\"\"\n\n    record_stats: bool = True\n    \"\"\" Whether to record statistics in the analyser \"\"\"\n\n    update_ignore_list: bool = False\n    \"\"\" Whether to update the ignore list of the executor \"\"\"\n\n    reuse_boosts: bool = False\n    \"\"\" Whether to reuse the boosted inputs collected by the previous stage of the round \"\"\"\n\n    reuse_ctraces: bool = False\n    \"\"\" Whether to reuse contract traces collected by the previous stage of the round \"\"\"\n\n    extend_htraces: bool = False\n    \"\"\" If true, all new collected htraces will be added\n    to the existing ones instead of replacing them \"\"\"\n\n    is_initial: bool = True\n    \"\"\" Whether this is the first round of the fuzzing process \"\"\"\n\n    def __init__(self, is_speculative: bool) -> None:\n        self.executor_n_reps = CONF.executor_sample_sizes[0]\n\n        self._start_nesting = CONF.model_min_nesting if is_speculative else 1\n        self.max_nesting = CONF.model_max_nesting if is_speculative else 1\n        assert self._start_nesting <= self.max_nesting\n        self.model_nesting = self._start_nesting\n\n        self.enable_fast_contract_tracing = CONF.enable_fast_path_model\n        self.enable_priming = CONF.enable_priming\n\n\nclass _RoundManager:\n    \"\"\"\n    A helper class responsible for maintaining a consistent configuration throughout\n    a fuzzing round, as well as for dispatching the test case to the model and executor.\n    \"\"\"\n    test_case: TestCaseProgram\n    org_inputs: List[InputData]\n    boosted_inputs: List[InputData]\n\n    htraces: List[HTrace]\n    _reference_htraces: List[HTrace]\n\n    ctraces: List[CTrace]\n    _non_boosted_ctraces: List[CTrace]\n\n    violations: List[Violation]\n    arch_violations: List[Violation]\n\n    fuzzer: Final[Fuzzer]\n    conf: Final[_RoundState]\n\n    def __init__(self, fuzzer: Fuzzer, test_case: TestCaseProgram, inputs: List[InputData]) -> None:\n        self.test_case = test_case\n        self.org_inputs = inputs\n        self.boosted_inputs = []\n\n        self.htraces = []\n        self.ctraces = []\n        self.violations = []\n        self.arch_violations = []\n\n        self.fuzzer = fuzzer\n        self.conf = _RoundState(fuzzer.model.is_speculative)\n\n        self.fuzzer.model.load_test_case(self.test_case)\n        self.fuzzer.executor.load_test_case(self.test_case)\n\n    def execute_stage(self, stage: RoundStage) -> None:\n        \"\"\" Run a given stage of the fuzzing round \"\"\"\n        # pylint: disable=too-many-return-statements\n        # pylint: disable=too-many-branches\n        # NOTE: This a selector function, so the large number of returns is justified\n\n        if stage == \"fast\":\n            assert self.conf.is_initial, \"Fast path can be run only in the first round\"\n            self._normal_stage()\n            self.conf.is_initial = False  # make sure that the fast path is run only once\n            self.conf.record_stats = False  # record stats only in the fast path\n            self._reference_htraces = self.htraces  # use the fast path traces as a reference\n            return\n\n        if stage == \"nesting\":\n            if self.conf.model_nesting != self.conf.max_nesting:\n                self.conf.model_nesting = self.conf.max_nesting\n                self._normal_stage()\n\n            # after this stage, the list of boosted inputs is stable, so we can start reusing\n            # them, and we can also start ignoring non-violating inputs in the executor\n            self.conf.reuse_boosts = True\n            self.conf.update_ignore_list = True\n            return\n\n        if stage == \"taint_mistake\":\n            if self.conf.enable_fast_contract_tracing:  # applicable only after fast tracing\n\n                self.conf.enable_fast_contract_tracing = False\n                self._normal_stage()\n\n            # after `nesting` and `taint_mistake` stages, we can be confident in contract traces\n            # and can start reusing them\n            assert self.conf.model_nesting == self.conf.max_nesting, \"Invalid stage order\"\n            self.conf.reuse_ctraces = True\n            return\n\n        if stage == \"priming\":\n            if not self.conf.enable_priming:\n                return\n            self._priming_check()\n            return\n\n        if stage == \"noise\":\n            if len(CONF.executor_sample_sizes) == 1:\n                return\n            self.conf.extend_htraces = True\n\n            for sample_size in CONF.executor_sample_sizes[1:]:\n                self.fuzzer.log.sample_size_increase(sample_size)\n                self.conf.executor_n_reps = sample_size - len(self.htraces[0])\n                self._normal_stage()\n                if not self.violations:\n                    return\n            return\n\n        if stage == \"priming_large\":\n            if not self.conf.enable_priming or len(CONF.executor_sample_sizes) == 1:\n                return\n            self.conf.executor_n_reps = CONF.executor_sample_sizes[-1]\n            self._priming_check()\n            return\n\n        if stage == \"arch_mismatch\":\n            self._check_for_architectural_mismatch()\n            return\n\n    def finalize(self) -> None:\n        \"\"\" Finalize the fuzzing round \"\"\"\n        self.fuzzer.log.dbg_dump_traces(self.boosted_inputs, self.htraces, self._reference_htraces,\n                                        self.ctraces)\n\n    def _normal_stage(self) -> None:\n        \"\"\" Run a single stage of the fuzzing round \"\"\"\n        self._boost_inputs()\n        self._collect_ctraces()\n        try:\n            self._collect_htraces()\n        except IOError:\n            self.violations = []\n            return\n        if len(self.org_inputs) > 0:\n            self._check_violations()\n            if not self.violations:\n                return\n            self._update_ignore_list()\n\n    def _boost_inputs(self) -> None:\n        \"\"\"\n        Trace the test case with the original inputs, collect taints, and use them to\n        generate boosted inputs\n        \"\"\"\n        # no need to taint track if we aren't going to boost\n        if CONF.inputs_per_class == 1:\n            self._non_boosted_ctraces = \\\n                self.fuzzer.model.trace_test_case(self.org_inputs, self.conf.model_nesting)\n            self.boosted_inputs = self.org_inputs\n            return\n\n        # Normal case - boost the inputs\n        self._non_boosted_ctraces, taints = \\\n            self.fuzzer.model.trace_test_case_with_taints(self.org_inputs, self.conf.model_nesting)\n        self.boosted_inputs = self.fuzzer.data_gen.generate_boosted(self.org_inputs, taints,\n                                                                    CONF.inputs_per_class)\n\n    def _collect_ctraces(self) -> None:\n        \"\"\" Collect contract traces for the boosted inputs \"\"\"\n        # contract traces are already collected\n        if self.conf.reuse_ctraces:\n            assert len(self.ctraces) == len(self.boosted_inputs), \"No ctraces to reuse\"\n            return\n\n        # records same ctrace for all members of the same input class\n        if self.conf.enable_fast_contract_tracing:\n            self.ctraces = self._non_boosted_ctraces * CONF.inputs_per_class\n            return\n\n        # compute ctraces separately for every boosted input\n        self.ctraces = \\\n            self.fuzzer.model.trace_test_case(self.boosted_inputs, self.conf.model_nesting)\n\n    def _collect_htraces(self) -> None:\n        \"\"\" Collect hardware traces for the boosted inputs \"\"\"\n        new_htraces = self.fuzzer.executor.trace_test_case(self.boosted_inputs,\n                                                           self.conf.executor_n_reps)\n        if not self.conf.extend_htraces:\n            self.htraces = new_htraces\n            return\n\n        # Merge new htraces with the existing ones\n        assert len(self.htraces) == len(new_htraces), \"Number of htraces does not match\"\n        for i, htrace in enumerate(new_htraces):\n            self.htraces[i] = htrace.merge(self.htraces[i])\n\n    def _check_violations(self) -> None:\n        \"\"\" Check the collected traces for contract violations \"\"\"\n        assert self.ctraces and len(self.ctraces) == len(self.htraces), \\\n            f\"Invalid number of c- or htraces: {len(self.ctraces)} vs {len(self.htraces)}\"\n        self.violations = self.fuzzer.analyser.filter_violations(\n            ctraces=self.ctraces,\n            htraces=self.htraces,\n            test_case_code=self.test_case,\n            inputs=self.boosted_inputs,\n            stats_=self.conf.record_stats)\n\n    def _update_ignore_list(self) -> None:\n        \"\"\"\n        Label all non-violating inputs as ignored by executor, so that we don't trigger\n        a chain reaction of false positives when the measurement results are non-deterministic\n        \"\"\"\n        if self.conf.update_ignore_list:\n            violating_ids = [m.input_id for v in self.violations for m in v.measurements]\n            ignored_input_ids = [\n                i for i in range(len(self.boosted_inputs)) if i not in violating_ids\n            ]\n            self.fuzzer.executor.extend_ignore_list(ignored_input_ids)\n\n    def _priming_check(self) -> None:\n        \"\"\"\n        Perform a priming check, as described next.\n\n        Goal: Distinguish between violations caused by input data leaks and those caused by\n        cross-talk between inputs.\n\n        Approach: The priming check swaps the inputs that caused the violation with each other and\n        checks if the violation is still present. If the violation is still present, it is a genuine\n        violation; otherwise, it is a false positive.\n\n        Example: Given a violation caused by an input sequence (i1, i2, i1', i2'), where inputs\n        i2 and i2' produce the same contract trace but different hardware traces, hence causing a\n        violation. The violation could be caused either by the difference in the input data of\n        i2 vs i2', or it could be caused by the difference in the microarchitectural state\n        created by i1 vs i1'. The former is a genuine violation, while the latter is a false\n        positive.\n\n        To distinguish between the two, the priming check creates two new sequences:\n        (i1, i2', i1', i2') and (i1, i2, i1', i2).\n\n        If the trace produced by the first instance of i2' in the first sequence matches\n        the trace produced by i2' in the original sequence, AND\n        the trace produced by the second instance of i2 in the second sequence matches\n        the trace produced by i2 in the original sequence,\n        then the violation is genuine.\n        \"\"\"\n\n        while self.violations:\n            self.fuzzer.log.priming(len(self.violations))\n\n            violation: Violation = self.violations.pop()\n            n_reps = violation.measurements[0].htrace.sample_size()\n            measurements_to_test = [hc[0] for hc in violation.get_hw_classes()]\n\n            for current_measurement in measurements_to_test:\n                current_input_id = current_measurement.input_id\n                htrace_to_reproduce = current_measurement.htrace\n                other_measurements = [m for m in measurements_to_test if m != current_measurement]\n\n                # list of inputs that produced a different HTrace\n                input_ids_to_test: List[int] = [m.input_id for m in other_measurements]\n\n                # iterate over inputs in the violation and swap them with current_input_id\n                for input_id in input_ids_to_test:\n                    self.fuzzer.log.dbg_priming_progress(input_id, current_input_id)\n\n                    # insert the tested input into its new place\n                    primer = list(self.boosted_inputs)\n                    primer[current_input_id] = self.boosted_inputs[input_id]\n\n                    # try the new input sequence and check if the traces observed for the new input\n                    # are equivalent to the original ones\n                    htraces: List[HTrace] = self.fuzzer.executor.trace_test_case(primer, n_reps)\n                    new_htrace = htraces[current_input_id]\n\n                    # fast exit in case of a tracing error\n                    if new_htrace.is_empty() or new_htrace.is_corrupted_or_ignored():\n                        warning(\"fuzzer\", \"Tracing error during priming. \"\n                                \"Skipping this test case\")\n                        self.violations = []\n                        return\n\n                    if self.fuzzer.analyser.htraces_are_equivalent(new_htrace, htrace_to_reproduce):\n                        continue\n\n                    self.fuzzer.log.dbg_priming_fail(input_id, current_input_id,\n                                                     htrace_to_reproduce, new_htrace)\n\n                    # could not reproduce; it's a genuine violation\n                    self.violations = [violation]\n                    return\n\n            # all traces were reproduced, so it's a false positive\n            self.violations = []\n            return\n\n    def _check_for_architectural_mismatch(self) -> None:\n        \"\"\"\n        Check if the given test cases causes an architectural mismatch between the model\n        and the executor. For example, this may happen if the model incorrectly emulates the\n        execution of an instruction due to a bug in the emulator.\n        \"\"\"\n        hardware_regs: List[List[int]] = []\n        model_regs: List[List[int]] = []\n\n        self.fuzzer.arch_model.load_test_case(self.test_case)\n        self.fuzzer.arch_executor.load_test_case(self.test_case)\n\n        # This function may be called standalone (see ArchitecturalFuzzer),\n        # in which case boosted_inputs are not yet set\n        if not self.boosted_inputs:\n            self.boosted_inputs = self.org_inputs\n\n        # Collect architectural hardware traces\n        try:\n            htraces = self.fuzzer.arch_executor.trace_test_case(self.boosted_inputs, n_reps=1)\n        except IOError:\n            warning(\"fuzz\", \"Error during architectural mismatch check. Skipping this test case\")\n            self.arch_violations = []  # skip test case in case of a tracing error\n            return\n        for htrace_obj in htraces:\n            raw_traces = htrace_obj.get_raw_readings()\n            assert len(raw_traces) == 1, \"Expected only one hardware trace\"\n            raw_trace_int = [int(v) for v in raw_traces[0]]\n            hardware_regs.append(raw_trace_int)\n\n        # Collect architectural model traces\n        ctraces = self.fuzzer.arch_model.trace_test_case(self.boosted_inputs,\n                                                         CONF.model_max_nesting)\n        for ctrace in ctraces:\n            model_regs.append([v % (2**64) for v in ctrace.get_untyped()[:6]])\n\n        # Debug outputs\n        self.fuzzer.log.dbg_dump_architectural_traces(hardware_regs, model_regs)\n\n        # Check for violations\n        # Note: since we simply check the equality of traces, we don't need to invoke the analyser\n        for i, input_ in enumerate(self.boosted_inputs):\n            if model_regs[i] == hardware_regs[i]:\n                continue\n            measurement = TraceBundle(i, input_, ctraces[i], htraces[i])\n            violation = Violation([measurement], self.boosted_inputs, self.test_case)\n            violation.set_trivial_hw_classes()\n            self.arch_violations = [violation]\n            return\n        return\n\n\n# ==================================================================================================\n# Public: Fuzzer\n# ==================================================================================================\nclass Fuzzer:\n    \"\"\"\n    The main class that orchestrates the fuzzing process. It creates all necessary modules\n    and takes care of invoking them in the right order and passing the data between them.\n\n    The main interface to start fuzzing is the `start` method, which implements a multi-stage\n    algorithm to detect contract violations. The method implements the core fuzzing loop, which\n    is to generate a test case, prepare inputs, collect their traces, and check for violations.\n\n    The class also provides a set of stand-alone interfaces for generating test cases, analyzing\n    traces from files, and filtering out non-useful test cases.\n    \"\"\"\n\n    model: Model\n    executor: Executor\n    asm_parser: AsmParser\n    code_gen: CodeGenerator\n    data_gen: DataGenerator\n    analyser: Analyser\n    elf_parser: ELFParser\n\n    arch_executor: Executor\n    arch_model: Model\n    log: FuzzLogger\n\n    _isa_spec: InstructionSet\n    _existing_test_case: str\n    _work_dir: str\n    _input_paths: List[str]\n    _generation_function: Callable[[str], TestCaseProgram]\n\n    def __init__(self,\n                 instruction_set_spec: str,\n                 work_dir: str,\n                 existing_test_case: str = \"\",\n                 input_paths: Optional[List[str]] = None):\n        self._adjust_config(existing_test_case)\n\n        self._existing_test_case = existing_test_case\n        self._input_paths = input_paths if input_paths is not None else []\n        self._work_dir = work_dir\n\n        # Create all main modules\n        self.log = FuzzLogger()\n        self._isa_spec = InstructionSet(instruction_set_spec, CONF.instruction_categories)\n        self.code_gen = factory.get_program_generator(CONF.program_generator_seed, self._isa_spec)\n        self.data_gen = factory.get_data_generator(CONF.data_generator_seed)\n        self.executor = factory.get_executor()\n        self.model = factory.get_model(self.executor.read_base_addresses())\n        self.analyser = factory.get_analyser()\n        self.asm_parser = factory.get_asm_parser(self._isa_spec)\n        self.elf_parser = factory.get_elf_parser()\n\n        self.arch_executor = factory.get_executor(enable_mismatch_check_mode=True)\n        self.arch_model = factory.get_model(\n            self.arch_executor.read_base_addresses(), enable_mismatch_check_mode=True)\n\n    # ==============================================================================================\n    # Fuzzing Interface\n    def start(self, num_test_cases: int, num_inputs: int, timeout: int, nonstop: bool,\n              save_violations: bool, type_: FuzzingMode) -> bool:\n        \"\"\"\n        Start the fuzzing process with the given parameters.\n        :param num_test_cases: the number of test cases to be generated\n        :param num_inputs: the number of inputs to be generated for each test case\n        :param timeout: the maximum time (in seconds) to run the fuzzer\n        :param nonstop: whether to continue the fuzzing process after the first violation\n        :param save_violations: whether to store the violation artifacts\n        :param type_: the type of fuzzing to be performed (random, template, asm)\n        :return: True if at least one violation was detected, False otherwise\n        \"\"\"\n        # Print header\n        start_time = datetime.today()\n        self.log.start(num_test_cases, start_time)\n\n        # Find an appropriate generation function\n        self._set_generation_function(type_)\n\n        # Start the fuzzing loop\n        for i in range(num_test_cases):\n            self.log.start_round(i)\n\n            # Generate a test case\n            test_case: TestCaseProgram = self._generation_function(self._existing_test_case)\n            STAT.test_cases += 1\n\n            # Prepare inputs\n            inputs: List[InputData]\n            if self._input_paths:\n                inputs = self.data_gen.load(self._input_paths)\n            else:\n                inputs = self.data_gen.generate(num_inputs, n_actors=test_case.n_actors())\n            STAT.num_inputs += len(inputs) * CONF.inputs_per_class\n\n            # Check if the test case is useful\n            if self._filter(test_case, inputs):\n                continue\n\n            # Fuzz the test case\n            violation = self.fuzzing_round(test_case, inputs, [])\n            if violation:\n                self.log.report_violations(violation)\n                self.log.dbg_violation(violation, self.model)\n                if save_violations:\n                    self._store_violation_artifact(violation, self._work_dir)\n                STAT.violations += 1\n                if not nonstop:\n                    break\n\n            # Terminate the fuzzer if the timeout has expired\n            if timeout:\n                now = datetime.today()\n                if (now - start_time).total_seconds() > timeout:\n                    self.log.timeout()\n                    break\n\n        self.log.finish()\n        self.log.report_model_coverage(self.model)\n        return STAT.violations > 0\n\n    def fuzzing_round(self, test_case: TestCaseProgram, inputs: List[InputData],\n                      starting_ignore_list: List[int]) -> Optional[Violation]:\n        \"\"\"\n        Run a single fuzzing round: collect contract and hardware traces for the given test\n        case and inputs, and check for contract violations.\n\n        The function is typically used as a part of the fuzzing loop\n        (invoked by .start), but can be also used stand-alone by other classes.\n\n        The function implements a multi-stage approach to testing, with the first measurement being\n        fast but with a chance of false positives, and the later stages filtering out various\n        types of potential false positives. The exact number of stages depends on\n        the configuration.\n\n        :param test_case: the test case to be executed\n        :param inputs: the inputs to be tested\n        :param starting_ignore_list: a list of input IDs to be ignored by the executor\n        :return: the first detected violation or None if no violations were found\n        \"\"\"\n        # pylint: disable=too-many-return-statements\n\n        # Initialize the round manager and load the test case\n        round_manager = _RoundManager(self, test_case, inputs)\n\n        # If a list of ignored inputs is provided, set it in the executor\n        if starting_ignore_list:\n            self.executor.set_ignore_list(starting_ignore_list)\n\n        # 1. Fast path: Collect traces with minimal nesting and repetitions\n        round_manager.execute_stage(\"fast\")\n        if not round_manager.violations:\n            STAT.fast_path += 1\n            round_manager.finalize()\n            return None\n\n        # 2. Slow path: Go through potential sources of false violations in the fast path,\n        #    and check them one at a time, starting with the most likely ones\n        self.log.slow_path()\n\n        # 2.1 FP might appear because the model did not go deep enough into nested speculation.\n        #     To remove such FPs, we re-run the model tracing with max nesting. As taints depend on\n        #     contract traces, we also have to re-boost the inputs, and re-collect hardware traces\n        #     for the new inputs\n        round_manager.execute_stage(\"nesting\")\n        if not round_manager.violations:\n            STAT.fp_nesting += 1\n            round_manager.finalize()\n            return None\n\n        # 2.2 FP might appear because of imperfect tainting (e.g., due to a bug in taint tracker).\n        #     To remove such FPs, we collect contract traces for all boosted inputs, and check if\n        #     the violation is still present\n        prev_ctraces = list(round_manager.ctraces)\n        round_manager.execute_stage(\"taint_mistake\")\n        if not round_manager.violations:\n            if round_manager.ctraces != prev_ctraces:  # this should not normally happen\n                self._report_bug_tainting(round_manager)\n            STAT.fp_taint_mistakes += 1\n            round_manager.finalize()\n            return None\n\n        # 2.3 FP might appear because of interference between inputs. To remove such FPs, we\n        #     use the priming test where we swap inputs that caused the violation with each other\n        round_manager.execute_stage(\"priming\")\n        if not round_manager.violations:\n            STAT.fp_priming += 1\n            round_manager.finalize()\n            return None\n\n        # 2.4 FP might appear because we experienced noise. Retry the experiment with a larger\n        #     sample size to reduce the impact of noise\n        round_manager.execute_stage(\"noise\")\n        if not round_manager.violations:\n            STAT.fp_large_sample += 1\n            return None\n\n        # 2.5 Priming might have failed because the sample size was too small, causing\n        #     non-deterministic results. Retry the priming test with the largest sample size\n        round_manager.execute_stage(\"priming_large\")\n        if not round_manager.violations:\n            STAT.fp_priming += 1\n            round_manager.finalize()\n            return None\n\n        # 2.6 FP might appear because of a mismatch between the model and the executor.\n        # Such cases are rare, hence we check for them last.\n        # To remove such FPs, we check if the violation is caused by an architectural mismatch\n        round_manager.execute_stage(\"arch_mismatch\")\n        if round_manager.arch_violations:\n            self._report_bug_arch(round_manager)\n            round_manager.finalize()\n            return None\n\n        # Violation survived all checks. Report it\n        round_manager.finalize()\n        return round_manager.violations[0]\n\n    # ==============================================================================================\n    # Single-stage Interfaces\n    def standalone_filter(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        \"\"\" Check if the given test case should be filtered out \"\"\"\n        return self._filter(test_case, inputs)\n\n    def standalone_generate(self, program_generator_seed: int, num_test_cases: int, num_inputs: int,\n                            permit_overwrite: bool) -> None:\n        \"\"\"\n        Run a standalone test case generation and store the generated test case programs\n        and their inputs in the work directory\n        \"\"\"\n        self.log.start(0, datetime.today())\n\n        # prepare for generation\n        STAT.test_cases = num_test_cases\n        CONF.program_generator_seed = program_generator_seed\n        program_gen = factory.get_program_generator(CONF.program_generator_seed, self._isa_spec)\n        data_gen = factory.get_data_generator(CONF.data_generator_seed)\n\n        # generate test cases\n        Path(self._work_dir).mkdir(exist_ok=True)\n        for i in range(0, num_test_cases):\n            test_case_dir = self._work_dir + \"/tc\" + str(i)\n            try:\n                Path(test_case_dir).mkdir(exist_ok=permit_overwrite)\n            except FileExistsError:\n                raise FileExistsError(f\"Directory '{test_case_dir}' already exists\\n\"\n                                      \"       Use --permit-overwrite to overwrite the test case\")\n\n            program_gen.create_test_case(test_case_dir + \"/\" + \"program.asm\", True)\n            inputs = data_gen.generate(num_inputs, n_actors=1)\n            for j, input_ in enumerate(inputs):\n                input_.save(f\"{test_case_dir}/input{j}.bin\")\n\n        self.log.finish()\n\n    def standalone_analyse(self, ctrace_file: str, htrace_file: str) -> None:\n        \"\"\" Check the contract and hardware traces in the given files for contract violations \"\"\"\n        if \"dbg_violation\" in CONF.logging_modes:\n            CONF.logging_modes.remove(\"dbg_violation\")\n            update_logging_after_config_change()\n\n        self.log.start(0, datetime.today())\n        STAT.test_cases = 1\n\n        # read traces\n        ctraces: List[CTrace] = []\n        htraces: List[HTrace] = []\n\n        with open(ctrace_file, 'r') as f:\n            for line in f:\n                ctraces.append(CTrace([CTraceEntry(\"val\", int(line))]))\n        with open(htrace_file, 'r') as f:\n            for line in f:\n                sample: ArrayOfSamples = np.ndarray(1, dtype=RawHTraceSample)\n                sample[0]['trace'] = int(line)\n                htraces.append(HTrace(sample))\n\n        assert len(ctraces) == len(htraces), \\\n            \"The number of hardware traces does not match the number of contract traces\"\n\n        dummy_inputs = factory.get_data_generator(0).generate(len(ctraces), n_actors=1)\n        dummy_tc = TestCaseProgram(\"generated.asm\", 0)\n\n        # check for violations\n        analyser = factory.get_analyser()\n        violations = analyser.filter_violations(ctraces, htraces, dummy_tc, dummy_inputs, True)\n\n        # print results\n        if violations:\n            self.log.report_violations(violations[0])\n\n        self.log.finish()\n\n    # ==============================================================================================\n    # Private Methods\n    def _set_generation_function(self, type_: FuzzingMode) -> None:\n        \"\"\" Set the generation function based on the fuzzing mode \"\"\"\n        if type_ == \"random\":\n            self._generation_function = self.code_gen.create_test_case\n        elif type_ == \"template\":\n            self._generation_function = self.code_gen.create_test_case_from_template\n        elif type_ == \"asm\":\n            self._generation_function = self._asm_parser_adapter\n        else:\n            assert_never(f\"Unknown fuzzing mode: {type_}\")\n\n    @staticmethod\n    def _create_timestamped_dir(path: str) -> str:\n        timestamp = datetime.today().strftime('%y%m%d-%H%M%S')\n        violation_dir = f\"{path}/violation-{timestamp}\"\n        Path(path).mkdir(exist_ok=True)\n        Path(violation_dir).mkdir()\n        return violation_dir\n\n    def _store_violation_artifact(self, violation: Violation, path: str) -> None:\n        \"\"\"\n        Store a violation artifact into the given directory.\n\n        A violation artifact consists of:\n        - the test case that caused the violation (program.asm)\n        - the inputs that caused the violation (input_*.bin)\n        - the original configuration file (org-config.yaml)\n        - the configuration file for reproducing violation from artifact (reproduce.yaml)\n        - the configuration file for minimization (minimize.yaml)\n\n        :param violation: the violation to be stored\n        :param path: the path to the directory where the artifact should be stored;\n                    if empty, the artifact is stored in the current directory\n        \"\"\"\n        # if the path is empty, store the artifact in the current directory\n        if not path:\n            path = \".\"\n\n        # create a subdirectory for the violation artifact\n        violation_dir = self._create_timestamped_dir(path)\n\n        # store violation\n        test_case = violation.test_case_code\n        test_case.save(f\"{violation_dir}/program.asm\")\n        for i, input_ in enumerate(violation.input_sequence):\n            input_.save(f\"{violation_dir}/input_{i:04}.bin\")\n\n        # store the original configuration file\n        if CONF._config_path:\n            shutil.copy2(CONF._config_path, f\"{violation_dir}/org-config.yaml\")\n        else:\n            with open(f\"{violation_dir}/org-config.yaml\", \"w\") as f:\n                f.write(\"# Original violation used a default config, hence this file is empty\\n\")\n\n        # create patched configs for reproducing and minimizing the violation\n        shutil.copy2(f\"{violation_dir}/org-config.yaml\", f\"{violation_dir}/reproduce.yaml\")\n        with open(f\"{violation_dir}/reproduce.yaml\", \"a\") as f:\n            f.write(\"\\n# Overwrite some of the configuration options to reproduce the violation\\n\")\n            f.write(f\"data_generator_seed: {violation.input_sequence[0].seed}\\n\")\n            f.write(\"inputs_per_class: 1\\n\")\n        shutil.copy2(f\"{violation_dir}/org-config.yaml\", f\"{violation_dir}/minimize.yaml\")\n        with open(f\"{violation_dir}/minimize.yaml\", \"a\") as f:\n            f.write(\"\\n# Overwrite some of the configuration options to reproduce the violation\\n\")\n            f.write(f\"data_generator_seed: {violation.input_sequence[0].seed}\\n\")\n\n        # we're about to store stats into a file - disable colors\n        color_on = CONF.color\n        CONF.color = False\n\n        # store the violation report\n        with open(f\"{violation_dir}/report.txt\", \"w\") as f:\n            f.write(\"# Violation Report\\n\\n\")\n            f.write(f\"* Test Case ID: {STAT.test_cases - 1}\\n\")\n            f.write(f\"* Detected: {datetime.today().strftime('%d.%m.%y at %H:%M:%S')}\\n\\n\")\n            f.write(\"* Time to detection:\"\n                    f\" {(datetime.today() - self.log.start_time).total_seconds()}\\n\")\n            f.write(\"* Statistics:\\n\")\n            f.write(str(STAT) + \"\\n\")\n\n            f.write(\"\\n## Generation Properties\\n\")\n            f.write(f\"* Program seed: {test_case.generator_seed}\\n\")\n            f.write(f\"* Input seed: {violation.input_sequence[0].seed}\\n\")\n            f.write(\"* Faulty page properties:\\n\")\n            target_desc = self.code_gen._target_desc\n            for actor in test_case.get_actors(sorted_=True):\n                actor_id = actor.get_id()\n                f.write(f\"  - Actor {actor_id}:\\n\")\n\n                pte_fields = []\n                for field in target_desc.pte_bits:\n                    offset, default = target_desc.pte_bits[field]\n                    value = bool(actor.data_properties & (1 << offset))\n                    if value != default:\n                        pte_fields.append(f\"{field}={value}\")\n                f.write(f\"    * PTE: {'; '.join(pte_fields)}\\n\")\n\n                if actor.mode != ActorMode.GUEST:\n                    continue\n                vm_pte_fields = []\n                for field in target_desc.vm_pte_bits:\n                    offset, default = target_desc.vm_pte_bits[field]\n                    value = bool(actor.data_ept_properties & (1 << offset))\n                    if value != default:\n                        vm_pte_fields.append(f\"{field}={value}\")\n                f.write(f\"    * EPTE: {'; '.join(vm_pte_fields)}\\n\")\n\n            f.write(\"\\n## Counterexample Inputs\\n\")\n            for m in violation.measurements:\n                f.write(f\"\\nInput #{m.input_id}\\n\")\n                f.write(f\"* Hardware trace:\\n {m.htrace.full_str()}\\n\")\n                f.write(f\"* Contract trace (hash): {m.ctrace}\\n\")\n                f.write(f\"* Contract trace (detailed): {m.ctrace.full_str()}\\n\")\n\n        # re-enable colors if enabled previously\n        CONF.color = color_on\n\n    def _report_bug_tainting(self, round_manager: _RoundManager) -> None:\n        warning(\"fuzzer\", \"Fast path contract traces do not match the full traces\")\n        if self._work_dir and CONF.is_generation_enabled():\n            warning(\"fuzzer\", f\"Storing the bug into {self._work_dir}/bugs/\")\n\n            # note that we don't use _store_violation_artifact here because there is no actual\n            # violation - we just want to store the test case and inputs\n            violation_dir = self._create_timestamped_dir(f\"{self._work_dir}/bugs/\")\n            round_manager.test_case.save(f\"{violation_dir}/program.asm\")\n            for i, input_ in enumerate(round_manager.org_inputs):\n                input_.save(f\"{violation_dir}/input_{i:04}.bin\")\n\n    def _report_bug_arch(self, round_manager: _RoundManager) -> None:\n        warning(\"fuzzer\", \"Architectural mismatch between model and executor detected\")\n        if self._work_dir and CONF.is_generation_enabled():\n            warning(\"fuzzer\", f\"Storing the bug into {self._work_dir}/bugs/\")\n            self._store_violation_artifact(round_manager.violations[0], f\"{self._work_dir}/bugs/\")\n\n    # ----------------------------------------------------------------------------------------------\n    # Private: Subclass hooks for ISA-specific customization\n    def _filter(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        \"\"\"\n        A filter function that can be used to check if a test case is not useful.\n\n        The function is typically used as a part of the fuzzing loop\n        (invoked by self.start_* methods), but can be also used stand-alone by other classes.\n\n        :param test_case: The test case to be checked\n        :param inputs: The inputs to be used with the test case\n        :return: True if the test case should be filtered out (not useful), False otherwise (useful)\n        \"\"\"\n        return False  # implemented by architecture-specific subclasses\n\n    def _adjust_config(self, _: str) -> None:\n        \"\"\" Adjust the configuration based on the given test case \"\"\"\n\n    def _asm_parser_adapter(self, asm: str) -> TestCaseProgram:\n        # FIXME: this is a hack to fit the interface; refactor this\n        return self.asm_parser.parse_file(asm, self.code_gen, self.elf_parser)\n\n\nclass ArchitecturalFuzzer(Fuzzer):\n    \"\"\"\n    A simplified fuzzer that checks for architectural mismatches between the model and the\n    executor. This fuzzer is useful for detecting bugs in Revizor, but it cannot detect\n    contract violations.\n\n    The fuzzer piggy-backs on the check_for_architectural_mismatch function of Fuzzer\n    to check for mismatches.\n    \"\"\"\n\n    def __init__(self,\n                 instruction_set_spec: str,\n                 work_dir: str,\n                 existing_test_case: str = \"\",\n                 inputs: Optional[List[str]] = None):\n        super().__init__(instruction_set_spec, work_dir, existing_test_case, inputs)\n        warning(\"fuzzer\", \"Running in architectural mode. \"\n                \"Contract violations can't be detected!\")\n\n    def fuzzing_round(self, test_case: TestCaseProgram, inputs: List[InputData],\n                      _: List[int]) -> Optional[Violation]:\n        \"\"\"\n        Run a single fuzzing round: collect contract and hardware traces for the given test\n        case and inputs, and check for architectural mismatches.\n        \"\"\"\n        round_manager = _RoundManager(self, test_case, inputs)\n        round_manager.execute_stage(\"arch_mismatch\")\n        return round_manager.arch_violations[0] if round_manager.arch_violations else None\n\n\nclass ArchDiffFuzzer(Fuzzer):\n    \"\"\"\n    Fuzzer that compares the execution of a test case with and without speculation fences.\n    If the results differ, it reports a violation.\n\n    Used to detect architectural bugs caused by speculative execution.\n    \"\"\"\n\n    def fuzzing_round(self, test_case: TestCaseProgram, inputs: List[InputData],\n                      _: List[int]) -> Optional[Violation]:\n        # collect non-fenced traces\n        self.arch_executor.load_test_case(test_case)\n        reg_values: List[List[int]] = []\n        try:\n            htraces: List[HTrace] = self.arch_executor.trace_test_case(inputs, 1)\n        except IOError:\n            return None\n        for htrace in htraces:\n            reg_values.append(htrace.get_raw_readings()[0].tolist())\n\n        # collect fenced traces\n        with tempfile.NamedTemporaryFile(delete=False) as fenced:\n            fenced_name = fenced.name\n        fenced_test_case = self._create_fenced_test_case(test_case.asm_path(), fenced_name,\n                                                         self.asm_parser, self.code_gen,\n                                                         self.elf_parser)\n        self.arch_executor.load_test_case(fenced_test_case)\n        fenced_reg_values: List[List[int]] = []\n        try:\n            htraces = self.arch_executor.trace_test_case(inputs, 1)\n        except IOError:\n            return None\n        for htrace in htraces:\n            fenced_reg_values.append(htrace.get_raw_readings()[0].tolist())\n        os.remove(fenced_name)\n\n        for i, input_ in enumerate(inputs):\n            if fenced_reg_values[i] == reg_values[i]:\n                if \"dbg_dump_htraces\" in CONF.logging_modes:\n                    print(f\"Input #{i}\")\n                    print(f\"Fenced:       {list(fenced_reg_values[i])}\")\n                    print(f\"Non-fenced:   {list(reg_values[i])}\")\n                continue\n\n            if \"dbg_violation\" in CONF.logging_modes:\n                print(f\"Input #{i}\")\n                print(f\"Fenced:       {list(fenced_reg_values[i])}\")\n                print(f\"Non-fenced:   {list(reg_values[i])}\")\n\n            return Violation.pseudo_violation_from_inputs([input_], test_case)\n        return None\n\n    @staticmethod\n    def _create_fenced_test_case(original_asm: str, fenced_asm: str, asm_parser: AsmParser,\n                                 generator: CodeGenerator,\n                                 elf_parser: ELFParser) -> TestCaseProgram:\n        \"\"\"\n        Hook function to create a test case with speculation fences.\"\n        Must be implemented by ISA-specific subclasses that know what a fence looks like for\n        their architecture.\n        \"\"\"\n\n        raise NotImplementedError(\"This method should be implemented by the subclass\")\n"
  },
  {
    "path": "rvzr/instruction_spec.py",
    "content": "\"\"\"\nFile: Collection of classes that represent instruction specifications.\nThe specifications typically originate from a JSON ISA spec file.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom dataclasses import dataclass\nfrom enum import Enum\nfrom typing import List, Final, Tuple, Literal, Optional\n\n\nclass OT(Enum):\n    \"\"\"\n    Enumeration class representing an Operand Type (OT) of an instruction.\n    \"\"\"\n    REG = 1  # Register Operand\n    MEM = 2  # Memory Operand\n    IMM = 3  # Immediate Operand\n    LABEL = 4  # Label Operand\n    AGEN = 5  # Memory address in LEA instructions\n    FLAGS = 6  # Flags Operand\n    COND = 7  # Condition Operand\n\n    def __str__(self) -> str:\n        return str(self._name_)  # pylint: disable=no-member  # This is an intended private use\n\n\nXOT = Literal[\"f64\", \"f32\", \"f16\", \"2f16\", \"bf16\", \"int\", \"i64\", \"i32\", \"i16\", \"i8\", \"u256\", \"u128\",\n              \"u64\", \"u32\", \"u16\", \"u8\"]\n\"\"\" Extended Operand Type (XOT) provides extra information (sign, type) about the operand. \"\"\"\n\n\n@dataclass\nclass OperandSpec:\n    \"\"\"\n    Specification of an operand in an instruction.\n    Typically used in connection with an InstructionSpec.\n    \"\"\"\n\n    values: Final[Tuple[str, ...]]\n    \"\"\" List of operand values (e.g., register names, immediate values). \"\"\"\n\n    type: Final[OT]\n    \"\"\" Type of the operand (e.g., register, memory, immediate). \"\"\"\n\n    xtype: Final[Optional[XOT]]\n    \"\"\" Extended type of a SIMD register operand (e.g., packed double-precision FP is f64) \"\"\"\n\n    width: Final[int]\n    \"\"\" Width of the operand in bits, if applicable (e.g., 64 for 64-bit register). \"\"\"\n\n    src: bool\n    \"\"\" Indicates if the operand is a source; i.e., if it is read by the instruction. \"\"\"\n\n    dest: bool\n    \"\"\" Indicates if the operand is a destination; i.e., if it is written by the instruction. \"\"\"\n\n    is_signed: Final[bool]\n    \"\"\" Indicates if the operand is signed. \"\"\"\n\n    has_magic_value: Final[bool]\n    \"\"\" Indicates if the operand has a special value that requires unique handling.\n    (e.g., separate opcode when RAX is a destination)\n    \"\"\"\n\n    def __init__(self,\n                 values: List[str],\n                 type_: OT,\n                 src: bool,\n                 dest: bool,\n                 width: int = 0,\n                 is_signed: bool = True,\n                 has_magic_value: bool = False,\n                 xtype: Optional[XOT] = None):\n        self.values = tuple(values)\n        self.type = type_\n        self.src = src\n        self.dest = dest\n        self.width = width\n        self.is_signed = is_signed\n        self.has_magic_value = has_magic_value\n        self.xtype = xtype\n\n    def __str__(self) -> str:\n        return \"(\" + \", \".join(self.values) + \")\"\n\n\n@dataclass\nclass InstructionSpec:\n    \"\"\"\n    Specification of an instruction.\n    Typically originates from a JSON specification file (base.json).\n    \"\"\"\n\n    name: Final[str]\n    \"\"\" Name of the instruction. \"\"\"\n\n    category: Final[str]\n    \"\"\" Category of the instruction. Originates from the JSON specification file. \"\"\"\n\n    is_control_flow: Final[bool]\n    \"\"\" Indicates if the instruction alters control flow (e.g., jumps, calls). \"\"\"\n\n    operands: List[OperandSpec]\n    \"\"\" List of explicit operands for the instruction. \"\"\"\n\n    implicit_operands: List[OperandSpec]\n    \"\"\" List of implicit operands for the instruction. \"\"\"\n\n    has_mem_operand: bool = False\n    \"\"\" Indicates if the instruction has a memory operand. \"\"\"\n\n    has_write: bool = False\n    \"\"\" Indicates if the instruction writes to a destination operand. \"\"\"\n\n    has_magic_value: bool = False\n    \"\"\" Indicates if the instruction has a special value that requires unique handling. \"\"\"\n\n    def __init__(self, name: str, category: str, is_control_flow: bool = False):\n        self.name = name\n        self.category = category\n        self.is_control_flow = is_control_flow\n\n        self.operands = []\n        self.implicit_operands = []\n\n    def __str__(self) -> str:\n        ops = \"\"\n        for o in self.operands:\n            ops += str(o) + \" \"\n        return f\"{self.name} {ops}\"\n\n    def __hash__(self) -> int:\n        return hash(str(self))\n"
  },
  {
    "path": "rvzr/isa_spec.py",
    "content": "\"\"\"\nFile:\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nimport json\nfrom copy import deepcopy\nfrom typing import Dict, List, Optional, Any, get_args\n\nfrom .instruction_spec import OT, XOT, OperandSpec, InstructionSpec\nfrom .config import CONF\nfrom .logs import ISALogger\n\n_OT_STR_TO_ENUM = {\n    \"REG\": OT.REG,\n    \"MEM\": OT.MEM,\n    \"IMM\": OT.IMM,\n    \"LABEL\": OT.LABEL,\n    \"AGEN\": OT.AGEN,\n    \"FLAGS\": OT.FLAGS,\n    \"COND\": OT.COND,\n}\n\n_FP_XOT = [\"f64\", \"f32\", \"f16\", \"2f16\"]\n_BFP_XOT = [\"bf16\"]\n\n\nclass InstructionSet:\n    \"\"\"\n    Class representing an instruction set of a given architecture.\n    Contains a list of InstructionSpec objects as well as type-based lists of instructions.\n    \"\"\"\n\n    instructions: List[InstructionSpec]\n    instructions_unfiltered: List[InstructionSpec]\n    logger: ISALogger\n\n    has_unconditional_branch: bool = False\n    has_conditional_branch: bool = False\n    has_indirect_branch: bool = False\n    has_reads: bool = False\n    has_writes: bool = False\n\n    control_flow_specs: List[InstructionSpec]\n    non_control_flow_specs: List[InstructionSpec]\n    non_memory_access_specs: List[InstructionSpec]\n    load_instruction: List[InstructionSpec]\n    store_instructions: List[InstructionSpec]\n    cond_branches: List[InstructionSpec]\n\n    def __init__(self, filename: str, include_categories: Optional[List[str]] = None):\n        self.instructions = []\n        _read_json_spec(self, filename)\n        self.instructions_unfiltered = deepcopy(self.instructions)\n        _reduce(self, include_categories)\n        _set_isa_properties(self)\n        _dedup(self)\n        _set_categories(self)\n\n    def get_return_spec(self) -> InstructionSpec:\n        \"\"\" Return the instruction spec for the RET instruction on the given architecture \"\"\"\n        if CONF.instruction_set == \"x86-64\":\n            return InstructionSpec(\"ret\", \"BASE-RET\", is_control_flow=True)\n        if CONF.instruction_set == \"arm64\":\n            return InstructionSpec(\"ret\", \"general-ret\", is_control_flow=True)\n        raise NotImplementedError(f\"Unsupported instruction set: {CONF.instruction_set}\")\n\n    def get_unconditional_jump_spec(self) -> InstructionSpec:\n        \"\"\"\n        Return the instruction spec for the unconditional jump instruction\n        on the given architecture\n        \"\"\"\n        if CONF.instruction_set == \"x86-64\":\n            spec = InstructionSpec(\"jmp\", \"BASE-UNCOND_BR\", is_control_flow=True)\n            spec.operands.append(OperandSpec([], OT.LABEL, src=True, dest=False, width=64))\n            return spec\n        if CONF.instruction_set == \"arm64\":\n            spec = InstructionSpec(\"b\", \"general-uncond_branch\", is_control_flow=True)\n            spec.operands.append(OperandSpec([], OT.LABEL, src=True, dest=False, width=64))\n            return spec\n        raise NotImplementedError(f\"Unsupported instruction set: {CONF.instruction_set}\")\n\n\n# ==================================================================================================\n# Local service functions that post-process the instruction set\n# ==================================================================================================\ndef _read_json_spec(isa: InstructionSet, filename: str) -> None:\n    with open(filename, \"r\") as f:\n        root = json.load(f)\n    for instruction_node in root:\n        instruction = InstructionSpec(instruction_node[\"name\"], instruction_node[\"category\"],\n                                      instruction_node[\"is_control_flow\"])\n\n        for op_node in instruction_node[\"operands\"]:\n            op = _parse_json_operand(op_node, instruction)\n            instruction.operands.append(op)\n            if op.has_magic_value:\n                instruction.has_magic_value = True\n\n        for op_node in instruction_node[\"implicit_operands\"]:\n            op = _parse_json_operand(op_node, instruction)\n            instruction.implicit_operands.append(op)\n\n        isa.instructions.append(instruction)\n\n\ndef _parse_json_operand(op: Dict[str, Any], parent: InstructionSpec) -> OperandSpec:\n    op_type = _OT_STR_TO_ENUM[op[\"type_\"]]\n    op_values = op.get(\"values\", [])\n    if op_type == OT.REG:\n        op_values = sorted(op_values)\n\n    spec = OperandSpec(\n        values=op_values,\n        type_=op_type,\n        src=op[\"src\"],\n        dest=op[\"dest\"],\n        width=op[\"width\"],\n        is_signed=op.get(\"is_signed\", True),\n        xtype=op.get(\"xtype\", None),\n    )\n\n    if op_type == OT.MEM:\n        parent.has_mem_operand = True\n        if spec.dest:\n            parent.has_write = True\n\n    return spec\n\n\ndef _reduce(isa: InstructionSet, include_categories: Optional[List[str]]) -> None:\n    \"\"\" Remove unsupported instructions and operand values \"\"\"\n\n    def is_supported(spec: InstructionSpec) -> bool:\n        # pylint: disable=too-many-return-statements\n        # This is justified as it is a filtering function\n\n        if not CONF.is_generation_enabled():\n            # if we use an existing test case, then instruction filtering is irrelevant\n            return True\n\n        # allowlist has priority over blocklist\n        if spec.name in CONF.instruction_allowlist:\n            return True\n\n        if include_categories and spec.category not in include_categories:\n            logger.dbg_dump_filtering_reason(spec, \"category not in include_categories\")\n            return False\n\n        if spec.name in CONF.instruction_blocklist:\n            logger.dbg_dump_filtering_reason(spec, \"in instruction_blocklist\")\n            return False\n\n        for operand in spec.operands:\n            if operand.type == OT.MEM and operand.values \\\n                    and operand.values[0] in register_blocklist:\n                logger.dbg_dump_filtering_reason(spec, \"mem operand uses blocked register\")\n                return False\n\n        # FP SIMD is not supported\n        for operand in spec.operands:\n            if operand.type != OT.REG or operand.xtype is None:\n                continue\n            assert operand.xtype in get_args(XOT), f\"Unknown xtype value: {operand.xtype}\"\n            if operand.xtype in _FP_XOT or operand.xtype in _BFP_XOT:\n                logger.dbg_dump_filtering_reason(spec, \"uses unsupported FP/SIMD registers\")\n                return False\n\n        for implicit_operand in spec.implicit_operands:\n            assert implicit_operand.type != OT.LABEL  # I know no such instructions\n            if implicit_operand.type == OT.MEM \\\n                    and implicit_operand.values[0] in register_blocklist:\n                logger.dbg_dump_filtering_reason(spec, \"implicit mem operand uses blocked register\")\n                return False\n\n            if implicit_operand.type == OT.REG \\\n                    and implicit_operand.values[0] in register_blocklist:\n                assert len(implicit_operand.values) == 1\n                logger.dbg_dump_filtering_reason(spec, \"implicit reg operand uses blocked register\")\n                return False\n        return True\n\n    logger = ISALogger()\n\n    # Identify which registers should not be used\n    register_blocklist = set(CONF.register_blocklist) - set(CONF.register_allowlist)\n\n    # Remove unsupported instructions\n    skip_list = []\n    for s in isa.instructions:\n        if not is_supported(s):\n            skip_list.append(s)\n    for s in skip_list:\n        isa.instructions.remove(s)\n\n    # Remove unsupported operand values from operand specs;\n    # If all operand values are unsupported, remove the instruction\n    skip_list = []\n    for s in isa.instructions:\n        operands = list(s.operands)  # make a copy\n        for op_id, op in enumerate(operands):\n            # filtering applies only to registers\n            if op.type != OT.REG:\n                continue\n\n            # identify supported registers\n            op_values = sorted(list(set(op.values) - register_blocklist))\n\n            # FIXME: temporary disabled generation of higher reg. bytes for x86\n            for i, reg in enumerate(op_values):\n                if reg[-1] == 'h':\n                    op_values[i] = reg.replace('h', 'l')\n\n            # no supported values -> skip this instruction\n            if not op_values:\n                skip_list.append(s)\n                break\n\n            # otherwise, update the operand\n            s.operands[op_id] = OperandSpec(op_values, op.type, op.src, op.dest, op.width,\n                                            op.is_signed, op.has_magic_value, op.xtype)\n    for s in skip_list:\n        isa.instructions.remove(s)\n\n\ndef _set_isa_properties(isa: InstructionSet) -> None:\n    \"\"\"\n    Set properties of the instruction set that are used in the generation process.\n    \"\"\"\n    for inst in isa.instructions:\n        if inst.is_control_flow:\n            if inst.category in [\"BASE-UNCOND_BR\", \"general-uncond_branch\"]:\n                isa.has_unconditional_branch = True\n            else:\n                isa.has_conditional_branch = True\n        elif inst.has_mem_operand:\n            if inst.has_write:\n                isa.has_writes = True\n            else:\n                isa.has_reads = True\n\n\ndef _dedup(isa: InstructionSet) -> None:\n    \"\"\"\n    Instruction set spec may contain several copies of the same instruction.\n    Remove them.\n    \"\"\"\n    skip_list = set()\n    n_instructions = len(isa.instructions)\n    for i in range(n_instructions):\n        for j in range(i + 1, n_instructions):\n            inst1 = isa.instructions[i]\n            inst2 = isa.instructions[j]\n            if inst1.name == inst2.name and len(inst1.operands) == len(inst2.operands):\n                match = True\n                for k, op1 in enumerate(inst1.operands):\n                    op2 = inst2.operands[k]\n\n                    if op1.type != op2.type:\n                        match = False\n                        continue\n\n                    if op1.values != op2.values:\n                        match = False\n                        continue\n\n                    if op1.width != op2.width and op1.type != OT.IMM:\n                        match = False\n                        continue\n\n                    # assert op1.src == op2.src\n                    # assert op1.dest == op2.dest\n\n                if match:\n                    skip_list.add(inst1)\n\n    for s in skip_list:\n        isa.instructions.remove(s)\n\n\ndef _set_categories(isa: InstructionSet) -> None:\n    isa.control_flow_specs = [i for i in isa.instructions if i.is_control_flow]\n    # adjust the config to the available instruction set\n    if len(isa.control_flow_specs) == 0:\n        CONF.min_successors_per_bb = 1\n        CONF.max_successors_per_bb = 1\n\n    isa.non_control_flow_specs = [i for i in isa.instructions if not i.is_control_flow]\n    assert isa.non_control_flow_specs, \\\n        \"The instruction set is insufficient to generate a test case\"\n\n    isa.non_memory_access_specs = \\\n        [i for i in isa.non_control_flow_specs if not i.has_mem_operand]\n    if CONF.avg_mem_accesses != 0:\n        memory_access_instructions = \\\n            [i for i in isa.non_control_flow_specs if i.has_mem_operand]\n        isa.load_instruction = [i for i in memory_access_instructions if not i.has_write]\n        isa.store_instructions = [i for i in memory_access_instructions if i.has_write]\n        assert isa.load_instruction or isa.store_instructions, \\\n               \"The instruction set does not have memory accesses while `avg_mem_accesses > 0`\"\n    else:\n        isa.load_instruction = []\n        isa.store_instructions = []\n\n    uncond_name = isa.get_unconditional_jump_spec().name.lower()\n    isa.cond_branches = \\\n        [i for i in isa.control_flow_specs if i.name.lower() != uncond_name]\n"
  },
  {
    "path": "rvzr/logs.py",
    "content": "\"\"\"\nFile: Global classes that provide service to all Revizor modules\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nimport sys\nfrom datetime import datetime\nfrom typing import TYPE_CHECKING, NoReturn, Dict, List, Optional, Set, Any, Final, Tuple\nfrom pprint import pformat\nfrom traceback import print_stack\n\nfrom .config import CONF\nfrom .stats import FuzzingStats\n\nif TYPE_CHECKING:\n    from .model import Model\n    from .sandbox import SandboxLayout\n    from .model_unicorn.execution_context import ModelExecutionState\n    from .model_unicorn.speculator_abc import UnicornSpeculator\n    from .model_unicorn.model import UnicornModel\n    from .instruction_spec import InstructionSpec\n    from .traces import HTrace, Violation, CTrace\n    from .tc_components.test_case_data import InputData\n\nMASK_64BIT = pow(2, 64)\nPOW2_64 = pow(2, 64)\n\nRED = '\\033[33;31m'\nGREEN = '\\033[33;32m'\nYELLOW = '\\033[33;33m'\nBLUE = '\\033[33;34m'\nPURPLE = '\\033[33;35m'\nCYAN = '\\033[33;36m'\nGRAY = '\\033[33;37m'\nCOL_RESET = \"\\033[0m\"\n\nM_COL = PURPLE\nPC_COL = COL_RESET\nVAL_COL = CYAN\n\nHTRACE_R1_COL = CYAN\nHTRACE_R2_COL = YELLOW\n\nSTAT = FuzzingStats()\n\n\n# ==================================================================================================\n# Private: Logging configuration\n# ==================================================================================================\nclass _LoggingConfig:  # pylint: disable=too-few-public-methods  # because this is a data class\n    \"\"\"\n    A global object responsible for keeping track of how stuff should be printed.\n    This object is shared among all modules (via Borg pattern)\n    and is used to determine the logging behavior.\n    \"\"\"\n    _borg_shared_state: Dict[Any, Any] = {}\n\n    redraw_mode: bool = True\n    line_ending: str = \"\"\n\n    # info modes\n    info: bool = False\n    stat: bool = False\n    debug: bool = False\n\n    # debugging specific modules\n    dbg_timestamp: bool = False\n    dbg_violation: bool = False\n    dbg_dump_htraces: bool = False\n    dbg_dump_ctraces: bool = False\n    dbg_dump_traces_unlimited: bool = False\n    dbg_executor_raw: bool = False\n    dbg_model: bool = False\n    dbg_coverage: bool = False\n    dbg_generator: bool = False\n    dbg_priming: bool = False\n    dbg_isa_filter: bool = False\n\n    dbg_model_print_id: bool = True\n\n    _all_modes: List[str] = [\n        \"info\", \"stat\", \"dbg_timestamp\", \"dbg_violation\", \"dbg_dump_htraces\", \"dbg_dump_ctraces\",\n        \"dbg_dump_traces_unlimited\", \"dbg_executor_raw\", \"dbg_model\", \"dbg_coverage\",\n        \"dbg_generator\", \"dbg_priming\", \"dbg_isa_filter\"\n    ]\n\n    def __init__(self) -> None:\n        self.__dict__ = self._borg_shared_state\n        if not self._borg_shared_state:\n            self.update_logging_modes()\n            self.line_ending = '\\n' if CONF.multiline_output else ''\n            self.redraw_mode = not CONF.multiline_output\n\n    def update_logging_modes(self) -> None:\n        \"\"\"\n        Function that adjust the logging configuration after\n        a change has been made to the CONF object \"\"\"\n        # Check that all entries in the config a valid\n        for mode in CONF.logging_modes:\n            if not mode:  # skip empty values\n                continue\n            if mode not in self._all_modes:\n                error(f\"Unknown value '{mode}' of config variable 'logging_modes'\")\n\n        # Set the logging modes\n        self.debug = False\n        for mode in self._all_modes:\n            val = mode in CONF.logging_modes\n            setattr(self, mode, val)\n            if \"dbg\" in mode:\n                self.debug |= val\n\n        # Check if Python is not running in optimized mode if debugging is required\n        # (otherwise, the debug messages won't be printed)\n        if not __debug__:\n            dbg_required = any([\n                self.dbg_timestamp, self.dbg_model, self.dbg_coverage, self.dbg_dump_htraces,\n                self.dbg_dump_ctraces, self.dbg_generator, self.dbg_priming, self.dbg_executor_raw,\n                self.dbg_isa_filter\n            ])\n            if dbg_required:\n                warning(\n                    \"\", \"Current value of `logging_modes` requires debugging mode!\\n\"\n                    \"Remove '-O' from python arguments\")\n\n\n# ==================================================================================================\n# Public interface to logging configuration\n# ==================================================================================================\n# create an initial instance of the logging configuration\n# to be used by functions in this module\n_LOG_CONF = _LoggingConfig()\n\n\ndef update_logging_after_config_change() -> None:\n    \"\"\" Update the logging configuration after a change has been made to the CONF object \"\"\"\n    _LOG_CONF.update_logging_modes()\n\n\n# ==================================================================================================\n# Public: Simple logging functions\n# ==================================================================================================\n# FIXME: deprecated; use exceptions instead\ndef error(msg: str, print_tb: bool = False, print_last_tb: bool = False) -> NoReturn:\n    \"\"\" Print an error message and exit the program \"\"\"\n    if _LOG_CONF.redraw_mode:\n        print(\"\")\n\n    if print_tb:\n        print(\"Encountered an unrecoverable error\\nTraceback:\")\n        print_stack()\n        print(\"\\n\")\n    elif print_last_tb:\n        print(\"Encountered an unrecoverable error\\nTraceback:\")\n        print_stack(limit=3)\n        print(\"\\n\")\n\n    if CONF.color:\n        print(f\"{RED}ERROR:{COL_RESET} {msg}\")\n    else:\n        print(f\"ERROR: {msg}\")\n    sys.exit(1)\n\n\ndef warning(src: str, msg: str) -> None:\n    \"\"\" Print a warning message \"\"\"\n    if _LOG_CONF.redraw_mode:\n        print(\"\")\n    if CONF.color:\n        print(f\"{RED}WARNING:{COL_RESET} [{src}] {msg}\")\n    else:\n        print(f\"WARNING: [{src}] {msg}\")\n\n\ndef inform(src: str, msg: str, end: str = \"\\n\") -> None:\n    \"\"\" Print a general information message \"\"\"\n    if _LOG_CONF.info:\n        if _LOG_CONF.redraw_mode:\n            print(\"\")\n        print(f\"INFO: [{src}] {msg}\", end=end, flush=True)\n\n\ndef dbg(src: str, msg: str) -> None:\n    \"\"\" Print a debug message \"\"\"\n    if not __debug__:\n        return\n    if _LOG_CONF.debug:\n        if _LOG_CONF.redraw_mode:\n            print(\"\")\n        print(f\"DBG: [{src}] {msg}\")\n\n\n# ==================================================================================================\n# Public: Module-specific logging\n# ==================================================================================================\nclass FuzzLogger:\n    \"\"\" A class that provides logging services for the Fuzzer module \"\"\"\n\n    one_percent_progress: float = 0.0\n    progress: float = 0.0\n    progress_percent: int = 0\n    msg: str = \"\"\n    _msg_width: int = 0\n    start_time: datetime\n    _conf: Final[_LoggingConfig]\n\n    def __init__(self) -> None:\n        self._conf = _LoggingConfig()\n\n    # ----------------------------------------------------------------------------------------------\n    # Phases of the fuzzer\n\n    def reset(self, max_iterations: int, start_time: datetime) -> None:\n        \"\"\" Reset the state of the fuzzer \"\"\"\n        self.one_percent_progress = max_iterations / 100\n        self.progress = 0\n        self.progress_percent = 0\n        self.msg = \"\"\n        self.start_time = start_time\n\n    def start(self, iterations: int, start_time: datetime) -> None:\n        \"\"\" Print the start message of the fuzzer (namely, the start time) \"\"\"\n        self.reset(iterations, start_time)\n        if not self._conf.info:\n            return\n        inform(\"fuzzer\", start_time.strftime('Starting at %H:%M:%S'))\n\n    def start_round(self, round_id: int) -> None:\n        \"\"\" Update the progress bar for the next fuzzing round \"\"\"\n        if not self._conf.info:\n            return\n\n        # Update the progress state\n        if STAT.test_cases > self.progress:\n            self.progress += self.one_percent_progress\n            self.progress_percent += 1\n        if STAT.test_cases == 0:\n            msg = \"\"\n        else:\n            msg = f\"\\r{STAT.test_cases:<6}({self.progress_percent:>2}%)| Stats: \"\n            msg += STAT.get_brief()\n        self.msg = msg\n\n        # Print the progress bar\n        if STAT.test_cases > 0:\n            print(f\"{self.msg:<{self._msg_width}}\", end=self._conf.line_ending, flush=True)\n        if self._conf.dbg_timestamp and round_id and round_id % 1000 == 0:\n            dbg(\n                \"fuzzer\", f\"Time: {datetime.today()} | \"\n                f\" Duration: {(datetime.today() - self.start_time).total_seconds()} seconds\")\n\n    def priming(self, num_violations: int) -> None:\n        \"\"\" Print a message indicating that the fuzzer is in the priming phase \"\"\"\n        if not self._conf.info:\n            return\n        msg = self.msg + f\"> Priming  {num_violations}             \"\n        print(msg, end=self._conf.line_ending, flush=True)\n        self._msg_width = max(self._msg_width, len(msg))\n\n    def nesting_increased(self) -> None:\n        \"\"\" Print a message indicating that the model's nesting level has been increased \"\"\"\n        if not self._conf.info:\n            return\n        msg = self.msg + f\"> Nest   {CONF.model_max_nesting}         \"\n        self._msg_width = max(self._msg_width, len(msg))\n        print(msg, end=self._conf.line_ending, flush=True)\n\n    def slow_path(self) -> None:\n        \"\"\" Print a message indicating that the fuzzer has entered the slow path \"\"\"\n        if not self._conf.info:\n            return\n        msg = self.msg + \">\" + \" Entering slow path...\"\n        self._msg_width = max(self._msg_width, len(msg))\n        print(msg, end=self._conf.line_ending, flush=True)\n\n    def timeout(self) -> None:\n        \"\"\" Print a message indicating that the fuzzer has timed out \"\"\"\n        if not self._conf.info:\n            return\n        inform(\"fuzzer\", \"\\nTimeout expired\")\n\n    def sample_size_increase(self, sample_size: int) -> None:\n        \"\"\" Print a message indicating that the sample size has been increased \"\"\"\n        if not self._conf.info:\n            return\n        msg = self.msg + \">\" + \" Increasing sample size... to \" + str(sample_size)\n        self._msg_width = max(self._msg_width, len(msg))\n        print(msg, end=self._conf.line_ending, flush=True)\n\n    def report_violations(self, violation: Violation) -> None:\n        \"\"\" Print the detected violations \"\"\"\n        print(\"\\n\\n================================ Violations detected ==========================\")\n        print(violation.full_str())\n\n    def finish(self) -> None:\n        \"\"\" Print the finish message of the fuzzer (namely, the finish\n        time and the duration of the fuzzer) \"\"\"\n        if not self._conf.info:\n            return\n        now = datetime.today()\n        print(\"\")  # new line after the progress bar\n        if self._conf.stat:\n            print(\"================================ Statistics ================================\"\n                  \"===\\n\")\n            print(STAT)\n        print(f\"Duration: {(now - self.start_time).total_seconds():.1f}\")\n        print(datetime.today().strftime('Finished at %H:%M:%S'))\n\n    def report_model_coverage(self, model: Model) -> None:\n        \"\"\" Save model coverage \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_coverage:\n            return\n        model.report_coverage(\"coverage.txt\")\n\n    # ----------------------------------------------------------------------------------------------\n    # Debugging\n    def dbg_dump_traces(self, inputs: List[InputData], htraces: List[HTrace],\n                        reference_htraces: List[HTrace], ctraces: List[CTrace]) -> None:\n        \"\"\" Print the collected traces \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_dump_htraces and not self._conf.dbg_dump_ctraces:\n            return\n        if not htraces:  # might be empty due to tracing errors\n            return\n\n        # Optionally trim the output\n        if len(inputs) > 100 and not self._conf.dbg_dump_traces_unlimited:\n            warning(\"fuzzer\", \"Trace output is will be limited to 100 traces\")\n            inputs = inputs[:100]\n\n        # Replace corrupted traces with the reference traces\n        for i, htrace in enumerate(htraces):\n            if htrace.is_corrupted_or_ignored() \\\n               and not reference_htraces[i].is_corrupted_or_ignored():\n                htraces[i] = reference_htraces[i]\n\n        print(\"\\n================================ Collected Traces =============================\")\n        org_debug_state = self._conf.dbg_model\n        self._conf.dbg_model = False\n        for i, _ in enumerate(inputs):\n            print(f\"- Input {i}:\")\n            colors: Tuple[str, ...]\n            if self._conf.dbg_dump_ctraces:\n                colors = (M_COL, PC_COL, VAL_COL, COL_RESET) if CONF.color else ()\n                ctrace_str = ctraces[i].full_str(*colors)\n                print(f\"  CTr: {ctrace_str} | Hash: {ctraces[i]}\")\n            if self._conf.dbg_dump_htraces:\n                colors = (HTRACE_R1_COL, HTRACE_R2_COL, COL_RESET) if CONF.color else ()\n                htrace_str = htraces[i].full_str('    ', *colors)\n                print(f\"  HTr:\\n{htrace_str}\")\n            if CONF.color and htraces[i].get_max_pfc()[0] > htraces[i].get_max_pfc()[1]:\n                print(f\"  Feedback: {YELLOW}{htraces[i].get_max_pfc()}{COL_RESET}\")\n            else:\n                print(f\"  Feedback: {htraces[i].get_max_pfc()}\")\n        self._conf.dbg_model = org_debug_state\n\n    def dbg_dump_architectural_traces(self, hardware_regs: List[List[int]],\n                                      model_regs: List[List[int]]) -> None:\n        \"\"\" Print the architectural traces \"\"\"\n        if not __debug__:\n            return\n        if CONF.fuzzer != \"architectural\":\n            return\n        if not self._conf.dbg_dump_htraces and not self._conf.dbg_dump_ctraces:\n            return\n\n        print(\"\\n========================== Architectural Traces ==============================\")\n        for i, _ in enumerate(hardware_regs):\n            if i > 100 and not self._conf.dbg_dump_traces_unlimited:\n                warning(\"fuzzer\", \"Trace output is limited to 100 traces\")\n                break\n            print(f\"Input {i}:\")\n            if self._conf.dbg_dump_ctraces:\n                print(f\"  Model Registers: {[hex(v) for v in model_regs[i]]}\")\n            if self._conf.dbg_dump_htraces:\n                print(f\"  HW Registers:    {[hex(v) for v in hardware_regs[i]]}\")\n\n    def dbg_violation(self, violation: Violation, model: Model) -> None:\n        \"\"\" Print a detailed report of the violation \"\"\"\n        if not __debug__:\n            return\n\n        if self._conf.dbg_violation:\n            print(\"================================ Violation Traces =============================\")\n            hw_classes = violation.get_hw_classes()\n            model.load_test_case(violation.test_case_code)\n            for hw_class in hw_classes:\n                measurement = hw_class.measurements[0]\n                print(f\"                      ##### Input {measurement.input_id} #####\")\n                model_debug_state = self._conf.dbg_model, self._conf.dbg_model_print_id\n                self._conf.dbg_model = True\n                self._conf.dbg_model_print_id = False\n                model.trace_test_case([measurement.input_], CONF.model_max_nesting)\n                self._conf.dbg_model, self._conf.dbg_model_print_id = model_debug_state\n                print(\"\\n\\n\")\n\n    def dbg_priming_progress(self, input_id: int, current_input_id: int) -> None:\n        \"\"\" Print a message indicating the progress of the priming phase \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_priming:\n            return\n        print(f\"\\nPriming #{input_id} in place of #{current_input_id}\")\n\n    def dbg_priming_fail(self, input_id: int, current_input_id: int, htrace_to_reproduce: HTrace,\n                         new_htrace: HTrace) -> None:\n        \"\"\" Print a message indicating that the priming phase has failed \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_priming:\n            return\n\n        print(f\"\\nPriming failed for input {input_id} in place of {current_input_id}\")\n        print(f\"{'HTrace':64} Original|New\")\n        print(htrace_to_reproduce.full_pair_str(new_htrace))\n\n\nclass ModelLogger:\n    \"\"\"\n    A class that provides logging services for the Model modules. Primarily, this class\n    is responsible for printing the debug trace of the model.\n    (printed when dbg_model or dbg_violation is set in the config file)\n    \"\"\"\n\n    model_layout: Optional[SandboxLayout] = None\n\n    def __init__(self) -> None:\n        self._conf = _LoggingConfig()\n\n    def set_model_layout(self, layout: SandboxLayout) -> None:\n        \"\"\" Store the layout of the model being debugged \"\"\"\n        self.model_layout = layout\n\n    def dbg_header(self, input_id: int) -> None:\n        \"\"\" Print the header of the debug information \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_model or not self._conf.dbg_model_print_id:\n            return\n\n        print(f\"\\n                     ##### Input {input_id} #####\")\n\n    def dbg_mem_access(self, is_store: bool, value: int, address: int, size: int,\n                       model: UnicornModel, layout: SandboxLayout) -> None:\n        \"\"\"\n        Print debug information about memory access, if debugging is enabled.\n        The information includes:\n            - Memory address (as an offset from the start of the main actor's data section)\n            - Type of access (load or store)\n            - Value being read or written\n\n        :param type_: The type of memory access (UC_MEM_READ or UC_MEM_WRITE)\n        :param value: The value being read or written\n        :param address: The address being accessed\n        :param size: The size of the memory access\n        :param model: The model being debugged\n        :param layout: The layout of the model being debugged\n        :return: None\n        \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_model:\n            return\n\n        # Address details\n        normalized_address = layout.data_addr_to_offset(address)\n\n        # Value details\n        val = value if is_store else int.from_bytes(\n            model.emulator.mem_read(address, size), byteorder='little')\n\n        # Build and print the report string\n        type_str = \"store to\" if is_store else \"load from\"\n        if CONF.color:\n            msg = f\"    > {CYAN}{type_str}{COL_RESET} +0x{normalized_address:x} \" \\\n                  f\"{CYAN}value {COL_RESET}0x{val:x}\"\n        else:\n            msg = f\"    > {type_str} +0x{normalized_address:x} value 0x{val:x}\"\n\n        print(msg)\n\n    def dbg_instruction(self, pc: int, model: UnicornModel, state: ModelExecutionState,\n                        speculator: UnicornSpeculator) -> None:\n        \"\"\"\n        Print debug information about the current instruction, if debugging is enabled.\n        The information includes:\n          - Instruction name and operands\n          - Current register values\n          - Whether the instruction is speculative, and if so, the speculative nesting level\n        \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_model:\n            return\n\n        # Instruction details\n        instruction = state.current_instruction\n        name = str(instruction)\n        code_offset = model.layout.code_addr_to_offset(pc)\n        is_exit = state.is_exit_addr(pc)\n\n        # Speculation details\n        in_speculation = speculator.in_speculation()\n        nesting = speculator.nesting()\n\n        # Build and print the report string\n        inst_str = name\n        if CONF.color:\n            if in_speculation:\n                inst_str = YELLOW + inst_str + COL_RESET\n            else:\n                inst_str = GREEN + inst_str + COL_RESET\n        if in_speculation:\n            inst_str = f\"[transient, nesting = {nesting}] \" + inst_str\n        inst_str = f\"0x{code_offset:<2x}: {inst_str}\"\n        if is_exit:\n            inst_str += \" [test_case_exit]\"\n        print(inst_str)\n\n        # Print the register values\n        model.print_registers(oneline=True)\n\n    def dbg_rollback(self, address: int) -> None:\n        \"\"\" Print a message indicating that the model has rolled back to a specific address \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_model:\n            return\n\n        assert self.model_layout is not None\n        base = self.model_layout.code_start()\n\n        msg = f\"ROLLBACK to 0x{address - base:x}\"\n        if CONF.color:\n            msg = YELLOW + msg + COL_RESET\n        print(msg)\n\n    def dbg_exception(self, errno: int, descr: str) -> None:\n        \"\"\" Print a message indicating that an exception has occurred \"\"\"\n        if not __debug__:\n            return\n\n        if not self._conf.dbg_model:\n            return\n\n        msg = f\"EXCEPTION #{errno}: {descr}\"\n        if CONF.color:\n            msg = RED + msg + COL_RESET\n        print(msg)\n\n\nclass GeneratorLogger:\n    \"\"\" A class that provides logging services for the Program Generator module \"\"\"\n\n    def __init__(self) -> None:\n        self._conf = _LoggingConfig()\n\n    def dbg_dump_instruction_pool(self, instructions: List[InstructionSpec]) -> None:\n        \"\"\"\n        Print the instruction pool used by the Program Generator, if debugging is enabled.\n        The instructions are grouped by category and printed in a human-readable format.\n        \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_generator or not CONF.is_generation_enabled():\n            return\n\n        instructions_by_category: Dict[str, Set[str]] = {i.category: set() for i in instructions}\n        for i in instructions:\n            instructions_by_category[i.category].add(i.name)\n        n_instructions = sum(len(v) for v in instructions_by_category.values())\n\n        dbg(\"generator\", f\"Instructions under test {n_instructions}:\")\n        for k, instruction_list in instructions_by_category.items():\n            print(\"  - \" + k + \": \" + pformat(sorted(instruction_list), indent=4, compact=True))\n        print(\"\")\n\n\nclass ExecutorLogger:\n    \"\"\" A class that provides logging services for the Executor module \"\"\"\n\n    def __init__(self) -> None:\n        self._conf = _LoggingConfig()\n\n    def dbg_dump_raw_traces(self, htraces: List[HTrace]) -> None:\n        \"\"\" Print the raw traces collected by the executor \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_executor_raw:\n            return\n\n        print(\"Collected raw traces:\")\n        for input_id, htrace in enumerate(htraces):\n            prefix = f\"{input_id:03}, \"\n            print(htrace.full_str(prefix))\n\n\nclass ISALogger:\n    \"\"\" A class that provides logging services for the isa_spec module \"\"\"\n\n    def __init__(self) -> None:\n        self._conf = _LoggingConfig()\n\n    def dbg_dump_filtering_reason(self, instruction: InstructionSpec, reason: str) -> None:\n        \"\"\"\n        Print the reason why a specific instruction was filtered out by the ISA module,\n        if debugging is enabled.\n        \"\"\"\n        if not __debug__:\n            return\n        if not self._conf.dbg_isa_filter or not CONF.is_generation_enabled():\n            return\n\n        dbg(\"isa_spec\", f\"{instruction.name} ({instruction.category}) filtered out: {reason}\")\n"
  },
  {
    "path": "rvzr/model.py",
    "content": "\"\"\"\nFile: Model Interface (Backend- and ISA-independent)\n      A model is a module that can execute a test case according to a contract\n      and collect contract traces.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom abc import ABC, abstractmethod\nfrom typing import List, Tuple, TYPE_CHECKING, Any\n\nfrom .traces import CTrace\nfrom .tc_components.test_case_data import InputData, InputTaint\n\nif TYPE_CHECKING:\n    from .sandbox import SandboxLayout, BaseAddrTuple\n    from .tc_components.test_case_code import TestCaseProgram\n\n\nclass Model(ABC):\n    \"\"\"\n    Abstract interface for all contract models.\n    The specific implementation depends on the selected backend and the target ISA.\n    \"\"\"\n\n    layout: SandboxLayout\n    \"\"\" The memory layout of the most-recently loaded test case within the model \"\"\"\n\n    is_speculative: bool\n    \"\"\" Indicates whether the model implements any form of speculative execution \"\"\"\n\n    _enable_mismatch_check_mode: bool = False\n    \"\"\" mismatch_check_mode: If True, the model will return GPR values instead of\n    contract traces, which is used to check for mismatches between the model and the executor \"\"\"\n\n    @abstractmethod\n    def __init__(self,\n                 bases: BaseAddrTuple,\n                 *args: Any,\n                 enable_mismatch_check_mode: bool = False) -> None:\n        pass\n\n    @abstractmethod\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        \"\"\"\n        Load a test case into the model, which implies allocating memory for the code\n        and data, initializing permissions, and doing other necessary setup.\n\n        This method *must* be called before calling `trace_test_case`.\n        \"\"\"\n\n    @abstractmethod\n    def trace_test_case(self, inputs: List[InputData], nesting: int) -> List[CTrace]:\n        \"\"\"\n        Execute a previously loaded test case in the model with the given inputs,\n        and collect the traces for each execution (i.e., one trace per input).\n        \"\"\"\n\n    @abstractmethod\n    def trace_test_case_with_taints(self, inputs: List[InputData],\n                                    nesting: int) -> Tuple[List[CTrace], List[InputTaint]]:\n        \"\"\"\n        Execute a previously loaded test case in the model with the given inputs,\n        and collect the traces for each execution (i.e., one trace per input).\n        While collecting the traces, also collect the taints for each input.\n        \"\"\"\n\n    @abstractmethod\n    def report_coverage(self, path: str) -> None:\n        \"\"\"\n        Report the coverage of the fuzzing campaign w.r.t. the model, and store the report\n        in the given file path.\n        \"\"\"\n\n\nclass DummyModel(Model):\n    \"\"\"\n    Dummy implementation of the Model interface that does nothing. All traces produced by\n    this model are empty, and thus all inputs form the same equivalence class.\n\n    This model is useful for testing purposes or for cases where it's necessary to\n    run the fuzzer without a model (e.g., for standalone hardware tracing).\n    \"\"\"\n    is_speculative: bool = False\n\n    def __init__(self,\n                 bases: BaseAddrTuple,\n                 *args: Any,\n                 enable_mismatch_check_mode: bool = False) -> None:\n        pass\n\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        pass\n\n    def trace_test_case(self, inputs: List[InputData], nesting: int) -> List[CTrace]:\n        return [CTrace.empty_trace() for _ in inputs]\n\n    def trace_test_case_with_taints(self, inputs: List[InputData],\n                                    nesting: int) -> Tuple[List[CTrace], List[InputTaint]]:\n        taints = [InputTaint() for _ in inputs]\n        traces = [CTrace.empty_trace() for _ in inputs]\n        return traces, taints\n\n    def report_coverage(self, path: str) -> None:\n        pass\n"
  },
  {
    "path": "rvzr/model_dynamorio/Makefile",
    "content": "mkfile_path = $(abspath $(lastword $(MAKEFILE_LIST)))\ncurrent_dir = $(dir $(mkfile_path))\n\ndr_path := $(HOME)/.local/dynamorio/\nmodel_path := $(HOME)/.local/dynamorio/model/\n\nrelease := 11.2.0\n\nCMAKE_FLAGS := -DCMAKE_EXPORT_COMPILE_COMMANDS=ON\n\n.PHONY: all download_dr backend adapter clean-backend clean-adapter clean\n\nall: download_dr backend adapter\n\ndownload_dr: $(dr_path)/drrun\n\t@echo \"DynamoRIO is downloaded and ready to use.\"\n\n$(dr_path)/drrun:\n\tmkdir -p $(dr_path)\n\twget https://github.com/DynamoRIO/dynamorio/releases/download/release_$(release)/DynamoRIO-Linux-$(release).tar.gz -O $(dr_path)/DynamoRIO-Linux-$(release).tar.gz\n\ttar xf $(dr_path)/DynamoRIO-Linux-$(release).tar.gz -C $(dr_path)\n\trm $(dr_path)/DynamoRIO-Linux-$(release).tar.gz\n\techo \"#!/usr/bin/env bash\" > $(dr_path)/drrun\n\techo '$(dr_path)/DynamoRIO-Linux-$(release)/bin64/drrun -disable_traces\t$$@' >> $(dr_path)/drrun\n\tchmod +x $(dr_path)/drrun\n\nbackend:\n\tmkdir -p $(current_dir)/backend/build\n\tcd $(current_dir)/backend/build && cmake $(CMAKE_FLAGS) -DDynamoRIO_DIR=$(dr_path)/DynamoRIO-Linux-$(release)/cmake -D CMAKE_BUILD_TYPE=Debug  $(current_dir)/backend\n\tmake -C $(current_dir)/backend/build\n\tcp $(current_dir)/backend/build/libdr_model.so $(dr_path)\n\nadapter:\n\tmkdir -p $(current_dir)/adapter/build\n\tcd $(current_dir)/adapter/build && cmake $(CMAKE_FLAGS) $(current_dir)/adapter\n\tmake -C $(current_dir)/adapter/build\n\tmake -C $(current_dir)/adapter/build install\n\nclean-backend:\n\trm -rf $(current_dir)/backend/build\n\nclean-adapter:\n\trm -rf $(current_dir)/adapter/build\n\nclean:  clean-backend clean-adapter\n\n# * build dr_model\n\n# ```\n# cd adapters\n# make example\n# drrun -c ~/.local/dr_model/libdr_model.so --tracer ct --disable-normalization -- ./example 2\n# ```\n"
  },
  {
    "path": "rvzr/model_dynamorio/__init__.py",
    "content": ""
  },
  {
    "path": "rvzr/model_dynamorio/adapter/.clang-format",
    "content": "ColumnLimit: 100\nIndentWidth: 4\nBreakBeforeBraces: Linux\nAllowShortIfStatementsOnASingleLine: false\nAlignConsecutiveMacros:\n  Enabled: true\n  AcrossEmptyLines: false\n  AcrossComments: false\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/.clang-tidy",
    "content": "Checks: >\n    -*,\n    bugprone-*,\n    cert-*,\n    clang-analyzer-*,\n    llvm-*,\n    misc-*,\n    modernize-*,\n    performance-*,\n    portability-*,\n    readability-*,\n    -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling,\n    -misc-include-cleaner,\n    -cert-err33-c,\n    -llvm-header-guard,\n    -clang-analyzer-unix.StdCLibraryFunctions,\n    -clang-analyzer-optin.taint.TaintedAlloc,\n    -clang-analyzer-security.MmapWriteExec\n\n# Turn all the warnings from the checks above into errors.\nWarningsAsErrors: \"*\"\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nproject(adapter C ASM)\n\n# Set compile flags\nset(CMAKE_C_FLAGS \"${CMAKE_C_FLAGS} -ggdb\")\n\n# Define source files\nset(SRC_FILES\n    main.c\n    parser.c\n    sandbox.c\n)\n\nset(SRC_FILES_ASM\n    test_case_entry.S\n)\n\n# Create executable\nadd_executable(adapter ${SRC_FILES} ${SRC_FILES_ASM})\ntarget_include_directories(adapter PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})\ntarget_link_libraries(adapter rt)\n\n# Install target\ninstall(TARGETS adapter\n        DESTINATION $ENV{HOME}/.local/dynamorio)\n\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/main.c",
    "content": "///\n/// File: Module responsible for loading binary test cases produced by\n///       the Revizor generator, and executing them in a sandboxed environment\n///       that mirrors the one used by x86 executor.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <assert.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include \"parser.h\"\n#include \"rcbf.h\"\n#include \"rdbf.h\"\n#include \"sandbox.h\"\n\nstatic const char *rcbf_file = NULL;\nstatic const char *rdbf_file = NULL;\nstatic const char *bases_file = NULL;\n\n// Defined in test_case_entry.asm\nvoid test_case_entry_outer(uint8_t *main_area_base, uint8_t *code_base);\n\nstatic int parse_args(int argc, char const *argv[])\n{\n    // Check usage\n    if (argc != 4) {\n        printf(\"Usage: %s <RCBF file> <RDBF file> <bases_file>\\n\", argv[0]);\n        return -1;\n    }\n    rcbf_file = argv[1];\n    rdbf_file = argv[2];\n    bases_file = argv[3];\n\n    // Check if files exist\n    if (access(rcbf_file, F_OK) == -1) {\n        fprintf(stderr, \"ERROR: RCBF file %s does not exist\\n\", rcbf_file);\n        return -1;\n    }\n    if (access(rdbf_file, F_OK) == -1) {\n        fprintf(stderr, \"ERROR: RDBF file %s does not exist\\n\", rdbf_file);\n        return -1;\n    }\n    if (access(bases_file, F_OK) == -1) {\n        fprintf(stderr, \"ERROR: Bases file %s does not exist\\n\", bases_file);\n        return -1;\n    }\n\n    return 0;\n}\n\n/// @brief Free all resources allocated by the module\n/// @param rcbf Allocated RCBF structure\n/// @param rdbf Allocated RDBF structure\n/// @return void\nstatic void cleanup(rcbf_t *rcbf, rdbf_t *rdbf)\n{\n    free_rcbf(rcbf);\n    free_rdbf(rdbf);\n}\n\nint main(int argc, char const *argv[])\n{\n    // Parse CLI arguments\n    if (parse_args(argc, argv) != 0) {\n        return -1;\n    }\n\n    // Parse input files\n    rcbf_t *rcbf_data = parse_rcbf(rcbf_file);\n    rdbf_t *rdbf_data = parse_rdbf(rdbf_file);\n    if (rcbf_data->header.n_actors != rdbf_data->header.n_actors) {\n        fprintf(stderr, \"ERROR: RCBF and RDBF files have different number of actors\\n\");\n        cleanup(rcbf_data, rdbf_data);\n        return -1;\n    }\n\n    // Allocate memory for the sandbox and load code\n    if (allocate_sandbox(rcbf_data->header.n_actors) != 0) {\n        fprintf(stderr, \"ERROR: Failed to allocate memory for the sandbox\\n\");\n        cleanup(rcbf_data, rdbf_data);\n        return -1;\n    }\n    if (load_code_in_sandbox(rcbf_data) != 0) {\n        fprintf(stderr, \"ERROR: Failed to load code into the sandbox\\n\");\n        cleanup(rcbf_data, rdbf_data);\n        return -1;\n    }\n\n    // Communicate sandbox base addresses to the python model, in binary format\n    sandbox_t *sandbox = get_sandbox();\n    FILE *bases_fp = fopen(bases_file, \"wb\");\n    if (bases_fp == NULL) {\n        perror(\"fopen:bases_file\");\n        cleanup(rcbf_data, rdbf_data);\n        return -1;\n    }\n    fwrite((const void *)&sandbox->code, sizeof(uint8_t *), 1, bases_fp);\n    fwrite((const void *)&sandbox->data, sizeof(uint8_t *), 1, bases_fp);\n    fflush(bases_fp);\n    fclose(bases_fp);\n\n    // Load data into the sandbox and execute the test case\n    for (int i = 0; i < rdbf_data->header.n_inputs; i++) {\n        if (load_data_in_sandbox(rdbf_data, i) != 0) {\n            fprintf(stderr, \"ERROR: Failed to load data into the sandbox\\n\");\n            cleanup(rcbf_data, rdbf_data);\n            return -1;\n        }\n        sandbox_t *sandbox = get_sandbox();\n        test_case_entry_outer(&sandbox->data->main_area[0], &sandbox->code->code[0]);\n    }\n\n    cleanup(rcbf_data, rdbf_data);\n    return 0;\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/parser.c",
    "content": "///\n/// File: RCBF and RDBF parsing functions for the DynamoRIO backend adapter\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <stdio.h>\n#include <stdlib.h>\n\n#include \"rcbf.h\"\n#include \"rdbf.h\"\n#include \"sandbox_const.h\"\n\n/// @brief Parse the file in RCBF format and return\n///        pointer to the parsed data\n/// @param filename The name of the file to parse\n/// @return Pointer to the parsed data\nrcbf_t *parse_rcbf(const char *filename)\n{\n    // Open the file in binary mode\n    FILE *rdbf_fp = fopen(filename, \"rb\");\n    if (rdbf_fp == NULL) {\n        perror(\"fopen:parse_rcbf\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate memory for the RCBF structure\n    rcbf_t *rcbf = (rcbf_t *)malloc(sizeof(rcbf_t));\n    if (rcbf == NULL) {\n        perror(\"malloc:rcbf\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Read the header\n    if (fread(&rcbf->header, sizeof(rcbf_header_t), 1, rdbf_fp) != 1) {\n        perror(\"fread:rcbf->header\");\n        exit(EXIT_FAILURE);\n    }\n    uint64_t n_actors = rcbf->header.n_actors;\n    if (n_actors <= 0 || n_actors > MAX_ACTORS) {\n        fprintf(stderr, \"ERROR: invalid number of actors in the RCBF file\\n\");\n        exit(EXIT_FAILURE);\n    }\n    uint64_t n_symbols = rcbf->header.n_symbols;\n    if (n_symbols <= 0) {\n        fprintf(stderr, \"ERROR: invalid number of symbols in the RCBF file\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate memory for the actor table and read it\n    rcbf->actor_table = (actor_metadata_t *)malloc(n_actors * sizeof(actor_metadata_t));\n    if (rcbf->actor_table == NULL) {\n        perror(\"malloc:rcbf->actor_table\");\n        exit(EXIT_FAILURE);\n    }\n    if (fread(rcbf->actor_table, sizeof(actor_metadata_t), n_actors, rdbf_fp) != n_actors) {\n        perror(\"fread:rcbf->actor_table\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate memory for the symbol table and read it\n    rcbf->symbol_table = (symbol_entry_t *)malloc(n_symbols * sizeof(symbol_entry_t));\n    if (rcbf->symbol_table == NULL) {\n        perror(\"malloc:rcbf->symbol_table\");\n        exit(EXIT_FAILURE);\n    }\n    if (fread(rcbf->symbol_table, sizeof(symbol_entry_t), n_symbols, rdbf_fp) != n_symbols) {\n        perror(\"fread:rcbf->symbol_table\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate memory for the section metadata and read it\n    rcbf->section_metadata =\n        (code_section_metadata_t *)malloc(n_actors * sizeof(code_section_metadata_t));\n    if (rcbf->section_metadata == NULL) {\n        perror(\"malloc:rcbf->section_metadata\");\n        exit(EXIT_FAILURE);\n    }\n    if (fread(rcbf->section_metadata, sizeof(code_section_metadata_t), n_actors, rdbf_fp) !=\n        n_actors) {\n        perror(\"fread:rcbf->section_metadata\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate memory for the code sections and read it\n    rcbf->sections = (rcbf_code_section_t *)malloc(n_actors * sizeof(rcbf_code_section_t));\n    if (rcbf->sections == NULL) {\n        perror(\"malloc:rcbf->sections\");\n        exit(EXIT_FAILURE);\n    }\n    for (uint64_t i = 0; i < n_actors; i++) {\n        if (fread(rcbf->sections[i].code, 1, rcbf->section_metadata[i].size, rdbf_fp) !=\n            rcbf->section_metadata[i].size) {\n            perror(\"fread:rcbf->sections\");\n            exit(EXIT_FAILURE);\n        }\n    }\n\n    // Close the file\n    fclose(rdbf_fp);\n\n    return rcbf;\n}\n\n/// @brief Free the memory allocated for the RCBF structure\n/// @param rcbf The RCBF structure to free\nvoid free_rcbf(rcbf_t *rcbf)\n{\n    free(rcbf->actor_table);\n    free(rcbf->symbol_table);\n    free(rcbf->section_metadata);\n    free(rcbf->sections);\n    free(rcbf);\n}\n\n/// @brief Parse the file in RDBF format and return pointer to the parsed data\n/// @param filename The name of the file to parse\n/// @return Pointer to the parsed data\nrdbf_t *parse_rdbf(const char *filename)\n{\n    // Open the file in binary mode\n    FILE *rdbf_fp = fopen(filename, \"rb\");\n    if (rdbf_fp == NULL) {\n        perror(\"fopen:parse_rdbf\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate memory for the RDBF structure\n    rdbf_t *rdbf = (rdbf_t *)malloc(sizeof(rdbf_t));\n    if (rdbf == NULL) {\n        perror(\"malloc:rdbf\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Read the header\n    if (fread(&rdbf->header, sizeof(rdbf_header_t), 1, rdbf_fp) != 1) {\n        perror(\"fread:rdbf->header\");\n        exit(EXIT_FAILURE);\n    }\n    uint64_t n_actors = rdbf->header.n_actors;\n    uint64_t n_inputs = rdbf->header.n_inputs;\n\n    // Allocate memory for the data section metadata and read it\n    rdbf->metadata = (data_section_metadata_t *)malloc(n_actors * sizeof(data_section_metadata_t));\n    if (rdbf->metadata == NULL) {\n        perror(\"malloc:rdbf->metadata\");\n        exit(EXIT_FAILURE);\n    }\n    if (fread(rdbf->metadata, sizeof(data_section_metadata_t), n_actors, rdbf_fp) != n_actors) {\n        perror(\"fread:rdbf->metadata\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Allocate memory for the data sections and read them\n    uint64_t data_size = n_inputs * n_actors * sizeof(rdbf_data_section_t);\n    rdbf->data = (rdbf_data_section_t *)malloc(data_size);\n    if (rdbf->data == NULL) {\n        perror(\"malloc:rdbf->data\");\n        exit(EXIT_FAILURE);\n    }\n    if (fread(rdbf->data, 1, data_size, rdbf_fp) != data_size) {\n        perror(\"fread:rdbf->data\");\n        exit(EXIT_FAILURE);\n    }\n\n    // By this point the whole file should be read\n    if (fgetc(rdbf_fp) != EOF) {\n        fprintf(stderr, \"ERROR: unexpected file format of the RDBF file\\n\");\n        exit(EXIT_FAILURE);\n    }\n\n    // Close the file\n    fclose(rdbf_fp);\n\n    return rdbf;\n}\n\n/// @brief Free the memory allocated for the RDBF structure\n/// @param rdbf The RDBF structure to free\nvoid free_rdbf(rdbf_t *rdbf)\n{\n    free(rdbf->metadata);\n    free(rdbf->data);\n    free(rdbf);\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/parser.h",
    "content": "/// File: Interface to the RCBF/RDBF parser\n///\n/// Copyright (C) Microsoft Corporation\n/// SPDX-License-Identifier: MIT\n\n#ifndef PARSER_H\n#define PARSER_H\n\n#include \"rcbf.h\"\n#include \"rdbf.h\"\n\nrcbf_t *parse_rcbf(const char *filename);\nvoid free_rcbf(rcbf_t *rcbf);\n\nrdbf_t *parse_rdbf(const char *filename);\nvoid free_rdbf(rdbf_t *rdbf);\n\n#endif // PARSER_H\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/rcbf.h",
    "content": "/// File: Representation of a RCBF binary\n/// (see docs/devel/binary-formats.md for format description)\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef RCBF_H\n#define RCBF_H\n\n#include <stdbool.h>\n#include <stddef.h>\n#include <stdint.h>\n\nenum {\n    MODE_HOST = 0,\n    MODE_GUEST = 1,\n};\n\nenum {\n    PL_KERNEL = 0,\n    PL_USER = 1,\n};\n\n#define MAX_SECTION_SIZE 4096\n\ntypedef struct {\n    uint64_t n_actors;\n    uint64_t n_symbols;\n} rcbf_header_t;\n\ntypedef struct {\n    uint64_t actor_id;\n    uint64_t mode;\n    uint64_t pl;\n    uint64_t data_permissions;\n    uint64_t data_ept_properties;\n    uint64_t code_permissions;\n} actor_metadata_t;\n\ntypedef struct {\n    uint64_t section_id;\n    uint64_t offset;\n    uint64_t symbol_id;\n    uint64_t args;\n} symbol_entry_t;\n\ntypedef struct {\n    uint64_t section_id;\n    uint64_t size;\n    uint64_t reserved;\n} code_section_metadata_t;\n\ntypedef struct {\n    char code[MAX_SECTION_SIZE];\n} rcbf_code_section_t;\n\ntypedef struct {\n    rcbf_header_t header;\n    actor_metadata_t *actor_table;\n    symbol_entry_t *symbol_table;\n    code_section_metadata_t *section_metadata;\n    rcbf_code_section_t *sections;\n} rcbf_t;\n\nextern rcbf_t *test_case_code;\n\n#endif // RCBF_H\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/rdbf.h",
    "content": "/// File: Representation of a RDBF binary\n/// (see docs/devel/binary-formats.md for format description)\n///\n/// Copyright (C) Microsoft Corporation\n/// SPDX-License-Identifier: MIT\n\n#ifndef RDBF_H\n#define RDBF_H\n\n#include <stdint.h>\n\n#define RDBF_AREA_SIZE 4096\n\ntypedef struct {\n    uint64_t n_actors;\n    uint64_t n_inputs;\n} rdbf_header_t;\n\ntypedef struct {\n    uint64_t size;\n    uint64_t reserved;\n} data_section_metadata_t;\n\ntypedef struct {\n    char main_area[RDBF_AREA_SIZE];\n    char faulty_area[RDBF_AREA_SIZE];\n    char reg_init_region[RDBF_AREA_SIZE];\n} rdbf_data_section_t;\n\ntypedef struct {\n    rdbf_header_t header;\n    data_section_metadata_t *metadata;\n    rdbf_data_section_t *data;\n} rdbf_t;\n\nextern rdbf_t *input_sequence;\n\n#endif // RDBF_H\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/sandbox.c",
    "content": "/// File: Allocation and management of the sandbox memory for test cases\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <stdlib.h>\n#include <string.h>\n#include <sys/mman.h>\n\n#include \"rcbf.h\"\n#include \"rdbf.h\"\n#include \"sandbox.h\"\n\nstatic sandbox_t *sandbox;\nstatic const int nop_opcode = 0x90;\nstatic const int ret_opcode = 0xc3;\n\nstatic const int eflags_must_clear_mask = 2263;\nstatic const int eflags_must_set_mask = 2;\n\n// Defined in test_case_entry.asm\nvoid test_case_entry(uint8_t *main_area_base);\n\n/// @brief Load a test case code into the sandbox\n/// @param rcbf_data Parsed RCBF data\n/// @return 0 on success, -1 on failure\nint load_code_in_sandbox(rcbf_t *rcbf_data)\n{\n    for (uint64_t section_id = 0; section_id < rcbf_data->header.n_actors; section_id++) {\n        code_section_t *code_section = &sandbox->code[section_id];\n        // printf(\"Loading code section %lx into sandbox %llx\\n\", section_id, code_section);\n\n        // Copy the code into the sandbox\n        int code_size = (int)rcbf_data->section_metadata[section_id].size;\n        memcpy(code_section->code, rcbf_data->sections[section_id].code, code_size);\n\n        // The end of the code section in the main area is the test case exit point;\n        // We insert a RETQ instruction to return to the caller\n        if (section_id == 0) {\n            code_section->code[code_size] = ret_opcode;\n            code_size++;\n        }\n\n        // Initialize the remaining space with NOPs\n        int uninitialized_size = MAX_EXPANDED_SECTION_SIZE - code_size;\n        memset(code_section->code + code_size, nop_opcode, uninitialized_size);\n        memset(code_section->unused, nop_opcode, MACRO_AREA_SIZE);\n    }\n\n    return 0;\n}\n\n/// @brief Load a test case data into the sandbox\n/// @param rdbf_data Parsed RDBF data\n/// @param input_id Index of the input to load from the RDBF data\n/// @return 0 on success, -1 on failure\nint load_data_in_sandbox(rdbf_t *rdbf_data, int input_id)\n{\n    rdbf_data_section_t *data = &rdbf_data->data[input_id];\n    for (uint64_t section_id = 0; section_id < rdbf_data->header.n_actors; section_id++) {\n        data_section_t *data_section = &sandbox->data[section_id];\n\n        // Zero out underflow and overflow pads\n        memset(data_section->macro_stack, 0, MACRO_STACK_SIZE);\n        memset(data_section->underflow_pad, 0, UNDERFLOW_PAD_SIZE);\n        memset(data_section->overflow_pad, 0, OVERFLOW_PAD_SIZE);\n\n        // Copy the data into the sandbox\n        memcpy(data_section->main_area, data[section_id].main_area, MAIN_AREA_SIZE);\n        memcpy(data_section->faulty_area, data[section_id].faulty_area, FAULTY_AREA_SIZE);\n        memcpy(data_section->reg_init_area, data[section_id].reg_init_region, REG_INIT_AREA_SIZE);\n\n        // Fixup the EFLAGS init value to ensure we don't set invalid flags\n        uint64_t eflags_value = ((uint64_t *)data_section->reg_init_area)[EFLAGS_INIT_ID];\n        eflags_value = (eflags_value & eflags_must_clear_mask) | eflags_must_set_mask;\n        ((uint64_t *)data_section->reg_init_area)[EFLAGS_INIT_ID] = eflags_value;\n    }\n\n    return 0;\n}\n\n/// @brief Accessor for the sandbox\n/// @return Pointer to the sandbox\nsandbox_t *get_sandbox() { return sandbox; }\n\n// =================================================================================================\n// Constructor and destructor\n// =================================================================================================\n/// @brief Allocate memory for the sandbox\n/// @param n_actors Number of actors in the test case\n/// @return -1 on failure, 0 on success\nint allocate_sandbox(uint64_t n_actors)\n{\n    sandbox = (sandbox_t *)malloc(sizeof(sandbox_t));\n    if (sandbox == NULL) {\n        return -1;\n    }\n\n    // Allocate memory for the data and code sections, with page alignment\n    sandbox->data = (data_section_t *)aligned_alloc(PAGE_SIZE, n_actors * sizeof(data_section_t));\n    if (sandbox->data == NULL) {\n        free(sandbox);\n        return -1;\n    }\n\n    sandbox->code = (code_section_t *) test_case_entry;\n    mprotect(sandbox->code, n_actors * sizeof(code_section_t), PROT_READ | PROT_WRITE | PROT_EXEC);\n\n    return 0;\n}\n\n/// @brief Free the memory allocated for the sandbox\nvoid free_sandbox()\n{\n    free(sandbox->data);\n    free(sandbox);\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/sandbox.h",
    "content": "/// File: Sandbox layout\n/// (see docs/sandbox.md for layout description)\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef SANDBOX_H\n#define SANDBOX_H\n\n#include <stdint.h>\n\n#include \"rcbf.h\"\n#include \"rdbf.h\"\n#include \"sandbox_const.h\"\n\n// =================================================================================================\n// Data sections\n// =================================================================================================\n\n// IMPORTANT! This structure must match the layout in rvzr/executor_km/include/sandbox_manager.h\ntypedef struct {\n    uint8_t macro_stack[MACRO_STACK_SIZE];     // stack for storing registers when calling macros\n    uint8_t underflow_pad[UNDERFLOW_PAD_SIZE]; // zero-initialized region for accidental underflows\n    uint8_t main_area[MAIN_AREA_SIZE];         // first input page; does not cause faults\n    uint8_t faulty_area[FAULTY_AREA_SIZE];     // second input page; causes a (configurable) fault\n    uint8_t reg_init_area[REG_INIT_AREA_SIZE]; // region for initializing registers\n    uint8_t overflow_pad[OVERFLOW_PAD_SIZE];   // zero-initialized region for accidental overflows\n} __attribute__((packed)) data_section_t;\n\n// =================================================================================================\n// Code sections\n// =================================================================================================\n\n// IMPORTANT! This structure must match the layout in rvzr/executor_km/include/sandbox_manager.h\ntypedef struct {\n    uint8_t code[MAX_EXPANDED_SECTION_SIZE];\n    uint8_t unused[MACRO_AREA_SIZE]; // unused; mirrors the macro area in sandbox_manager.h\n} __attribute__((packed)) code_section_t;\n\n_Static_assert(MAX_ACTORS * sizeof(code_section_t) == (unsigned long)TEST_CASE_MAX_SIZE,\n               \"Invalid value of TEST_CASE_MAX_SIZE\");\n\n// =================================================================================================\n// sandbox_t\n// =================================================================================================\ntypedef struct {\n    data_section_t *data;\n    code_section_t *code;\n} sandbox_t;\n\n// =================================================================================================\n// Functions\n// =================================================================================================\nint load_code_in_sandbox(rcbf_t *rcbf_data);\nint load_data_in_sandbox(rdbf_t *rdbf_data, int input_id);\nsandbox_t *get_sandbox();\nint allocate_sandbox(uint64_t n_actors);\nvoid free_sandbox();\n\n#endif // SANDBOX_H\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/sandbox_const.h",
    "content": "/// File: Constants for the sandbox layout\n/// (see docs/sandbox.md for layout description)\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#ifndef SANDBOX_CONST_H\n#define SANDBOX_CONST_H\n\n#define PAGE_SIZE 4096U\n\n// layout of code_section_t\n#define MACRO_STACK_SIZE   64\n#define UNDERFLOW_PAD_SIZE (PAGE_SIZE - MACRO_STACK_SIZE)\n#define MAIN_AREA_SIZE     PAGE_SIZE\n#define FAULTY_AREA_SIZE   PAGE_SIZE\n#define REG_INIT_AREA_SIZE 320 // 8 64-bit GPRs + 8 256-bit YMMs\n#define OVERFLOW_PAD_SIZE  (PAGE_SIZE - REG_INIT_AREA_SIZE)\n\n#define REG_INIT_AREA_SIZE_ALIGNED PAGE_SIZE\n#define STACK_OFFSET               (MAIN_AREA_SIZE - 8)\n#define REG_INIT_OFFSET            (MAIN_AREA_SIZE + FAULTY_AREA_SIZE)\n#define SIMD_INIT_OFFSET           (REG_INIT_OFFSET + 64)\n\n#define GPR_SIZE       8\n#define EFLAGS_INIT_ID 6\n#define RSP_INIT_ID    7\n\n// layout of code_section_t\n#define MAX_ACTORS                16U\n#define MAX_EXPANDED_SECTION_SIZE (PAGE_SIZE * 2)\n#define MACRO_AREA_SIZE           (PAGE_SIZE)\n#define TEST_CASE_MAX_SIZE        (MAX_ACTORS * (MAX_EXPANDED_SECTION_SIZE + MACRO_AREA_SIZE))\n\n#endif // SANDBOX_CONST_H\n"
  },
  {
    "path": "rvzr/model_dynamorio/adapter/test_case_entry.S",
    "content": ".intel_syntax noprefix\n\n#include \"sandbox_const.h\"\n#define xstr(s) _str(s)\n#define _str(s) str(s)\n#define str(s)  #s\n\n// =================================================================================================\n.text\n\n/// @brief Placeholder for the test case code; This will be dynamically populated at runtime\n///        by sandbox.c\n///        At compile time, a zero-filled area of size TEST_CASE_MAX_SIZE is reserved here.\n/// r15: Address of test case code\n.global test_case_entry\n.align PAGE_SIZE\ntest_case_entry:\n    .rept TEST_CASE_MAX_SIZE\n    .byte 0\n    .endr\n\n\n/// @brief Outer wrapper to set up registers and FLAGS\n///        See docs/devel/registers.md for the explanation of the initialized registers\n/// rdi: Base address of sandbox main_area\n/// rsi: Base address of test case code\n.global test_case_entry_outer\n.align PAGE_SIZE\ntest_case_entry_outer:\n    pushfq\n    push rbx\n    push rcx\n    push rdx\n    push r14\n    push r15\n    push rsp\n\n    // Save arguments\n    mov r14, rdi\n    mov r15, rsi\n\n    // Initialize FLAGS\n    mov rax, qword ptr [r14 + REG_INIT_OFFSET + EFLAGS_INIT_ID * GPR_SIZE]\n    push rax\n    popfq\n\n    // Stack pointer\n    // push rsp\n    // mov rax, qword ptr [r14 + xstr(REG_INIT_OFFSET + RSP_INIT_ID * GPR_SIZE)]\n    // mov rsp, rax\n\n    // Initialize registers\n    mov rax, qword ptr [r14 + REG_INIT_OFFSET + 0x00]\n    mov rbx, qword ptr [r14 + REG_INIT_OFFSET + 0x08]\n    mov rcx, qword ptr [r14 + REG_INIT_OFFSET + 0x10]\n    mov rdx, qword ptr [r14 + REG_INIT_OFFSET + 0x18]\n    mov rsi, qword ptr [r14 + REG_INIT_OFFSET + 0x20]\n    mov rdi, qword ptr [r14 + REG_INIT_OFFSET + 0x28]\n\n    // Initialize MMX registers\n    movq mm0, qword ptr [r14 + SIMD_INIT_OFFSET + 0x00]\n    movq mm1, qword ptr [r14 + SIMD_INIT_OFFSET + 0x08]\n    movq mm2, qword ptr [r14 + SIMD_INIT_OFFSET + 0x10]\n    movq mm3, qword ptr [r14 + SIMD_INIT_OFFSET + 0x18]\n    movq mm4, qword ptr [r14 + SIMD_INIT_OFFSET + 0x20]\n    movq mm5, qword ptr [r14 + SIMD_INIT_OFFSET + 0x28]\n    movq mm6, qword ptr [r14 + SIMD_INIT_OFFSET + 0x30]\n    movq mm7, qword ptr [r14 + SIMD_INIT_OFFSET + 0x38]\n\n    // Initialize YMM registers (overlap with MMX init values is intentional)\n    vmovdqa ymm0, ymmword ptr [r14 + SIMD_INIT_OFFSET + 0x00]\n    vmovdqa ymm1, ymmword ptr [r14 + SIMD_INIT_OFFSET + 0x20]\n    vmovdqa ymm2, ymmword ptr [r14 + SIMD_INIT_OFFSET + 0x40]\n    vmovdqa ymm3, ymmword ptr [r14 + SIMD_INIT_OFFSET + 0x60]\n    vmovdqa ymm4, ymmword ptr [r14 + SIMD_INIT_OFFSET + 0x80]\n    vmovdqa ymm5, ymmword ptr [r14 + SIMD_INIT_OFFSET + 0xa0]\n    vmovdqa ymm6, ymmword ptr [r14 + SIMD_INIT_OFFSET + 0xc0]\n    vmovdqa ymm7, ymmword ptr [r14 + SIMD_INIT_OFFSET + 0xe0]\n\n    call r15\n\n    pop rsp\n    pop r15\n    pop r14\n    pop rdx\n    pop rcx\n    pop rbx\n    popfq\n\n    ret\n\n\n\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/.clang-format",
    "content": "ColumnLimit: 100\nIndentWidth: 4\nBreakBeforeBraces: Linux\nAllowShortIfStatementsOnASingleLine: false\nAlignConsecutiveMacros:\n  Enabled: true\n  AcrossEmptyLines: false\n  AcrossComments: false\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/.clang-tidy",
    "content": "# Reasoning behind excluding some checks:\n# - `misc-include-cleaner`: This is incompatible with the DR headers, where dr_api.h is\n#   expected to be included in all source files.\n# - `modernize-use-trailing-return-type`: This style diverges from the rest of the code base\n#   (e.g., the source of `executor_km`), so it is excluded for uniformity.\n# - `readability-braces-around-statements`: This style diverges from the rest of the code base\n#   (e.g., the source of `executor_km`), so it is excluded for uniformity.\n# - `cppcoreguidelines-pro-type-vararg`: Incompatible with the DR API, such as dr_printf\n# - `cppcoreguidelines-pro-type-union-access`: Incompatible with dr_mcontext_t\n# - `cppcoreguidelines-pro-type-cstyle-cast`: C-style casts are necessary for DR API\n# - `coreguidelines-non-private-member-variables-in-classes`: Accessors make code unreasonably verbose\n# - `cppcoreguidelines-non-private-member-variables-in-classes`: Alias of the above\n# - `llvm-header-guard`: Checks for \"LLVM-style\" include guards, we want to use #pragma once instead\n# - `modernize-use-using`: This style diverges from the rest of the code base\n# - `readability-convert-member-functions-to-static`: This makes code less uniform with the rest of the code base\n# - `bugprone-easily-swappable-parameters`: DR has many C APIs, and this check causes many false positives\n# - `performance-enum-size`: The enum size in the backend is often dictated by the serialization formats, and it causes false positives on this check\n# - `cppcoreguidelines-avoid-magic-numbers`: Duplicate of readability-magic-numbers; excluded for consistency\nChecks: >\n    -*,\n    bugprone-*,\n    cppcoreguidelines-*,\n    llvm-*,\n    misc-*,\n    modernize-*,\n    performance-*,\n    readability-*,\n    -misc-include-cleaner,\n    -misc-use-anonymous-namespace,\n    -modernize-use-trailing-return-type,\n    -readability-braces-around-statements,\n    -cppcoreguidelines-pro-type-vararg,\n    -cppcoreguidelines-pro-type-union-access,\n    -cppcoreguidelines-pro-type-reinterpret-cast,\n    -cppcoreguidelines-pro-type-cstyle-cast,\n    -performance-no-int-to-ptr,\n    -misc-non-private-member-variables-in-classes,\n    -cppcoreguidelines-non-private-member-variables-in-classes,\n    -llvm-header-guard,\n    -modernize-use-using,\n    -readability-convert-member-functions-to-static,\n    -bugprone-easily-swappable-parameters,\n    -performance-enum-size,\n    -cppcoreguidelines-avoid-magic-numbers\n\n# Turn all the warnings from the checks above into errors.\nWarningsAsErrors: \"*\"\n\nHeaderFilterRegex: \"^backend/include/.*$\"\n\nCheckOptions:\n    - key: readability-identifier-length.IgnoredVariableNames\n      value: \"pc|mc|bb|dc|it\"\n    - key: readability-identifier-length.IgnoredParameterNames\n      value: \"pc|mc|bb|dc|it\"\n    - key: bugprone-easily-swappable-parameters.IgnoredParameterNames\n      value: '\"\";drcontext;wrapctx;;user_data;xflags;max_nesting_'\n    - key: readability-magic-numbers.IgnoredIntegerValues\n      value: '0;1;2;4;8;-1;-0x1ULL'\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.13)\n\nproject(dr_model)\n\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_STANDARD_REQUIRED TRUE)\nset(CMAKE_EXPORT_COMPILE_COMMANDS ON)\n\n# add all C++ files in the current directory\nfile(GLOB MAIN_SOURCES \"*.cpp\")\nfile(GLOB TRACERS \"tracers/*.cpp\")\nfile(GLOB SPECULATORS \"speculators/*.cpp\")\nadd_library(dr_model SHARED ${MAIN_SOURCES} ${TRACERS} ${SPECULATORS})\n\n# add `include` directory to the include path\ntarget_include_directories(dr_model PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)\n\nfind_package(DynamoRIO)\nif (NOT DynamoRIO_FOUND)\n    message(FATAL_ERROR \"DynamoRIO package required to build\")\nendif(NOT DynamoRIO_FOUND)\n\nconfigure_DynamoRIO_client(dr_model)\nuse_DynamoRIO_extension(dr_model drmgr)\nuse_DynamoRIO_extension(dr_model drreg)\nuse_DynamoRIO_extension(dr_model drutil)\nuse_DynamoRIO_extension(dr_model drx)\nuse_DynamoRIO_extension(dr_model drwrap)\nuse_DynamoRIO_extension(dr_model drsyms)\n\n# Ensure C++17 standard is preserved after DynamoRIO configuration\nset_target_properties(dr_model PROPERTIES\n    CXX_STANDARD 17\n    CXX_STANDARD_REQUIRED ON\n)\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/cli.cpp",
    "content": "///\n/// File: Dr. Model Command Line Interface\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <algorithm>\n#include <cstdint>\n#include <string>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_tools.h>\n#include <droption.h>\n\n#include \"cli.hpp\"\n#include \"factory.hpp\"\n\nusing std::string;\n\nusing dynamorio::droption::DROPTION_SCOPE_CLIENT;\nusing dynamorio::droption::droption_t;\n\nstatic bool validate_tracer(cli_args_t *parsed_args);\nstatic bool validate_speculator(cli_args_t *parsed_args);\nstatic bool validate_taint_tracker(cli_args_t *parsed_args);\n\nstatic const int max_reasonable_nesting = 100;\nstatic const int max_reasonable_spec_window = 1000;\n\n// =================================================================================================\n// List of options\n// =================================================================================================\nnamespace\n{\n// clang-format off\n// General Configuration\n\n// Mode selector: standalone vs serving as a backend for rvzr\nconst droption_t<string> op_mode(DROPTION_SCOPE_CLIENT,\n                        \"mode\", \"standalone\",\n                        \"Mode of operation: standalone or rvzr\",\n                        \"Mode of operation: \"\n                        \"standalone (default) or rvzr (used as a backend for rvzr)\");\n\n// Tracer Configuration\nconst droption_t<string> op_tracer_name(DROPTION_SCOPE_CLIENT,\n                        \"tracer\", \"ct\",\n                        \"Type of the tracer; equivalent to the observation clause of a contract\",\n                        \"Type of the tracer; equivalent to the observation clause of a contract\");\nconst droption_t<string> op_instrumented_func(DROPTION_SCOPE_CLIENT,\n                        \"instrumented-func\", \"__libc_start_main\",\n                        \"Name of the function to instrument.\",\n                        \"Name of the function to instrument.\");\nconst droption_t<string> op_trace_output(DROPTION_SCOPE_CLIENT,\n                        \"trace-output\", \"rvzr_trace.dat\",\n                        \"Where to save the trace (in binary format).\",\n                        \"Where to save the trace (in binary format).\");\nconst droption_t<bool>  op_print_trace(DROPTION_SCOPE_CLIENT,\n                        \"print-trace\", false,\n                        \"Dump trace entries to STDOUT while they are being produced.\",\n                        \"Dump trace entries to STDOUT while they are being produced.\");\n\n// Debugging\nconst droption_t<int>    op_log_level(DROPTION_SCOPE_CLIENT,\n                        \"log-level\", 0,\n                        \"Verbosity level of the debug logger (0 = disabled).\",\n                        \"Verbosity level of the debug logger (0 = disabled).\");\nconst droption_t<string> op_debug_output(DROPTION_SCOPE_CLIENT,\n                        \"debug-trace-output\", \"rvzr_dbg_trace.dat\",\n                        \"Where to save the debug log (in binary format).\",\n                        \"Where to save the debug log (in binary format).\");\nconst droption_t<bool>   op_print_dbg_trace(DROPTION_SCOPE_CLIENT,\n                        \"print-debug-trace\", false,\n                        \"Dump log entries to STDOUT while they are being produced.\",\n                        \"Dump log entries to STDOUT while they are being produced.\");\n\n// Speculator Configuration\nconst droption_t<string> op_speculator_name(DROPTION_SCOPE_CLIENT,\n                        \"speculator\", \"seq\",\n                        \"Type of the speculator; equivalent to the execution clause of a contract\",\n                        \"Type of the speculator; equivalent to the execution clause of a contract\");\nconst droption_t<int>    op_max_nesting(DROPTION_SCOPE_CLIENT,\n                        \"max-nesting\", 1,\n                        \"Maximum number of nested speculations.\",\n                        \"Maximum number of nested speculations.\");\nconst droption_t<int>    op_max_spec_window(DROPTION_SCOPE_CLIENT,\n                        \"max-spec-window\", 250,\n                        \"Maximum number of speculative instructions.\",\n                        \"Maximum number of speculative instructions.\");\nconst droption_t<uint64_t> op_poison_value(DROPTION_SCOPE_CLIENT,\n                        \"poison-value\", 0,\n                        \"Value to forward on speculative faulty loads. If 0, speculative loads cause a rollback.\",\n                        \"Value to forward on speculative faulty loads. If 0, speculative loads cause a rollback.\");\n\n// Taint Tracker Configuration\nconst droption_t<bool>   op_enable_taint_tracker(DROPTION_SCOPE_CLIENT,\n                        \"enable-taint-tracker\", false,\n                        \"Enable the taint tracker for contract-based input generation.\",\n                        \"Enable the taint tracker for contract-based input generation.\");\nconst droption_t<string> op_taint_output(DROPTION_SCOPE_CLIENT,\n                        \"taint-output\", \"\",\n                        \"Where to save the taint information (in binary format).\",\n                        \"Where to save the taint information (in binary format).\");\n\n// Listing Options\nconst droption_t<bool> op_list_tracers(DROPTION_SCOPE_CLIENT,\n                        \"list-tracers\", false,\n                        \"List all available tracers (aka, observation clauses).\",\n                        \"List all available tracers (aka, observation clauses).\");\nconst droption_t<bool> op_list_speculators(DROPTION_SCOPE_CLIENT,\n                        \"list-speculators\", false,\n                        \"List all available speculators (aka execution clauses).\",\n                        \"List all available speculators (aka execution clauses).\");\n// clang-format on\n} // namespace\n\n// =================================================================================================\n// CLI parser\n// =================================================================================================\nvoid parse_cli(int argc, const char **argv, DR_PARAM_OUT cli_args_t &parsed_args)\n{\n    // Parse the arguments using DynamoRIO's droption parser\n    string err_msg;\n    const bool parsed = dynamorio::droption::droption_parser_t::parse_argv(\n        DROPTION_SCOPE_CLIENT, argc, argv, &err_msg, nullptr);\n\n    // Print error message and abort if the arguments cannot be parsed\n    if (not parsed) {\n        dr_printf(\"Error parsing arguments: %s\\n\", err_msg.c_str());\n        dr_printf(\n            \"Usage: %s\\n\",\n            dynamorio::droption::droption_parser_t::usage_long(DROPTION_SCOPE_CLIENT).c_str());\n        dr_abort();\n    }\n\n    // Select overall mode\n    const std::string mode_str = op_mode.get_value();\n    if (mode_str == \"standalone\") {\n        parsed_args.mode = Mode::STANDALONE;\n    } else if (mode_str == \"rvzr\") {\n        parsed_args.mode = Mode::RVZR_BACKEND;\n    } else {\n        dr_printf(\"Invalid mode: %s\\n\", mode_str.c_str());\n        dr_abort();\n    }\n\n    // Set the parsed arguments\n    parsed_args.tracer_type = op_tracer_name.get_value();\n    parsed_args.instrumented_func = op_instrumented_func.get_value();\n    parsed_args.trace_output = op_trace_output.get_value();\n    parsed_args.print_trace = op_print_trace.get_value();\n    parsed_args.log_level = op_log_level.get_value();\n    parsed_args.debug_output = op_debug_output.get_value();\n    parsed_args.print_dbg_trace = op_print_dbg_trace.get_value();\n    parsed_args.speculator_type = op_speculator_name.get_value();\n    parsed_args.max_nesting = op_max_nesting.get_value();\n    parsed_args.max_spec_window = op_max_spec_window.get_value();\n    parsed_args.enable_taint_tracker = op_enable_taint_tracker.get_value();\n    parsed_args.taint_output = op_taint_output.get_value();\n    parsed_args.list_tracers = op_list_tracers.get_value();\n    parsed_args.list_speculators = op_list_speculators.get_value();\n    uint64_t poison_value = op_poison_value.get_value();\n    if (poison_value == 0) {\n        parsed_args.poison_value = {};\n    } else {\n        parsed_args.poison_value = poison_value;\n    }\n\n    // Check values\n    if (not validate_tracer(&parsed_args)) {\n        dr_abort();\n    }\n    if (not validate_speculator(&parsed_args)) {\n        dr_abort();\n    }\n    if (not validate_taint_tracker(&parsed_args)) {\n        dr_abort();\n    }\n}\n\n// =================================================================================================\n// Validators\n// =================================================================================================\nbool validate_tracer(cli_args_t *parsed_args)\n{\n    // Check if the tracer type is supported\n    auto tracer_names = get_tracer_list();\n    auto match = std::find(tracer_names.begin(), tracer_names.end(), parsed_args->tracer_type);\n    if (match == tracer_names.end()) {\n        dr_printf(\"Invalid tracer type: %s\\n\", parsed_args->tracer_type.c_str());\n        dr_printf(\"Available tracers: [ \");\n        for (const auto &tracer : tracer_names) {\n            dr_printf(\"%s, \", tracer.c_str());\n        }\n        dr_printf(\"]\\n\");\n        return false;\n    }\n\n    return true;\n}\n\nbool validate_speculator(cli_args_t *parsed_args)\n{\n    // Check if the speculator type is supported\n    auto speculator_names = get_speculator_list();\n    auto match =\n        std::find(speculator_names.begin(), speculator_names.end(), parsed_args->speculator_type);\n    if (match == speculator_names.end()) {\n        dr_printf(\"Invalid speculator type: %s\\n\", parsed_args->speculator_type.c_str());\n        dr_printf(\"Available speculators: [ \");\n        for (const auto &spec : speculator_names) {\n            dr_printf(\"%s, \", spec.c_str());\n        }\n        dr_printf(\"]\\n\");\n        return false;\n    }\n\n    // Check if the maximum nesting level is valid\n    // - Negative or zero values have no meaning;\n    // - Anything greater than 100 is unrealistic on modern hardware\n    if (parsed_args->max_nesting <= 0) {\n        dr_printf(\"Invalid maximum nesting level: %d\\n\", parsed_args->max_nesting);\n        dr_printf(\"Maximum nesting level must be greater than 0.\\n\");\n        return false;\n    }\n    if (parsed_args->max_nesting > max_reasonable_nesting) {\n        dr_printf(\"Invalid maximum nesting level: %d\\n\", parsed_args->max_nesting);\n        dr_printf(\"Maximum nesting level must be less than or equal to 100.\\n\");\n        return false;\n    }\n\n    // Check if the maximum speculation window is valid\n    // - Negative or zero values have no meaning;\n    // - Anything greater than 1000 is unrealistic on modern hardware\n    if (parsed_args->max_spec_window <= 0) {\n        dr_printf(\"Invalid maximum speculation window: %d\\n\", parsed_args->max_spec_window);\n        dr_printf(\"Maximum speculation window must be greater than 0.\\n\");\n        return false;\n    }\n    if (parsed_args->max_spec_window > max_reasonable_spec_window) {\n        dr_printf(\"Invalid maximum speculation window: %d\\n\", parsed_args->max_spec_window);\n        dr_printf(\"Maximum speculation window must be less than or equal to 1000.\\n\");\n        return false;\n    }\n\n    return true;\n}\n\nstatic bool validate_taint_tracker(cli_args_t *parsed_args)\n{\n    // Taint tracker is only available for backend mode\n    if (parsed_args->enable_taint_tracker and parsed_args->mode != Mode::RVZR_BACKEND) {\n        dr_printf(\n            \"Taint tracker can only be enabled when the model is used as a backend for rvzr.\\n\");\n        return false;\n    }\n\n    // If the taint tracker is enabled, check that the output path is valid\n    if (parsed_args->enable_taint_tracker) {\n        if (parsed_args->taint_output.empty()) {\n            dr_printf(\n                \"Taint tracker output path cannot be empty when the taint tracker is enabled.\\n\");\n            return false;\n        }\n    }\n\n    return true;\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/dispatcher.cpp",
    "content": "///\n/// File: Class responsible for instrumenting instructions\n///       in the target application with a call to a dispatch function.\n///       The function, in turn, calls service classes (e.g., Tracer, Speculator, etc)\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <cstdint>\n#include <memory>\n#include <string>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_tools.h>\n#include <drmgr.h>\n#include <drwrap.h>\n\n#include \"cli.hpp\"\n#include \"dispatcher.hpp\"\n#include \"factory.hpp\"\n#include \"observables.hpp\"\n#include \"util.hpp\"\n\nusing std::string;\n\n/// Defined by model.cpp\nextern std::unique_ptr<Dispatcher> glob_dispatcher; // NOLINT\n\n// =================================================================================================\n// Runtime functions\n// =================================================================================================\n\n/// @brief Dispatch function that calls the per-instruction functions in the service modules\n/// @param mc Machine context of the current instruction\n/// @param bundle The bundle of service modules to be called\n/// @param instr Observables of the current instruction\n/// @return The PC of the next instruction to be executed (if redirection is necessary);\n///         otherwise, 0 (zero)\nstatic pc_t instruction_dispatch(dr_mcontext_t *mc, void *dc, const Dispatcher *dispatcher,\n                                 instr_obs_t instr)\n{\n    dispatcher->logger->log_instruction(instr, mc, dispatcher->speculator->get_nesting_level());\n    dispatcher->taint_tracker->track_instruction(instr, mc, dc);\n    dispatcher->tracer->observe_instruction(instr, mc, dc);\n    const pc_t next_pc = dispatcher->speculator->handle_instruction(instr, mc, dc);\n    return next_pc;\n}\n\n/// @brief Dispatch function that calls the per-memory-access functions in the service modules\n/// @param drcontext\n/// @param mc\n/// @param bundle\n/// @param pc\n/// @return The PC of the next instruction to be executed (if redirection is necessary);\n///         otherwise, 0 (zero)\nstatic pc_t mem_access_dispatch(void *dc, dr_mcontext_t *mc, const Dispatcher *dispatcher, pc_t pc)\n{\n    // Decode the instruction using the shared cache to extract its memory references\n    instr_t *instr = dispatcher->decoder->get_decoded_instr(dc, (byte *)pc);\n\n    // Identify the size of the memory reference\n    // (assumed that all memory references for the instruction are of the same size)\n    const uint64_t size = instr_memory_reference_size(instr);\n\n    // Loop over all memory operands and call service modules for each\n    uint index = 0;\n    bool is_write = false;\n    app_pc addr = nullptr;\n    while (instr_compute_address_ex(instr, mc, index, &addr, &is_write)) {\n        dispatcher->logger->log_mem_access(is_write, addr, size);\n        dispatcher->taint_tracker->track_memory_access(is_write, (void *)addr, size);\n        dispatcher->tracer->observe_mem_access(is_write, addr, size);\n        if (not dispatcher->speculator->handle_mem_access(is_write, (void *)addr, size)) {\n            return dispatcher->speculator->rollback(mc);\n        }\n\n        index++;\n    }\n\n    return 0;\n}\n\n/// @brief Callback function called for every instruction in the instrumented function\n/// @param bundle The bundle of service modules to be called\n/// @param opcode The opcode of the instruction\n/// @param pc The program counter (address) of the instruction\n/// @param has_mem_ref Flag indicating whether the instruction has a memory reference\nstatic void dispatch_callback(uint64_t opcode, uint64_t pc, uint64_t has_mem_ref)\n{\n    // Get the global dispatcher\n    const Dispatcher *dispatcher = glob_dispatcher.get();\n    DR_ASSERT_MSG(dispatcher != nullptr, \"[ERROR] glob_dispatcher is null\\n\");\n\n    // Nothing to do if we're outside of the instrumented function\n    if (not dispatcher->is_instrumentation_on()) {\n        return;\n    }\n\n    // get current context\n    void *drcontext = dr_get_current_drcontext();\n    dr_mcontext_t mc = {sizeof(mc), DR_MC_ALL};\n    dr_get_mcontext(drcontext, &mc);\n\n    // create an instruction instance for the current instruction\n    const instr_obs_t instr = {\n        .opcode = opcode,\n        .pc = (pc_t)pc,\n        .has_mem_access = (bool)has_mem_ref,\n    };\n\n    // pass down to instruction dispatch functions and redirect execution if needed\n    pc_t next_pc = instruction_dispatch(&mc, drcontext, dispatcher, instr);\n    if (next_pc != 0) {\n        mc.pc = (byte *)next_pc;\n        dr_redirect_execution(&mc);\n        return; // unreachable\n    }\n    dr_set_mcontext(drcontext, &mc);\n    if (has_mem_ref == 0) {\n        return;\n    }\n\n    // pass down to memory access dispatch functions and redirect execution if needed\n    next_pc = mem_access_dispatch(drcontext, &mc, dispatcher, instr.pc);\n    if (next_pc != 0) {\n        mc.pc = (byte *)next_pc;\n        dr_redirect_execution(&mc);\n        return; // unreachable\n    }\n    dr_set_mcontext(drcontext, &mc);\n}\n\n/// @brief Callback function called upon return from the instrumented function\nstatic void exit_callback()\n{\n    // Get the global dispatcher\n    Dispatcher *dispatcher = glob_dispatcher.get();\n    DR_ASSERT_MSG(dispatcher != nullptr, \"[ERROR] glob_dispatcher is null\\n\");\n    DR_ASSERT_MSG(dispatcher->is_instrumentation_on(),\n                  \"[ERROR] Instrumentation disabled when exiting instrumented function\");\n\n    // get current context\n    void *drcontext = dr_get_current_drcontext();\n    dr_mcontext_t mc = {sizeof(mc), DR_MC_ALL};\n    dr_get_mcontext(drcontext, &mc);\n\n    // Rollback speculation if we're speculatively exiting the target function\n    if (dispatcher->speculator->in_speculation) {\n        // Perform rollback\n        const pc_t newpc = dispatcher->speculator->rollback(&mc);\n        mc.pc = (byte *)newpc;\n        dr_redirect_execution(&mc);\n        return; // unreachable\n    }\n\n    // Architectural exit: stop the instrumentation\n    flush_bb_cache();\n    dispatcher->finalize();\n    dr_set_mcontext(drcontext, &mc);\n}\n\nbool Dispatcher::handle_exception(void *drcontext, dr_siginfo_t *siginfo) const\n{\n    logger->log_exception(siginfo);\n    // Exceptions on speculative paths are handled by the speculator.\n    const bool redirected = speculator->handle_exception(drcontext, siginfo);\n    if (redirected)\n        return true; // intercepted\n\n    // Architectural exceptions are forwarded to the program\n    dr_printf(\"[XCPT] Dispatcher::handle_exception: exception on a non-speculative path\\n\");\n    tracer->observe_exception(siginfo);\n    return false; // not intercepted\n}\n\n// =================================================================================================\n// Instrumentation-time Methods\n// =================================================================================================\nvoid Dispatcher::start()\n{\n    DR_ASSERT_MSG(not is_initialized,\n                  \"[ERROR] Attempting to initialize Dispatcher multiple times.\");\n\n    instrumentation_on = true;\n    is_initialized = true;\n\n    // Turn service modules on\n    taint_tracker->enable();\n    tracer->enable();\n    speculator->enable();\n}\n\nvoid Dispatcher::restart()\n{\n    DR_ASSERT_MSG(is_initialized,\n                  \"[ERROR] Attempting to restart Dispatcher without initialization.\");\n\n    instrumentation_on = true;\n\n    // Turn service modules on\n    taint_tracker->enable();\n    tracer->enable();\n    speculator->enable();\n}\n\nvoid Dispatcher::finalize()\n{\n    if (not instrumentation_on)\n        return;\n\n    // Turn service modules off\n    taint_tracker->finalize();\n    tracer->finalize();\n    speculator->disable();\n\n    instrumentation_on = false;\n}\n\ndr_emit_flags_t Dispatcher::instrument_instruction(void *drcontext, instrlist_t *bb,\n                                                   instr_t *instr) const\n{\n    // Nothing to do if we're outside of the instrumented function\n    if (not instrumentation_on) {\n        return DR_EMIT_DEFAULT;\n    }\n\n    // Get a pointer to the instruction's original form (pre event_bb_app2app call)\n    instr_t *org_instr = drmgr_orig_app_instr_for_fetch(drcontext);\n    if (org_instr == nullptr) { // DR tell us that this instruction should be skipped\n        return DR_EMIT_DEFAULT;\n    }\n\n    // Get instruction parameters\n    const opnd_t opcode = OPND_CREATE_INT64(instr_get_opcode(org_instr));\n    const opnd_t pc_op = OPND_CREATE_INTPTR(instr_get_app_pc(org_instr));\n    const opnd_t has_mem_ref =\n        OPND_CREATE_INT64(instr_reads_memory(org_instr) or instr_writes_memory(org_instr));\n\n    // Add a clean call to the dispatch callback, which will forward the call to the service\n    // modules\n    const int dispatch_callback_nargs = 3;\n    dr_insert_clean_call(drcontext, bb, instr, (void *)dispatch_callback, false,\n                         dispatch_callback_nargs, opcode, pc_op, has_mem_ref);\n\n    return DR_EMIT_DEFAULT;\n}\n\nvoid Dispatcher::instrument_exit(void *drcontext, instrlist_t *bb, instr_t *instr) const\n{\n    dr_insert_clean_call(drcontext, bb, instr, (void *)exit_callback, false, 0);\n}\n\n// =================================================================================================\n// Constructors and Destructors\n// =================================================================================================\nDispatcher::Dispatcher(cli_args_t *cli_args) : instrumentation_on(false)\n{\n    // Create service modules\n    logger = create_logger(cli_args->debug_output, cli_args->log_level, cli_args->print_dbg_trace);\n    decoder = std::make_unique<Decoder>();\n    taint_tracker = create_taint_tracker(cli_args->enable_taint_tracker, cli_args->taint_output,\n                                         *logger, *decoder);\n    tracer = create_tracer(cli_args->tracer_type, cli_args->trace_output, *logger, *taint_tracker,\n                           *decoder, cli_args->print_trace);\n    speculator = create_speculator(cli_args->speculator_type, cli_args->max_nesting,\n                                   cli_args->max_spec_window, *logger, *taint_tracker, *decoder,\n                                   cli_args->poison_value);\n}\n\nDispatcher::~Dispatcher()\n{\n    logger.reset();\n    decoder.reset();\n    tracer.reset();\n    speculator.reset();\n    taint_tracker.reset();\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/factory.cpp",
    "content": "///\n/// File: Implementation factory functions defined in factory.hpp\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <functional>\n#include <memory>\n#include <stdexcept>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"factory.hpp\"\n#include \"logger.hpp\"\n#include \"speculator_abc.hpp\"\n#include \"speculators/cond.hpp\"\n#include \"speculators/seq.hpp\"\n#include \"taint_tracker.hpp\"\n#include \"tracer_abc.hpp\"\n#include \"tracers/ct.hpp\"\n#include \"tracers/ind.hpp\"\n#include \"tracers/pc.hpp\"\n#include \"types/decoder.hpp\"\n\nusing std::function;\nusing std::string;\nusing std::unique_ptr;\nusing std::vector;\n\nnamespace\n{\n\nconst std::unordered_map<string, function<unique_ptr<TracerABC>(const string &, Logger &,\n                                                                TaintTracker &, Decoder &, bool)>>\n    tracer_factories = {\n        {\n            \"ct\",\n            [](const string &out_path, Logger &logger, TaintTracker &taint_tracker,\n               Decoder &decoder, bool print) {\n                return std::make_unique<TracerCT>(out_path, logger, taint_tracker, decoder, print);\n            },\n        },\n        {\n            \"pc\",\n            [](const string &out_path, Logger &logger, TaintTracker &taint_tracker,\n               Decoder &decoder, bool print) {\n                return std::make_unique<TracerPC>(out_path, logger, taint_tracker, decoder, print);\n            },\n        },\n        {\n            \"ind\",\n            [](const string &out_path, Logger &logger, TaintTracker &taint_tracker,\n               Decoder &decoder, bool print) {\n                return std::make_unique<TracerInd>(out_path, logger, taint_tracker, decoder, print);\n            },\n        }};\n\nconst std::unordered_map<string,\n                         function<unique_ptr<SpeculatorABC>(int, int, Logger &, TaintTracker &,\n                                                            Decoder &, std::optional<uint64_t>)>>\n    speculator_factories = {\n        {\n            \"seq\",\n            [](int max_nesting_, int max_spec_window_, Logger &logger, TaintTracker &taint_tracker,\n               Decoder &decoder, std::optional<uint64_t> poison_value) {\n                return std::make_unique<SpeculatorSeq>(max_nesting_, max_spec_window_, logger,\n                                                       taint_tracker, decoder, poison_value);\n            },\n        },\n        {\n            \"cond\",\n            [](int max_nesting_, int max_spec_window_, Logger &logger, TaintTracker &taint_tracker,\n               Decoder &decoder, std::optional<uint64_t> poison_value) {\n                return std::make_unique<SpeculatorCond>(max_nesting_, max_spec_window_, logger,\n                                                        taint_tracker, decoder, poison_value);\n            },\n        }};\n\n} // namespace\n\nunique_ptr<TracerABC> create_tracer(const string &tracer_type, const string &out_path,\n                                    Logger &logger, TaintTracker &taint_tracker, Decoder &decoder,\n                                    bool print)\n{\n    try {\n        return tracer_factories.at(tracer_type)(out_path, logger, taint_tracker, decoder, print);\n    } catch (const std::out_of_range &e) {\n        throw std::invalid_argument(\"Unexpected tracer type: \" + tracer_type);\n    }\n}\n\nvector<string> get_tracer_list()\n{\n    vector<string> tracer_list;\n    tracer_list.reserve(tracer_factories.size());\n    for (const auto &tracer : tracer_factories) {\n        tracer_list.push_back(tracer.first);\n    }\n    return tracer_list;\n}\n\nstd::unique_ptr<SpeculatorABC> create_speculator(const string &speculator_type, int max_nesting_,\n                                                 int max_spec_window_, Logger &logger,\n                                                 TaintTracker &taint_tracker, Decoder &decoder,\n                                                 std::optional<uint64_t> poison_value)\n{\n    try {\n        return speculator_factories.at(speculator_type)(max_nesting_, max_spec_window_, logger,\n                                                        taint_tracker, decoder, poison_value);\n    } catch (const std::out_of_range &e) {\n        throw std::invalid_argument(\"Unexpected speculator type: \" + speculator_type);\n    }\n}\n\nvector<string> get_speculator_list()\n{\n    vector<string> speculator_list;\n    speculator_list.reserve(speculator_factories.size());\n    for (const auto &speculator : speculator_factories) {\n        speculator_list.push_back(speculator.first);\n    }\n    return speculator_list;\n}\n\nunique_ptr<Logger> create_logger(const string &out_path, int level, bool print)\n{\n    // Sanitize log level\n    if (level >= Logger::log_level_t::LOG_MAX) {\n        level = Logger::log_level_t::LOG_MAX - 1;\n    } else if (level < 0) {\n        level = 0;\n    }\n\n    return std::make_unique<Logger>(out_path, (Logger::log_level_t)level, print);\n}\n\nstd::unique_ptr<TaintTracker> create_taint_tracker(bool enable, const std::string &out_path,\n                                                   Logger &logger, Decoder &decoder)\n{\n    if (enable) {\n        return std::make_unique<TaintTracker>(out_path, logger, decoder);\n    }\n    return std::make_unique<NoneTaintTracker>(out_path, logger, decoder);\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/cli.hpp",
    "content": "///\n/// File: Header for the Command Line Interface (cli.cpp)\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstdint>\n#include <optional>\n#include <string>\n\n#include <dr_defines.h> // DR_PARAM_OUT\n\nenum class Mode : uint8_t {\n    STANDALONE,\n    RVZR_BACKEND,\n};\n\nstruct cli_args_t {\n    Mode mode;\n    std::string tracer_type;\n    std::string instrumented_func;\n    std::string trace_output;\n    bool print_trace;\n    int log_level;\n    std::string debug_output;\n    bool print_dbg_trace;\n    std::string speculator_type;\n    int max_nesting;\n    int max_spec_window;\n    bool enable_taint_tracker;\n    std::string taint_output;\n    bool list_tracers;\n    bool list_speculators;\n    std::optional<uint64_t> poison_value;\n};\n\n/// @brief Parse the command line arguments\n/// @param argc Standard argument count\n/// @param argv Standard argument vector\n/// @param parsed_args Output structure with the parsed arguments\n/// @return void\n/// @exception dr_abort() if the arguments cannot be parsed\nvoid parse_cli(int argc, const char **argv, DR_PARAM_OUT cli_args_t &parsed_args);\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/dispatcher.hpp",
    "content": "///\n/// File: Header for the Dispatcher class,\n///       responsible for instrumenting the target application with calls to service classes\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <memory>\n\n#include <dr_api.h> // NOLINT\n\n#include \"cli.hpp\"\n#include \"logger.hpp\"\n#include \"speculator_abc.hpp\"\n#include \"taint_tracker.hpp\"\n#include \"tracer_abc.hpp\"\n#include \"types/decoder.hpp\"\n\n/// @brief Dispatcher class responsible for adding instrumentation to instructions\n///        in the target application and calling the appropriate\n///        service classes (e.g., Tracer, Speculator, etc)\nclass Dispatcher\n{\n  public:\n    Dispatcher(cli_args_t *cli_args);\n    virtual ~Dispatcher();\n    Dispatcher(const Dispatcher &) = delete;\n    Dispatcher &operator=(const Dispatcher &) = delete;\n    Dispatcher(Dispatcher &&) = delete;\n    Dispatcher &operator=(Dispatcher &&) = delete;\n\n    // ---------------------------------------------------------------------------------------------\n    // Public Attributes\n\n    /// @param logger: shared logger for event tracing\n    std::unique_ptr<Logger> logger = nullptr;\n    /// @param decoder: shared instruction decode cache used by all modules\n    std::unique_ptr<Decoder> decoder = nullptr;\n    /// @param tracer: implements observation clause\n    std::unique_ptr<TracerABC> tracer = nullptr;\n    /// @param speculator: implements execution clause\n    std::unique_ptr<SpeculatorABC> speculator = nullptr;\n    /// @param taint_tracker: implements taint tracking for input boosting\n    ///        (aka contract-based input generation)\n    std::unique_ptr<TaintTracker> taint_tracker = nullptr;\n\n    /// @param initialized: true if the dispatcher has been already initialized (start was called)\n    bool is_initialized = false;\n\n    // ---------------------------------------------------------------------------------------------\n    // Public Methods\n\n    /// @brief Starts the instrumentation process for a wrapped function\n    /// @return void\n    void start();\n\n    /// @brief Restarts the instrumentation process for a wrapped function\n    /// @return void\n    void restart();\n\n    /// @brief Finalizes the instrumentation process\n    /// @return void\n    void finalize();\n\n    /// @brief Check if the instrumentation has started and is not finalized.\n    [[nodiscard]] bool is_instrumentation_on() const { return instrumentation_on; };\n\n    /// @brief Instruments the instruction \\p instr with calls to callback functions of the\n    /// corresponding type\n    /// @param drcontext The drcontext of the current thread\n    /// @param bb The basic block to be instrumented\n    /// @param instr The instruction to instrument\n    /// @return Flags to be consumed by DynamoRIO instrumentation callbacks\n    dr_emit_flags_t instrument_instruction(void *drcontext, instrlist_t *bb, instr_t *instr) const;\n\n    /// @brief Instruments the exit instruction with the finalization callback\n    /// @param drcontext The drcontext of the current thread\n    /// @param bb The basic block to be instrumented\n    /// @param instr The instruction to instrument\n    /// @return Flags to be consumed by DynamoRIO instrumentation callbacks\n    void instrument_exit(void *drcontext, instrlist_t *bb, instr_t *instr) const;\n\n    /// @brief Passes the exception down to service modules for handling\n    /// @param drcontext The drcontext of the current thread\n    /// @param excpt Pointer to the exception data\n    /// @return True if the exception has been handled (control-flow should be redirected)\n    bool handle_exception(void *drcontext, dr_siginfo_t *siginfo) const;\n\n    // ---------------------------------------------------------------------------------------------\n    // Private Attributes\n  private:\n    bool instrumentation_on;\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/factory.hpp",
    "content": "///\n/// File: Implementation of the Factory Function pattern to create instances\n/// of core classes in the DR Model\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"speculator_abc.hpp\"\n#include \"taint_tracker.hpp\"\n#include \"tracer_abc.hpp\"\n#include \"types/decoder.hpp\"\n\n/// @brief Create a tracer instance based on the tracer name\n/// @param tracer_name The name of the tracer to create\n/// @param out_path The path of the trace output file\n/// @param logger Where to log events for debugging\n/// @param taint_tracker Taint tracker for input boosting\n/// @param decoder Shared instruction decode cache\n/// @param print Print every trace entry to STDOUT during tracing (slow)\n/// @return A unique pointer to the created tracer instance\n/// @throw std::invalid_argument if the tracer name is unknown\nstd::unique_ptr<TracerABC> create_tracer(const std::string &tracer_type,\n                                         const std::string &out_path, Logger &logger,\n                                         TaintTracker &taint_tracker, Decoder &decoder, bool print);\n\n/// @brief Get a list of all available tracers\n/// @return A list of all available tracers\nstd::vector<std::string> get_tracer_list();\n\n/// @brief Create a speculator instance based on the speculator name\n/// @param speculator_name The name of the speculator to create\n/// @param max_nesting_ The maximum nesting level for the speculator\n/// @param max_spec_window_ The maximum size of the speculation window\n/// @param logger Where to log events for debugging\n/// @param taint_tracker Taint tracker for input boosting\n/// @param decoder Shared instruction decode cache\n/// @param poison_value If not empty, this value will be forwarded on speculative faulty loads\n/// @return A unique pointer to the created speculator instance\n/// @throw std::invalid_argument if the speculator name is unknown\nstd::unique_ptr<SpeculatorABC> create_speculator(const std::string &speculator_type,\n                                                 int max_nesting_, int max_spec_window_,\n                                                 Logger &logger, TaintTracker &taint_tracker,\n                                                 Decoder &decoder,\n                                                 std::optional<uint64_t> poison_value);\n\n/// @brief Get a list of all available speculators\n/// @return A list of all available speculators\nstd::vector<std::string> get_speculator_list();\n\n/// @brief Create the shared logger to log debug events\n/// @param out_path Where the logger should log\n/// @param level Verbosity level of the logger\nstd::unique_ptr<Logger> create_logger(const std::string &out_path, int level, bool print);\n\n/// @brief Create the taint tracker instance\n/// @param enable Whether to enable taint tracking\n/// @param out_path Where to write taint tracking output\n/// @param logger Where to log events for debugging\n/// @param decoder Shared instruction decode cache\n/// @return A unique ptr to the created taint tracker instance\nstd::unique_ptr<TaintTracker> create_taint_tracker(bool enable, const std::string &out_path,\n                                                   Logger &logger, Decoder &decoder);\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/logger.hpp",
    "content": "///\n/// File: The Logger centralizes the collection of debug traces from different components\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstdint>\n\n#include <dr_defines.h>\n#include <dr_events.h>\n\n#include \"observables.hpp\"\n#include \"types/debug_trace.hpp\"\n#include \"types/file_buffer.hpp\"\n\n// =================================================================================================\n// Class Definition\n// =================================================================================================\n\n/// @brief The Logger centralizes the collection of debug traces from different components\nclass Logger\n{\n  public:\n    /// @brief Verbosity level of the logger\n    enum log_level_t : uint8_t {\n        LOG_NONE = 0,         // Disabled\n        LOG_INSTRUCTIONS = 1, // Report PC, registers, memory operations and exceptions\n        LOG_SPEC = 2,         // Also report rollbacks and checkpoints\n        LOG_DISASM = 3,       // Also report module_name+offset of each instruction\n        LOG_MAX = 4,\n    };\n\n    /// @param logs_path Path of the file where to dump the binary logs\n    /// @param log_level Verbosity level of the logger\n    /// @param print If true, every debug entry will be printed to STDOUT when inserted\n    Logger(const std::string &logs_path, log_level_t log_level, bool print);\n    ~Logger();\n    Logger(const Logger &) = delete;\n    Logger(Logger &&) = delete;\n    Logger &operator=(const Logger &) = delete;\n    Logger &operator=(Logger &&) = delete;\n\n    /// @return true if logging is enabled\n    [[nodiscard]] bool is_enabled() const { return log_level > LOG_NONE; }\n    /// @brief close the file that backs the logger\n    void close();\n    /// @return the path of the file where the logs are dumped to\n    [[nodiscard]] const std::string &get_filename() const;\n\n    /// @brief log the PC and registers of the current instruction, and whether it is speculative\n    void log_instruction(instr_obs_t instr, dr_mcontext_t *mc, unsigned int nesting_level);\n    /// @brief log a memory operation, including the value that is currently stored at the address\n    void log_mem_access(bool is_write, void *address, uint64_t size);\n    /// @brief log an exception\n    void log_exception(dr_siginfo_t *siginfo);\n    /// @brief log a checkpoint that marks a new speculative window\n    void log_checkpoint(pc_t rollback_pc, uint64_t cur_window_size, size_t cur_store_log_size);\n    /// @brief log a rollback that marks the end of the current speculative window\n    void log_rollback(unsigned nesting, pc_t rollback_pc);\n    /// @brief log a store that is executed to restore the memory state during a rollback\n    void log_rollback_store(uint64_t addr, uint64_t val, size_t size, uint64_t nesting_level);\n    /// @brief log end of trace\n    void log_eot();\n\n  private:\n    static constexpr const unsigned buf_sz = 8 * 1024;\n    /// @param the actual log, implemented as a file-backed buffer\n    FileBackedBuf<debug_trace_entry_t, buf_sz> log;\n    /// @param verbosity level of the logger\n    const log_level_t log_level;\n    /// @param current nesting level of speculation\n    uint8_t cur_nesting_level;\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/observables.hpp",
    "content": "///\n/// File: Collection of types describing an instruction\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstdint>\n\ntypedef uint64_t opcode_t;\ntypedef uint64_t pc_t;\n\n/// @brief Structure describing observable information of an instruction\ntypedef struct {\n    uint64_t opcode;\n    uint64_t pc;\n    bool has_mem_access;\n} instr_obs_t;\n\n/// @brief Structure describing observable information of a memory access\ntypedef struct {\n    uint64_t addr;\n    uint64_t size;\n} mem_access_obs_t;\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/speculator_abc.hpp",
    "content": "///\n/// File: Header for speculator_abc.cpp\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <optional>\n#include <vector>\n\n#include <dr_api.h> // NOLINT\n\n#include \"logger.hpp\"\n#include \"observables.hpp\"\n#include \"taint_tracker.hpp\"\n#include \"types/decoder.hpp\"\n#include \"types/store_log.hpp\"\n\nusing std::uint64_t;\n\n// =================================================================================================\n// Constants and Types\n// =================================================================================================\n\ntypedef struct {\n    pc_t rollback_pc;\n    uint64_t spec_window;\n    dr_mcontext_t mc;\n} checkpoint_t;\n\n// =================================================================================================\n// Class Definition\n// =================================================================================================\n\n/// @brief Abstract base class for all speculators\nclass SpeculatorABC\n{\n  public:\n    SpeculatorABC(int max_nesting_, int max_spec_window_, Logger &logger,\n                  TaintTracker &taint_tracker, Decoder &decoder,\n                  std::optional<uint64_t> poison_value)\n        : max_nesting(max_nesting_), max_spec_window(max_spec_window_), logger(logger),\n          taint_tracker(taint_tracker), decoder(decoder), poison_value(poison_value)\n    {\n    }\n    virtual ~SpeculatorABC() = default;\n    SpeculatorABC(const SpeculatorABC &) = delete;\n    SpeculatorABC &operator=(const SpeculatorABC &) = delete;\n    SpeculatorABC(SpeculatorABC &&) = delete;\n    SpeculatorABC &operator=(SpeculatorABC &&) = delete;\n\n    // ---------------------------------------------------------------------------------------------\n    // Public Attributes\n\n    /// @param Boolean flag indicating whether the speculator is currently active\n    bool in_speculation = false;\n\n    // ---------------------------------------------------------------------------------------------\n    // Public Methods\n\n    void enable();\n    void disable();\n    [[nodiscard]] unsigned int get_nesting_level() const { return nesting; }\n\n    /// @brief Rollback to the last checkpoint, thus undoing all speculative changes to the process\n    ///        state.\n    ///\n    ///        NOTE: The `rollback` method is public, because it a rollback could be caused\n    ///        by external events, such exceptions. The `checkpoint` method, however, is protected\n    ///        because it should never be called externally; instead, the `handle_instruction`\n    ///        and `handle_mem_access` methods will call it internally as a part of\n    ///        the speculation process.\n    ///\n    /// @param mc The machine context of the current instruction\n    /// @return The PC of the next instruction to be executed\n    virtual pc_t rollback(dr_mcontext_t *mc);\n\n    /// @brief Check if the speculation should be skipped (e.g., due to exceeding the maximum\n    ///        nesting, speculation window, or other conditions).\n    /// @param void\n    /// @return true if speculation should be skipped, false otherwise\n    [[nodiscard]] bool skip_speculation() const;\n\n    /// @brief Emulates speculation for the given instruction according to the target contract.\n    ///        Each subclass implements a different contract, hence the implementation\n    ///        of this method is different for each subclass.\n    /// @param opcode The opcode of the instruction\n    /// @param pc The program counter (address) of the instruction\n    /// @param mc The machine context of the instruction\n    /// @param dc The current DR context\n    /// @return 0 if no speculation was triggered or no redirection is needed;\n    ///         otherwise, the PC of the instruction to which the execution should be redirected\n    virtual pc_t handle_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc);\n\n    /// @brief Emulates speculation for the memory access according to the target contract.\n    ///        Each subclass implements a different contract, hence the implementation\n    ///        of this method is different for each subclass.\n    /// @param type The type of the memory access (read or write)\n    /// @param address The address of the memory access\n    /// @param size The size of the memory access\n    /// @return false if the memory access is invalid and is going to produce an exception\n    virtual bool handle_mem_access(bool is_write, void *address, uint64_t size);\n\n    /// @brief Notifies the speculator of an exception, needed to possibly reset internal state.\n    /// @return Whether the speculator redirected execution or not.\n    virtual bool handle_exception(void *drcontext, dr_siginfo_t *siginfo);\n\n  protected:\n    // ---------------------------------------------------------------------------------------------\n    // Protected Attributes\n\n    /// @brief Boolean flag indicating whether the speculation is enabled\n    bool enabled = false;\n\n    /// @param Stack of program state checkpoints (one checkpoint per nested speculation level)\n    std::vector<checkpoint_t> checkpoints;\n\n    /// @param Log of store operations performed during speculation; used to undo the operations\n    ///        during rollback\n    StoreLog store_log;\n\n    /// @param Maximum number of nested speculations\n    unsigned int max_nesting = 0;\n\n    /// @param Current speculation nesting level\n    unsigned int nesting = 0;\n\n    /// @param Maximum speculation window size\n    unsigned int max_spec_window = 0;\n\n    /// @param Current speculation window\n    unsigned int spec_window = 0;\n\n    /// @param Should faulty loads cause a rollback or continue speculation with a poisoned value?\n    const std::optional<uint64_t> poison_value;\n\n    /// @param Used to log checkpoint and rollback events\n    Logger &logger;\n\n    /// @brief Used to taint-track dependencies between input values and contract trace\n    TaintTracker &taint_tracker;\n\n    /// @brief Shared cache for decoded instructions\n    Decoder &decoder;\n\n    // ---------------------------------------------------------------------------------------------\n    // Protected Methods\n\n    /// @brief Record a checkpoint of the current state and store it in the `checkpoints` stack\n    /// @param mc The machine context of the current instruction\n    /// @param pc The program counter (address) of the instruction\n    /// @return void\n    virtual void checkpoint(dr_mcontext_t *mc, pc_t pc);\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/speculators/cond.hpp",
    "content": "///\n/// File: Header for the COND Speculator\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <dr_api.h> // NOLINT\n\n#include \"speculator_abc.hpp\"\n\n/// @brief Conditional Branch Misprediction (COND) Speculator;\n///        This speculator implements a conditional branch misprediction\n///        by flipping the conditions for all conditional branches.\nclass SpeculatorCond : public SpeculatorABC\n{\n  public:\n    using SpeculatorABC::SpeculatorABC;\n\n    /// @brief If the current instruction is a branch, then the speculator will\n    ///        checkpoint the process state and emulate a branch misprediction\n    ///        by jumping to the opposite branch target (e.g., will take the branch if it\n    ///        was supposed to fall though).\n    ///\n    /// @param instr The current instruction\n    /// @param mc The machine context of the instruction\n    /// @param dc The current DR context\n    /// @return 0 if no speculation was triggered or no redirection is needed;\n    ///         otherwise, the PC of the instruction to which the execution should be redirected\n    pc_t handle_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc) override;\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/speculators/seq.hpp",
    "content": "///\n/// File: Header for the Seq (Sequential) Speculator\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"speculator_abc.hpp\"\n\n/// @brief Sequential (SEQ) Speculator;\n///        This speculator implements a sequential execution model with no speculation\n///        It is the simplest form of a speculator and it used to test the parts of the instruction\n///        set where no speculation is expected\nclass SpeculatorSeq : public SpeculatorABC\n{\n  public:\n    using SpeculatorABC::SpeculatorABC;\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/taint_tracker.hpp",
    "content": "///\n/// File: Header for Taint Tracker class,\n///       which performs backward taint analysis to identify parts of the input that influence\n///       contract traces.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstdint>\n#include <fstream>\n#include <iostream>\n#include <map>\n#include <memory>\n#include <set>\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include <dr_api.h> // NOLINT\n#include <dr_ir_opnd.h>\n\n#include \"logger.hpp\"\n#include \"observables.hpp\"\n#include \"types/decoder.hpp\"\n#include \"types/input_taint.hpp\"\n\n// =================================================================================================\n// Constants and Types\n// =================================================================================================\ntypedef reg_id_t tracker_reg_label_t;\ntypedef uint64_t tracked_mem_label_t;\ntypedef uint64_t tracked_label_t;\n\n// Extra register IDs for those registers that do not have a direct mapping in DynamoRIO\n// We use here the values that are guaranteed to be unused by other registers\n// in our logic (this defined in taint_tracer.cpp:normalize_reg) and thus are safe to re-use.\n#define DR_REG_RIP DR_REG_NULL\n#define DR_FLAG_CF DR_REG_AX // all GPRs are normalized to 64-bit, so AX and others are free\n#define DR_FLAG_PF DR_REG_BX\n#define DR_FLAG_AF DR_REG_CX\n#define DR_FLAG_ZF DR_REG_DX\n#define DR_FLAG_SF DR_REG_SI\n#define DR_FLAG_OF DR_REG_DI\n#define DR_FLAG_DF DR_REG_R8W\n\n/// @brief Register IDs used by RVZR code\n/// must match the register offsets defined in the rvzr/sandbox.py\nenum class RVZRRegId : uint64_t {\n    RVZR_REG_RAX = 0x2000,\n    RVZR_REG_RBX = 0x2008,\n    RVZR_REG_RCX = 0x2010,\n    RVZR_REG_RDX = 0x2018,\n    RVZR_REG_RSI = 0x2020,\n    RVZR_REG_RDI = 0x2028,\n    RVZR_REG_FLAGS = 0x2030,\n    RVZR_REG_XMM0 = 0x2040,\n    RVZR_REG_XMM1 = 0x2060,\n    RVZR_REG_XMM2 = 0x2080,\n    RVZR_REG_XMM3 = 0x20A0,\n    RVZR_REG_XMM4 = 0x20C0,\n    RVZR_REG_XMM5 = 0x20E0,\n    RVZR_REG_XMM6 = 0x2100,\n    RVZR_REG_XMM7 = 0x2120,\n    RVZR_REG_IGNORED = 0x2FFF,\n};\n\n/// @brief Structure holding source and destination operands of the tracked instruction\nstruct TrackedInstruction {\n    instr_obs_t instr_obs;\n    // dr_mcontext_t *mc;\n    void *dc;\n\n    std::set<tracker_reg_label_t> src_regs;\n    std::set<tracker_reg_label_t> dest_regs;\n    std::set<tracked_mem_label_t> src_mems;\n    std::set<tracked_mem_label_t> dest_mems;\n    std::set<tracker_reg_label_t> mem_address_regs;\n};\n\n/// @brief Structure tracking all dependencies collected by TaintTracker\nstruct Dependencies {\n    std::map<tracker_reg_label_t, std::set<tracked_label_t>> reg;\n    std::map<tracked_mem_label_t, std::set<tracked_label_t>> mem;\n};\n\n// =================================================================================================\n// Class Definitions\n// =================================================================================================\n\n/// @brief Tracking of the input data that impacts contract traces.\n///  The algorithm is as follows:\n///  - start_instruction: get the static source and destination operands of the instruction\n///  - track_memory_access: get dynamic source and destination memory addresses\n///  - taint: collect the labels (register names or mem. addresses) that are\n///    exposed by this instruction in the contract trace\n///  - finalize_instruction:\n///    1. propagate the dependencies of the source operands to the destination operands\n///    2. update the list of tainted labels with the dependencies of the labels\n///       collected by taint_* methods\n///  - get_taint: produce an InputTaint object based on the all tainted labels\nclass TaintTracker\n{\n  public:\n    TaintTracker(const std::string &out_path_, Logger &logger_, Decoder &decode_cache_)\n        : logger(logger_), decoder(decode_cache_)\n    {\n        stream.open(out_path_, std::ios::binary | std::ios::out);\n    }\n    virtual ~TaintTracker()\n    {\n        if (stream.is_open())\n            stream.close();\n    }\n    TaintTracker(const TaintTracker &) = delete;\n    TaintTracker &operator=(const TaintTracker &) = delete;\n    TaintTracker(TaintTracker &&) = delete;\n    TaintTracker &operator=(TaintTracker &&) = delete;\n\n    // ---------------------------------------------------------------------------------------------\n    // Public Attributes\n    bool enabled = false;\n    bool tracking_in_progress = false;\n\n    // ---------------------------------------------------------------------------------------------\n    // Public Methods (state management)\n\n    /// @brief Enable the taint tracker\n    virtual void enable();\n\n    /// @brief Disable the taint tracker and store the collected taints to out_path file\n    virtual void finalize();\n\n    /// @brief Save the current state of the taint tracker\n    /// @param include_current_inst Whether to include the currently-tracked instruction in the\n    ///        checkpoint. (This is currently unused; will be necessary in the future, when\n    ///        implementing more complex contracts)\n    /// @return void\n    virtual void checkpoint(bool include_current_inst);\n\n    /// @brief Restore the state of the taint tracker from the top-most checkpoint\n    /// @return void\n    virtual void rollback();\n\n    // ---------------------------------------------------------------------------------------------\n    // Public Methods (dependency propagation)\n\n    /// @brief Parse instruction and record its static source and destination operands.\n    ///        Static means the operands that we can identify without executing the instruction.\n    ///        The remaining dynamic operands are collected by track_* methods.\n    /// @return void\n    virtual void track_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc);\n\n    /// @brief Add the address of the memory access to the list of current instruction dependencies\n    virtual void track_memory_access(bool is_write, void *address, uint64_t size);\n\n    // ---------------------------------------------------------------------------------------------\n    // Public Methods (tainting)\n\n    /// @brief Taint the operands of a given type for the tracked instruction\n    ///        (tracked instruction is the last instruction on which track_instruction was called)\n    /// @param value_type The type of the value to taint\n    virtual void taint(taint_entry_type_t value_type);\n\n  private:\n    // ---------------------------------------------------------------------------------------------\n    // Private Attributes\n\n    /// @param stream Output stream for taint entries\n    std::ofstream stream;\n\n    /// @param logger Used to log checkpoint and rollback events\n    Logger &logger;\n\n    /// @param sandbox_base Base address of the sandbox (stored in R14)\n    uint64_t sandbox_base = 0;\n\n    /// @param checkpoints Stack of dependency states for speculation support\n    std::vector<Dependencies> checkpoints;\n\n    /// @param tainted_labels Set of labels (register IDs or memory addresses) that are tainted\n    std::set<tracked_label_t> tainted_labels;\n\n    /// @param pending_taint Set of labels to be tainted when `finalize` is called\n    std::set<tracked_label_t> pending_taint;\n\n    /// @param current_instruction The instruction currently being tracked\n    std::unique_ptr<TrackedInstruction> current_instruction;\n\n    /// @param dependencies Current dependency state\n    Dependencies dependencies;\n\n    /// @param decoder Shared cache for decoded instructions (reference)\n    Decoder &decoder;\n\n    // ---------------------------------------------------------------------------------------------\n    // Private Methods\n\n    /// @brief Store all collected taints to the output file\n    void store_taints();\n\n    /// @brief Parse instruction operands and populate TrackedInstruction structure\n    void parse_instruction_operands(TrackedInstruction *tracked_inst);\n\n    /// @brief Add dependencies from tracked instruction to the global dependency state\n    void add_dependencies(const TrackedInstruction *tracked_inst);\n\n    /// @brief Remove overwritten dependencies (for MOV/LEA-like instructions)\n    void remove_overwritten_dependencies(const TrackedInstruction *tracked_inst);\n\n    /// @brief Collect all source dependencies from a tracked instruction\n    /// @param tracked_inst The instruction to collect dependencies from\n    /// @return Set of all labels that the instruction's sources depend on\n    std::set<tracked_label_t> collect_source_dependencies(\n        const TrackedInstruction *tracked_inst) const;\n\n    /// @brief Propagate source dependencies to a destination in the dependency map\n    /// @tparam LabelT The type of label (register or memory)\n    /// @param dest_label The destination label to update\n    /// @param src_dependencies The source dependencies to propagate\n    /// @param dep_map The dependency map to update (either reg or mem)\n    template <typename LabelT>\n    void propagate_dependencies_to_dest(LabelT dest_label,\n                                        const std::set<tracked_label_t> &src_dependencies,\n                                        std::map<LabelT, std::set<tracked_label_t>> &dep_map);\n\n    /// @brief Debug: Print the current tainted labels. Should be unused in release builds.\n    void dbg_print_taints();\n\n    /// @brief Debug: Print the current dependencies. Should be unused in release builds.\n    void dbg_print_dependencies();\n\n    // ---------------------------------------------------------------------------------------------\n    // Protected Methods\n\n    /// @brief Propagate dependencies and record the taints of the tracked instruction\n    /// @throw dr_abort if called when tracking is not in progress\n    void finalize_instruction();\n};\n\n/// @brief A no-op implementation of TaintTracker; Used when taint tracking is disabled\nclass NoneTaintTracker : public TaintTracker\n{\n  public:\n    NoneTaintTracker(const std::string &out_path_, Logger &logger_, Decoder &decode_cache_)\n        : TaintTracker(out_path_, logger_, decode_cache_)\n    {\n    }\n    ~NoneTaintTracker() override = default;\n    NoneTaintTracker(const NoneTaintTracker &) = delete;\n    NoneTaintTracker &operator=(const NoneTaintTracker &) = delete;\n    NoneTaintTracker(NoneTaintTracker &&) = delete;\n    NoneTaintTracker &operator=(NoneTaintTracker &&) = delete;\n\n    void enable() override {}\n\n    void finalize() override {}\n\n    void checkpoint(bool include_current_inst) override {}\n\n    void rollback() override {}\n\n    void track_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc) override {}\n\n    void track_memory_access(bool is_write, void *address, uint64_t size) override {}\n\n    void taint(taint_entry_type_t value_type) override {}\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/tracer_abc.hpp",
    "content": "///\n/// File: Header for the Tracer abstract base class\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstdint>\n#include <string>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_events.h>\n#include <drvector.h>\n\n#include \"logger.hpp\"\n#include \"observables.hpp\"\n#include \"taint_tracker.hpp\"\n#include \"types/decoder.hpp\"\n#include \"types/file_buffer.hpp\"\n#include \"types/trace.hpp\"\n\nusing std::uint64_t;\n\n// =================================================================================================\n// Class Definition\n// =================================================================================================\n\n/// @brief Abstract base class for all tracers\nclass TracerABC\n{\n  public:\n    TracerABC(const std::string &out_path, Logger &logger, TaintTracker &taint_tracker,\n              Decoder &decoder, bool print);\n    virtual ~TracerABC();\n    TracerABC(const TracerABC &) = delete;\n    TracerABC &operator=(const TracerABC &) = delete;\n    TracerABC(TracerABC &&) = delete;\n    TracerABC &operator=(TracerABC &&) = delete;\n\n    static constexpr const unsigned buf_sz = 8 * 1024;\n    /// @param  Buffer containing collected trace entries\n    mutable FileBackedBuf<trace_entry_t, buf_sz> trace;\n\n    // ---------------------------------------------------------------------------------------------\n    // Public Methods\n\n    /// @brief Starts the tracing process for a wrapped functions\n    /// @return void\n    virtual void enable();\n\n    /// @brief Finalizes the tracing process for a wrapped function\n    /// @return void\n    virtual void finalize();\n\n    /// @brief Record per-instruction information on the trace (e.g., its address) as defined\n    ///        by the target contract.\n    ///        Note: some subclasses may not record any information as the corresponding\n    ///        contract may not require it. For such subclasses, this method should be a no-op.\n    /// @param instr the observed instruction\n    /// @param mc The machine context of the instruction\n    /// @param dc The DR context of the instruction\n    /// @return void\n    virtual void observe_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc);\n\n    /// @brief Record per-memory access information on the trace (e.g., its address and value)\n    ///        as defined by the target contract.\n    ///        Note: some subclasses may not record any information as the corresponding\n    ///        contract may not require it. For such subclasses, this method should be a no-op.\n    /// @param type The type of the memory access (read or write)\n    /// @param address The address of the memory access\n    /// @param size The size of the memory access\n    /// @return void\n    virtual void observe_mem_access(bool is_write, void *address, uint64_t size);\n\n    /// @brief Record an architectural exception with a special marker in the trace.\n    /// @param siginfo Information about the exception coming from DynamoRIO.\n    void observe_exception(dr_siginfo_t *siginfo) const;\n\n  protected:\n    // ---------------------------------------------------------------------------------------------\n    // Protected Fields\n    /// @param If true, the tracer will instrument the instructions in the traced function\n    bool tracing_on = false;\n\n    /// @param If true, tracing has been finalized; no more tracing is allowed\n    bool tracing_finalized = false;\n\n    /// @param Where to log events for debugging\n    Logger &logger;\n\n    /// @brief Used to taint-track dependencies between input values and contract trace\n    TaintTracker &taint_tracker;\n\n    /// @brief Shared cache for decoded instructions\n    Decoder &decoder;\n\n    // ---------------------------------------------------------------------------------------------\n    // Protected Methods\n\n    /// @brief Create an new PC entry and push it on the trace buffer\n    ///        This method is meant to be used by observe_instruction if a given contract requires\n    ///        recording PCs in the trace.\n    /// @param instr The observed instruction\n    void record_pc(instr_obs_t instr);\n\n    /// @brief Create an new mem entry and push it on the trace buffer\n    ///        This method is meant to be used by observe_mem_access if a given contract requires\n    ///        recording memory accesses in the trace.\n    /// @param address The address of the memory access\n    /// @param size The size of the memory access\n    void record_mem_access(bool is_write, void *address, uint64_t size);\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/tracers/ct.hpp",
    "content": "///\n/// File: Header for the CT Tracer and its variants\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n\n#include \"tracer_abc.hpp\"\n\n/// @brief \"Constant-Time\" (CT) Tracer;\n/// This tracer collects addresses of memory accesses and PCs of the executed instructions\nclass TracerCT : public TracerABC\n{\n  public:\n    using TracerABC::TracerABC;\n\n    /// @brief Record the PC of the executed instruction on the contract trace\n    /// @param instr the observed instruction\n    /// @param mc unused\n    /// @param dc unused\n    /// @return void\n    void observe_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc) override;\n\n    /// @brief Record the memory access\n    /// @param type The type of the memory access (read or write)\n    /// @param address The address of the memory access\n    /// @param size The size of the memory access\n    /// @param value The value of the memory access\n    /// @return void\n    void observe_mem_access(bool is_write, void *address, uint64_t size) override;\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/tracers/ind.hpp",
    "content": "///\n/// File: Header for the Indirect Call Tracer\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n\n#include \"tracer_abc.hpp\"\n\n/// @brief Indirect Tracer;\n/// This tracer collects target addresses of indirect calls, indirect branches and returns\nclass TracerInd : public TracerABC\n{\n  public:\n    using TracerABC::TracerABC;\n\n    /// @brief Record the target of the executed indirect call (or branch or ret) on the contract\n    /// trace\n    /// @param instr the instruction being observed\n    /// @param mc the instructions's memory context\n    /// @param dc the instructions's DR context\n    /// @return void\n    void observe_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc) override;\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/tracers/pc.hpp",
    "content": "///\n/// File: Header for the CT Tracer and its variants\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n\n#include \"tracer_abc.hpp\"\n\n/// @brief \"Constant-Time\" (CT) Tracer;\n/// This tracer collects addresses of memory accesses and PCs of the executed instructions\nclass TracerPC : public TracerABC\n{\n  public:\n    using TracerABC::TracerABC;\n\n    /// @brief Record the PC of the executed instruction on the contract trace\n    /// @param instr the observed instruction\n    /// @param mc unused\n    /// @param dc unused\n    /// @return void\n    void observe_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc) override;\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/types/debug_trace.hpp",
    "content": "///\n/// File: Debug trace entries produced when debugging\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <array>\n#include <cstdint>\n#include <ostream>\n\n/// @brief Type of entry (single element of a trace)\nenum class debug_trace_entry_type_t : uint8_t {\n    ENTRY_EOT = 0, // end of trace\n    ENTRY_REG_DUMP = 1,\n    ENTRY_READ = 2,\n    ENTRY_WRITE = 3,\n    ENTRY_LOC = 4,\n    ENTRY_EXCEPTION = 5,\n    ENTRY_CHECKPOINT = 6,\n    ENTRY_ROLLBACK = 7,\n    ENTRY_ROLLBACK_STORE = 8,\n    ENTRY_REG_DUMP_EXTENDED = 9,\n};\n\n/// @brief Pretty-printer for trace_entry_type_t\nstatic constexpr const char *to_string(const debug_trace_entry_type_t &type)\n{\n    switch (type) {\n    case debug_trace_entry_type_t::ENTRY_EOT:\n        return \"EOT\";\n    case debug_trace_entry_type_t::ENTRY_REG_DUMP:\n        return \"REG_DUMP \";\n    case debug_trace_entry_type_t::ENTRY_READ:\n        return \"READ \";\n    case debug_trace_entry_type_t::ENTRY_WRITE:\n        return \"WRITE\";\n    case debug_trace_entry_type_t::ENTRY_LOC:\n        return \"LOC\";\n    case debug_trace_entry_type_t::ENTRY_EXCEPTION:\n        return \"XCPT\";\n    case debug_trace_entry_type_t::ENTRY_CHECKPOINT:\n        return \"CHECKPOINT\";\n    case debug_trace_entry_type_t::ENTRY_ROLLBACK_STORE:\n        return \"ROLLBACK_STR\";\n    case debug_trace_entry_type_t::ENTRY_ROLLBACK:\n        return \"ROLLBACK\";\n    case debug_trace_entry_type_t::ENTRY_REG_DUMP_EXTENDED:\n        return \"REG_DUMP2\";\n    }\n\n    return \"UNKNOWN\";\n}\n\nstruct debug_trace_entry_t {\n    // What does this entry contain\n    debug_trace_entry_type_t type;\n    // Nested speculation (0 is architectural)\n    uint8_t nesting_level;\n    // Unused for now\n    uint8_t padding[6]; // NOLINT\n\n    // Union of all possible entry types\n    union {\n        // ENTRY_REG_DUMP\n        struct {\n            uint64_t xax;\n            uint64_t xbx;\n            uint64_t xcx;\n            uint64_t xdx;\n            uint64_t xsi;\n            uint64_t xdi;\n            uint64_t pc;\n        } regs;\n        // ENTRY_REG_DUMP_EXTENDED\n        struct {\n            uint64_t rsp;\n            uint64_t rbp;\n            uint64_t flags;\n            uint64_t r8;\n            uint64_t r9;\n            uint64_t r10;\n            uint64_t r11;\n        } regs_2;\n        // ENTRY_MEM (read or write)\n        struct {\n            uint64_t address;\n            uint64_t value;\n            uint64_t size;\n        } mem;\n        // ENTRY_LOC (module name and offset, for disassembly)\n        struct {\n            uint64_t offset;\n            std::array<char, 48> module_name; // NOLINT\n        } loc;\n        // ENTRY_EXCEPTION\n        struct {\n            int signal;\n            uint64_t address;\n        } xcpt;\n        // ENTRY_CHECKPOINT\n        struct {\n            uint64_t rollback_pc;\n            uint64_t cur_window_size;\n            size_t cur_store_log_size;\n        } checkpoint;\n        // ENTRY_ROLLBACK\n        struct {\n            unsigned nesting;\n            uint64_t rollback_pc;\n        } rollback;\n        // ENTRY_ROLLBACK_STORE\n        struct {\n            uint64_t addr;\n            uint64_t val;\n            size_t size;\n            uint64_t nesting_level;\n        } rollback_store;\n    };\n\n    /// @param Declare a marker to identify traces of this type\n    static constexpr char marker = 'D';\n\n    /// @brief Pretty-printer for debug_trace_entry_t\n    void dump(std::ostream &out) const\n    {\n        // Arch or spec\n        if (nesting_level == 0)\n            out << \"[ARCH] \";\n        else\n            out << \"[SPEC_\" << std::dec << (uint)nesting_level << \"] \";\n        // Print entry type\n        out << \"[\" << to_string(type) << \"] \";\n\n        // Print content\n        switch (type) {\n        case debug_trace_entry_type_t::ENTRY_REG_DUMP:\n            out << \" pc: \" << std::hex << regs.pc;\n            out << \"  (rax: 0x\" << std::hex << regs.xax;\n            out << \" rbx: 0x\" << std::hex << regs.xbx;\n            out << \" rcx: 0x\" << std::hex << regs.xcx;\n            out << \" rdx: 0x\" << std::hex << regs.xdx;\n            out << \" rsi: 0x\" << std::hex << regs.xsi;\n            out << \" rdi: 0x\" << std::hex << regs.xdi << \")\";\n            break;\n\n        case debug_trace_entry_type_t::ENTRY_LOC:\n            for (const char name_char : loc.module_name) {\n                if (name_char == '\\0')\n                    break;\n                out << name_char;\n            }\n            out << \"+0x\" << std::hex << loc.offset;\n            break;\n\n        case debug_trace_entry_type_t::ENTRY_READ:\n        case debug_trace_entry_type_t::ENTRY_WRITE:\n            out << \" addr: \" << std::hex << mem.address;\n            out << \"  val: \" << std::hex << mem.value;\n            out << \"  (sz: \" << std::dec << mem.size << \")\";\n            break;\n\n        case debug_trace_entry_type_t::ENTRY_EXCEPTION:\n            out << \" sig: \" << std::dec << xcpt.signal;\n            out << \"  addr: \" << std::hex << xcpt.address;\n            break;\n\n        case debug_trace_entry_type_t::ENTRY_EOT:\n            out << \"---- END OF TRACE ----\\n\";\n            break;\n        case debug_trace_entry_type_t::ENTRY_CHECKPOINT:\n            out << \" rollback_pc: \" << std::hex << checkpoint.rollback_pc;\n            out << \" (storelog_sz: \" << std::dec << checkpoint.cur_store_log_size;\n            out << \" window_sz: \" << std::dec << checkpoint.cur_window_size << \")\";\n            break;\n        case debug_trace_entry_type_t::ENTRY_ROLLBACK:\n            out << \" rollback_pc: \" << std::hex << rollback.rollback_pc;\n            out << \" (nesting: \" << std::dec << rollback.nesting << \")\";\n            break;\n\n        case debug_trace_entry_type_t::ENTRY_ROLLBACK_STORE:\n            out << \" addr: 0x\" << std::hex << rollback_store.addr;\n            out << \" val: 0x\" << std::hex << rollback_store.val;\n            out << \" (sz: \" << std::dec << rollback_store.size;\n            out << \" nesting: \" << std::dec << rollback_store.nesting_level << \")\";\n            break;\n        case debug_trace_entry_type_t::ENTRY_REG_DUMP_EXTENDED:\n            out << \" rsp: 0x\" << std::hex << regs_2.rsp;\n            out << \" rbp: 0x\" << std::hex << regs_2.rbp;\n            out << \" flags: 0x\" << std::hex << regs_2.flags;\n            out << \" r8: 0x\" << std::hex << regs_2.r8;\n            out << \" r9: 0x\" << std::hex << regs_2.r9;\n            out << \" r10: 0x\" << std::hex << regs_2.r10;\n            out << \" r11: 0x\" << std::hex << regs_2.r11;\n            break;\n        }\n\n        out << \"\\n\";\n    }\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/types/decoder.hpp",
    "content": "///\n/// File: Instruction Decoder\n///       Decodes and caches DynamoRIO instructions to avoid redundant decoding\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <unordered_map>\n\n#include <dr_api.h>      // NOLINT\n#include <dr_ir_instr.h> // NOLINT\n#include <dr_ir_utils.h> // NOLINT\n\n/// @brief Cached entry containing decoded instruction and next PC\nstruct CachedInstr {\n    instr_noalloc_t instr;\n    byte *next_pc;\n};\n\n/// @brief Decoder for DynamoRIO instructions with caching\n///\n/// This class decodes instructions and caches them indexed by their program counter (PC).\n/// Instructions are stored as instr_noalloc_t objects, which handle their own cleanup\n/// automatically, eliminating the need for manual memory management.\n/// The cache also stores the next PC (address after the instruction) for efficient\n/// sequential access.\n///\n/// Usage:\n///   Decoder decoder;\n///   instr_t *instr = decoder.get_decoded_instr(drcontext, pc);\n///   byte *next_pc = decoder.get_next_pc(drcontext, pc);\n///   decoder.clear(); // Clear when done\nclass Decoder\n{\n  public:\n    Decoder() = default;\n    ~Decoder() { clear(); }\n\n    // Delete copy/move constructors and assignment operators\n    Decoder(const Decoder &) = delete;\n    Decoder &operator=(const Decoder &) = delete;\n    Decoder(Decoder &&) = delete;\n    Decoder &operator=(Decoder &&) = delete;\n\n    /// @brief Get a decoded instruction from cache or decode and cache it\n    /// @param drcontext DynamoRIO context\n    /// @param pc Program counter of the instruction\n    /// @return Pointer to the decoded instruction\n    /// @throw dr_abort if decoding fails\n    instr_t *get_decoded_instr(void *drcontext, byte *pc)\n    {\n        // NOLINTNEXTLINE(misc-const-correctness) ; False Positive\n        CachedInstr &cached_entry = cache_access(drcontext, pc);\n        instr_noalloc_t *noalloc = &cached_entry.instr;\n        return instr_from_noalloc(noalloc);\n    }\n\n    /// @brief Get the next PC (address after the instruction) from cache or decode and cache it\n    /// @param drcontext DynamoRIO context\n    /// @param pc Program counter of the instruction\n    /// @return The next PC (address immediately following the instruction)\n    /// @throw dr_abort if decoding fails\n    byte *get_next_pc(void *drcontext, byte *pc)\n    {\n        const CachedInstr &cached_entry = cache_access(drcontext, pc);\n        return cached_entry.next_pc;\n    }\n\n    /// @brief Clear the instruction cache\n    void clear()\n    {\n        // Note: instr_noalloc_t destructor handles cleanup automatically\n        cache.clear();\n    }\n\n    /// @brief Get the number of cached instructions\n    /// @return Number of cached instructions\n    [[nodiscard]] size_t size() const { return cache.size(); }\n\n    /// @brief Check if the cache is empty\n    /// @return True if cache is empty\n    [[nodiscard]] bool empty() const { return cache.empty(); }\n\n  private:\n    /// @brief Cache of decoded instructions and their next PCs, indexed by PC\n    std::unordered_map<byte *, CachedInstr> cache;\n\n    /// @brief Access cached entry by PC. If not present, creates a new entry.\n    /// @param pc Program counter of the instruction\n    /// @return Reference to the cached instruction entry\n    CachedInstr &cache_access(void *drcontext, byte *pc)\n    {\n        // Cache hit\n        auto it = cache.find(pc);\n        if (it != cache.end()) {\n            return it->second;\n        }\n\n        // Cache miss - create new entry\n        CachedInstr &cached_entry = cache[pc];\n        instr_noalloc_init(drcontext, &cached_entry.instr);\n        instr_t *instr = instr_from_noalloc(&cached_entry.instr);\n\n        byte *next_pc = decode(drcontext, pc, instr);\n        if (next_pc == nullptr) {\n            // Decode failed - remove from cache and abort\n            cache.erase(pc);\n            dr_printf(\"[ERROR] Decoder: Failed to decode instruction at PC %p\\n\", (void *)pc);\n            dr_abort();\n        }\n\n        // Cache the next_pc\n        cached_entry.next_pc = next_pc;\n\n        return cached_entry;\n    }\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/types/file_buffer.hpp",
    "content": "///\n/// File: A buffer that gets automatically spilled to a file when it becomes too big\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include \"dr_api.h\"\n#include \"dr_tools.h\"\n\n#include <array>\n#include <cstdint>\n#include <fstream>\n#include <iostream>\n#include <sstream>\n\n/// @brief A buffer backed by a file: once the buffer reaches a given threshold, it gets\n/// automatically spilled into the backing file. Entries can only be appended to the buffer.\n/// @tparam T All entries pushed to the buffer have this type.\n/// @tparam BufSize Threshold for the buffer.\ntemplate <typename T, unsigned BufSize> class FileBackedBuf\n{\n    // An exact number of entries of type T should fit in the buffer\n    static_assert(BufSize % sizeof(T) == 0,\n                  \"[ASSERT] FileBackedBuf size must fit an exact number of elements\");\n\n  private:\n    static constexpr const unsigned max_elems = BufSize / sizeof(T);\n    unsigned n_elems = 0;\n    std::array<T, max_elems> buf;\n    std::ofstream stream;\n    std::string filename;\n\n    const bool print;\n\n  public:\n    FileBackedBuf(bool print) : print(print) {}\n    ~FileBackedBuf()\n    {\n        if (stream.is_open())\n            stream.close();\n    }\n    FileBackedBuf(const FileBackedBuf &) = delete;\n    FileBackedBuf(FileBackedBuf &&) = delete;\n    FileBackedBuf &operator=(const FileBackedBuf &other) = delete;\n    FileBackedBuf &operator=(FileBackedBuf &&other) = delete;\n\n    /// @brief Open the backing ostream and print the header\n    /// @param filename Path of backing file\n    void open(const std::string &filename_)\n    {\n        if (stream.is_open())\n            return;\n\n        // Open the backing stream\n        filename = filename_;\n        stream.open(filename_, std::ios::binary | std::ios::out);\n\n        // Write header so that the parser knows which type of trace we are generating\n        char marker = T::marker;\n        stream.write(&marker, 1);\n\n        // pad the header to 8 bytes for readability\n        const char padding[7] = {0, 0, 0, 0, 0, 0, 0}; // NOLINT\n        stream.write(padding, 7);                      // NOLINT\n    }\n\n    /// @brief Flush the current buffer contents into the backing file\n    /// TODO: this could be made asynchronous\n    void flush()\n    {\n        uint32_t n_bytes = n_elems * sizeof(T);\n        stream.write(reinterpret_cast<const char *>(buf.data()), n_bytes);\n        n_elems = 0;\n    }\n\n    /// @brief Append an element to the buffer\n    /// @param elem The element to add\n    void push_back(const T &elem)\n    {\n        buf[n_elems] = elem;\n        n_elems++;\n\n        if (print) {\n            const std::string entry_str;\n            std::stringstream entry_stream(entry_str);\n            elem.dump(entry_stream);\n            dr_printf(\"%s\", entry_stream.str().c_str());\n        }\n\n        if (n_elems == max_elems)\n            flush();\n    }\n\n    /// @brief Close the backing ostream\n    void clear()\n    {\n        if (not stream.is_open())\n            return;\n\n        flush();\n        stream.close();\n    }\n\n    /// @brief Get the name of the backing file\n    const std::string &get_filename() const { return filename; }\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/types/input_taint.hpp",
    "content": "///\n/// File: Class representing the taints collected by TaintTracker class\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstdint>\n#include <cstdio>\n#include <fstream>\n#include <iostream>\n#include <vector>\n\n/// @brief Type of trace entries that can be observed\nenum class taint_entry_type_t : uint8_t {\n    TAINT_ENTRY_EOT = 0, // end of trace\n    TAINT_ENTRY_PC = 1,\n    TAINT_ENTRY_MEM = 2,\n};\n\n/// @brief An entry of an observed taint trace\ntypedef struct taint_entry_t {\n    taint_entry_type_t type;\n    uint64_t value;\n} taint_entry_t;\n\n/// @brief Class that acts as a container for taint info collected by TaintTracker class.\n///        Partially implements std::vector interface\nclass InputTaint\n{\n  public:\n    InputTaint() = default;\n    ~InputTaint() = default;\n    InputTaint(const InputTaint &) = delete;\n    InputTaint &operator=(const InputTaint &) = delete;\n    InputTaint(InputTaint &&) = delete;\n    InputTaint &operator=(InputTaint &&) = delete;\n\n    // ---------------------------------------------------------------------------------------------\n    // Public Methods\n\n    /// @brief Implement std::vector::push_back\n    void push_back(const taint_entry_t &entry) { entries.push_back(entry); }\n    /// @brief Implement std::vector::size\n    [[nodiscard]] size_t size() const { return entries.size(); }\n    /// @brief Implement std::vector::empty\n    [[nodiscard]] bool empty() const { return entries.empty(); }\n\n    /// @brief Iterator access operator\n    taint_entry_t operator[](size_t index) const { return entries[index]; }\n\n    /// @brief [Non-vector method] Store input taints into a file\n    /// @param file_path Path to the output file\n    void store_to_file(const char *file_path)\n    {\n        std::ofstream stream;\n        stream.open(file_path, std::ios::binary | std::ios::out);\n\n        // Write all collected entries\n        for (const auto &entry : entries) {\n            stream.write(reinterpret_cast<const char *>(&entry.type), sizeof(uint8_t));\n            stream.write(reinterpret_cast<const char *>(&entry.value), sizeof(uint64_t));\n        }\n\n        // Write end-of-trace marker\n        auto eot = static_cast<uint8_t>(taint_entry_type_t::TAINT_ENTRY_EOT);\n        auto eot_value = 0ULL;\n        stream.write(reinterpret_cast<const char *>(&eot), sizeof(uint8_t));\n        stream.write(reinterpret_cast<const char *>(&eot_value), sizeof(uint64_t));\n\n        stream.close();\n    };\n\n  private:\n    // ---------------------------------------------------------------------------------------------\n    // Private Attributes\n    std::vector<taint_entry_t> entries;\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/types/store_log.hpp",
    "content": "\n///\n/// File: Class representing a log of store operations performed during speculation.\n///       Used to be able to undo memory writes upon rollback.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <vector>\n\ntypedef struct {\n    uint64_t addr;\n    uint64_t val;\n    size_t size;\n    unsigned int nesting_level;\n} store_log_entry_t;\n\n/// @brief The StoreLog is a wrapper around an std::vector of store_log_entries that keeps track of\n/// which entries have been committed and which entries are in-flight. This is needed since we\n/// populate the store_log before actually executing the instruction, which might fail.\nclass StoreLog\n{\n  public:\n    StoreLog() = default;\n    ~StoreLog() = default;\n    StoreLog(const StoreLog &) = delete;\n    StoreLog(StoreLog &&) = delete;\n    StoreLog &operator=(const StoreLog &) = delete;\n    StoreLog &operator=(StoreLog &&) = delete;\n\n    /// @brief Implement std::vector::back\n    [[nodiscard]] const store_log_entry_t &back() const { return entries.back(); }\n    /// @brief Implement std::vector::pop_back. This will also update the committed state.\n    void pop_back()\n    {\n        const bool was_committed_entry = (entries.size() == last_committed);\n        entries.pop_back();\n\n        if (was_committed_entry)\n            last_committed -= 1;\n    }\n    /// @brief Implement std::vector::push_back\n    void push_back(const store_log_entry_t &entry) { entries.push_back(entry); }\n    /// @brief Implement std::vector::size\n    [[nodiscard]] size_t size() const { return entries.size(); }\n    /// @brief Implement std::vector::empty\n    [[nodiscard]] bool empty() const { return entries.empty(); }\n\n    /// @brief The last instruction actually committed: mark all entries as committed.\n    void update_committed() { last_committed = entries.size(); }\n    /// @brief Check if the instruction has any in-flight entries.\n    [[nodiscard]] bool has_uncommitted() const { return entries.size() > last_committed; }\n    /// @brief Remove all uncommitted entries from the store_log.\n    void flush_uncommitted()\n    {\n        while (has_uncommitted())\n            pop_back();\n    }\n\n  private:\n    std::vector<store_log_entry_t> entries;\n    size_t last_committed = 0;\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/types/trace.hpp",
    "content": "///\n/// File: Trace entries generated by the tracer\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstdint>\n#include <ostream>\n\n/// @brief Type of trace entries that can be observed\nenum class trace_entry_type_t : uint8_t {\n    ENTRY_EOT = 0, // end of trace\n    ENTRY_PC = 1,\n    ENTRY_READ = 2,\n    ENTRY_WRITE = 3,\n    ENTRY_EXCEPTION = 4,\n    ENTRY_IND = 5,\n};\n\n/// @brief Pretty-printer for trace_entry_type_t\nstatic constexpr const char *to_string(const trace_entry_type_t &type)\n{\n    switch (type) {\n    case trace_entry_type_t::ENTRY_EOT:\n        return \"EOT\";\n    case trace_entry_type_t::ENTRY_PC:\n        return \"PC\";\n    case trace_entry_type_t::ENTRY_READ:\n        return \"READ\";\n    case trace_entry_type_t::ENTRY_WRITE:\n        return \"WRITE\";\n    case trace_entry_type_t::ENTRY_EXCEPTION:\n        return \"XCPT\";\n    case trace_entry_type_t::ENTRY_IND:\n        return \"IND\";\n    }\n\n    return \"UNKNOWN\";\n}\n\n/// @brief An entry of an observed trace\nstruct trace_entry_t {\n    // pc for instructions; address for memory accesses; target for indirect calls\n    uint64_t addr;\n    // instruction size for instructions; memory access size for memory accesses\n    uint32_t size;\n    // see trace_entry_type_t\n    trace_entry_type_t type;\n    // unused for now\n    uint8_t padding[3]; // NOLINT\n\n    /// @brief Declare a marker to identify traces of this type\n    static constexpr char marker = 'T';\n\n    /// @brief Pretty-printing for tracer output\n    void dump(std::ostream &out) const\n    {\n        out << \"[\" << to_string(type) << \"]\";\n\n        switch (type) {\n        case trace_entry_type_t::ENTRY_EOT:\n            out << \"---- END OF TRACE ----\\n\";\n            break;\n        case trace_entry_type_t::ENTRY_PC:\n            out << \" pc: \" << std::hex << addr;\n            out << \"  (instr sz: \" << std::dec << size << \")\";\n            break;\n        case trace_entry_type_t::ENTRY_READ:\n        case trace_entry_type_t::ENTRY_WRITE:\n            out << \" addr: \" << std::hex << addr;\n            out << \"  (sz: \" << std::dec << size << \")\";\n            break;\n        case trace_entry_type_t::ENTRY_EXCEPTION:\n            out << \" faulty_addr: \" << std::hex << addr;\n            out << \"  (sig: \" << std::dec << size << \")\";\n            break;\n        case trace_entry_type_t::ENTRY_IND:\n            out << \"  --> target: \" << std::hex << addr;\n            break;\n        }\n\n        out << \"\\n\";\n    }\n};\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/include/util.hpp",
    "content": "///\n/// File: Helper functions for DR model\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#pragma once\n\n#include <cstddef>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_ir_opnd.h>\n#include <dr_ir_utils.h> // NOLINT\n#include <drvector.h>\n\n#include \"observables.hpp\"\n#include \"types/decoder.hpp\"\n\n#define INSERT_BEFORE instrlist_meta_preinsert\n\n/// @brief A wrapper around drreg_reserve_register that aborts on failure\n/// @param drcontext The drcontext of the current thread\n/// @param ilist Current instruction list\n/// @param where Current instruction\n/// @param permitted The set of registers that can be reserved\n/// @param [out] reg The reserved register\n/// @return void\nvoid reserve_register_checked(void *drcontext, instrlist_t *ilist, instr_t *where,\n                              drvector_t *permitted, DR_PARAM_OUT reg_id_t *reg);\n\n/// @brief A wrapper around drreg_reserve_register that aborts on failure\n/// @param drcontext The drcontext of the current thread\n/// @param ilist Current instruction list\n/// @param where Current instruction\n/// @param reg The register to unreserve\n/// @return void\nvoid unreserve_register_checked(void *drcontext, instrlist_t *ilist, instr_t *where, reg_id_t reg);\n\n/// @brief Write to the given addres ignoring any permission check.\n/// NOTE: Writing to executable memory might clash with DynamoRIO's code cache, use with caution.\n/// @param addr address to write to\n/// @param size size of the write\n/// @param val pointer to the value to write\n/// @param w_size number of bytes written is stored here (see dr_safe_write)\n/// @return true if the write was successfull\nbool force_write(byte *addr, size_t size, const uint64_t *val, size_t *w_size);\n\n/// @brief Check if an instruction is an indirect call or return to an illegal instruction.\n///        This has to be called from _within_ the dispatcher's clean_call to make sure that the\n///        runtime value of the target is known.\n/// NOTE: On illegal jumps to non-executable memory, DynamoRIO will still fetch the corresponding\n///       basic block and mark it as DR_MEMPROT_PRETEND_WRITE, which forbids writing to it from a\n///       clean call. This is problematic for rollbacks, as speculative code might be jumping to\n///       non-executable pages that we need to rollback after the speculation ends.\n/// NOTE: This operation is not cheap, use with caution.\n/// @param instr The instruction to check\n/// @param mc\n/// @param dc\nbool is_illegal_jump(instr_obs_t instr, dr_mcontext_t *mc, void *dc, Decoder &decoder);\n\n/// @brief Flush dynamorio's basic-block cache. This is needed when transitioning from\n/// non-instrumented code to instrumented code, as any shared code (e.g. libc) might be cached and\n/// therefore inaccessible for instrumentation.\nvoid flush_bb_cache();\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/logger.cpp",
    "content": "///\n/// File: Implementation for logger\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <cassert>\n#include <cstdint>\n#include <cstring>\n#include <string>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_events.h>\n#include <dr_ir_instr.h>\n#include <dr_ir_opnd.h>\n#include <dr_ir_utils.h>\n#include <dr_tools.h>\n\n#include \"logger.hpp\"\n\n// =================================================================================================\n// Local helper functions\n// =================================================================================================\n\nstatic std::pair<std::string, size_t> get_module(uint64_t pc)\n{\n    module_data_t *mod = dr_lookup_module((byte *)pc);\n    if (mod == nullptr)\n        return {\"Unknown Module\", 0};\n\n    // Calculate the offset from the beginning of the module.\n    auto offset = (size_t)(pc - (pc_t)mod->start);\n\n    // Get the name of the current module.\n    std::string module_name(mod->full_path);\n\n    // If the name is too long, get only the last part.\n    const size_t max_path_size = sizeof(debug_trace_entry_t::loc.module_name) - 1;\n    if (module_name.size() > max_path_size)\n        module_name = module_name.substr(module_name.size() - max_path_size - 1, max_path_size);\n\n    dr_free_module_data(mod);\n    return {module_name, offset};\n}\n\n/// @brief convert an integer of src_type into a smaller dst_type, saturating the value if needed.\ntemplate <typename dst_type, typename src_type>\nstatic constexpr dst_type saturate_cast(const src_type &val)\n{\n    if (val > std::numeric_limits<dst_type>::max())\n        return std::numeric_limits<dst_type>::max();\n\n    return (dst_type)val;\n}\n\n// =================================================================================================\n// Constructors and Destructors\n// =================================================================================================\n\nLogger::Logger(const std::string &logs_path, log_level_t log_level, bool print)\n    : log_level(log_level), log(print), cur_nesting_level(0)\n{\n    if (is_enabled())\n        log.open(logs_path);\n}\n\nLogger::~Logger() { log.clear(); }\n\n// =================================================================================================\n// Public methods\n// =================================================================================================\n\nconst std::string &Logger::get_filename() const { return log.get_filename(); }\n\nvoid Logger::close() { log.clear(); }\n\n// =================================================================================================\n// Logging methods\n// =================================================================================================\n\nvoid Logger::log_instruction(instr_obs_t instr, dr_mcontext_t *mc, unsigned int nesting_level)\n{\n    if (not is_enabled())\n        return;\n\n    // Set the nesting level for all the entries until the next instruction\n    cur_nesting_level = saturate_cast<uint8_t>(nesting_level);\n    // Log PC and registers\n    log.push_back({.type = debug_trace_entry_type_t::ENTRY_REG_DUMP,\n                   .nesting_level = cur_nesting_level,\n                   .regs{\n                       .xax = mc->xax,\n                       .xbx = mc->xbx,\n                       .xcx = mc->xcx,\n                       .xdx = mc->xdx,\n                       .xsi = mc->xsi,\n                       .xdi = mc->xdi,\n                       .pc = instr.pc,\n                   }});\n    // Log more registers\n    log.push_back({.type = debug_trace_entry_type_t::ENTRY_REG_DUMP_EXTENDED,\n                   .nesting_level = cur_nesting_level,\n                   .regs_2{\n                       .rsp = mc->rsp,\n                       .rbp = mc->rbp,\n                       .flags = mc->xflags,\n                       .r8 = mc->r8,\n                       .r9 = mc->r9,\n                       .r10 = mc->r10,\n                       .r11 = mc->r11,\n                   }});\n    // Optionally, output each instruction's module and location to aid disassembly\n    if (log_level >= LOG_DISASM) {\n        // Recover module name from DynamoRIO\n        const auto &[module_name, offset] = get_module(instr.pc);\n        assert(module_name.size() <= sizeof(debug_trace_entry_t::loc.module_name));\n\n        debug_trace_entry_t loc_entry = {.type = debug_trace_entry_type_t::ENTRY_LOC,\n                                         .nesting_level = cur_nesting_level,\n                                         .loc{\n                                             .offset = offset,\n                                             .module_name = {'\\0'},\n                                         }};\n        // Move the recovered module name into the corresponding member of the entry\n        std::move(module_name.begin(), module_name.end(), loc_entry.loc.module_name.begin());\n        log.push_back(loc_entry);\n    }\n}\n\nvoid Logger::log_mem_access(bool is_write, void *address, uint64_t size)\n{\n    if (not is_enabled())\n        return;\n\n    auto cur_address = (uint64_t)address;\n    uint64_t remaining_size = size;\n\n    // Vector instructions can read/write more that 64-bits: translate these cases into multiple\n    // 64-bit entries.\n    while (remaining_size > 0) {\n        const uint64_t cur_size = std::min(remaining_size, sizeof(uint64_t));\n\n        // Magic value that marks failed reads in the log.\n        const uint64_t marker = 0xDEADBEEFDEADBEEF;\n        // Read current memory value.\n        uint64_t val = marker;\n        size_t r_size = marker;\n        const bool success = dr_safe_read((byte *)cur_address, cur_size, &val, &r_size);\n\n        log.push_back({.type = is_write ? debug_trace_entry_type_t::ENTRY_WRITE\n                                        : debug_trace_entry_type_t::ENTRY_READ,\n                       .nesting_level = cur_nesting_level,\n                       .mem{\n                           .address = cur_address,\n                           .value = val,\n                           .size = size,\n                       }});\n\n        cur_address += cur_size;\n        remaining_size -= cur_size;\n    }\n}\n\nvoid Logger::log_exception(dr_siginfo_t *siginfo)\n{\n    if (not is_enabled())\n        return;\n\n    log.push_back({.type = debug_trace_entry_type_t::ENTRY_EXCEPTION,\n                   .nesting_level = cur_nesting_level,\n                   .xcpt{\n                       .signal = siginfo->sig,\n                       .address = (uint64_t)siginfo->access_address,\n                   }});\n}\n\nvoid Logger::log_checkpoint(pc_t rollback_pc, uint64_t cur_window_size, size_t cur_store_log_size)\n{\n    if (log_level < LOG_SPEC)\n        return;\n\n    log.push_back({.type = debug_trace_entry_type_t::ENTRY_CHECKPOINT,\n                   .nesting_level = cur_nesting_level,\n                   .checkpoint{\n                       .rollback_pc = rollback_pc,\n                       .cur_window_size = cur_window_size,\n                       .cur_store_log_size = cur_store_log_size,\n                   }});\n}\n\nvoid Logger::log_rollback(unsigned nesting, pc_t rollback_pc)\n{\n    if (log_level < LOG_SPEC)\n        return;\n\n    log.push_back({.type = debug_trace_entry_type_t::ENTRY_ROLLBACK,\n                   .nesting_level = cur_nesting_level,\n                   .rollback{\n                       .nesting = nesting,\n                       .rollback_pc = rollback_pc,\n                   }});\n}\n\nvoid Logger::log_rollback_store(uint64_t addr, uint64_t val, size_t size, uint64_t nesting_level)\n{\n    if (log_level < LOG_SPEC)\n        return;\n\n    log.push_back({.type = debug_trace_entry_type_t::ENTRY_ROLLBACK_STORE,\n                   .nesting_level = cur_nesting_level,\n                   .rollback_store{\n                       .addr = addr,\n                       .val = val,\n                       .size = size,\n                       .nesting_level = nesting_level,\n                   }});\n}\n\nvoid Logger::log_eot()\n{\n    if (not is_enabled())\n        return;\n\n    log.push_back({.type = debug_trace_entry_type_t::ENTRY_EOT});\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/model.cpp",
    "content": "///\n/// File: DynamoRIO client entry point and instrumentation orchestrator\n///\n/// This file implements the main DynamoRIO client (dr_client_main) and coordinates\n/// the instrumentation lifecycle. The file is responsible for detecting when to start/stop\n/// instrumentation of the target function based on its name. It also registers event callbacks\n/// for module loading, basic block transformation, and instruction-level instrumentation.\n/// The callbacks transfer control to the Dispatcher class, which manages the rest of\n/// the model's logic.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <cstddef>\n#include <cstdio>\n#include <cstdlib>\n#include <stdexcept>\n#include <string>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_events.h>\n#include <dr_tools.h>\n#include <drmgr.h>\n#include <drsyms.h>\n#include <drutil.h>\n#include <drwrap.h>\n#include <drx.h>\n\n#include \"cli.hpp\"\n#include \"dispatcher.hpp\"\n#include \"factory.hpp\"\n#include \"util.hpp\"\n\nusing std::size_t;\nusing std::string;\n\n/// @brief Pointer to the dispatcher instance;\n/// @note We have to use a global pointer to share state (tracer, speculator, state of the\n///       instrumentation) with the callbacks. This is the reason for NOLINT as well.\nstd::unique_ptr<Dispatcher> glob_dispatcher = nullptr; // NOLINT\n\nnamespace dr_model\n{\n\nstatic void dr_model_del() noexcept;\n\n// =================================================================================================\n// State machine of instrumentation\n// =================================================================================================\n\n/// @brief Class holding information about the function to instrument and managing the state of the\n/// instrumentation process.\nclass InstrumentationStateMachine\n{\n  public:\n    InstrumentationStateMachine(std::string name_) : name(std::move(name_)) {}\n    ~InstrumentationStateMachine() = default;\n    InstrumentationStateMachine(const InstrumentationStateMachine &) = delete;\n    InstrumentationStateMachine &operator=(const InstrumentationStateMachine &) = delete;\n    InstrumentationStateMachine(InstrumentationStateMachine &&) = delete;\n    InstrumentationStateMachine &operator=(InstrumentationStateMachine &&) = delete;\n\n    /// @brief Name of the function to instrument\n    std::string name;\n\n    /// @brief Whether DynamoRIO is currently executing inside the instrumented function.\n    bool in_function = false;\n\n    void register_entry_pc(app_pc pc)\n    {\n        DR_ASSERT_MSG(not entry_found, \"Function entry pc already registered\");\n        entry_pc = pc;\n        entry_found = true;\n    }\n\n    bool is_entry_pc(byte const *pc) const { return entry_found and pc == entry_pc; }\n\n    void register_exit_pc(app_pc pc)\n    {\n        DR_ASSERT_MSG(not exit_found, \"Function exit pc already registered\");\n        exit_pc = pc;\n        exit_found = true;\n    }\n\n    bool is_exit_pc(byte const *pc) const { return exit_found and pc == exit_pc; }\n\n    /// @return true on first call (to trigger code cache flush, which will cause re-execution),\n    ///         false afterwards\n    bool start_instrumentation(void *drcontext)\n    {\n        DR_ASSERT_MSG(in_function == false,\n                      \"[ERROR] Recursive calls to the instrumented function are not supported.\");\n        in_function = true;\n\n        // Flush all code cache: we might want to instrument basic blocks that have already\n        // been translated (e.g. libc)\n        if (not entry_flush_done) {\n            flush_bb_cache();\n            entry_flush_done = true;\n            in_function = false;\n\n            // quick return: the flush will cause a re-instrumentation, so this function will be\n            // called again immediately after this return\n            return true;\n        }\n\n        // If this is the first time we instrument the function, we need to initialize the\n        // dispatcher and store the function's return address for later instrumentation.\n        if (not glob_dispatcher->is_initialized) {\n            glob_dispatcher->start();\n        } else {\n            glob_dispatcher->restart();\n        }\n\n        // Also, if this is the first time we instrument the function, we have to\n        // identify the exit pc by inspecting the return address on the stack\n        // (we assume that the function is always called from the same location, hence\n        // this is done only once)\n        if (not exit_found) {\n            dr_mcontext_t mc = {sizeof(mc), DR_MC_ALL};\n            dr_get_mcontext(drcontext, &mc);\n            exit_found = true;\n            exit_pc = *((app_pc *)mc.xsp);\n        }\n        return false;\n    }\n\n    void end_instrumentation(void *drcontext, instrlist_t *bb, instr_t *instr)\n    {\n        DR_ASSERT_MSG(in_function == true,\n                      \"[ERROR] Found function exit pc while not in the function.\");\n        in_function = false;\n        glob_dispatcher->instrument_exit(drcontext, bb, instr);\n    }\n\n  private:\n    /// @brief First pc executed when entering the instrumented function. This is populated\n    /// dynamically by `event_module_load` based on symbol resolution.\n    app_pc entry_pc = nullptr;\n    /// @brief Whether the function entry point has been found.\n    bool entry_found = false;\n    /// @brief The first time the entry point is executed, we flush the code cache, but only once.\n    /// This flag tracks whether we already did it.\n    bool entry_flush_done = false;\n    /// @brief First pc executed after the instrumented function. This is populated dynamically once\n    /// we reach a call to the instrumented function by inspecting the return address on the stack.\n    app_pc exit_pc = nullptr;\n    /// @brief Whether the function exit point has been found at least once.\n    /// @note Currently we assume that the exit point is always the same, that is the function\n    ///       is always called by the same instruction.\n    bool exit_found = false;\n};\n\n/// @brief State machine instance\n/// @note We have to use a global pointer since it is the only way to make it accessible from\n///       DynamoRIO callbacks. This is the reason for NOLINT as well.\n/// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)\nstatic std::unique_ptr<InstrumentationStateMachine> instrumentation_state_machine = nullptr;\n\n// =================================================================================================\n// Event callbacks\n// =================================================================================================\n\n/// @brief Callback executed before loading a module.\n///        This callback is responsible for detecting the presence of the function to instrument.\n///        It checks if the module being loaded contains the function to instrument, and if so,\n///        communicates its address to Dispatcher, so that is knows when\n///        to start the instrumentation (see `event_instrumentation_start`).\n/// @param unused\n/// @param module_ Pointer to the module data\n/// @param unused\n/// @return void\nstatic void event_module_load(void * /*drcontext*/, const module_data_t *module_, bool /*loaded*/)\n{\n    size_t offset = 0;\n    const char *symbol = instrumentation_state_machine->name.c_str();\n    const drsym_error_t sym_res =\n        drsym_lookup_symbol(module_->full_path, symbol, &offset, DRSYM_DEMANGLE);\n    if (sym_res == DRSYM_SUCCESS) {\n        instrumentation_state_machine->register_entry_pc(module_->start + offset);\n    }\n}\n\n/// @brief Callback executed at the first instrumentation stage:\n///        application-to-application transformation.\n///        The implementation expands string ops and scatter/gather\n///        into a sequence of normal memory references.\n/// @param drcontext The drcontext of the current thread\n/// @param unused\n/// @param bb The basic block to be transformed\n/// @param unused\n/// @param unused\n/// @return BB emitted state (dr_emit_flags_t)\nstatic dr_emit_flags_t event_bb_app2app(void *drcontext, void * /*tag*/, instrlist_t *bb,\n                                        bool /*for_trace*/, bool /*translating*/)\n{\n    bool err = false;\n    err |= !drutil_expand_rep_string(drcontext, bb);\n    err |= !drx_expand_scatter_gather(drcontext, bb, nullptr);\n    if (err) {\n        dr_printf(\"ERROR: failed to expand string ops or scatter/gather\\n\");\n        dr_abort();\n    }\n    return DR_EMIT_DEFAULT;\n}\n\n/// @brief Callback executed at the third instrumentation stage: instrumentation insertion.\n///        The implementation invokes the Dispatcher::instrument_instruction method for every\n///        (post-expanded) instruction in the basic block.\n/// @param drcontext The drcontext of the current thread\n/// @param unused\n/// @param bb Parent basic block\n/// @param instr The instruction to instrument\n/// @param unused\n/// @param unused\n/// @param unused\n/// @return BB emitted state (dr_emit_flags_t)\nstatic dr_emit_flags_t event_bb_instrumentation(void *drcontext, void * /*tag*/, instrlist_t *bb,\n                                                instr_t *instr, bool /*for_trace*/,\n                                                bool /*translating*/, void * /*user_data*/)\n{\n    // disassemble_with_info(drcontext, instr_get_app_pc(org_instr), STDOUT, true, true);\n    app_pc instr_pc = instr_get_app_pc(instr);\n\n    if (instrumentation_state_machine->is_entry_pc(instr_pc)) {\n        const bool triggers_reexecute =\n            instrumentation_state_machine->start_instrumentation(drcontext);\n        if (triggers_reexecute) {\n            // start_instrumentation triggered a code cache flush, so we return early to re-execute\n            return DR_EMIT_DEFAULT;\n        }\n        // no return here: this is the first instruction of the target function,\n        // so we still need to instrument it as all other instructions\n    }\n\n    if (instrumentation_state_machine->is_exit_pc(instr_pc)) {\n        // We found the end pc: add the corresponding callback\n        instrumentation_state_machine->end_instrumentation(drcontext, bb, instr);\n\n        // return early: this instruction is already outside the instrumented function (it's\n        // the first instruction after the return), so we don't need to instrument it\n        return DR_EMIT_DEFAULT;\n    }\n\n    // Add a clean call to the dispatch callback, which will forward the call to the service\n    // modules\n    return glob_dispatcher->instrument_instruction(drcontext, bb, instr);\n}\n\n/// @brief Callback executed upon exceptions\n/// @param drcontext The drcontext of the current thread\n/// @param excpt Pointer to the exception data\n/// @return whether the signal should be redirected or delivered to the application\nstatic dr_signal_action_t event_signal(void *drcontext, dr_siginfo_t *siginfo)\n{\n    if (glob_dispatcher->handle_exception(drcontext, siginfo)) {\n        return DR_SIGNAL_REDIRECT;\n    }\n\n    // Continue with the default exception handling if no redirection happened\n    return DR_SIGNAL_DELIVER;\n}\n\n/// @brief Callback executed before exiting the application.\n/// @return void\nstatic void event_exit()\n{\n    // There is a possibility that the tracing process has not been finalized\n    // because the traced function has not been called\n    glob_dispatcher->finalize();\n\n    // Make sure we've sent all the collected data\n    fflush(stdout);\n\n    // Delete the dispatcher\n    glob_dispatcher.reset();\n\n    // Close the DR extensions\n    dr_model_del();\n}\n\n// =================================================================================================\n// Model constructor and destructor\n// =================================================================================================\n\n/// @brief Constructor of the DR model.\n///        The function initializes the DR extensions and registers callbacks.\n/// @return void\n/// @throw std::runtime_error if any of the DR extensions fails to start\nstatic void dr_model_init()\n{\n    // Start DR extensions\n    if (!drmgr_init())\n        throw std::runtime_error(\"ERROR: failed to start drmgr\\n\");\n    if (!drutil_init())\n        throw std::runtime_error(\"ERROR: failed to start drutil\\n\");\n    if (!drx_init())\n        throw std::runtime_error(\"ERROR: failed to start drx\\n\");\n    if (!drwrap_init())\n        throw std::runtime_error(\"ERROR: failed to start drwrap\\n\");\n    if (drsym_init(0) != DRSYM_SUCCESS)\n        throw std::runtime_error(\"ERROR: failed to start drsym\\n\");\n\n    // Register callbacks\n    if (!drmgr_register_module_load_event(event_module_load))\n        throw std::runtime_error(\"ERROR: failed to register a callback\\n\");\n    if (!drmgr_register_bb_app2app_event(event_bb_app2app, nullptr))\n        throw std::runtime_error(\"ERROR: failed to register a callback\\n\");\n    if (!drmgr_register_bb_instrumentation_event(nullptr, event_bb_instrumentation, nullptr))\n        throw std::runtime_error(\"ERROR: failed to register a callback\\n\");\n\n    drmgr_register_signal_event(event_signal);\n    dr_register_exit_event(event_exit);\n}\n\n/// @brief Destructor of the DR model.\n///        The function unregisters callbacks and closes the DR extensions.\n/// @return void\nvoid dr_model_del() noexcept\n{\n    drmgr_unregister_module_load_event(event_module_load);\n    drmgr_unregister_bb_app2app_event(event_bb_app2app);\n    drmgr_unregister_bb_insertion_event(event_bb_instrumentation);\n\n    drsym_exit();\n    drwrap_exit();\n    drx_exit();\n    drutil_exit();\n    drmgr_exit();\n\n    instrumentation_state_machine.reset();\n}\n\n} // namespace dr_model\n\n// =================================================================================================\n// Model entry point\n// =================================================================================================\n\n/// @brief Entry point of the DR model.\n///        The function initializes the dispatcher, registers callbacks,\n///        and starts the DR extensions.\n/// @param _ Unused\n/// @param argc Number of CLI arguments\n/// @param argv CLI arguments\n/// @return void\nDR_EXPORT void dr_client_main(client_id_t /* client_id */, int argc, const char **argv)\n{\n    // Parse CLI arguments\n    cli_args_t parsed_args = {};\n    parse_cli(argc, argv, parsed_args);\n\n    // Special cases:\n    if (parsed_args.list_tracers) {\n        for (const auto &tracer_name : get_tracer_list()) {\n            dr_printf(\"%s\\n\", tracer_name.c_str());\n        }\n        return;\n    }\n    if (parsed_args.list_speculators) {\n        for (const auto &speculator_name : get_speculator_list()) {\n            dr_printf(\"%s\\n\", speculator_name.c_str());\n        }\n        return;\n    }\n\n    // Create a dispatcher instance\n    glob_dispatcher = std::make_unique<Dispatcher>(&parsed_args);\n\n    // Set the target function\n    dr_model::instrumentation_state_machine =\n        std::make_unique<dr_model::InstrumentationStateMachine>(parsed_args.instrumented_func);\n\n    // Initialize the DR model\n    dr_model::dr_model_init();\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/speculator_abc.cpp",
    "content": "///\n/// File: Abstract interface to be implemented by all speculators.\n///       For implementations of concrete speculators, see speculators/*.cpp files.\n///\n///      A speculator is a component that modifies the execution process of a test case when it\n///      runs on the contract model (e.g., it can emulate misprediction of branches).\n///      As such, speculators implement execution clauses of different contracts.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <algorithm>\n#include <array>\n#include <cstdint>\n\n#include <dr_api.h>\n#include <dr_ir_opcodes_x86.h>\n#include <dr_ir_opnd.h>\n#include <dr_os_utils.h>\n#include <memory>\n\n#include \"dr_events.h\"\n#include \"dr_ir_instr.h\"\n#include \"dr_tools.h\"\n#include \"observables.hpp\"\n#include \"speculator_abc.hpp\"\n#include \"util.hpp\"\n\n// =================================================================================================\n// Local helper functions\n// =================================================================================================\n\n// See Intel Manual https://cdrdv2.intel.com/v1/dl/getContent/671200\n// chapter 10.3 - Serializing Instructions.\nstatic constexpr const std::array<uint64_t, 35> serializing_opcodes = {\n    // Non-privileged memory-ordering instructions\n    OP_lfence, OP_mfence, OP_sfence,\n    // Privileged serializing instructions\n    // TODO: add MOV CR (except CR8)\n    OP_invd, OP_invept, OP_invlpg, OP_invvpid, OP_lgdt, OP_lidt, OP_lldt, OP_ltr, OP_wbinvd,\n    OP_wrmsr,\n    // Non-privileged serializing instructions\n    OP_cpuid, OP_iret, OP_rsm, OP_serialize,\n    // TSX/RTM instructions (not tracked by DynamoRIO).\n    OP_xbegin, OP_xabort, OP_xend, OP_xtest,\n    // XSAVE/XRESTORE instructions (not tracked by DynamoRIO).\n    OP_xsave32, OP_xsave64, OP_xsavec32, OP_xsavec64, OP_xsaves32, OP_xsaves64, OP_xsaveopt32,\n    OP_xsaveopt64, OP_xrstor32, OP_xrstor64, OP_xrstors32, OP_xrstors64,\n    // Other special instructions\n    OP_hlt,\n    // NOTE: syscalls are not instrumented by Dynamorio, this makes sure that speculation is aborted\n    // on speculative syscall instructions.\n    OP_syscall};\n\nstatic bool is_speculation_barrier(const uint64_t opcode)\n{\n    return std::any_of(serializing_opcodes.begin(), serializing_opcodes.end(),\n                       [&opcode](const uint64_t barrier) { return opcode == barrier; });\n}\n\n// =================================================================================================\n// Public Methods\n// =================================================================================================\nvoid SpeculatorABC::enable() { enabled = true; }\n\nvoid SpeculatorABC::disable() { enabled = false; }\n\nbool SpeculatorABC::skip_speculation() const\n{\n    if (not enabled)\n        return true;\n    if (nesting >= max_nesting)\n        return true;\n    if (spec_window >= max_spec_window)\n        return true;\n    return false;\n}\n\nvoid SpeculatorABC::checkpoint(dr_mcontext_t *mc, pc_t pc)\n{\n    // store the register state and the rollback address\n    checkpoints.push_back({.rollback_pc = pc, .spec_window = spec_window, .mc = *mc});\n    logger.log_checkpoint(pc, spec_window, store_log.size());\n    taint_tracker.checkpoint(false);\n\n    // update the state machine that tracks the speculation proces\n    in_speculation = true;\n    nesting += 1;\n}\n\npc_t SpeculatorABC::rollback(dr_mcontext_t *mc)\n{\n    // restore the last checkpoint\n    if (checkpoints.empty()) {\n        dr_printf(\"[ERROR] SpeculatorABC::rollback: no checkpoints to rollback\");\n        dr_abort();\n    }\n    const checkpoint_t checkpoint = checkpoints.back();\n    checkpoints.pop_back();\n    *mc = checkpoint.mc;\n    spec_window = checkpoint.spec_window;\n\n    // undo all store operations performed during speculation\n    while (not store_log.empty()) {\n        const auto store = store_log.back();\n\n        // Rollback only entries of the last (nested) speculative window\n        if (store.nesting_level < nesting)\n            break;\n\n        // Try restoring the previous value in memory.\n        size_t w_size = 0;\n        const bool success = dr_safe_write((byte *)store.addr, store.size, &store.val, &w_size);\n        logger.log_rollback_store(store.addr, store.val, w_size, store.nesting_level);\n\n        // The rollback should always be successful.\n        // NOTE: The following cases are already handled elsewhere:\n        //       1. Rollback of an invalid store.\n        //            - stores to non-valid memory are handled by handle_mem_access()\n        //            - stores to non-writable memory cause an exception, and their\n        //            corresponding entires are flushed by handle_exception()\n        //       2. Rollback after faulty indirect call/ret.\n        //            - should never be executed, handled by handle_instruction()\n        if (not success) {\n            // If ignoring permissions does not work, we cannot recover.\n            dr_printf(\"[ERROR] Failed to rollback store -- addr: %lx  val: %lx  sx: %d\\n\",\n                      store.addr, store.val, store.size);\n            // Read page protections\n            uint prot = -1;\n            dr_query_memory((byte *)store.addr, nullptr, nullptr, &prot);\n            dr_printf(\"[ERROR] Page prot 0x%x\\n\", prot);\n            dr_abort();\n            return 0; // unreachable\n        }\n        store_log.pop_back();\n    }\n\n    // update the state machine that tracks the speculation process\n    nesting -= 1;\n    if (nesting <= 0) {\n        nesting = 0;\n        in_speculation = false;\n        if (not checkpoints.empty() or not store_log.empty()) {\n            dr_printf(\"[ERROR] Speculation ended but there are still %d checkpoints and %d \"\n                      \"store logs to consume\\n\",\n                      checkpoints.size(), store_log.size());\n            dr_abort();\n        }\n    }\n\n    taint_tracker.rollback();\n    logger.log_rollback(nesting, checkpoint.rollback_pc);\n    return checkpoint.rollback_pc;\n}\n\npc_t SpeculatorABC::handle_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc)\n{\n    // the last instruction committed: all entries in the store_log are valid\n    store_log.update_committed();\n\n    if (not in_speculation)\n        return 0;\n\n    // rollback if we hit a speculation barrier\n    if (is_speculation_barrier(instr.opcode)) {\n        return rollback(mc);\n    }\n\n    // rollback if we hit a speculation window limit\n    spec_window += 1;\n    if (spec_window >= max_spec_window) {\n        return rollback(mc);\n    }\n\n    // rollback if we're about to jump/ret to an illegal address\n    if (is_illegal_jump(instr, mc, dc, decoder)) {\n        return rollback(mc);\n    }\n\n    return 0;\n}\n\nbool SpeculatorABC::handle_mem_access(bool is_write, void *address, uint64_t size)\n{\n    if (not in_speculation)\n        return true;\n\n    if (not is_write)\n        return true;\n\n    // record changes made to the memory\n    auto cur_address = (uint64_t)address;\n    size_t remaining_size = size;\n\n    // The store might be bigger than 64 bits (e.g. vector ops): save 64 bits at a time\n    while (remaining_size > 0) {\n        const uint64_t cur_size = std::min(remaining_size, sizeof(uint64_t));\n        // NOTE: on speculative paths, safe reads are the only way to load from memory, since\n        // pointers might be invalid.\n        size_t r_size = 0;\n        uint64_t val = 0;\n        const bool success = dr_safe_read((byte *)cur_address, cur_size, (byte *)&val, &r_size);\n\n        if (not success) {\n            // If the memory access is illegal, the store is bound to fail: rollback.\n            return false;\n        }\n\n        // Save the previous memory value to be restored after speculation\n        store_log.push_back({\n            .addr = cur_address,\n            .val = val,\n            .size = cur_size,\n            .nesting_level = nesting,\n        });\n\n        // Advance until all relevant memory has been saved\n        cur_address += cur_size;\n        remaining_size -= cur_size;\n    }\n\n    return true;\n}\n\nstatic bool is_supported_reg(const reg_id_t reg)\n{\n    // Some registers cannot be modified from the API, see DynamoRIO NYI i#3504\n    return reg_is_gpr(reg) or (reg >= DR_REG_START_XMM && reg <= DR_REG_STOP_XMM) or\n           (reg >= DR_REG_START_YMM && reg <= DR_REG_STOP_YMM) or\n           (reg >= DR_REG_START_ZMM && reg <= DR_REG_STOP_ZMM);\n}\n\nstatic std::pair<instr_t *, byte *> get_load_inst(void *dc, byte *pc, Decoder &decoder)\n{\n    // Decode the instruction and get its next PC\n    instr_t *cur_instr = decoder.get_decoded_instr(dc, pc);\n    byte *next_pc = decoder.get_next_pc(dc, pc);\n\n    // Return a nullptr if it's not a load.\n    if (not instr_reads_memory(cur_instr))\n        return {nullptr, nullptr};\n\n    return {cur_instr, next_pc};\n}\n\nbool SpeculatorABC::handle_exception(void *drcontext, dr_siginfo_t *siginfo)\n{\n    if (not in_speculation)\n        return false; // nothing to do\n\n    // Get faulty instruction's context\n    dr_mcontext_t *mc = siginfo->mcontext;\n\n    // Check if we need to poison the destination register. If not, just rollback.\n    if (poison_value.has_value()) {\n        // Decode the instruction\n        const auto [cur_instr, next_pc] = get_load_inst(drcontext, mc->pc, decoder);\n\n        // Forward poison value\n        if (cur_instr != nullptr and instr_num_dsts(cur_instr) > 0) {\n            // Get the first destination register\n            // TODO: what if the instruction has more than one destination register?\n            const opnd_t dst = instr_get_dst(cur_instr, 0);\n            // TODO: what if the destination is memory?\n            if (opnd_is_reg(dst)) {\n                reg_id_t reg = opnd_get_reg(dst);\n                reg = reg_to_pointer_sized(reg);\n                // Not all registers can be written from the API\n                if (is_supported_reg(reg)) {\n                    // Create a buffer with the repeated poison value\n                    constexpr int max_reg_size = 64;\n                    constexpr int n_elems = max_reg_size / sizeof(uint64_t);\n                    std::array<uint64_t, n_elems> poison_buf = {};\n                    std::fill(poison_buf.begin(), poison_buf.end(), poison_value.value());\n\n                    // Set the destination register to the poison value\n                    reg_set_value_ex(reg, mc, (uint8_t *)(poison_buf.data()));\n                    // Skip to the next instruction\n                    // TODO: what if the instruction is supposed to have other side effects?\n                    mc->pc = next_pc;\n                    return true; // execution was redirected\n                }\n            }\n        }\n    }\n\n    // Flush stores that are in-flight (i.e. were performed by the failing instruction)\n    store_log.flush_uncommitted();\n    // Perform rollback\n    const pc_t newpc = rollback(mc);\n    mc->pc = (byte *)newpc;\n\n    return true; // execution was redirected\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/speculators/cond.cpp",
    "content": "///\n/// File: Implementation of the Conditional Branch (COND) Speculator\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <optional>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_ir_decode.h>\n#include <dr_ir_instr.h>\n#include <dr_ir_opcodes_x86.h>\n\n#include \"observables.hpp\"\n#include \"speculator_abc.hpp\"\n#include \"speculators/cond.hpp\"\n#include \"types/decoder.hpp\"\n\n// =================================================================================================\n// Local helper functions\n// =================================================================================================\n\n/// @brief Summary of the relevant information for conditional branches\ntypedef struct {\n    pc_t target;\n    pc_t fallthrough;\n    bool is_loop;\n    bool will_jump;\n} BranchInfo;\n\n/// @brief If the instruction is a conditional branch return its relevant information, otherwise\n/// return an empty option.\nstatic std::optional<BranchInfo> get_branch_info(instr_obs_t instr, dr_mcontext_t *mc, void *dc,\n                                                 Decoder &decoder)\n{\n    // Decode the instruction using the shared cache\n    instr_t *cur_instr = decoder.get_decoded_instr(dc, (byte *)instr.pc);\n\n    // Not a branch, return empty option\n    if (not instr_is_cbr(cur_instr))\n        return {};\n\n    // Get the next PC (fallthrough address) from the cache\n    byte *next_pc = decoder.get_next_pc(dc, (byte *)instr.pc);\n\n    // Parse branch information\n    return BranchInfo{\n        .target = (pc_t)instr_get_branch_target_pc(cur_instr),\n        .fallthrough = (pc_t)next_pc,\n        .is_loop = instr_is_cti_loop(cur_instr),\n        .will_jump = instr_jcc_taken(cur_instr, mc->xflags),\n    };\n}\n\n// =================================================================================================\n// Class implementation\n// =================================================================================================\n\npc_t SpeculatorCond::handle_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc)\n{\n    // Handling in the superclass takes priority\n    const pc_t next_pc = SpeculatorABC::handle_instruction(instr, mc, dc);\n    if (next_pc != 0)\n        return next_pc;\n\n    // Check if speculation should be skipped\n    if (skip_speculation())\n        return 0;\n\n    // Decode the instruction using the shared cache\n    const auto &branch_info = get_branch_info(instr, mc, dc, decoder);\n\n    // Skip if not a branch\n    if (not branch_info)\n        return 0;\n\n    // LOOP instructions must also decrement RCX\n    if (branch_info->is_loop)\n        mc->rcx -= 1;\n\n    // Simulate misprediction: checkpoint the correct path, speculate the opposite one\n    pc_t speculated_pc = 0;\n    if (branch_info->will_jump) {\n        checkpoint(mc, branch_info->target);\n        speculated_pc = branch_info->fallthrough;\n    } else {\n        checkpoint(mc, branch_info->fallthrough);\n        speculated_pc = branch_info->target;\n    }\n\n    // Redirect execution to the next speculative instruction\n    return speculated_pc;\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/speculators/seq.cpp",
    "content": "///\n/// File: Sequential Tracer; this tracer implements the contract model\n///       for the sequential execution of instructions.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"speculator_abc.hpp\"\n#include \"speculators/seq.hpp\"\n\n// Nothing here so far; the class identically mirrors the base class"
  },
  {
    "path": "rvzr/model_dynamorio/backend/taint_tracker.cpp",
    "content": "///\n/// File: Taint Tracker class\n///\n/// Performs backward taint analysis during DynamoRIO instrumentation to identify which parts\n/// of the test input (registers and memory) influence contract trace observations. Tracks data\n/// dependencies through instruction execution, supporting speculative execution via checkpoint/\n/// rollback. Used to implement `trace_test_case_with_taints` interface in the DR Model backend,\n/// and is therefore tightly coupled with rvzr's InputTaint class.\n///\n/// The module should only be used when the model is running in `rvzr` mode.\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include \"taint_tracker.hpp\"\n\n#include <cstdint>\n#include <iomanip>\n#include <sstream>\n#include <string>\n#include <unordered_set>\n\n#include <dr_api.h>        // NOLINT\n#include <dr_ir_instr.h>   // NOLINT\n#include <dr_ir_opcodes.h> // NOLINT\n#include <dr_ir_opnd.h>    // NOLINT\n#include <dr_ir_utils.h>   // NOLINT\n\n// End-of-taint marker written to taint output file\nconst uint64_t EOT_MARKER = -1ULL;\n\n// Maximum register ID used by DynamoRIO (register IDs are < 256)\n// Used to distinguish register labels from memory address labels\nconst unsigned MAX_REG_ID = 255;\n\n// Memory tracking granularity and alignment\nconst uint64_t MEM_TRACKING_GRANULARITY = 8; // Track memory at 8-byte granularity\nconst uint64_t QWORD_ALIGN_MASK = ~0x7ULL;   // Mask for 8-byte alignment (0xFFFFFFFFFFFFFFF8)\n\n/// @brief Mapping between DynamoRIO EFLAGS bits and our register IDs\nstruct FlagMapping {\n    uint read_flag;\n    uint write_flag;\n    reg_id_t reg_id;\n};\n\n/// @brief Table of all tracked flags\n/// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)\nstatic const FlagMapping FLAG_MAPPINGS[] = {\n    {EFLAGS_READ_CF, EFLAGS_WRITE_CF, DR_FLAG_CF}, {EFLAGS_READ_PF, EFLAGS_WRITE_PF, DR_FLAG_PF},\n    {EFLAGS_READ_AF, EFLAGS_WRITE_AF, DR_FLAG_AF}, {EFLAGS_READ_ZF, EFLAGS_WRITE_ZF, DR_FLAG_ZF},\n    {EFLAGS_READ_SF, EFLAGS_WRITE_SF, DR_FLAG_SF}, {EFLAGS_READ_OF, EFLAGS_WRITE_OF, DR_FLAG_OF},\n    {EFLAGS_READ_DF, EFLAGS_WRITE_DF, DR_FLAG_DF},\n};\n\n/// @brief Set of opcodes that override dependencies (MOV and LEA variants)\nstatic const std::unordered_set<int> OVERRIDE_OPCODES = {\n    OP_mov_ld, OP_mov_st, OP_mov_imm, OP_mov_priv, OP_movd,  OP_movq,\n    OP_movs,   OP_movsx,  OP_movzx,   OP_movsxd,   OP_movbe, OP_lea,\n};\n\n// =================================================================================================\n// Helper Functions\n// =================================================================================================\n\n/// @brief Normalize register to its 64-bit equivalent for tracking\n/// @param reg The register ID\n/// @return Normalized register ID\nstatic inline reg_id_t normalize_reg(reg_id_t reg)\n{\n    // For GPRs, normalize to 64-bit version\n    if (reg_is_gpr(reg)) {\n        return reg_resize_to_opsz(reg, OPSZ_8);\n    }\n    return reg;\n}\n\nstatic inline void track_operand(const bool is_src, const opnd_t opnd,\n                                 struct TrackedInstruction *tracked_inst)\n{\n    // Process register operands\n    if (opnd_is_reg(opnd)) {\n        const reg_id_t reg = normalize_reg(opnd_get_reg(opnd));\n        if (reg == DR_REG_NULL) {\n            return;\n        }\n        if (is_src) {\n            tracked_inst->src_regs.insert(reg);\n        } else {\n            tracked_inst->dest_regs.insert(reg);\n        }\n        return;\n    }\n\n    // Non-memory non-register - ignore\n    if (not opnd_is_memory_reference(opnd)) {\n        return;\n    }\n\n    // Base + displacement memory reference\n    if (opnd_is_base_disp(opnd)) {\n        const reg_id_t base = opnd_get_base(opnd);\n        const reg_id_t index = opnd_get_index(opnd);\n        if (base != DR_REG_NULL) {\n            tracked_inst->mem_address_regs.insert(normalize_reg(base));\n        }\n        if (index != DR_REG_NULL) {\n            tracked_inst->mem_address_regs.insert(normalize_reg(index));\n        }\n        return;\n    }\n\n    // Base-only memory reference\n    const reg_id_t base = opnd_get_base(opnd);\n    if (base != DR_REG_NULL) {\n        tracked_inst->mem_address_regs.insert(normalize_reg(base));\n    }\n}\n\nstatic inline void track_flags(const uint eflags, struct TrackedInstruction *tracked_inst)\n{\n    // Process each flag in the mapping table\n    for (const auto &mapping : FLAG_MAPPINGS) {\n        if ((eflags & mapping.read_flag) != 0) {\n            tracked_inst->src_regs.insert(mapping.reg_id);\n        }\n        if ((eflags & mapping.write_flag) != 0) {\n            tracked_inst->dest_regs.insert(mapping.reg_id);\n        }\n    }\n}\n\n/// @brief Convert DynamoRIO register ID to the encoding expected by rvzr (hardcoded mapping)\n/// @param reg\n/// @return rvzr register ID\nstatic RVZRRegId dr_reg_id_to_rvzr_reg_id(reg_id_t reg)\n{\n    // These IDs must match the offsets in the sandbox, as defined in docs/devel/\n    switch (reg) {\n    case DR_REG_RAX:\n        return RVZRRegId::RVZR_REG_RAX;\n    case DR_REG_RBX:\n        return RVZRRegId::RVZR_REG_RBX;\n    case DR_REG_RCX:\n        return RVZRRegId::RVZR_REG_RCX;\n    case DR_REG_RDX:\n        return RVZRRegId::RVZR_REG_RDX;\n    case DR_REG_RSI:\n        return RVZRRegId::RVZR_REG_RSI;\n    case DR_REG_RDI:\n        return RVZRRegId::RVZR_REG_RDI;\n    case DR_REG_XMM0:\n    case DR_REG_YMM0:\n        return RVZRRegId::RVZR_REG_XMM0;\n    case DR_REG_XMM1:\n    case DR_REG_YMM1:\n        return RVZRRegId::RVZR_REG_XMM1;\n    case DR_REG_XMM2:\n    case DR_REG_YMM2:\n        return RVZRRegId::RVZR_REG_XMM2;\n    case DR_REG_XMM3:\n    case DR_REG_YMM3:\n        return RVZRRegId::RVZR_REG_XMM3;\n    case DR_REG_XMM4:\n    case DR_REG_YMM4:\n        return RVZRRegId::RVZR_REG_XMM4;\n    case DR_REG_XMM5:\n    case DR_REG_YMM5:\n        return RVZRRegId::RVZR_REG_XMM5;\n    case DR_REG_XMM6:\n    case DR_REG_YMM6:\n        return RVZRRegId::RVZR_REG_XMM6;\n    case DR_REG_XMM7:\n    case DR_REG_YMM7:\n        return RVZRRegId::RVZR_REG_XMM7;\n    case DR_FLAG_AF:\n    case DR_FLAG_CF:\n    case DR_FLAG_DF:\n    case DR_FLAG_OF:\n    case DR_FLAG_PF:\n    case DR_FLAG_SF:\n    case DR_FLAG_ZF:\n        // All tainted flags map to a single taint ID\n        return RVZRRegId::RVZR_REG_FLAGS;\n    default:\n        // The rest of the registers are not used by rvzr; if they get tainted, this is\n        // an artifact of the adaptor, and they should be ignored.\n        return RVZRRegId::RVZR_REG_IGNORED;\n    }\n}\n\n/// @brief Check if an instruction is a 64+ bit MOV-like instruction that overrides dependencies\n/// @param tracked_inst The tracked instruction to check\n/// @param decoder Decoder for instruction analysis\n/// @return true if the instruction overrides dependencies\nstatic bool is_override_instruction(const TrackedInstruction *tracked_inst, Decoder *decoder)\n{\n    // Only MOV and LEA opcodes override dependencies\n    auto opcode = static_cast<int>(tracked_inst->instr_obs.opcode);\n    if (OVERRIDE_OPCODES.find(opcode) == OVERRIDE_OPCODES.end()) {\n        return false;\n    }\n\n    // Skip instructions with more than one destination operand\n    if (tracked_inst->dest_regs.size() != 1) {\n        return false;\n    }\n\n    // Check that the destination register is 64-bit or more\n    const reg_id_t dest_reg = *tracked_inst->dest_regs.begin();\n    if (reg_is_xmm(dest_reg) || reg_is_ymm(dest_reg)) {\n        return true;\n    }\n    if (reg_is_gpr(dest_reg)) {\n        instr_t *instr =\n            decoder->get_decoded_instr(tracked_inst->dc, (byte *)tracked_inst->instr_obs.pc);\n        const int size = opnd_get_size(instr_get_dst(instr, 0));\n        return size == OPSZ_8;\n    }\n    return false;\n}\n\n/// @brief Heuristic to determine if a label represents a register or memory address\n/// @param label The label to check\n/// @return true if label likely represents a register, false if likely a memory address\n/// @note DynamoRIO register IDs are < 256, while memory addresses are much larger.\n///       May incorrectly classify low memory addresses (e.g., NULL page) as registers,\n///       but this is rare and only affects performance, not correctness.\nstatic bool label_is_reg(tracked_label_t label) { return label <= MAX_REG_ID; }\n\n// =================================================================================================\n// Public Methods\n// =================================================================================================\nvoid TaintTracker::enable()\n{\n    DR_ASSERT(not tracking_in_progress);\n    enabled = true;\n    tracking_in_progress = true;\n    sandbox_base = 0; // Will be set on first instruction\n}\n\nvoid TaintTracker::finalize()\n{\n    if (current_instruction != nullptr) {\n        finalize_instruction();\n    }\n\n    store_taints();\n    enabled = false;\n    tracking_in_progress = false;\n}\n\nvoid TaintTracker::checkpoint(bool include_current_inst)\n{\n    if (not enabled)\n        return;\n\n    if (include_current_inst && current_instruction != nullptr) {\n        finalize_instruction();\n    }\n\n    // Deep copy the dependencies\n    checkpoints.push_back(dependencies);\n}\n\nvoid TaintTracker::rollback()\n{\n    if (not enabled)\n        return;\n    DR_ASSERT_MSG(not checkpoints.empty(), \"TaintTracker::rollback: no checkpoints to rollback\");\n\n    if (current_instruction != nullptr) {\n        finalize_instruction();\n    }\n\n    // Restore dependencies from the last checkpoint\n    dependencies = checkpoints.back();\n    checkpoints.pop_back();\n}\n\nvoid TaintTracker::track_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc)\n{\n    if (not enabled)\n        return;\n\n    // Capture sandbox base from R14 on first instruction\n    if (sandbox_base == 0 && mc != nullptr) {\n        sandbox_base = mc->r14;\n    }\n\n    // Finalize the previous instruction\n    if (current_instruction != nullptr) {\n        finalize_instruction();\n    }\n    // dr_printf(\"--------------------------------\\n\");\n\n    // Create a new tracked instruction\n    current_instruction = std::make_unique<TrackedInstruction>();\n    current_instruction->instr_obs = instr;\n    current_instruction->dc = dc;\n\n    // Parse the instruction operands\n    parse_instruction_operands(current_instruction.get());\n\n    // Reset pending taint\n    pending_taint.clear();\n}\n\nvoid TaintTracker::track_memory_access(bool is_write, void *address, uint64_t size)\n{\n    if (not enabled)\n        return;\n    DR_ASSERT_MSG(current_instruction != nullptr,\n                  \"TaintTracker::track_memory_access called before track_instruction\");\n\n    // The following logic records the memory address into the list of\n    // source/destination memory operands\n    // The challenge here is that we track memory at 8-byte granularity, and a memory access\n    // may span multiple 8-byte blocks (e.g., a YMM store of 32 bytes).\n\n    // 1. Identify the range of 8-byte blocks accessed\n    auto addr = (uint64_t)address;\n    const uint64_t end_addr = addr + (size - 1);\n    const uint64_t range_start = addr & QWORD_ALIGN_MASK;\n    const uint64_t range_end = end_addr & QWORD_ALIGN_MASK;\n\n    // 2. Identify whether it's a read or write, and add to the appropriate set\n    std::set<tracked_mem_label_t> *updated_set =\n        is_write ? &current_instruction->dest_mems : &current_instruction->src_mems;\n\n    // 3. Add all addresses to tracking\n    for (uint64_t i = range_start; i <= range_end; i += MEM_TRACKING_GRANULARITY) {\n        updated_set->insert(i);\n    }\n}\n\nvoid TaintTracker::taint(taint_entry_type_t value_type)\n{\n    if (not enabled)\n        return;\n    if (current_instruction == nullptr)\n        return;\n\n    switch (value_type) {\n    case taint_entry_type_t::TAINT_ENTRY_PC:\n        // For PC: if the instruction is a control-flow instruction, taint RIP\n        {\n            instr_t *instr = decoder.get_decoded_instr(current_instruction->dc,\n                                                       (byte *)current_instruction->instr_obs.pc);\n            if ((instr_is_cbr(instr) || instr_is_ubr(instr))) {\n                pending_taint.insert(static_cast<tracked_label_t>(DR_REG_RIP));\n            }\n        }\n        break;\n\n    case taint_entry_type_t::TAINT_ENTRY_MEM:\n        // For MEM: taint memory address registers\n        {\n            for (const auto &reg : current_instruction->mem_address_regs) {\n                pending_taint.insert(static_cast<tracked_label_t>(reg));\n            }\n        }\n        break;\n\n    case taint_entry_type_t::TAINT_ENTRY_EOT:\n        // End of trace - do nothing\n        break;\n    }\n}\n\nvoid TaintTracker::finalize_instruction()\n{\n    DR_ASSERT_MSG(current_instruction != nullptr,\n                  \"TaintTracker::finalize_instruction called before track_instruction\");\n\n    // Extract dependencies of the tracked instruction\n    add_dependencies(current_instruction.get());\n\n    // Update taints - propagate dependencies to tainted labels\n    for (const auto &label : pending_taint) {\n        std::set<tracked_label_t> tainted_values;\n\n        // Check if label is a memory address (high bit set or > max register ID)\n        // Register IDs are typically small (< 256), memory addresses are large\n        if (label_is_reg(label)) {\n            auto it = dependencies.reg.find(static_cast<tracker_reg_label_t>(label));\n            if (it != dependencies.reg.end()) {\n                tainted_values = it->second;\n            } else {\n                tainted_values.insert(label);\n            }\n        } else {\n            auto it = dependencies.mem.find(label);\n            if (it != dependencies.mem.end()) {\n                tainted_values = it->second;\n            } else {\n                tainted_values.insert(label);\n            }\n        }\n\n        tainted_labels.insert(tainted_values.begin(), tainted_values.end());\n    }\n    dbg_print_taints();\n\n    // Clear dependencies of overwritten registers\n    // NOTE: this must be done *after* the taint update, or the taints will be lost\n    // dbg_print_dependencies();\n    remove_overwritten_dependencies(current_instruction.get());\n    dbg_print_dependencies();\n\n    // Reset the instruction\n    current_instruction.reset();\n}\n\n// =================================================================================================\n// Private Methods\n// =================================================================================================\n\nvoid TaintTracker::parse_instruction_operands(TrackedInstruction *tracked_inst)\n{\n    // Decode the instruction from PC (using cache for efficiency)\n    instr_t *instr =\n        decoder.get_decoded_instr(tracked_inst->dc, (byte *)tracked_inst->instr_obs.pc);\n\n    // Process destination operands\n    const int num_dsts = instr_num_dsts(instr);\n    for (int i = 0; i < num_dsts; i++) {\n        const opnd_t opnd = instr_get_dst(instr, i);\n        track_operand(false, opnd, tracked_inst);\n    }\n\n    // Process source operands\n    const int num_srcs = instr_num_srcs(instr);\n    for (int i = 0; i < num_srcs; i++) {\n        const opnd_t opnd = instr_get_src(instr, i);\n        track_operand(true, opnd, tracked_inst);\n    }\n\n    // Check for implicit EFLAGS operands (DynamoRIO doesn't include them in explicit operands)\n    const uint eflags = instr_get_eflags(instr, DR_QUERY_DEFAULT);\n    track_flags(eflags, tracked_inst);\n\n    // Check for implicit RIP operand (for control-flow instructions)\n    if (instr_is_cbr(instr) || instr_is_ubr(instr)) {\n        tracked_inst->dest_regs.insert(DR_REG_RIP);\n    }\n}\n\nstd::set<tracked_label_t>\nTaintTracker::collect_source_dependencies(const TrackedInstruction *tracked_inst) const\n{\n    std::set<tracked_label_t> src_dependencies;\n\n    // Collect dependencies from source registers\n    for (const auto &reg : tracked_inst->src_regs) {\n        auto it = dependencies.reg.find(reg);\n        if (it != dependencies.reg.end()) {\n            src_dependencies.insert(it->second.begin(), it->second.end());\n        } else {\n            src_dependencies.insert(static_cast<tracked_label_t>(reg));\n        }\n    }\n\n    // Collect dependencies from source memory locations\n    for (const auto &addr : tracked_inst->src_mems) {\n        auto it = dependencies.mem.find(addr);\n        if (it != dependencies.mem.end()) {\n            src_dependencies.insert(it->second.begin(), it->second.end());\n        } else {\n            src_dependencies.insert(addr);\n        }\n    }\n\n    // Collect dependencies from memory address registers\n    for (const auto &reg : tracked_inst->mem_address_regs) {\n        auto it = dependencies.reg.find(reg);\n        if (it != dependencies.reg.end()) {\n            src_dependencies.insert(it->second.begin(), it->second.end());\n        } else {\n            src_dependencies.insert(static_cast<tracked_label_t>(reg));\n        }\n    }\n\n    return src_dependencies;\n}\n\ntemplate <typename LabelT>\nvoid TaintTracker::propagate_dependencies_to_dest(\n    LabelT dest_label, const std::set<tracked_label_t> &src_dependencies,\n    std::map<LabelT, std::set<tracked_label_t>> &dep_map)\n{\n    // If destination already has dependencies, merge with source dependencies\n    if (dep_map.find(dest_label) != dep_map.end()) {\n        dep_map[dest_label].insert(src_dependencies.begin(), src_dependencies.end());\n    } else {\n        // Create new dependency entry with source dependencies\n        dep_map[dest_label] = src_dependencies;\n        // Add the destination itself to its own dependencies\n        dep_map[dest_label].insert(static_cast<tracked_label_t>(dest_label));\n    }\n}\n\n// Explicit template instantiations\ntemplate void TaintTracker::propagate_dependencies_to_dest(\n    tracker_reg_label_t, const std::set<tracked_label_t> &,\n    std::map<tracker_reg_label_t, std::set<tracked_label_t>> &);\ntemplate void TaintTracker::propagate_dependencies_to_dest(\n    tracked_mem_label_t, const std::set<tracked_label_t> &,\n    std::map<tracked_mem_label_t, std::set<tracked_label_t>> &);\n\nvoid TaintTracker::add_dependencies(const TrackedInstruction *tracked_inst)\n{\n    // Get dependencies of the source operands\n    const std::set<tracked_label_t> src_dependencies = collect_source_dependencies(tracked_inst);\n\n    // Propagate source dependencies to destination registers\n    for (const auto &reg : tracked_inst->dest_regs) {\n        propagate_dependencies_to_dest(reg, src_dependencies, dependencies.reg);\n    }\n\n    // Propagate source dependencies to destination memory locations\n    for (const auto &mem : tracked_inst->dest_mems) {\n        propagate_dependencies_to_dest(mem, src_dependencies, dependencies.mem);\n    }\n}\n\nvoid TaintTracker::remove_overwritten_dependencies(const TrackedInstruction *tracked_inst)\n{\n    // Check if this is a MOV or LEA instruction that overrides previous dependencies\n    if (not is_override_instruction(tracked_inst, &decoder)) {\n        return;\n    }\n\n    // Get source dependencies (reuse helper to avoid duplication)\n    // Note: We only need src_regs and src_mems here, not mem_address_regs,\n    // but including them doesn't affect correctness and keeps code simple\n    std::set<tracked_label_t> src_dependencies = collect_source_dependencies(tracked_inst);\n\n    // Remove dependencies that are not in source dependencies\n    const tracker_reg_label_t dest_reg = *tracked_inst->dest_regs.begin();\n    auto it = dependencies.reg.find(dest_reg);\n    if (it != dependencies.reg.end()) {\n        auto &deps = it->second;\n        for (auto dep_it = deps.begin(); dep_it != deps.end();) {\n            if (src_dependencies.find(*dep_it) == src_dependencies.end()) {\n                dep_it = deps.erase(dep_it);\n            } else {\n                ++dep_it;\n            }\n        }\n    }\n}\n\nvoid TaintTracker::store_taints()\n{\n    DR_ASSERT_MSG(stream.is_open(), \"TaintTracker::store_taints: output stream is not open\");\n\n    // Write all collected labels to the output file\n    for (const auto &label : tainted_labels) {\n        uint64_t value = 0;\n        if (label_is_reg(label)) {\n            auto dr_reg_id = static_cast<reg_id_t>(label);\n            const RVZRRegId reg_id = dr_reg_id_to_rvzr_reg_id(dr_reg_id);\n            if (reg_id == RVZRRegId::RVZR_REG_IGNORED) {\n                continue; // Do not store taints of ignored registers\n            }\n            value = static_cast<uint64_t>(reg_id);\n        } else {\n            // Memory address: convert from absolute to sandbox-relative\n            DR_ASSERT_MSG(sandbox_base != 0,\n                          \"TaintTracker::store_taints: sandbox_base not initialized\");\n            value = label - sandbox_base;\n        }\n\n        stream.write(reinterpret_cast<const char *>(&value), sizeof(uint64_t));\n    }\n\n    // Write end-of-taint marker\n    auto eot = EOT_MARKER;\n    stream.write(reinterpret_cast<const char *>(&eot), sizeof(uint64_t));\n\n    // NOTE: the file is not closed here as more taints may be added later if we\n    // are tracing multiple inputs; the stream is closed in the destructor\n}\n\nvoid TaintTracker::dbg_print_taints()\n{\n    dr_printf(\"[TAINT] Tainted labels after instruction:\\n\");\n    for (const auto &label : tainted_labels) {\n        dr_printf(\"  Tainted label: %lx\\n\", label);\n    }\n}\n\nvoid TaintTracker::dbg_print_dependencies()\n{\n    dr_printf(\"[TAINT] Dependencies after instruction:\\n\");\n    for (const auto &reg_dep : dependencies.reg) {\n        dr_printf(\"  Reg 0x%2lx depends on: \", reg_dep.first);\n        for (const auto &dep : reg_dep.second) {\n            dr_printf(\"0x%lx \", dep);\n        }\n        dr_printf(\"\\n\");\n    }\n    for (const auto &mem_dep : dependencies.mem) {\n        dr_printf(\"  Mem 0x%lx depends on: \", mem_dep.first);\n        for (const auto &dep : mem_dep.second) {\n            dr_printf(\"0x%lx \", dep);\n        }\n        dr_printf(\"\\n\");\n    }\n    dr_printf(\"------------------------------------------------\\n\");\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/tracer_abc.cpp",
    "content": "///\n/// File: Abstract Model TracerABC\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <cstdint>\n#include <cstdio>\n#include <cstdlib>\n#include <string>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_events.h>\n#include <dr_ir_instr.h>\n#include <dr_ir_opnd.h>\n#include <dr_ir_utils.h>\n#include <dr_tools.h>\n\n#include <drmgr.h>\n#include <drreg.h>\n#include <drvector.h>\n\n#include \"observables.hpp\"\n#include \"tracer_abc.hpp\"\n#include \"types/input_taint.hpp\"\n#include \"types/trace.hpp\"\n\nusing std::string;\n\n// =================================================================================================\n// Constructors and Destructors\n// =================================================================================================\nTracerABC::TracerABC(const std::string &out_path, Logger &logger, TaintTracker &taint_tracker,\n                     Decoder &decoder, bool print)\n    : logger(logger), taint_tracker(taint_tracker), decoder(decoder), trace(print)\n{\n    // Initialize trace buffers\n    trace.open(out_path);\n}\n\nTracerABC::~TracerABC()\n{\n    if (not tracing_finalized) {\n        finalize();\n    }\n    trace.clear();\n    if (logger.is_enabled()) {\n        logger.close();\n    }\n}\n\n// =================================================================================================\n// Public Methods\n// =================================================================================================\nvoid TracerABC::enable()\n{\n    tracing_on = true;\n    tracing_finalized = false;\n}\n\nvoid TracerABC::finalize()\n{\n    if (tracing_finalized) {\n        return;\n    }\n\n    // Push the end-of-trace marker and flush the remaining entries.\n    trace.push_back({.type = trace_entry_type_t::ENTRY_EOT});\n    trace.flush();\n\n    // Print the trace buffers\n    if (logger.is_enabled()) {\n        logger.log_eot();\n    }\n\n    // Reset tracing flags\n    tracing_on = false;\n    tracing_finalized = true;\n}\n\nvoid TracerABC::observe_instruction(instr_obs_t /*instr*/, dr_mcontext_t * /*mc*/, void * /*dc*/)\n{\n    // The rest of the functionality - if any - is implemented by subclasses\n}\n\nvoid TracerABC::observe_mem_access(bool /*is_write*/, void * /*address*/, uint64_t /*size*/)\n{\n    // The rest of the functionality - if any - is implemented by subclasses\n}\n\nvoid TracerABC::observe_exception(dr_siginfo_t *siginfo) const\n{\n    if (not tracing_on) {\n        return;\n    }\n\n    trace.push_back({.addr = (pc_t)siginfo->access_address,\n                     .size = (uint32_t)siginfo->sig,\n                     .type = trace_entry_type_t::ENTRY_EXCEPTION});\n}\n\nvoid TracerABC::record_pc(instr_obs_t instr)\n{\n    taint_tracker.taint(taint_entry_type_t::TAINT_ENTRY_PC);\n\n    const trace_entry_t entry = {\n        .addr = instr.pc,\n        .size = 0,\n        .type = trace_entry_type_t::ENTRY_PC,\n    };\n    trace.push_back(entry);\n}\n\nvoid TracerABC::record_mem_access(bool is_write, void *address, uint64_t size)\n{\n    taint_tracker.taint(taint_entry_type_t::TAINT_ENTRY_MEM);\n\n    const trace_entry_t entry = {\n        .addr = reinterpret_cast<uint64_t>(address),\n        .size = (uint32_t)size,\n        .type = (is_write) ? trace_entry_type_t::ENTRY_WRITE : trace_entry_type_t::ENTRY_READ,\n    };\n    trace.push_back(entry);\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/tracers/ct.cpp",
    "content": "///\n/// File: Constant-time (CT) Tracer implementation and its variants\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <cstddef>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_ir_macros.h>\n#include <dr_ir_macros_x86.h>\n#include <dr_ir_opnd.h>\n#include <dr_ir_utils.h>\n#include <drutil.h>\n\n#include \"tracers/ct.hpp\"\n#include \"util.hpp\"\n\nvoid TracerCT::observe_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc)\n{\n    TracerABC::observe_instruction(instr, mc, dc);\n\n    // Nothing to do if tracing is off\n    if (not tracing_on) {\n        return;\n    }\n\n    record_pc(instr);\n}\n\nvoid TracerCT::observe_mem_access(bool is_write, void *address, uint64_t size)\n{\n    TracerABC::observe_mem_access(is_write, address, size);\n\n    // Nothing to do if tracing is off\n    if (not tracing_on) {\n        return;\n    }\n\n    record_mem_access(is_write, address, size);\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/tracers/ind.cpp",
    "content": "///\n/// File: Indirect Call Tracer implementation\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <optional>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_ir_macros.h>\n#include <dr_ir_macros_x86.h>\n#include <dr_ir_opnd.h>\n#include <dr_ir_utils.h>\n#include <drutil.h>\n\n#include \"dr_tools.h\"\n#include \"tracers/ind.hpp\"\n#include \"types/decoder.hpp\"\n\n// =================================================================================================\n// Local helper functions\n// =================================================================================================\n\n/// @brief Summary of the relevant information for indirect branches\ntypedef struct {\n    pc_t src;\n    pc_t target;\n} mbr_info_t;\n\n/// @brief If the instruction is a multi-branch instruction, get the source and target,\n/// otherwise return an empty option.\nstatic std::optional<mbr_info_t> get_mbr_info(instr_obs_t instr, dr_mcontext_t *mc, void *dc,\n                                              Decoder &decoder)\n{\n    // Decode the instruction using the shared cache\n    instr_t *cur_instr = decoder.get_decoded_instr(dc, (byte *)instr.pc);\n\n    // Check if it's an indirect jump or ret (a.k.a. multi-way branch).\n    if (not instr_is_mbr(cur_instr))\n        return {}; // ignore\n\n    const opnd_t target = instr_get_target(cur_instr);\n    app_pc target_addr = nullptr;\n    bool is_target_in_memory = false;\n\n    // Get the target, depending on the type of instruction\n    if (instr_is_return(cur_instr)) {\n        target_addr = (app_pc)mc->xsp;\n        is_target_in_memory = true;\n    } else if (opnd_is_reg(target)) {\n        const reg_id_t reg = opnd_get_reg(target);\n        target_addr = (app_pc)reg_get_value(reg, mc);\n        is_target_in_memory = false;\n    } else if (opnd_is_memory_reference(target)) {\n        target_addr = opnd_compute_address(target, mc);\n        is_target_in_memory = true;\n    } else {\n        dr_printf(\"[ERROR] ind_tracer: Unknown target operand type\\n\");\n        dr_abort();\n        return {}; // unreachable\n    }\n\n    // Load the target if it's in memory\n    if (is_target_in_memory) {\n        uint64_t loaded_val = 0;\n        if (dr_safe_read(target_addr, sizeof(uint64_t), &loaded_val, nullptr)) {\n            target_addr = (app_pc)loaded_val;\n        } else {\n            dr_printf(\"[ERROR] ind_tracer: Failed to read the target from memory\\n\");\n            dr_abort();\n            return {}; // unreachable\n        }\n    }\n\n    return mbr_info_t{.src = instr.pc, .target = (uint64_t)target_addr};\n}\n\n// =================================================================================================\n// Class implementation\n// =================================================================================================\n\nvoid TracerInd::observe_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc)\n{\n    TracerABC::observe_instruction(instr, mc, dc);\n\n    // Nothing to do if tracing is off\n    if (not tracing_on) {\n        return;\n    }\n\n    // Decode the instruction using the shared cache\n    const auto &mbr_info = get_mbr_info(instr, mc, dc, decoder);\n\n    // Skip if not a branch\n    if (not mbr_info)\n        return;\n\n    // FIXME: refactor to use similar pattern as in record_pc and record_mem_access\n    // Log source\n    trace.push_back({\n        .addr = mbr_info->src,\n        .size = 0,\n        .type = trace_entry_type_t::ENTRY_PC,\n    });\n    // Log destination\n    trace.push_back({\n        .addr = mbr_info->target,\n        .size = 0,\n        .type = trace_entry_type_t::ENTRY_IND,\n    });\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/tracers/pc.cpp",
    "content": "///\n/// File: Program Counter (PC) Tracer implementation\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <cstddef>\n\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_ir_macros.h>\n#include <dr_ir_macros_x86.h>\n#include <dr_ir_opnd.h>\n#include <dr_ir_utils.h>\n#include <drutil.h>\n\n#include \"tracers/pc.hpp\"\n#include \"util.hpp\"\n\nvoid TracerPC::observe_instruction(instr_obs_t instr, dr_mcontext_t *mc, void *dc)\n{\n    TracerABC::observe_instruction(instr, mc, dc);\n\n    // Nothing to do if tracing is off\n    if (not tracing_on) {\n        return;\n    }\n\n    record_pc(instr);\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/backend/util.cpp",
    "content": "///\n/// File: Helper functions for DR model\n///\n// Copyright (C) Microsoft Corporation\n// SPDX-License-Identifier: MIT\n\n#include <cstddef>\n\n#include <cstdint>\n#include <dr_api.h> // NOLINT\n#include <dr_defines.h>\n#include <dr_ir_opnd.h>\n#include <dr_tools.h>\n\n#include <drreg.h>\n#include <drvector.h>\n\n#include \"observables.hpp\"\n#include \"types/decoder.hpp\"\n#include \"util.hpp\"\n\nvoid reserve_register_checked(void *drcontext, instrlist_t *ilist, instr_t *where,\n                              drvector_t *permitted, DR_PARAM_OUT reg_id_t *reg)\n{\n    if (drreg_reserve_register(drcontext, ilist, where, permitted, reg) != DRREG_SUCCESS) {\n        dr_printf(\"ERROR: failed to reserve a register\\n\");\n        dr_abort();\n    }\n}\n\nvoid unreserve_register_checked(void *drcontext, instrlist_t *ilist, instr_t *where, reg_id_t reg)\n{\n    if (drreg_unreserve_register(drcontext, ilist, where, reg) != DRREG_SUCCESS) {\n        dr_printf(\"ERROR: failed to unreserve a register\\n\");\n        dr_abort();\n    }\n}\n\nbool force_write(byte *addr, size_t size, const uint64_t *val, size_t *w_size)\n{\n    // Read page protections\n    uint prot = -1;\n    dr_query_memory(addr, nullptr, nullptr, &prot);\n\n    // Make page writable\n    dr_memory_protect(addr, size, DR_MEMPROT_READ | DR_MEMPROT_WRITE | DR_MEMPROT_EXEC);\n    const bool success = dr_safe_write(addr, size, val, w_size);\n    // Restore previous protections\n    dr_memory_protect(addr, size, prot);\n\n    return success;\n}\n\nbool is_illegal_jump(instr_obs_t instr, dr_mcontext_t *mc, void *dc, Decoder &decoder)\n{\n    // Decode the instruction\n    instr_t *cur_instr = decoder.get_decoded_instr(dc, (byte *)instr.pc);\n\n    // Check if it's an indirect jump or ret (a.k.a. multi-way branch).\n    if (not instr_is_mbr(cur_instr))\n        return false; // ignore\n\n    const opnd_t target = instr_get_target(cur_instr);\n    app_pc target_addr = nullptr;\n    bool is_target_in_memory = false;\n\n    // Get the target, depending on the type of instruction\n    if (instr_is_return(cur_instr)) {\n        target_addr = (app_pc)mc->xsp;\n        is_target_in_memory = true;\n    } else if (opnd_is_reg(target)) {\n        const reg_id_t reg = opnd_get_reg(target);\n        target_addr = (app_pc)reg_get_value(reg, mc);\n        is_target_in_memory = false;\n    } else if (opnd_is_memory_reference(target)) {\n        target_addr = opnd_compute_address(target, mc);\n        is_target_in_memory = true;\n    } else {\n        return false; // ignore\n    }\n\n    // Load the target if it's in memory\n    if (is_target_in_memory) {\n        uint64_t loaded_val = 0;\n        if (dr_safe_read(target_addr, sizeof(uint64_t), &loaded_val, nullptr)) {\n            target_addr = (app_pc)loaded_val;\n        } else {\n            return true; // invalid target\n        }\n    }\n\n    // Check the permissions of the target address\n    uint prot = 0;\n    const bool target_exists = dr_query_memory(target_addr, nullptr, nullptr, &prot);\n    const bool target_is_executable = (prot & DR_MEMPROT_EXEC) != 0;\n\n    // Target can be followed only if it's a valid executable address\n    const bool target_is_valid = target_exists and target_is_executable;\n    return not target_is_valid;\n}\n\nvoid flush_bb_cache()\n{\n    const uint64_t flush_begin = 0;\n    const size_t flush_size = -1;\n\n    // NOTE: This is very conservative, but avoids any potentially expensive analysis of\n    // the target function\n    dr_delay_flush_region((byte *)flush_begin, flush_size, /*flush_id*/ 0, /*callback*/ nullptr);\n}\n"
  },
  {
    "path": "rvzr/model_dynamorio/model.py",
    "content": "\"\"\"\nFile: DynamoRIO-based backend to the contract model.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nimport os\nimport tempfile\nfrom subprocess import check_output, CalledProcessError, STDOUT\nfrom typing import List, Tuple, Optional, TYPE_CHECKING, Final, Any\nfrom typing_extensions import TypeAlias\n\nimport numpy as np\nfrom numpy.typing import NDArray\n\nfrom .trace_decoder import TraceDecoder, TraceEntryType, DebugTraceEntryType\n\nfrom ..model import Model\nfrom ..sandbox import BaseAddrTuple, SandboxLayout, DataArea\nfrom ..traces import CTrace, CTraceEntry\nfrom ..tc_components.test_case_data import save_input_sequence_as_rdbf, InputTaint\nfrom ..config import CONF\n\nif TYPE_CHECKING:\n    from ..tc_components.test_case_code import TestCaseProgram\n    from ..tc_components.test_case_data import InputData\n\n_DRRUN_TRACING_FLAGS: Final[str] = \" --mode rvzr --instrumented-func test_case_entry \"\n_ADAPTER_PATH: Final[str] = \"~/.local/dynamorio/adapter\"\n_DRRUN_CMD: Final[str] = \"~/.local/dynamorio/drrun -c ~/.local/dynamorio/libdr_model.so \" \\\n    \" {flags} -- {binary} {args}\"\n\n# Constants for trace processing\n_N_REGISTERS_IN_DUMP: Final[int] = 6  # rax, rbx, rcx, rdx, rsi, rdi\n_BYTES_PER_TAINT_ENTRY: Final[int] = 8  # each taint entry corresponds to 8 bytes\n_EOT_MARKER: Final[int] = np.iinfo(np.uint64).max  # end-of-transmission marker for taint files\n\n# Type aliases for raw trace entries (CFFI objects from TraceDecoder)\n# Note: These are dynamically typed CFFI objects, so we use Any with documentation\n_RawTraceEntry: TypeAlias = Any  # CFFI struct: trace_entry_t with addr, size, type fields\n_RawDebugTraceEntry: TypeAlias = Any  # CFFI struct: debug_trace_entry_t with type, regs union\n_RawTrace: TypeAlias = List[_RawTraceEntry]  # List of trace entries from one test execution\n_RawDebugTrace: TypeAlias = List[_RawDebugTraceEntry]  # List of debug trace entries\n\n\nclass DynamoRIOModel(Model):\n    \"\"\"\n    Adapter class that connects the DynamoRIO backend to the rest of Revizor.\n    \"\"\"\n    _obs_clause_name: Optional[str] = None\n    _exec_clause_name: Optional[str] = None\n\n    _installation_checked: bool = False  # flag to avoid checking DR installation multiple times\n\n    _test_case: Optional[TestCaseProgram] = None  # the current test case\n    _files: _DRFileManager\n\n    poison_value: int = 0  # If this value is != 0, it will be returned on speculative faulty loads\n\n    # ----------------------------------------------------------------------------------------------\n    # Constructor/Destructor\n    def __init__(self,\n                 bases: BaseAddrTuple,\n                 *args: Any,\n                 enable_mismatch_check_mode: bool = False) -> None:\n        # NOTE: the `bases` argument is not used as DynamoRIO backend does not allow\n        #       for customization of the memory layout\n        self._enable_mismatch_check_mode = enable_mismatch_check_mode\n        self.is_speculative = True  # may be changed by configure_clauses\n        self.poison_value = 0  # may be changed later\n        self._files = _DRFileManager()\n\n    def __del__(self) -> None:\n        self._files.delete_temp_files()\n\n    # ----------------------------------------------------------------------------------------------\n    # Public Interfaces\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        \"\"\"\n        Prepare the test case to be traced by the DynamoRIO backend.\n        This means creating a binary in the RCBF format so that it can be parsed by the backend.\n        :param test_case: the test case to load\n        :return: None\n        \"\"\"\n        self._test_case = test_case\n\n        # remove the previous RCBF file if it exists and create a new one\n        self._files.cleanup_on_load_test_case()\n\n        # store the test case in the RCBF format\n        test_case.get_obj().save_rcbf(self._files.rcbf)\n\n    def trace_test_case(self, inputs: List[InputData], nesting: int) -> List[CTrace]:\n        \"\"\" Implementation of Model.trace_test_case using the DynamoRIO backend. \"\"\"\n        trace = self._trace_test_case_common(inputs, nesting, enable_taints=False)\n        self._files.cleanup_after_tracing()\n        return trace\n\n    def trace_test_case_with_taints(self, inputs: List[InputData],\n                                    nesting: int) -> Tuple[List[CTrace], List[InputTaint]]:\n        \"\"\" Implementation of Model.trace_test_case_with_taints using the DynamoRIO backend. \"\"\"\n        traces = self._trace_test_case_common(inputs, nesting, enable_taints=True)\n        assert self._test_case is not None, \"Test case must be loaded before tracing\"\n        taint_reader = _TaintReader(self.layout, self._test_case)\n        taints = taint_reader.decode_taints(self._files.taints)\n        self._files.cleanup_after_tracing()\n        return traces, taints\n\n    def report_coverage(self, path: str) -> None:\n        raise NotImplementedError()\n\n    def configure_clauses(self, obs_clause_name: str, exec_clause_name: str) -> None:\n        \"\"\"\n        Configure the backend to use the given observation and execution clauses.\n        Also check if the given clauses are supported.\n        :param obs_clause_name: the name of the observation clause\n        :param exec_clause_name: the name of the execution clause\n        :return: None\n        :raises: ValueError if the given clauses are not supported\n        \"\"\"\n        assert self._obs_clause_name is None and self._exec_clause_name is None, \\\n            \"Cannot reconfigure the observation and execution clauses\"\n\n        # ensure that the DynamoRIO backend is installed\n        self._check_if_installed()\n\n        # check if the contract is supported\n        if obs_clause_name not in self.get_supported_obs_clauses(False):\n            raise ValueError(f\"Unsupported observation clause {obs_clause_name}\")\n        self._obs_clause_name = obs_clause_name\n\n        if exec_clause_name not in self.get_supported_exec_clauses(False):\n            raise ValueError(f\"Unsupported execution clause {exec_clause_name}\")\n        self._exec_clause_name = exec_clause_name\n\n        if exec_clause_name in [\"seq\", \"no_speculation\"]:\n            self.is_speculative = False\n\n    @classmethod\n    def get_supported_obs_clauses(cls, check_installation: bool = True) -> List[str]:\n        \"\"\"\n        Get the list of supported observation clauses.\n        :return: list of supported observation clauses\n        :raises: FileNotFoundError if the DynamoRIO backend is not installed\n        \"\"\"\n        if check_installation:\n            cls._check_if_installed()\n        cmd = _DRRUN_CMD.format(flags=\"--list-tracers\", binary=\"echo\", args=\"''\")\n        output = check_output(cmd, shell=True).decode(\"utf-8\")\n        return output.split(\"\\n\")[:-1]\n\n    @classmethod\n    def get_supported_exec_clauses(cls, check_installation: bool = True) -> List[str]:\n        \"\"\"\n        Get the list of supported execution clauses.\n        :return: list of supported execution clauses\n        :raises: FileNotFoundError if the DynamoRIO backend is not installed\n        \"\"\"\n        if check_installation:\n            cls._check_if_installed()\n        cmd = _DRRUN_CMD.format(flags=\"--list-speculators\", binary=\"echo\", args=\"''\")\n        output = check_output(cmd, shell=True).decode(\"utf-8\")\n        return output.split(\"\\n\")[:-1]\n\n    # ----------------------------------------------------------------------------------------------\n    # Private Methods\n    @classmethod\n    def _check_if_installed(cls) -> None:\n        \"\"\"\n        Ensure that the DynamoRIO backend is installed.\n        :return: None\n        :raises: FileNotFoundError if the DynamoRIO backend is not installed\n        \"\"\"\n        if not cls._installation_checked:  # check only once\n            cmd = _DRRUN_CMD.format(flags=\"--trace-output /dev/null\", binary=\"ls\", args=\"/dev/null\")\n            try:\n                output = check_output(cmd, shell=True, stderr=STDOUT).decode(\"utf-8\")\n            except (FileNotFoundError, CalledProcessError):\n                output = \"\"\n            if '/dev/null' not in output:\n                raise FileNotFoundError(\n                    \"DynamoRIO backend is not installed\\n\\n\\n\"\n                    \"Please follow the instructions in \"\n                    \"https://microsoft.github.io/side-channel-fuzzer/quick-start/\")\n            cls._installation_checked = True\n\n    def _trace_test_case_common(self, inputs: List[InputData], nesting: int,\n                                enable_taints: bool) -> List[CTrace]:\n        \"\"\"\n        Execute the test case with the given inputs on DR backend and return the traces\n        and the sandbox addresses.\n        :param inputs: input sequence to trace\n        :param nesting: maximum nesting level to emulated in the model\n        :return: list of contract traces, one per input\n        \"\"\"\n        assert self._test_case is not None, \"No test case was loaded\"\n        if len(inputs) == 0:\n            return []\n\n        # store the input sequence\n        save_input_sequence_as_rdbf(inputs, self._files.rdbf)\n\n        # call the backend\n        cmd = self._construct_drrun_cmd(enable_taints, nesting)\n        _ = check_output(cmd, shell=True)\n\n        # the execution might have had a different layout than before, update it\n        self._update_layout()\n\n        # read traces from the trace files\n        reader = _TraceReader(self.layout, self._test_case)\n        traces = reader.decode_traces(self._files.traces)\n        assert len(traces) > 0, \"No traces were retrieved from the DynamoRIO backend\"\n        assert len(traces) == len(inputs), \"Mismatch between the number of inputs and traces\"\n\n        if self._enable_mismatch_check_mode:\n            # In this mode, the contract trace is the register values at the end of the test case\n            dbg_reader = _DbgTraceReader(self.layout, self._test_case)\n            dbg_traces = dbg_reader.decode_traces(self._files.dbg_traces)\n            arch_traces = [CTrace(t.get_typed()[-_N_REGISTERS_IN_DUMP:]) for t in dbg_traces]\n            return arch_traces\n\n        return traces\n\n    def _construct_drrun_cmd(self, enable_taints: bool, nesting: int) -> str:\n        \"\"\"\n        Construct a command to call the DynamoRIO backend\n        with the given test case and input sequence.\n        \"\"\"\n        flags = _DRRUN_TRACING_FLAGS + \\\n            f\" --tracer {self._obs_clause_name}\" + \\\n            f\" --speculator {self._exec_clause_name}\" + \\\n            f\" --max-nesting {nesting}\" + \\\n            f\" --max-spec-window {CONF.model_max_spec_window}\" \\\n            f\" --trace-output {self._files.traces}\"\n        if enable_taints:\n            flags += f\" --taint-output {self._files.taints} --enable-taint-tracker\"\n        if self._enable_mismatch_check_mode:\n            flags += f\" --log-level 1 --debug-trace-output {self._files.dbg_traces}\"\n        if self.poison_value != 0:\n            flags += f\" --poison-value {self.poison_value}\"\n\n        binary = _ADAPTER_PATH\n        args = f\"{self._files.rcbf} {self._files.rdbf} {self._files.layout}\"\n        cmd = _DRRUN_CMD.format(flags=flags, binary=binary, args=args)\n        # print(cmd)\n        return cmd\n\n    def _update_layout(self) -> None:\n        \"\"\" Update the memory layout based on the addresses communicated by the adapter\n        via the bases file. \"\"\"\n        assert self._test_case is not None, \"No test case was loaded\"\n        with open(self._files.layout, 'rb') as f:\n            code_base_addr = int.from_bytes(f.read(8), byteorder=\"little\")\n            data_base_addr = int.from_bytes(f.read(8), byteorder=\"little\")\n        self.layout = SandboxLayout((data_base_addr, code_base_addr), self._test_case.n_actors())\n\n\n# ==================================================================================================\n# Private: File management\n# ==================================================================================================\nclass _DRFileManager:\n    \"\"\"\n    Local class responsible for managing temporary files used by the DynamoRIO backend.\n    \"\"\"\n\n    def __init__(self) -> None:\n        self.rcbf: str  # tmp file for current test case in RCBF format\n        self.rdbf: str  # tmp file for current input sequence in RDBF format\n        self.layout: str  # tmp file for receiving memory layout\n        self.traces: str  # tmp file for receiving contract traces\n        self.dbg_traces: str  # tmp file for receiving debug traces\n        self.taints: str  # tmp file for receiving taint traces\n        self._create_temp_files()\n\n    def cleanup_on_load_test_case(self) -> None:\n        \"\"\" Clean up RCBF and RDBF files when loading a new test case \"\"\"\n        with open(self.rcbf, 'wb') as f:\n            f.truncate()\n        with open(self.rdbf, 'wb') as f:\n            f.truncate()\n\n    def cleanup_after_tracing(self) -> None:\n        \"\"\" Clean up the files that will be used by the adapter to store its output \"\"\"\n        with open(self.traces, 'wb') as f:\n            f.truncate()\n        with open(self.dbg_traces, 'wb') as f:\n            f.truncate()\n        with open(self.taints, 'wb') as f:\n            f.truncate()\n        with open(self.layout, 'wb') as f:\n            f.truncate()\n\n    def _create_temp_files(self) -> None:\n        with tempfile.NamedTemporaryFile(\"wb\", delete=False) as rcbf_f:\n            self.rcbf = rcbf_f.name\n        with tempfile.NamedTemporaryFile(\"wb\", delete=False) as rdbf_f:\n            self.rdbf = rdbf_f.name\n        with tempfile.NamedTemporaryFile(\"wb\", delete=False) as trace_f:\n            self.traces = trace_f.name\n        with tempfile.NamedTemporaryFile(\"wb\", delete=False) as dbg_trace_f:\n            self.dbg_traces = dbg_trace_f.name\n        with tempfile.NamedTemporaryFile(\"wb\", delete=False) as taint_f:\n            self.taints = taint_f.name\n        with tempfile.NamedTemporaryFile(\"wb\", delete=False) as bases_f:\n            self.layout = bases_f.name\n\n    def delete_temp_files(self) -> None:\n        \"\"\" Delete all temporary files created for the DynamoRIO backend \"\"\"\n        if os.path.exists(self.rcbf):\n            os.unlink(self.rcbf)\n        if os.path.exists(self.rdbf):\n            os.unlink(self.rdbf)\n        if os.path.exists(self.traces):\n            os.unlink(self.traces)\n        if os.path.exists(self.dbg_traces):\n            os.unlink(self.dbg_traces)\n        if os.path.exists(self.taints):\n            os.unlink(self.taints)\n        if os.path.exists(self.layout):\n            os.unlink(self.layout)\n\n\n# ==================================================================================================\n# Private: Decoding of Traces\n# ==================================================================================================\nclass _TraceReader:\n    \"\"\"\n    Local class responsible for reading traces produced by DynamoRIO backend,\n    removing irrelevant information from them, and converting them to the\n    format that is expected by the contract model.\n    \"\"\"\n\n    def __init__(self, layout: SandboxLayout, test_case: TestCaseProgram) -> None:\n        self._layout = layout\n        self._test_case = test_case\n        self._decoder = TraceDecoder()\n\n    def decode_traces(self, trace_path: str) -> List[CTrace]:\n        \"\"\"\n        Read the traces produced by the DynamoRIO backend and return them in the format\n        that is expected by the contract model.\n        :return: list of contract traces\n        \"\"\"\n        traces: List[CTrace] = []\n\n        # iterate over the binary trace and parse the entries\n        raw_traces: List[_RawTrace] = self._decoder.decode_trace_file(trace_path)\n        for raw_trace in raw_traces:\n            converted = self._raw_to_ctrace(raw_trace)\n            if converted:\n                traces.append(converted)\n\n        # trim non relevant entries\n        traces = self._trim_traces(traces)\n\n        return traces\n\n    def _raw_to_ctrace(self, raw_trace: _RawTrace) -> CTrace:\n        trace: List[CTraceEntry] = []\n\n        for entry in raw_trace:\n            type_ = TraceEntryType(entry.type)\n            if type_ in (TraceEntryType.ENTRY_READ, TraceEntryType.ENTRY_WRITE):\n                val = self._layout.data_addr_to_offset(entry.addr)\n                trace.append(CTraceEntry(type_=\"mem\", value=val))\n            elif type_ == TraceEntryType.ENTRY_PC:\n                val = self._layout.code_addr_to_offset(entry.addr)\n                trace.append(CTraceEntry(type_=\"pc\", value=val))\n            elif type_ == TraceEntryType.ENTRY_IND:\n                val = self._layout.code_addr_to_offset(entry.addr)\n                trace.append(CTraceEntry(type_=\"ind\", value=val))\n\n        return CTrace(trace)\n\n    def _trim_traces(self, traces: List[CTrace]) -> List[CTrace]:\n        \"\"\"\n        Last instruction of the trace is the return instruction, which is inserted automatically\n        by the model and thus does not belong to the test case. We remove the corresponding entries\n        from the traces.\n        :return: the traces with the irrelevant entries removed\n        \"\"\"\n        new_traces: List[CTrace] = []\n        for trace in traces:\n            entry_list = trace.get_typed()\n\n            # Identify observations that belong to the return instruction\n            last_mem = None\n            last_pc = None\n            if entry_list[-1].type_ == \"mem\":\n                last_mem = entry_list[-1].value\n                entry_list.pop()\n            if entry_list[-1].type_ == \"pc\":\n                last_pc = entry_list[-1].value\n                entry_list.pop()\n\n            # In case the return happened multiple times (e.g., due to speculation),\n            # remove all corresponding entries\n            filtered_list = []\n            for entry in entry_list:\n                if last_pc is not None and entry.type_ == \"pc\" and entry.value == last_pc:\n                    continue\n                if last_mem is not None and entry.type_ == \"mem\" and entry.value == last_mem:\n                    continue\n                filtered_list.append(entry)\n\n            new_traces.append(CTrace(filtered_list))\n\n        return new_traces\n\n\nclass _DbgTraceReader:\n    \"\"\"\n    Local class responsible for reading debug traces produced by DynamoRIO backend.\n    \"\"\"\n\n    def __init__(self, layout: SandboxLayout, test_case: TestCaseProgram) -> None:\n        self._layout = layout\n        self._test_case = test_case\n        self._decoder = TraceDecoder()\n\n    def decode_traces(self, dbg_path: str) -> List[CTrace]:\n        \"\"\"\n        Read the debug traces produced by the DynamoRIO backend and return them in the format\n        that is expected by the contract model.\n        :return: list of debug traces\n        \"\"\"\n        dbg_traces: List[CTrace] = []\n\n        # do the same for debug traces\n        raw_dbg_traces: List[_RawDebugTrace] = self._decoder.decode_debug_trace_file(dbg_path)\n        for raw_dbg_trace in raw_dbg_traces:\n            converted = self._raw_dbg_to_ctrace(raw_dbg_trace)\n            if converted:\n                dbg_traces.append(converted)\n\n        # trim non relevant entries\n        if dbg_traces:\n            dbg_traces = self._trim_dbg_traces(dbg_traces)\n\n        return dbg_traces\n\n    def _raw_dbg_to_ctrace(self, raw_dbg_trace: _RawDebugTrace) -> CTrace:\n        trace: List[CTraceEntry] = []\n\n        for entry in raw_dbg_trace:\n            type_ = DebugTraceEntryType(entry.type)\n            if type_ == DebugTraceEntryType.ENTRY_REG_DUMP:\n                val = self._layout.code_addr_to_offset(entry.regs.pc)\n                trace.append(CTraceEntry(type_=\"pc\", value=val))\n                trace.append(CTraceEntry(type_=\"reg\", value=entry.regs.xax))\n                trace.append(CTraceEntry(type_=\"reg\", value=entry.regs.xbx))\n                trace.append(CTraceEntry(type_=\"reg\", value=entry.regs.xcx))\n                trace.append(CTraceEntry(type_=\"reg\", value=entry.regs.xdx))\n                trace.append(CTraceEntry(type_=\"reg\", value=entry.regs.xsi))\n                trace.append(CTraceEntry(type_=\"reg\", value=entry.regs.xdi))\n\n        return CTrace(trace)\n\n    def _trim_dbg_traces(self, dbg_traces: List[CTrace]) -> List[CTrace]:\n        \"\"\" Same as _trim_traces, but for debug traces \"\"\"\n        # Each register dump consists of 1 PC + N register values\n        dump_size = 1 + _N_REGISTERS_IN_DUMP\n\n        new_dbg_traces = []\n        for dbg_trace in dbg_traces:\n            entry_list = dbg_trace.get_typed()\n\n            # Remove the last register dump (corresponding to the return instruction)\n            last_pc = None\n            if entry_list[-dump_size].type_ == \"pc\":\n                last_pc = entry_list[-dump_size].value\n                entry_list = entry_list[:-dump_size]\n\n            # Remove all register dumps corresponding to the return instruction\n            filtered_list = []\n            skip_count = 0\n            for entry in entry_list:\n                if skip_count > 0:\n                    skip_count -= 1\n                    continue\n                if last_pc is not None and entry.type_ == \"pc\" and entry.value == last_pc:\n                    skip_count = _N_REGISTERS_IN_DUMP  # skip the register entries as well\n                    continue\n                filtered_list.append(entry)\n\n            new_dbg_traces.append(CTrace(filtered_list))\n\n        return new_dbg_traces\n\n\n# ==================================================================================================\n# Private: Decoding of Input Taints\n# ==================================================================================================\nclass _TaintReader:\n    \"\"\"\n    Local class responsible for reading input taints produced by DynamoRIO backend.\n\n    Taint output format:\n    - Input 1:\n        [taint_value (8 bytes)]\n        ... repeated for each tainted value\n        [end_marker (8 bytes)] (the marker is max_uint64)\n    - Input 2:\n        ...\n    \"\"\"\n\n    def __init__(self, layout: SandboxLayout, test_case: TestCaseProgram) -> None:\n        self._layout = layout\n        self._n_actors = test_case.n_actors()\n\n    def decode_taints(self, taint_path: str) -> List[InputTaint]:\n        \"\"\"\n        Read the input taints produced by the DynamoRIO backend and return them in the format\n        that is expected by the contract model.\n        :return: list of input taints\n        \"\"\"\n        taints: List[InputTaint] = []\n\n        # for convenience, read the entire file into a numpy array\n        array: NDArray[np.uint64] = self._file_to_ndarray(taint_path)\n        sandbox_end: int = self._layout.data_area_offset(DataArea.OVERFLOW_PAD)\n\n        taint = InputTaint(self._n_actors)\n        linear_view = taint.full_linear_view()\n        unfinished = False\n        for entry in array:\n            val = int(entry)\n\n            # end marker reached? store the current input taint and start a new one\n            if val == _EOT_MARKER:\n                taints.append(taint)\n                taint = InputTaint(self._n_actors)\n                linear_view = taint.full_linear_view()\n                unfinished = False\n                continue\n            unfinished = True\n\n            if val > sandbox_end:\n                # invalid taint value (may happen because some of the adapter code was tainted)\n                continue\n\n            linear_view[val // _BYTES_PER_TAINT_ENTRY] = True\n\n        assert not unfinished, \"Taint file ended unexpectedly without end marker\"\n        return taints\n\n    def _file_to_ndarray(self, path: str) -> NDArray[np.uint64]:\n        \"\"\"\n        Read the taint file and convert it to a numpy array.\n        :return: numpy array containing the taint entries (uint64 values)\n        \"\"\"\n        with open(path, 'rb') as f:\n            data = f.read()\n        n_entries = len(data) // _BYTES_PER_TAINT_ENTRY\n        array = np.frombuffer(data, dtype=np.uint64, count=n_entries)\n        return array\n"
  },
  {
    "path": "rvzr/model_dynamorio/trace_decoder.py",
    "content": "\"\"\"\nFile: This module provides a decoder for the binary traces generated by the DynamoRIO backend\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n\nfrom enum import Enum\nfrom typing import Any, Final, List, Literal, Union, cast\nfrom io import BufferedReader\nimport sys\nimport os\n\nfrom cffi import FFI\nfrom typing_extensions import get_args, assert_never\n\n_MarkerType = Literal[\"T\", \"D\"]\n\n# ==================================================================================================\n# Trace types\n# ==================================================================================================\n# TODO: autogenerate from trace.hpp\n# NOTE: cffi cannot parse CPP constructs (e.g. enum classes, sdt::array) so we\n#       need to manually adjust some of the fields.\n\n\nclass TraceEntryType(Enum):\n    \"\"\"\n    Enum used for the trace entry type, copied from trace.hpp\n    TODO: Cffi cannot parse enum classes, find a way to autogenerate from the header file\n    \"\"\"\n    ENTRY_EOT = 0  # end of trace\n    ENTRY_PC = 1\n    ENTRY_READ = 2\n    ENTRY_WRITE = 3\n    ENTRY_EXCEPTION = 4\n    ENTRY_IND = 5\n\n\n_TRACE_ENTRY_T: Final[str] = \"struct trace_entry_t\"\n_TRACE_ENTRY_DEF: Final[str] = \"\"\"\nstruct trace_entry_t {\n    // pc for instructions; address for memory accesses; target for indirect calls\n    uint64_t addr;\n    // instruction size for instructions; memory access size for memory accesses\n    uint32_t size;\n    // see trace_entry_type_t\n    uint8_t type;\n    // unused for now\n    uint8_t padding[3]; // NOLINT\n};\n\"\"\"\n\n# ==================================================================================================\n# Debug Trace types\n# ==================================================================================================\n# TODO: autogenerate from debug_trace.hpp\n# NOTE: cffi cannot parse CPP constructs (e.g. enum classes, sdt::array) so we\n#       need to manually adjust some of the fields.\n\n\nclass DebugTraceEntryType(Enum):\n    \"\"\"\n    Enum used for the debug trace entry type, copied from debug_trace.hpp\n    TODO: Cffi cannot parse enum classes, find a way to autogenerate from the header file\n    \"\"\"\n    ENTRY_EOT = 0  # end of trace\n    ENTRY_REG_DUMP = 1\n    ENTRY_READ = 2\n    ENTRY_WRITE = 3\n    ENTRY_LOC = 4\n    ENTRY_EXCEPTION = 5\n    ENTRY_CHECKPOINT = 6\n    ENTRY_ROLLBACK = 7\n    ENTRY_ROLLBACK_STORE = 8\n    ENTRY_REG_DUMP_EXTENDED = 9\n\n\n_DEBUG_TRACE_ENTRY_T: Final[str] = \"struct debug_trace_entry_t\"\n_DEBUG_TRACE_ENTRY_DEF: Final[str] = \"\"\"\nstruct debug_trace_entry_t {\n    // What does this entry contain\n    uint8_t type;\n    // Nested speculation (0 is architectural)\n    uint8_t nesting_level;\n    // Unused for now\n    uint8_t padding[6]; // NOLINT\n\n    // Union of all possible entry types\n    union {\n        // ENTRY_REG_DUMP\n        struct {\n            uint64_t xax;\n            uint64_t xbx;\n            uint64_t xcx;\n            uint64_t xdx;\n            uint64_t xsi;\n            uint64_t xdi;\n            uint64_t pc;\n        } regs;\n        // ENTRY_REG_EXTENDED\n        struct {\n            uint64_t rsp;\n            uint64_t rbp;\n            uint64_t flags;\n            uint64_t r8;\n            uint64_t r9;\n            uint64_t r10;\n            uint64_t r11;\n        } regs_2;\n        // ENTRY_MEM (read or write)\n        struct {\n            uint64_t address;\n            uint64_t value;\n            uint64_t size;\n        } mem;\n        // ENTRY_LOC (module name and offset, for disassembly)\n        struct {\n            uint64_t offset;\n            char module_name[48]; // NOLINT\n        } loc;\n        // ENTRY_EXCEPTION\n        struct {\n            int signal;\n            uint64_t address;\n        } xcpt;\n        // ENTRY_CHECKPOINT\n        struct {\n            uint64_t rollback_pc;\n            uint64_t cur_window_size;\n            size_t cur_store_log_size;\n        } checkpoint;\n        // ENTRY_ROLLBACK\n        struct {\n            unsigned nesting;\n            uint64_t rollback_pc;\n        } rollback;\n        // ENTRY_ROLLBACK_STORE\n        struct {\n            uint64_t addr;\n            uint64_t val;\n            size_t size;\n            uint64_t nesting_level;\n        } rollback_store;\n    };\n};\n\"\"\"\n\n\n# ==================================================================================================\n# Decoder\n# ==================================================================================================\nclass TraceDecoder:\n    \"\"\"\n    This class provides a unified API for decoding trace entries\n    \"\"\"\n\n    _ffi: FFI\n    _trace_entry_size: int\n    _debug_trace_entry_size: int\n\n    def __init__(self) -> None:\n        self._ffi = FFI()\n        # Parse trace defs\n        self._ffi.cdef(_TRACE_ENTRY_DEF)\n        self._trace_entry_size = self._ffi.sizeof(_TRACE_ENTRY_T)\n        # Parse debug trace defs\n        self._ffi.cdef(_DEBUG_TRACE_ENTRY_DEF)\n        self._debug_trace_entry_size = self._ffi.sizeof(_DEBUG_TRACE_ENTRY_T)\n\n    # ----------------------------------------------------------------------------------------------\n    # Public API\n    # ----------------------------------------------------------------------------------------------\n    def read_trace_marker(self, f: BufferedReader) -> Union[_MarkerType, Literal[\"\"]]:\n        \"\"\"\n        Get the type of the trace file.\n        \"\"\"\n        marker = f.read(1).decode('utf-8')\n        if len(marker) == 0:\n            return \"\"\n        assert marker in get_args(_MarkerType), f\"Unknown trace type marker: {marker}\"\n        f.read(7)  # skip padding bytes\n        return cast(_MarkerType, marker)\n\n    def decode_trace_file(self, file: str) -> List[List[Any]]:\n        \"\"\" Read a set of traces from a file. \"\"\"\n        with open(file, \"rb\") as f:\n            marker = self.read_trace_marker(f)\n            if marker == \"\":  # empty file\n                return []\n            assert marker == \"T\", f\"Expected Normal trace (T), got {marker}\"\n\n            # Read the traces\n            traces = []\n            eof = False\n            while not eof:\n\n                entries = []\n                while True:\n                    # Read one entry\n                    chunk = f.read(self._trace_entry_size)\n                    if len(chunk) < self._trace_entry_size:\n                        eof = True\n                        break  # no more bytes to read: exit\n\n                    # Decode it\n                    entry = self._decode_trace_entry(chunk)\n                    entries.append(entry)\n\n                    # If we reached EOT, move on to the next trace\n                    if TraceEntryType(entry.type) == TraceEntryType.ENTRY_EOT:\n                        traces.append(entries)\n                        break\n\n                # Check that the last trace ended with an EOT entry or EXCEPTION\n                if eof and len(entries) > 0:\n                    last_entry = entries[-1]\n                    if TraceEntryType(last_entry.type) != TraceEntryType.ENTRY_EOT:\n                        raise ValueError(\"Trace file does not end with an EOT entry\")\n\n        return traces\n\n    def decode_debug_trace_file(self, file: str) -> List[List[Any]]:\n        \"\"\" Read a debug trace from a file. \"\"\"\n        with open(file, \"rb\") as f:\n            marker = self.read_trace_marker(f)\n            if marker == \"\":  # empty file\n                return []\n            assert marker == \"D\", f\"Expected Debug trace (D), got {marker}\"\n\n            # Read the traces\n            traces = []\n            eof = False\n            while not eof:\n\n                entries = []\n                while True:\n                    # Read one entry\n                    chunk = f.read(self._debug_trace_entry_size)\n                    if len(chunk) < self._debug_trace_entry_size:\n                        eof = True\n                        break  # no more bytes to read: exit\n\n                    # Decode it\n                    entry = self._decode_debug_trace_entry(chunk)\n                    entries.append(entry)\n\n                    # If we reached EOT, move on to the next trace\n                    if DebugTraceEntryType(entry.type) == DebugTraceEntryType.ENTRY_EOT:\n                        traces.append(entries)\n                        break\n\n                # Check that the last trace ended with an EOT or EXCEPTION entry\n                if eof and len(entries) > 0:\n                    last_entry = entries[-1]\n                    if DebugTraceEntryType(last_entry.type) != DebugTraceEntryType.ENTRY_EOT:\n                        raise ValueError(\"Trace file does not end with an EOT entry\")\n\n        return traces\n\n    def is_trace_corrupted(self, trace_path: str) -> bool:\n        \"\"\"\n        Check if a trace ends with an EOT or EXCEPTION entry.\n        \"\"\"\n        # Handle empty and non-existing traces as corrupted\n        if not os.path.exists(trace_path) or os.stat(trace_path).st_size == 0:\n            return True\n\n        with open(trace_path, \"rb\") as f:\n            trace_type = self.read_trace_marker(f)\n            if trace_type == \"\":\n                return True\n\n            # Decode based on the type\n            if trace_type == \"T\":\n                entry_sz = self._ffi.sizeof(_TRACE_ENTRY_T)\n                if os.stat(trace_path).st_size < entry_sz:\n                    return True\n\n                # Decode last entry\n                f.seek(-entry_sz, os.SEEK_END)\n                last_entry = self._decode_trace_entry(f.read(entry_sz))\n\n                # Check its type\n                last_entry_type = TraceEntryType(last_entry.type)\n                return last_entry_type != TraceEntryType.ENTRY_EOT\n\n            if trace_type == \"D\":\n                entry_sz = self._ffi.sizeof(_DEBUG_TRACE_ENTRY_T)\n                if os.stat(trace_path).st_size < entry_sz:\n                    return True\n\n                # Decode last entry\n                f.seek(-entry_sz, os.SEEK_END)\n                last_dbg_entry = self._decode_debug_trace_entry(f.read(entry_sz))\n\n                # Check its type\n                last_dbg_entry_type = DebugTraceEntryType(last_dbg_entry.type)\n                return last_dbg_entry_type != DebugTraceEntryType.ENTRY_EOT\n\n            assert_never(trace_type)\n\n    # ----------------------------------------------------------------------------------------------\n    # Private API\n    # ----------------------------------------------------------------------------------------------\n    def _decode_trace_entry(self, chunk: bytes) -> Any:\n        \"\"\"\n        Decode a single entry from a chunk of bytes\n        \"\"\"\n        # Decode it with ffi\n        entry: Any = self._ffi.new(_TRACE_ENTRY_T + \"*\")\n        self._ffi.memmove(entry, chunk, self._trace_entry_size)\n\n        # Check that the entry type is valid\n        try:\n            TraceEntryType(entry.type)\n        except Exception:\n            raise ValueError(f\"Error: Unknown trace entry type {str(entry.type)}\")\n\n        return entry\n\n    def _decode_debug_trace_entry(self, chunk: bytes) -> Any:\n        \"\"\"\n        Decode a single debug entry from a chunk of bytes\n        \"\"\"\n        # Decode it with ffi\n        entry: Any = self._ffi.new(_DEBUG_TRACE_ENTRY_T + \"*\")\n        self._ffi.memmove(entry, chunk, self._debug_trace_entry_size)\n\n        # Check that the entry type is valid\n        try:\n            DebugTraceEntryType(entry.type)\n        except Exception:\n            raise ValueError(f\"Error: Unknown debug entry type {str(entry.type)}\")\n\n        return entry\n\n\ndef main() -> None:\n    \"\"\" Standalone decoding interface: pretty-print trace entries from a file \"\"\"\n    if len(sys.argv) != 2:\n        print(f\"Usage {sys.argv[0]} <TRACE_PATH>\")\n        sys.exit(1)\n\n    # 1. Create decoder\n    decoder = TraceDecoder()\n\n    # 2. Decode file\n    with open(sys.argv[1], \"rb\") as f:\n        trace_type = decoder.read_trace_marker(f)\n    if trace_type == \"\":\n        print(f\"Empty trace file: {sys.argv[1]}\")\n        sys.exit(1)\n    if trace_type == \"T\":\n        parsed_traces = decoder.decode_trace_file(sys.argv[1])\n    elif trace_type == \"D\":\n        parsed_traces = decoder.decode_trace_file(sys.argv[1])\n        print(f\"Only leakage traces allowed: found {len(parsed_traces)} debug traces instead\")\n        sys.exit(1)\n    else:\n        assert_never(trace_type)\n\n    # Check that the input contains leakage traces\n    if len(parsed_traces) == 0:\n        print(f\"No traces found in {sys.argv[1]}\")\n        sys.exit(1)\n\n    # 3. Print all entries\n    for nt, trace_ in enumerate(parsed_traces):\n        print(\"-------- TRACE --------\")\n        for ne, e in enumerate(trace_):\n            try:\n                # Parse the entry type\n                type_ = TraceEntryType(e.type)\n                print(f\"[{type_.name}] {hex(e.addr)}\")\n            except Exception:\n                raise ValueError(f\"Failed to decode entry {ne} of trace {nt}\")\n\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "rvzr/model_unicorn/__init__.py",
    "content": ""
  },
  {
    "path": "rvzr/model_unicorn/coverage.py",
    "content": "\"\"\"\nFile: Class for tracking instruction coverage in fuzzing campaigns with Unicorn backend.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom collections import defaultdict\n\nfrom typing import Dict, Optional\nfrom typing_extensions import assert_never\n\nfrom ..tc_components.instruction import Instruction, RegisterOp, MemoryOp, \\\n    ImmediateOp, LabelOp, AgenOp, FlagsOp, CondOp\nfrom ..config import CONF\n\n_SIGNATURE_CACHE: Dict[int, str] = {}\n\n\ndef _get_instruction_signature(instruction: Instruction) -> str:\n    \"\"\"\n    Get a brief string representation of the instruction.\n    Used as a unique identifier for the instruction when tracking coverage.\n    \"\"\"\n    inst_identifier = id(instruction)\n\n    # Cache the brief string to avoid recomputing it\n    if inst_identifier in _SIGNATURE_CACHE:\n        return _SIGNATURE_CACHE[inst_identifier]\n\n    # Compute the brief string\n    brief = instruction.name\n    for o in instruction.operands:\n        if isinstance(o, RegisterOp):\n            brief += f\" R{o.width}\"\n        elif isinstance(o, MemoryOp):\n            brief += f\" M{o.width}\"\n        elif isinstance(o, ImmediateOp):\n            brief += f\" I{o.width}\"\n        elif isinstance(o, LabelOp):\n            brief += \" L\"\n        elif isinstance(o, AgenOp):\n            brief += f\" A{o.width}\"\n        elif isinstance(o, FlagsOp):\n            brief += \" F\"\n        elif isinstance(o, CondOp):\n            brief += \" C\"\n\n        else:\n            assert_never(o)\n\n    _SIGNATURE_CACHE[inst_identifier] = brief\n    return brief\n\n\nclass InstructionCoverage:\n    \"\"\"\n    Tracks coverage of instructions executed on the model during a fuzzing campaign.\n    \"\"\"\n    _cov: Dict[str, int]\n    \"\"\" instruction coverage of the entire campaign \"\"\"\n\n    _local_cov: Optional[Dict[str, int]] = None\n    \"\"\" instruction coverage of the current test case \"\"\"\n\n    def __init__(self) -> None:\n        self._cov = defaultdict(int)\n\n    def start_test_case(self) -> None:\n        \"\"\"\n        Start tracking coverage for a new test case when CONF.coverage_type == \"model_instructions\".\n        Otherwise, disable coverage tracking.\n        \"\"\"\n\n        if CONF.coverage_type == \"model_instructions\":\n            self._local_cov = defaultdict(int)\n            return\n\n        self._local_cov = None\n\n    def add_instruction(self, inst: Instruction) -> None:\n        \"\"\" Record the given instruction as covered (if coverage tracking is enabled) \"\"\"\n        if self._local_cov is None:\n            return\n        if inst.is_instrumentation:\n            return\n        self._local_cov[_get_instruction_signature(inst)] += 1\n\n    def finish_test_case(self) -> None:\n        \"\"\" Finish tracking coverage for the current test case \"\"\"\n        if self._local_cov is None:\n            return\n\n        for inst_name in self._local_cov.keys():\n            self._cov[inst_name] += 1\n\n    def report(self, path: str) -> None:\n        \"\"\" Write the coverage data to a file \"\"\"\n        # Ensure that the last test case is included in the coverage report\n        self.finish_test_case()\n\n        # Sort the instructions by coverage count and write them to the file\n        inst_names = sorted(self._cov.items(), key=lambda x: x[1], reverse=True)\n        with open(path, \"w\") as f:\n            for inst_name, count in inst_names:\n                f.write(f\"{inst_name:<20} {count}\\n\")\n            if not inst_names:\n                f.write(\"    No coverage data available\")\n"
  },
  {
    "path": "rvzr/model_unicorn/execution_context.py",
    "content": "\"\"\"\nFile: Execution state of the model during a single test case execution\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom typing import TYPE_CHECKING, Final, Optional, Dict, Tuple\n\nfrom unicorn import Uc\nfrom ..sandbox import SandboxLayout, CodeArea\nfrom ..tc_components.actor import ActorID\n\nif TYPE_CHECKING:\n    from ..tc_components.test_case_code import TestCaseProgram\n    from ..tc_components.instruction import Instruction\n    from ..tc_components.actor import Actor\n    from ..target_desc import TargetDesc\n\nPAGE_PERMISSION_MAP = Dict[ActorID, Tuple[bool, bool]]\n\"\"\" Data type for storing page permissions for actors \"\"\"\n\n\nclass ModelExecutionState:\n    \"\"\"\n    Set of state variables that track a single execution of a test case program with a given\n    input on the model\n    \"\"\"\n\n    current_instruction: Instruction\n    \"\"\" The instruction currently being executed by the model \"\"\"\n\n    current_actor: Actor\n    \"\"\" The actor whose code is currently being executed by the model \"\"\"\n\n    exit_addr: int\n    \"\"\" The address of the exit instruction in the current test case \"\"\"\n\n    fault_handler_addr: int\n    \"\"\" The address of the fault handler in the current test case \"\"\"\n\n    pending_fault: int = 0\n    \"\"\" Interface to signal pending soft faults to the model;\n    If a fault was triggered but not handled yet, its ID is stored here \"\"\"\n\n    previous_context: Optional[object] = None\n    \"\"\" Context of the emulator before the current instruction was executed;\n    used to patch a bug in Unicorn \"\"\"\n\n    had_arch_fault: bool = False\n    \"\"\" Indicates whether the model has already had a non-speculative fault in the current run \"\"\"\n\n    page_permissions: Optional[PAGE_PERMISSION_MAP] = None\n    \"\"\" Dictionary of the page permissions for each actor at the start of execution.\n    Only contains permissions on the faulty area, as all other areas are always RW.\"\"\"\n\n    _test_case: Final[TestCaseProgram]  # The test case being currently executed by the model\n    _layout: Final[SandboxLayout]  # The layout of the sandbox\n\n    def __init__(self, test_case: TestCaseProgram, layout: SandboxLayout, target_desc: TargetDesc):\n        self._test_case = test_case\n        self._layout = layout\n\n        self.exit_addr = self._layout.get_exit_addr(test_case)\n        self._set_fault_handler_addr(target_desc.macro_specs[\"fault_handler\"].type_)\n        self.full_reset()\n\n    def full_reset(self) -> None:\n        \"\"\" Complete reset of the model state; has to be called before each test case \"\"\"\n        self.had_arch_fault = False\n        self.pending_fault = 0\n        self.current_actor = self._test_case.find_actor(name=\"main\")\n\n    def reset_after_em_stop(self, start_pc: int) -> None:\n        \"\"\"\n        Reset the model state after the emulator stops;\n        has to be called before each start of the emulator iteration\n        :param start_pc: the address where the emulator will start execution\n        :return: None\n        \"\"\"\n        self.pending_fault = 0\n        aid = self._layout.code_addr_to_actor_id(start_pc)\n        self.current_actor = self._test_case.find_actor(actor_id=aid)\n\n    def is_exit_addr(self, address: int) -> bool:\n        \"\"\" Check if the given address is the exit address \"\"\"\n        return address == self.exit_addr or \\\n            (self.current_actor.is_main and address > self.exit_addr)\n\n    def update_context(self, em: Uc, address: int) -> None:\n        \"\"\" Update the state of the model after each instruction \"\"\"\n        self.previous_context = em.context_save()\n        aid = self.current_actor.get_id()\n        section_start = self._layout.get_code_addr(CodeArea.MAIN, aid)\n        instruction_map = self._test_case.get_obj().instruction_map()\n        self.current_instruction = instruction_map[aid][address - section_start]\n\n    def current_test_case(self) -> TestCaseProgram:\n        \"\"\" Return the current test case being executed \"\"\"\n        return self._test_case\n\n    def _set_fault_handler_addr(self, fh_id: int) -> None:\n        test_case_obj = self._test_case.get_obj()\n        code_start = self._layout.code_start()\n        offset = test_case_obj.get_macro_offset(fh_id)\n        if offset == -1:\n            self.fault_handler_addr = self.exit_addr\n            return\n\n        self.fault_handler_addr = code_start + offset\n"
  },
  {
    "path": "rvzr/model_unicorn/interpreter.py",
    "content": "\"\"\"\nFile: Abstract interface and architecture-specific implementation of the extra interpreter logic.\n\n      The extra interpreter is a component that provides additional interpretation\n      logic over the one provided by Unicorn.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom abc import ABC, abstractmethod\nfrom typing import TYPE_CHECKING, Tuple, Dict, Callable, Set, Optional, List, Final\n\nfrom unicorn import UC_ERR_NOMEM, UcError, UC_ERR_EXCEPTION, UC_MEM_WRITE, UC_ERR_INSN_INVALID, \\\n    UC_ERR_READ_PROT, UC_ERR_WRITE_PROT\nimport unicorn.x86_const as x86ucc  # type: ignore  # no type hints available\n\nfrom ..tc_components.actor import ActorMode, ActorPL, Actor, ActorID, PTEMask\nfrom ..sandbox import CodeArea, DataArea\nfrom ..logs import warning\n\nif TYPE_CHECKING:\n    from .model import UnicornModel\n    from .execution_context import ModelExecutionState\n    from ..target_desc import TargetDesc, UnicornTargetDesc\n    from ..tc_components.instruction import Instruction\n    from ..tc_components.test_case_code import TestCaseProgram\n    from ..tc_components.test_case_binary import SymbolTableEntry\n    from ..tc_components.test_case_data import InputData\n\nCRITICAL_ERROR = UC_ERR_NOMEM  # the model never handles this error, hence it will always crash\n\n\n# ==================================================================================================\n# Public Interface\n# ==================================================================================================\nclass ExtraInterpreter(ABC):\n    \"\"\"\n    Wrapper class that implements extra interpretation logic over the one provided by Unicorn.\n    This, for example, includes the interpretation of macros and emulation of CPU modes.\n\n    This class provides a generic interface, which is instantiated by the ISA-specific subclasses.\n    \"\"\"\n    _model: Final[UnicornModel]\n    _target_desc: Final[TargetDesc]\n    _uc_target_desc: Final[UnicornTargetDesc]\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel):\n        self._target_desc = target_desc\n        self._model = model\n        self._uc_target_desc = target_desc.uc_target_desc\n\n    @abstractmethod\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        \"\"\" Load the test case into the interpreter \"\"\"\n\n    @abstractmethod\n    def load_input(self, input_: InputData) -> None:\n        \"\"\" Load the input into the interpreter \"\"\"\n\n    def interpret_instruction(self, address: int, state: ModelExecutionState) -> None:\n        \"\"\" Interpret the current instruction (stored in state.current_instruction) \"\"\"\n        instruction = state.current_instruction\n\n        if instruction.name == \"macro\":\n            self._interpret_macro(instruction, address)\n\n        # emulate invalid opcode for certain instructions when executed in VM guest mode\n        if state.current_actor.mode == ActorMode.GUEST:\n            self._emulate_vm_execution(address)\n        elif state.current_actor.privilege_level == ActorPL.USER:\n            self._emulate_userspace_execution(address)\n\n    def interpret_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        \"\"\" Interpret the given memory access \"\"\"\n\n    @abstractmethod\n    def _interpret_macro(self, macro: Instruction, pc: int) -> None:\n        \"\"\" Emulate execution of a macro instruction \"\"\"\n\n    @abstractmethod\n    def _emulate_vm_execution(self, address: int) -> None:\n        \"\"\" Emulate the execution of an instruction in VM guest mode \"\"\"\n\n    @abstractmethod\n    def _emulate_userspace_execution(self, address: int) -> None:\n        \"\"\" Emulate the execution of an instruction in userspace mode \"\"\"\n\n\n# ==================================================================================================\n# Architecture-specific Implementations\n# ==================================================================================================\nclass X86ExtraInterpreter(ExtraInterpreter):\n    \"\"\" ExtraInterpreter implementation for the x86 architecture \"\"\"\n\n    _macro_interpreter: _X86MacroInterpreter\n    _vm_interpreter: _X86VMInterpreter\n    _userspace_interpreter: _X86UserspaceInterpreter\n    _fault_interpreter: _X86FaultInterpreter\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel):\n        super().__init__(target_desc, model)\n        self._macro_interpreter = _X86MacroInterpreter(model, target_desc)\n        self._vm_interpreter = _X86VMInterpreter(model, target_desc)\n        self._userspace_interpreter = _X86UserspaceInterpreter(model, target_desc)\n        self._fault_interpreter = _X86FaultInterpreter(model, target_desc)\n\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        self._macro_interpreter.load_test_case(test_case)\n        self._fault_interpreter.load_test_case(test_case)\n        self._vm_interpreter.reset()\n        self._userspace_interpreter.reset()\n\n    def load_input(self, input_: InputData) -> None:\n        self._fault_interpreter.load_input(input_)\n\n    def interpret_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        super().interpret_mem_access(access, address, size, value)\n        self._fault_interpreter.induce_user_faults(self._model.state.current_actor, address)\n\n    def _interpret_macro(self, macro: Instruction, pc: int) -> None:\n        self._macro_interpreter.interpret(macro, pc)\n\n    def _emulate_vm_execution(self, address: int) -> None:\n        self._vm_interpreter.interpret(self._model.state.current_instruction, address)\n\n    def _emulate_userspace_execution(self, address: int) -> None:\n        self._userspace_interpreter.interpret(self._model.state.current_instruction, address)\n\n\nclass ARMExtraInterpreter(ExtraInterpreter):\n    \"\"\" ExtraInterpreter implementation for the arm architecture \"\"\"\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel):\n        super().__init__(target_desc, model)\n        self._macro_interpreter = _ARM64MacroInterpreter(model, target_desc)\n        self._fault_interpreter = _ARM64FaultInterpreter(model, target_desc)\n\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        self._macro_interpreter.load_test_case(test_case)\n        self._fault_interpreter.load_test_case(test_case)\n\n    def load_input(self, input_: InputData) -> None:\n        self._fault_interpreter.load_input(input_)\n\n    def interpret_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        super().interpret_mem_access(access, address, size, value)\n        self._fault_interpreter.emulate_crossing_fault(access, address, size)\n\n    def _interpret_macro(self, macro: Instruction, pc: int) -> None:\n        self._macro_interpreter.interpret(macro, pc)\n\n    def _emulate_vm_execution(self, address: int) -> None:\n        pass\n\n    def _emulate_userspace_execution(self, address: int) -> None:\n        pass\n\n\n# ==================================================================================================\n# Private: Macro Interpretation\n# ==================================================================================================\n\n_MacroCallback = Callable[[int, int, int, int], None]\n\n\nclass _MacroInterpreterCommon:\n    \"\"\" Implementation of architecture-independent macros and common logic \"\"\"\n    _model: UnicornModel\n    _uc_target_desc: UnicornTargetDesc\n\n    _test_case: Optional[TestCaseProgram] = None\n    _function_table: List[SymbolTableEntry]\n    _macro_table: List[SymbolTableEntry]\n    _macro_callbacks: Dict[str, _MacroCallback]\n\n    _curr_targets: Dict[str, int]\n    _sid_to_actor: Dict[int, Actor]\n\n    def __init__(self, model: UnicornModel, target_desc: TargetDesc):\n        self._model = model\n        self._uc_target_desc = target_desc.uc_target_desc\n        self._function_table = []\n        self._macro_table = []\n        self._curr_targets = {\n            \"h2g\": 0,\n            \"g2h\": 0,\n            \"k2u\": 0,\n            \"u2k\": 0,\n        }\n        self._macro_callbacks = {\n            \"measurement_start\": self._macro_measurement_start,\n            \"measurement_end\": self._macro_measurement_end,\n            \"switch\": self._macro_switch,\n            \"fault_handler\": lambda *_: None,\n        }\n\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        \"\"\" Load the test case into the interpreter \"\"\"\n        self._test_case = test_case\n        test_case_obj = test_case.get_obj()\n        symbol_table = test_case_obj.symbol_table()\n\n        self._function_table = [sym for sym in symbol_table if sym.type_ == 0]\n        self._function_table.sort(key=lambda s: [s.arg])\n        self._macro_table = [sym for sym in symbol_table if sym.type_ != 0]\n        self._sid_to_actor = {actor.get_id(): actor for actor in test_case.get_actors()}\n\n    def interpret(self, macro: Instruction, pc: int) -> None:\n        \"\"\"\n        Interpret the given macro instruction and execute the corresponding logic on the model\n        \"\"\"\n        actor_id = self._model.state.current_actor.get_id()\n        macro_start = self._model.layout.get_code_addr(CodeArea.MAIN, actor_id)\n        macro_offset = pc - macro_start\n        macro_args = self._get_macro_args(actor_id, macro_offset)\n        macro_name = macro.operands[0].value.lower()[1:]\n        if macro_name not in self._macro_callbacks:\n            warning(\"interpret\", f\"unknown macro: {macro_name}\")\n            raise UcError(CRITICAL_ERROR)\n\n        interpreter_func = self._macro_callbacks[macro_name]\n        interpreter_func(*macro_args)\n\n    def _get_macro_args(self, section_id: int, section_offset: int) -> Tuple[int, int, int, int]:\n        # find the macro entry in the symbol table\n        for symbol in self._macro_table:\n            if symbol.sid == section_id and symbol.offset == section_offset:\n                args = symbol.arg\n                return args & 0xFFFF, (args >> 16) & 0xFFFF, (args >> 32) & 0xFFFF, \\\n                    (args >> 48) & 0xFFFF\n        warning(\"get_macro_args\", \"macro not found in symbol table\")\n        raise UcError(CRITICAL_ERROR)\n\n    def _find_function_by_id(self, function_id: int) -> SymbolTableEntry:\n        if function_id < 0 or function_id >= len(self._function_table):\n            warning(\"find_function_by_id\", \"function not found in symbol table\")\n            raise UcError(CRITICAL_ERROR)\n        return self._function_table[function_id]\n\n    def _macro_measurement_start(self, _: int, __: int, ___: int, ____: int) -> None:\n        if not self._model.speculator.in_speculation():\n            self._model.tracer.enable_tracing = True\n\n    def _macro_measurement_end(self, _: int, __: int, ___: int, ____: int) -> None:\n        if not self._model.speculator.in_speculation():\n            self._model.tracer.enable_tracing = False\n\n    def _macro_switch(self, section_id: int, function_id: int, _: int, __: int) -> None:\n        \"\"\"\n        Switch the active actor, update data area base and SP,\n          and jump to the corresponding function address\n        \"\"\"\n        model = self._model\n        layout = model.layout\n\n        # PC update\n        section_addr = layout.get_code_addr(CodeArea.MAIN, section_id)\n        function_symbol = self._find_function_by_id(function_id)\n        function_addr = section_addr + function_symbol.offset\n        model.emulator.reg_write(self._uc_target_desc.pc_register, function_addr)\n\n        # data area base and SP update\n        new_base = layout.get_data_addr(DataArea.MAIN, section_id)\n        new_sp = layout.get_data_addr(DataArea.RSP_INIT, section_id)\n        model.emulator.reg_write(self._uc_target_desc.actor_base_register, new_base)\n        model.emulator.reg_write(self._uc_target_desc.sp_register, new_sp)\n\n        # actor update\n        model.state.current_actor = self._sid_to_actor[section_id]\n\n\nclass _X86MacroInterpreter(_MacroInterpreterCommon):\n    \"\"\" Implements the interpretation of x86-specific macros \"\"\"\n    _pseudo_lstar: int\n    _is_amd: bool\n\n    def __init__(self, model: UnicornModel, target_desc: TargetDesc):\n        super().__init__(model, target_desc)\n        self._is_amd = target_desc.cpu_desc.vendor == \"AMD\"\n        self._macro_callbacks.update({\n            \"switch_k2u\": self._macro_switch_k2u,\n            \"switch_u2k\": self._macro_switch_u2k,\n            \"set_k2u_target\": self._macro_set_k2u_target,\n            \"set_u2k_target\": self._macro_set_u2k_target,\n            \"switch_h2g\": self._macro_switch_h2g,\n            \"switch_g2h\": self._macro_switch_g2h,\n            \"set_h2g_target\": self._macro_set_h2g_target,\n            \"set_g2h_target\": self._macro_set_g2h_target,\n            \"landing_k2u\": self._macro_landing_k2u,\n            \"landing_u2k\": self._macro_landing_u2k,\n            \"landing_h2g\": self._macro_landing_h2g,\n            \"landing_g2h\": self._macro_landing_g2h,\n            \"set_data_permissions\": self._macro_set_data_permissions,\n        })\n\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        super().load_test_case(test_case)\n        self._pseudo_lstar = self._model.state.exit_addr\n\n    def _macro_set_k2u_target(self, section_id: int, function_id: int, _: int, __: int) -> None:\n        \"\"\"\n        Decode arguments and store destination into _curr_target\n        \"\"\"\n        section_addr = self._model.layout.get_code_addr(CodeArea.MAIN, section_id)\n        function_symbol = self._find_function_by_id(function_id)\n        function_addr = section_addr + function_symbol.offset\n        self._curr_targets[\"k2u\"] = function_addr\n\n    def _macro_switch_k2u(self, section_id: int, _: int, __: int, ___: int) -> None:\n        \"\"\" Read the destination from _curr_target and jump to it;\n        also update data area base and SP \"\"\"\n        model = self._model\n        layout = model.layout\n\n        # PC update\n        model.emulator.reg_write(self._uc_target_desc.pc_register, self._curr_targets[\"k2u\"])\n\n        # side effects\n        # flags = model.emulator.reg_read(x86ucc.UC_X86_REG_EFLAGS)\n        # rsp = model.emulator.reg_read(x86ucc.UC_X86_REG_RSP)\n        # model.emulator.mem_write(rsp - 8, flags.to_bytes(8, byteorder='little'))  # type: ignore\n\n        # data area base and SP update\n        new_base = layout.get_data_addr(DataArea.MAIN, section_id)\n        new_sp = layout.get_data_addr(DataArea.RSP_INIT, section_id)\n        model.emulator.reg_write(self._uc_target_desc.actor_base_register, new_base)\n        model.emulator.reg_write(x86ucc.UC_X86_REG_RSP, new_sp)\n\n        # actor update\n        model.state.current_actor = self._sid_to_actor[section_id]\n\n    def _macro_set_u2k_target(self, section_id: int, function_id: int, _: int, __: int) -> None:\n        \"\"\" Set LSTAR to the target address if in kernel mode; otherwise, throw an exception \"\"\"\n        if self._model.state.current_actor.privilege_level != ActorPL.KERNEL:\n            self._model.do_soft_fault(UC_ERR_EXCEPTION)\n            return\n        model = self._model\n\n        # update LSTAR\n        section_addr = model.layout.get_code_addr(CodeArea.MAIN, section_id)\n        function_symbol = self._find_function_by_id(function_id)\n        function_addr = section_addr + function_symbol.offset\n        self._pseudo_lstar = function_addr\n\n    def _macro_switch_u2k(self, section_id: int, _: int, __: int, ___: int) -> None:\n        \"\"\" Switch the active actor, update data area base and SP, and jump to\n            the _pseudo_lstar\n        \"\"\"\n        model = self._model\n\n        # PC update\n        model.emulator.reg_write(self._uc_target_desc.pc_register, self._pseudo_lstar)\n\n        # data area base and SP update\n        new_base = model.layout.get_data_addr(DataArea.MAIN, section_id)\n        new_sp = model.layout.get_data_addr(DataArea.RSP_INIT, section_id)\n        model.emulator.reg_write(self._uc_target_desc.actor_base_register, new_base)\n        model.emulator.reg_write(x86ucc.UC_X86_REG_RSP, new_sp)\n\n        # actor update\n        model.state.current_actor = self._sid_to_actor[section_id]\n\n    def _macro_switch_h2g(self, section_id: int, _: int, __: int, ___: int) -> None:\n        model = self._model\n\n        # PC update\n        model.emulator.reg_write(self._uc_target_desc.pc_register, self._curr_targets[\"h2g\"])\n\n        # data area base and SP update\n        new_base = model.layout.get_data_addr(DataArea.MAIN, section_id)\n        new_sp = model.layout.get_data_addr(DataArea.RSP_INIT, section_id)\n        model.emulator.reg_write(self._uc_target_desc.actor_base_register, new_base)\n        model.emulator.reg_write(x86ucc.UC_X86_REG_RSP, new_sp)\n\n        # reset flags\n        model.emulator.reg_write(x86ucc.UC_X86_REG_EFLAGS, 0b10)\n\n        # actor update\n        model.state.current_actor = self._sid_to_actor[section_id]\n\n        # AMD VMRUN clobbers RAX; we model it as a zero write to RAX\n        if self._is_amd:\n            model.emulator.reg_write(x86ucc.UC_X86_REG_RAX, 0)\n\n    def _macro_switch_g2h(self, section_id: int, _: int, __: int, ___: int) -> None:\n        model = self._model\n\n        # PC update\n        model.emulator.reg_write(self._uc_target_desc.pc_register, self._curr_targets[\"g2h\"])\n\n        # data area base and SP update\n        new_base = model.layout.get_data_addr(DataArea.MAIN, section_id)\n        new_sp = model.layout.get_data_addr(DataArea.RSP_INIT, section_id)\n        model.emulator.reg_write(self._uc_target_desc.actor_base_register, new_base)\n        model.emulator.reg_write(x86ucc.UC_X86_REG_RSP, new_sp)\n\n        # actor update\n        model.state.current_actor = self._sid_to_actor[section_id]\n\n        # AMD VMEXIT clobbers RAX; we model it as a zero write to RAX\n        if self._is_amd:\n            model.emulator.reg_write(x86ucc.UC_X86_REG_RAX, 0)\n\n    def _macro_set_h2g_target(self, section_id: int, function_id: int, _: int, __: int) -> None:\n        section_addr = self._model.layout.get_code_addr(CodeArea.MAIN, section_id)\n        function_symbol = self._find_function_by_id(function_id)\n        function_addr = section_addr + function_symbol.offset\n        self._curr_targets[\"h2g\"] = function_addr\n\n    def _macro_set_g2h_target(self, section_id: int, function_id: int, _: int, __: int) -> None:\n        section_addr = self._model.layout.get_code_addr(CodeArea.MAIN, section_id)\n        function_symbol = self._find_function_by_id(function_id)\n        function_addr = section_addr + function_symbol.offset\n        self._curr_targets[\"g2h\"] = function_addr\n\n    def _macro_landing_k2u(self, _: int, __: int, ___: int, ____: int) -> None:\n        \"\"\" Landing for the k2u switch \"\"\"\n        self._model.emulator.reg_write(x86ucc.UC_X86_REG_RCX, 0)\n\n    def _macro_landing_u2k(self, _: int, __: int, ___: int, ____: int) -> None:\n        \"\"\" Landing for the u2k switch \"\"\"\n        self._model.emulator.reg_write(x86ucc.UC_X86_REG_RCX, 0)\n\n    def _macro_landing_h2g(self, _: int, __: int, ___: int, ____: int) -> None:\n        \"\"\" Landing for the h2g switch \"\"\"\n\n    def _macro_landing_g2h(self, _: int, __: int, ___: int, ____: int) -> None:\n        \"\"\" Landing for the g2h switch \"\"\"\n\n    def _macro_set_data_permissions(self, actor_id: int, must_set: int, must_clear: int,\n                                    _: int) -> None:\n        \"\"\" Manual setting of data permissions for the actor \"\"\"\n\n\nclass _ARM64MacroInterpreter(_MacroInterpreterCommon):\n    \"\"\" Implements the interpretation of ARM64-specific macros \"\"\"\n\n    def __init__(self, model: UnicornModel, target_desc: TargetDesc):\n        super().__init__(model, target_desc)\n        self._is_amd = target_desc.cpu_desc.vendor == \"AMD\"\n        self._macro_callbacks.update({\n            \"fault_handler\": lambda *_: None,\n        })\n\n\n# ==================================================================================================\n# Private: VM mode and Userspace Emulation\n# ==================================================================================================\nclass _X86VMInterpreter:\n    \"\"\" Adds the ability to emulate VM guest execution to the Unicorn emulator \"\"\"\n\n    safe_address_cache: Set[int]\n    always_exit_instructions: Set[str] = {\n        \"cpuid\", \"getsec\", \"xgetbv\", \"xsetbv\", \"xrstors\", \"xsaves\", \"invd\", \"invept\", \"invvpid\",\n        \"vmptrld\", \"vmptrst\", \"vmclear\", \"vmxon\", \"vmxoff\", \"vmlaunch\", \"vmresume\", \"vmcall\",\n        \"vmfunc\", \"hlt\", \"invlpg\", \"invpcid\", \"lgdt\", \"lidt\", \"lldt\", \"ltr\", \"sgdt\", \"sidt\", \"sldt\",\n        \"str\", \"loadiwkey\", \"monitor\", \"mwait\", \"rdpmc\", \"rdrand\", \"rdseed\", \"rdtsc\", \"rdtscp\",\n        \"rsm\", \"tpause\", \"umwait\", \"vmread\", \"vmwrite\", \"wbinvd\", \"wbnoinvd\", \"wrmsr\", \"fxsave\",\n        \"fxsave64\", \"in\", \"ins\", \"insb\", \"insw\", \"insd\", \"out\", \"outs\", \"outsb\", \"outsw\", \"outsd\",\n        \"pause\", \"rdmsr\", \"swapgs\"\n    }\n    always_exiting_registers = [\"cr0\", \"cr3\", \"cr8\", \"dr0\", \"dr1\", \"dr2\", \"dr3\", \"dr6\", \"dr7\"]\n\n    def __init__(self, model: UnicornModel, target_desc: TargetDesc) -> None:\n        self._model = model\n        self._uc_target_desc = target_desc.uc_target_desc\n        self.safe_address_cache = set()\n\n    def reset(self) -> None:\n        \"\"\" Reset the state of the interpreter; MUST be called for every new test case \"\"\"\n        self.safe_address_cache.clear()\n\n    def interpret(self, inst: Instruction, address: int) -> None:\n        \"\"\" Interpret the given instruction \"\"\"\n\n        if address in self.safe_address_cache:\n            return\n        stripped_name = inst.name.split()[-1]\n\n        # always-exiting instruction\n        if stripped_name in self.always_exit_instructions:\n            # make sure that the memory accesses get exposed\n            if inst.has_mem_operand(True):\n                ops = inst.get_mem_operands(True)\n                for op in ops:\n                    words = op.value.split(\"+\")\n                    for word in words:\n                        reg = self._uc_target_desc.reg_str_to_constant.get(word.lower(), 0)\n                        if reg:\n                            value = int(self._model.emulator.reg_read(reg))  # type: ignore\n                            self._model.tracer.observe_mem_access(UC_MEM_WRITE, value, 8, 0)\n            self._model.do_soft_fault(UC_ERR_INSN_INVALID)\n            return\n\n        # conditional exit\n        if stripped_name == \"mov\":\n            if not self._emulate_move(inst, address):\n                return\n\n        # safe instruction\n        self.safe_address_cache.add(address)\n\n    def _emulate_move(self, inst: Instruction, _: int) -> bool:\n        for operand in inst.operands:\n            if operand.value in self.always_exiting_registers:\n                self._model.do_soft_fault(UC_ERR_INSN_INVALID)\n                return False\n        return True\n\n\nclass _X86UserspaceInterpreter(_X86VMInterpreter):\n    \"\"\"\n    Adds the ability to emulate user-space execution to the Unicorn emulator.\n    \"\"\"\n    always_exit_instructions: Set[str] = {\n        \"cpuid\", \"rdmsr\", \"wrmsr\", \"rdtsc\", \"rdtscp\", \"clac\", \"stac\", \"clgi\", \"stgi\", \"clts\", \"htl\",\n        \"invd\", \"invlpg\", \"invlpga\", \"invlpgb\", \"invpcid\", \"lgdt\", \"lldt\", \"lidt\", \"ltr\", \"sgdt\",\n        \"sidt\", \"sldt\", \"str\", \"psmash\", \"pvalidate\", \"rmpadjust\", \"rmpquery\", \"rmpupdate\",\n        \"skinit\", \"sysretq\", \"sysexitq\", \"tlbsync\", \"vmmcall\", \"vmload\", \"vmsave\", \"vmrun\",\n        \"wbinvd\", \"wbnoinvd\", \"smsw\", \"lmsw\", \"rdfsbase\", \"rdgsbase\", \"wrfsbase\", \"wrgsbase\",\n        \"swapgs\", \"vmclear\", \"vmlaunch\", \"vmptrld\", \"vmptrst\", \"vmread\", \"vmresume\", \"vmwrite\",\n        \"vmxoff\", \"invvpid\", \"getsec\", \"loadiwkey\", \"pconfig\", \"encls\", \"enclv\", \"hlt\", \"xgetbv\",\n        \"xsetbv\"\n    }\n    always_exiting_registers = [\n        \"cr0\", \"cr2\", \"cr3\", \"cr8\", \"dr0\", \"dr1\", \"dr2\", \"dr3\", \"dr6\", \"dr7\"\n    ]\n\n\n# ==================================================================================================\n# Private: Fault Handling and Permissions\n# ==================================================================================================\nclass _FaultInterpreterCommon(ABC):\n    \"\"\" Class that handles page faults and permissions in the emulator \"\"\"\n    _model: UnicornModel\n    _target_desc: TargetDesc\n    _uc_target_desc: UnicornTargetDesc\n    _test_case: Optional[TestCaseProgram] = None\n\n    _faulty_page_readable: Dict[ActorID, bool]\n    _faulty_page_writable: Dict[ActorID, bool]\n    _faulty_page_user_accessible: Dict[ActorID, bool]\n    _main_page_user_accessible: Dict[ActorID, bool]\n\n    def __init__(self, model: UnicornModel, target_desc: TargetDesc):\n        self._model = model\n        self._target_desc = target_desc\n        self._uc_target_desc = target_desc.uc_target_desc\n\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        \"\"\" Load the test case into the interpreter \"\"\"\n        self._test_case = test_case\n        self._faulty_page_readable = {}\n        self._faulty_page_writable = {}\n        self._faulty_page_user_accessible = {}\n        self._main_page_user_accessible = {}\n        for actor in test_case.get_actors(sorted_=True):\n            aid = actor.get_id()\n\n            pte: PTEMask = actor.data_properties\n            self._faulty_page_readable[aid] = self._page_is_readable(pte)\n            self._faulty_page_writable[aid] = self._page_is_writable(pte)\n            self._faulty_page_user_accessible[aid] = self._page_is_user_accessible(pte)\n            self._main_page_user_accessible[aid] = actor.privilege_level == ActorPL.USER\n\n            if actor.mode == ActorMode.GUEST:\n                epte: PTEMask = actor.data_ept_properties\n                self._faulty_page_readable[aid] &= self._extended_page_is_readable(epte)\n                self._faulty_page_writable[aid] &= self._extended_page_is_writable(epte)\n                # NOTE: EPT user-accessible bit is not supported yet\n\n        # make the permissions available to other components of the model\n        self._model.state.page_permissions = {}\n        for actor_id in range(test_case.n_actors()):\n            self._model.state.page_permissions[actor_id] = (self._faulty_page_readable[actor_id],\n                                                            self._faulty_page_writable[actor_id])\n\n    def load_input(self, _: InputData) -> None:\n        \"\"\" Set memory permissions for the given input \"\"\"\n        assert self._test_case is not None\n\n        # Set memory permissions\n        for actor_id in range(self._test_case.n_actors()):\n            if not self._faulty_page_readable[actor_id]:\n                self._model.set_faulty_area_rw(actor_id, False, False)\n            elif not self._faulty_page_writable[actor_id]:\n                self._model.set_faulty_area_rw(actor_id, True, False)\n\n    def induce_user_faults(self, current_actor: Actor, address: int) -> None:\n        \"\"\"\n        Induce page faults for user/kernel access based on the page permissions\n        and the current execution mode\n        \"\"\"\n        # identify the target page privilege level\n        if not self._model.layout.is_data_addr(address):\n            return\n        target_aid = self._model.layout.data_addr_to_actor_id(address)\n        faulty_area_start = self._model.layout.get_data_addr(DataArea.FAULTY, target_aid)\n        is_faulty_page = (address & 0xFFFFFFFFFFFFF000) == faulty_area_start\n        target_page_is_user = self._faulty_page_user_accessible[target_aid] \\\n            if is_faulty_page else self._main_page_user_accessible[target_aid]\n\n        # user actors produce faults when accessing data from kernel space\n        if current_actor.privilege_level == ActorPL.USER and not target_page_is_user:\n            self._model.do_soft_fault(13)\n            return\n\n        # kernel actors produce faults when accessing data of user actors\n        # NOTE: this code assumes that SMAP is enabled, which may or may not be the case in practice\n        if current_actor.privilege_level == ActorPL.KERNEL and target_page_is_user:\n            self._model.do_soft_fault(13)\n\n    @abstractmethod\n    def _page_is_readable(self, pet: PTEMask) -> bool:\n        \"\"\" Check if the page is readable according to the PTE bits \"\"\"\n\n    @abstractmethod\n    def _page_is_writable(self, pet: PTEMask) -> bool:\n        \"\"\" Check if the page is writable according to the PTE bits \"\"\"\n\n    @abstractmethod\n    def _page_is_user_accessible(self, pet: PTEMask) -> bool:\n        \"\"\" Check if the page is user-accessible according to the PTE bits \"\"\"\n\n    @abstractmethod\n    def _extended_page_is_readable(self, epet: PTEMask) -> bool:\n        \"\"\" Check if the page is readable according to the EPTE bits \"\"\"\n\n    @abstractmethod\n    def _extended_page_is_writable(self, epet: PTEMask) -> bool:\n        \"\"\" Check if the page is writable according to the EPTE bits \"\"\"\n\n\nclass _X86FaultInterpreter(_FaultInterpreterCommon):\n    \"\"\" Implements page fault handling and permission checking for the x86 architecture \"\"\"\n\n    def _page_is_readable(self, pet: PTEMask) -> bool:\n        pte_desc = self._target_desc.pte_bits\n        if (pet & (1 << pte_desc[\"present\"][0])) == 0:\n            return False\n        if (pet & (1 << pte_desc[\"accessed\"][0])) == 0:\n            return False\n        if (pet & (1 << pte_desc[\"reserved_bit\"][0])) != 0:\n            return False\n        return True\n\n    def _page_is_writable(self, pet: PTEMask) -> bool:\n        pte_desc = self._target_desc.pte_bits\n        if (pet & (1 << pte_desc[\"writable\"][0])) == 0:\n            return False\n        if (pet & (1 << pte_desc[\"dirty\"][0])) == 0:\n            return False\n        return True\n\n    def _page_is_user_accessible(self, pet: PTEMask) -> bool:\n        pte_desc = self._target_desc.pte_bits\n        if (pet & (1 << pte_desc[\"user\"][0])) == 0:\n            return False\n        return True\n\n    def _extended_page_is_readable(self, epet: PTEMask) -> bool:\n        epte_desc = self._target_desc.vm_pte_bits\n        if (epet & (1 << epte_desc[\"present\"][0])) == 0:\n            return False\n        if (epet & (1 << epte_desc[\"accessed\"][0])) == 0:\n            return False\n        if (epet & (1 << epte_desc[\"reserved_bit\"][0])) != 0:\n            return False\n        return True\n\n    def _extended_page_is_writable(self, epet: PTEMask) -> bool:\n        epte_desc = self._target_desc.vm_pte_bits\n        if (epet & (1 << epte_desc[\"writable\"][0])) == 0:\n            return False\n        if (epet & (1 << epte_desc[\"dirty\"][0])) == 0:\n            return False\n        return True\n\n\nclass _ARM64FaultInterpreter(_FaultInterpreterCommon):\n    \"\"\" Implements page fault handling and permission checking for the ARM64 architecture \"\"\"\n\n    def _page_is_readable(self, pet: PTEMask) -> bool:\n        pte_desc = self._target_desc.pte_bits\n        if (pet & (1 << pte_desc[\"valid\"][0])) == 0:\n            return False\n        return True\n\n    def _page_is_writable(self, pet: PTEMask) -> bool:\n        pte_desc = self._target_desc.pte_bits\n        if (pet & (1 << pte_desc[\"non_writable\"][0])) != 0:\n            return False\n        return True\n\n    def _page_is_user_accessible(self, pet: PTEMask) -> bool:\n        return True  # FIXME: implement user/supervisor bit check\n\n    def _extended_page_is_readable(self, epet: PTEMask) -> bool:\n        return True\n\n    def _extended_page_is_writable(self, epet: PTEMask) -> bool:\n        return True\n\n    def emulate_crossing_fault(self, access: int, address: int, size: int) -> None:\n        \"\"\"\n        Workaround: Unicorn does not trigger a fault if the memory access crosses a page\n        boundary and the first page is accessible but the second is not\n        \"\"\"\n        # No need for the workaround if the access is within a page\n        if address % 0x1000 + size < 0x1000:\n            return\n\n        # Also does not apply if the crossing goes to any other page than the faulty area\n        layout = self._model.layout\n        access_end = address + size - 1\n        actor_id = layout.data_addr_to_actor_id(address)\n        if actor_id == -1:\n            return\n        faulty_base = layout.get_data_addr(DataArea.FAULTY, actor_id)\n        faulty_end = faulty_base + layout.data_area_size(DataArea.FAULTY)\n        if access_end < faulty_base or access_end >= faulty_end:\n            return\n\n        # Emulate a fault if the faulty area is non-readable/non-writable\n        if not self._faulty_page_readable[actor_id]:\n            self._model.do_soft_fault(UC_ERR_READ_PROT)\n            return\n        if access == UC_MEM_WRITE and not self._faulty_page_writable[actor_id]:\n            self._model.do_soft_fault(UC_ERR_WRITE_PROT)\n            return\n"
  },
  {
    "path": "rvzr/model_unicorn/model.py",
    "content": "\"\"\"\nFile: Unicorn-based backend to the contract model.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n\nfrom __future__ import annotations\nfrom abc import ABC, abstractmethod\nfrom typing import List, Tuple, Optional, Set, TYPE_CHECKING, Final, Dict, Type\n\nimport numpy as np\n\nimport unicorn as uc\nimport unicorn.x86_const as x86ucc  # type: ignore # no type hints for unicorn.x86_const\nimport unicorn.arm64_const as armucc  # type: ignore # no type hints for unicorn.arm_const\nfrom unicorn import Uc, UC_HOOK_CODE, UC_HOOK_MEM_READ, UC_HOOK_MEM_WRITE, \\\n    UC_HOOK_MEM_UNMAPPED, UcError, UC_MEM_WRITE, UC_PROT_NONE, UC_PROT_READ\n\nfrom ..model import Model\nfrom ..sandbox import SandboxLayout, DataArea\nfrom ..config import CONF\nfrom ..logs import ModelLogger, BLUE, COL_RESET, error\nfrom ..traces import CTraceEntry\n\nfrom .taint_tracker import UnicornTaintTracker\nfrom .coverage import InstructionCoverage\nfrom .execution_context import ModelExecutionState\n\nif TYPE_CHECKING:\n    from ..tc_components.test_case_data import InputData\n    from ..tc_components.test_case_data import InputTaint\n    from ..tc_components.test_case_code import TestCaseProgram\n    from ..traces import CTrace\n    from .tracer import UnicornTracer\n    from .speculator_abc import UnicornSpeculator\n    from .interpreter import ExtraInterpreter\n    from ..target_desc import TargetDesc, UnicornTargetDesc\n    from ..sandbox import BaseAddrTuple\n\n\n_UC_FAULT_MAPPING: Final[Dict[str, List[int]]] = {  # map fault names to Unicorn fault IDs\n    \"DE\": [21],\n    \"DB\": [10],\n    \"BP\": [21],\n    \"BR\": [13],\n    \"UD\": [10],\n    \"PF\": [12, 13],\n    \"GP\": [6, 7],\n    \"assist\": [12, 13],\n}\n\n\n# ==================================================================================================\n# Private classes and functions\n# ==================================================================================================\nclass _Dispatcher:\n    \"\"\"\n    Class responsible for invoking callback functions in service classes upon events in Unicorn\n    \"\"\"\n    coverage: InstructionCoverage\n    _taint_tracker: UnicornTaintTracker\n    _tracer: UnicornTracer\n    _speculator: UnicornSpeculator\n    _interpreter: ExtraInterpreter\n\n    def __init__(self, taint_tracker: UnicornTaintTracker, speculator: UnicornSpeculator,\n                 tracer: UnicornTracer, interpreter: ExtraInterpreter,\n                 coverage: InstructionCoverage) -> None:\n        self._taint_tracker = taint_tracker\n        self._tracer = tracer\n        self._speculator = speculator\n        self._interpreter = interpreter\n        self.coverage = coverage\n\n    def test_case_load_dispatch(self, test_case: TestCaseProgram) -> None:\n        \"\"\" Call callbacks in service classes that need to be called when a test case is loaded \"\"\"\n        self._interpreter.load_test_case(test_case)\n        self._tracer.load_test_case(test_case)\n        self.coverage.finish_test_case()\n        self.coverage.start_test_case()\n\n    def execution_start_dispatch(self, input_: InputData) -> None:\n        \"\"\" Call callbacks in service classes that need to be called before model execution \"\"\"\n        self._tracer.reset(input_)\n        self._speculator.reset()\n        self._taint_tracker.reset()\n        self._interpreter.load_input(input_)\n\n    def instruction_dispatch(self, address: int, size: int, _: UnicornModel,\n                             state: ModelExecutionState) -> None:\n        \"\"\" Call instruction-related callbacks in service classes \"\"\"\n\n        if state.current_instruction.is_macro_placeholder:\n            # Skip macro placeholders as they are not real instructions\n            return\n\n        # NOTE: the order of the following calls is important\n        self._taint_tracker.track_instruction(state.current_instruction)\n        self._tracer.observe_instruction(address, size)\n        self._speculator.handle_instruction(address, size)\n        self._interpreter.interpret_instruction(address, state)\n        self.coverage.add_instruction(state.current_instruction)\n\n    def mem_access_dispatch(self, access: int, address: int, size: int, value: int,\n                            state: ModelExecutionState) -> None:\n        \"\"\" Call memory access-related callbacks in service classes \"\"\"\n\n        if state.current_instruction.is_macro_placeholder:\n            # Skip macro placeholders as they are not real instructions\n            return\n\n        # NOTE: the order of the following calls is important\n        self._taint_tracker.track_memory_access(address, size, access == UC_MEM_WRITE)\n        self._speculator.handle_mem_access(access, address, size, value)\n        self._tracer.observe_mem_access(access, address, size, value)\n        self._interpreter.interpret_mem_access(access, address, size, value)\n\n\ndef _instruction_hook(_: Uc, address: int, size: int, model: UnicornModel) -> None:\n    \"\"\" Dispatch the Unicorn instruction hook to the model. \"\"\"\n    model.instruction_callback(address, size)\n\n\ndef _mem_access_hook(_: Uc, access: int, address: int, size: int, value: int,\n                     model: UnicornModel) -> None:\n    \"\"\" Dispatch the Unicorn memory access hook to the model. \"\"\"\n    model.mem_access_callback(access, address, size, value)\n\n\ndef _mem_unmapped_hook(_: Uc, access: int, address: int, size: int, value: int,\n                       model: UnicornModel) -> None:\n    \"\"\" Dispatch the Unicorn memory unmapped hook to the model. \"\"\"\n    model.mem_access_callback(access, address, size, value)\n\n\n_ERR_DECODE = {\n    uc.UC_ERR_OK: \"OK (UC_ERR_OK)\",\n    uc.UC_ERR_NOMEM: \"No memory available or memory not present (UC_ERR_NOMEM)\",\n    uc.UC_ERR_ARCH: \"Invalid/unsupported architecture (UC_ERR_ARCH)\",\n    uc.UC_ERR_HANDLE: \"Invalid handle (UC_ERR_HANDLE)\",\n    uc.UC_ERR_MODE: \"Invalid mode (UC_ERR_MODE)\",\n    uc.UC_ERR_VERSION: \"Different API version between core & binding (UC_ERR_VERSION)\",\n    uc.UC_ERR_READ_UNMAPPED: \"Invalid memory read (UC_ERR_READ_UNMAPPED)\",\n    uc.UC_ERR_WRITE_UNMAPPED: \"Invalid memory write (UC_ERR_WRITE_UNMAPPED)\",\n    uc.UC_ERR_FETCH_UNMAPPED: \"Invalid memory fetch (UC_ERR_FETCH_UNMAPPED)\",\n    uc.UC_ERR_HOOK: \"Invalid hook type (UC_ERR_HOOK)\",\n    uc.UC_ERR_INSN_INVALID: \"Invalid instruction (UC_ERR_INSN_INVALID)\",\n    uc.UC_ERR_MAP: \"Invalid memory mapping (UC_ERR_MAP)\",\n    uc.UC_ERR_WRITE_PROT: \"Write to write-protected memory (UC_ERR_WRITE_PROT)\",\n    uc.UC_ERR_READ_PROT: \"Read from non-readable memory (UC_ERR_READ_PROT)\",\n    uc.UC_ERR_FETCH_PROT: \"Fetch from non-executable memory (UC_ERR_FETCH_PROT)\",\n    uc.UC_ERR_ARG: \"Invalid argument (UC_ERR_ARG)\",\n    uc.UC_ERR_READ_UNALIGNED: \"Read from unaligned memory (UC_ERR_READ_UNALIGNED)\",\n    uc.UC_ERR_WRITE_UNALIGNED: \"Write to unaligned memory (UC_ERR_WRITE_UNALIGNED)\",\n    uc.UC_ERR_FETCH_UNALIGNED: \"Fetch from unaligned memory (UC_ERR_FETCH_UNALIGNED)\",\n    uc.UC_ERR_RESOURCE: \"Insufficient resource (UC_ERR_RESOURCE)\",\n    uc.UC_ERR_EXCEPTION: \"Misc. CPU exception (UC_ERR_EXCEPTION)\",\n}\n\n\ndef _err_to_str(errno: int) -> str:\n    if errno in _ERR_DECODE:\n        return _ERR_DECODE[errno]\n    return \"Unknown error code\"\n\n\n# ==================================================================================================\n# Public Interface: Architecture-independent Model\n# ==================================================================================================\nclass UnicornModel(Model, ABC):\n    \"\"\"\n    Basic architecture-independent implementation of a Unicorn-based model.\n    This implementation does not support speculative execution; see UnicornSpec for that.\n    \"\"\"\n\n    # pylint: disable=too-many-instance-attributes\n    # This is a management class that connects many services together, so having many attributes\n    # is a necessary evil\n\n    # Service objects\n    emulator: Uc\n    tracer: Final[UnicornTracer]\n    speculator: Final[UnicornSpeculator]\n    _taint_tracker: UnicornTaintTracker\n    _log: Final[ModelLogger]\n    _dispatcher: Final[_Dispatcher]\n\n    # Model state\n    state: ModelExecutionState\n    layout: SandboxLayout\n\n    # Descriptors\n    _bases: BaseAddrTuple\n    _target_desc: Final[TargetDesc]\n    _uc_target_desc: Final[UnicornTargetDesc]\n    _architecture: Optional[Tuple[int, int]] = None  # (UC_ARCH, UC_MODE)\n    _handled_faults: Set[int]  # The set of fault types that do NOT terminate execution\n\n    # ----------------------------------------------------------------------------------------------\n    # Constructor and Service Module Initialization\n    def __init__(self,\n                 bases: BaseAddrTuple,\n                 target_desc: TargetDesc,\n                 speculator_cls: Type[UnicornSpeculator],\n                 tracer_cls: Type[UnicornTracer],\n                 interpreter_cls: Type[ExtraInterpreter],\n                 enable_mismatch_check_mode: bool = False) -> None:\n\n        assert self._architecture is not None, \\\n            \"Subclasses must define the `architecture` attribute before calling super().__init__\"\n\n        # Service modules\n        self.emulator = Uc(*self._architecture)\n        self._taint_tracker = UnicornTaintTracker(bases, target_desc)\n        self.tracer = tracer_cls(target_desc, self, self._taint_tracker)\n        self.speculator = speculator_cls(target_desc, self, self._taint_tracker)\n        self._dispatcher = _Dispatcher(self._taint_tracker, self.speculator, self.tracer,\n                                       interpreter_cls(target_desc, self), InstructionCoverage())\n        self._target_desc = target_desc\n        self._uc_target_desc = target_desc.uc_target_desc\n        self._log = ModelLogger()\n\n        # Set the base addresses and the mismatch check mode\n        self._bases = bases\n        self._enable_mismatch_check_mode = enable_mismatch_check_mode\n        self.is_speculative = not self.speculator.is_sequential\n\n        # Set the list of handled faults\n        self._handled_faults = set()\n        for fault in CONF._handled_faults:\n            if fault in _UC_FAULT_MAPPING:\n                self._handled_faults.update(_UC_FAULT_MAPPING[fault])\n            else:\n                raise NotImplementedError(f\"Fault type {fault} is not supported\")\n\n    # ----------------------------------------------------------------------------------------------\n    # Default Public Interface\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        \"\"\"\n        Load the test case into the model. This method must be called before tracing\n        the test case (trace_test_case or trace_test_case_with_taints).\n        :param test_case: the test case to load\n        :return: None\n        :raises UcError: if an error occurs while loading the test case\n        \"\"\"\n        test_case_obj = test_case.get_obj()\n\n        # Load the test case into the service classes\n        self.layout = SandboxLayout(self._bases, test_case.n_actors())\n        self._log.set_model_layout(self.layout)\n        self.state = ModelExecutionState(test_case, self.layout, self._target_desc)\n        self._dispatcher.test_case_load_dispatch(test_case)\n\n        # Create a new instance of the emulator\n        assert self._architecture is not None, \"_architecture must be set by subclass\"\n        self.emulator = Uc(*self._architecture)\n\n        # Get binary representation of the test case\n        code = test_case_obj.to_bytes(\n            padded_section_size=self.layout.code_size_per_actor(), padding_byte=b'\\x90')\n\n        # Allocate memory and write the binary\n        # Note: the data will be written later, by the _load_input method\n        try:\n            self.emulator.mem_map(self.layout.code_start(), self.layout.code_size)\n            self.emulator.mem_map(self.layout.data_start(), self.layout.data_size)\n            self.emulator.mem_write(self.layout.code_start(), code)\n        except UcError as e:\n            error(f\"[UnicornModel:load_test_case] {e}\")\n\n        # Set up callbacks\n        try:\n            self.emulator.hook_add(UC_HOOK_MEM_READ | UC_HOOK_MEM_WRITE, _mem_access_hook, self)\n            self.emulator.hook_add(UC_HOOK_MEM_UNMAPPED, _mem_unmapped_hook, self)\n            self.emulator.hook_add(UC_HOOK_CODE, _instruction_hook, self)\n        except UcError as e:\n            error(f\"[UnicornModel:load_test_case] {e}\")\n\n    def trace_test_case(self, inputs: List[InputData], nesting: int) -> List[CTrace]:\n        \"\"\"\n        Execute the previously loaded test case with the inputs and collect the contract traces.\n        :param inputs: the inputs to use for the test case\n        :param nesting: the maximum number of speculative levels that will be simulated\n        :return: list of collected contract traces, one per input\n        \"\"\"\n        self._taint_tracker.set_enable_tracking(False)\n        self.speculator.set_max_nesting(nesting)\n        ctraces, _ = self._execute_test_case_with_inputs(inputs)\n        return ctraces\n\n    def trace_test_case_with_taints(self, inputs: List[InputData],\n                                    nesting: int) -> Tuple[List[CTrace], List[InputTaint]]:\n        \"\"\"\n        Executes the previously loaded test case with the inputs and collects the contract traces\n        while also tracking taints.\n        :param inputs: the inputs to use for the test case\n        :param nesting: the maximum number of speculative levels that will be simulated\n        :return: list of collected contract traces and the taints, one of each per input\n        \"\"\"\n        self._taint_tracker.set_enable_tracking(True)\n        self.speculator.set_max_nesting(nesting)\n        ctraces, taints = self._execute_test_case_with_inputs(inputs)\n        return ctraces, taints\n\n    # ----------------------------------------------------------------------------------------------\n    # Unicorn-specific Public Interface\n    def instruction_callback(self, address: int, size: int) -> None:\n        \"\"\"\n        Callback function called when Unicorn executes an instruction\n        :param address: the address of the instruction\n        :param size: the size of the instruction\n        :return: None\n        \"\"\"\n        # Terminate execution if the exit instruction is reached\n        if self.state.is_exit_addr(address):\n            self.emulator.emu_stop()\n            return\n\n        # Otherwise, update the context ...\n        self.state.update_context(self.emulator, address)\n        self._log.dbg_instruction(address, self, self.state, self.speculator)\n\n        # .. and pass the instruction down to the service modules\n        self._dispatcher.instruction_dispatch(address, size, self, self.state)\n\n    def mem_access_callback(self, access: int, address: int, size: int, value: int) -> None:\n        \"\"\"\n        Callback function called when Unicorn accesses memory.\n        \"\"\"\n        self._log.dbg_mem_access(access == UC_HOOK_MEM_WRITE, value, address, size, self,\n                                 self.layout)\n        self._dispatcher.mem_access_dispatch(access, address, size, value, self.state)\n\n    def do_soft_fault(self, errno: int) -> None:\n        \"\"\"\n        Signal a fault to the model and stop the emulator\n        (without rising an exception in the emulator)\n        \"\"\"\n        assert self.state, \"Function called before load_test_case\"\n        self.state.pending_fault = errno\n        self.emulator.emu_stop()\n\n    def set_faulty_area_rw(self, actor_id: int, r: bool, w: bool) -> None:\n        \"\"\" Sets the 'readable' and 'writable' property of the faulty area for the given actor \"\"\"\n        if actor_id == -1:\n            actor_id = self.state.current_actor.get_id()\n        faulty_base = self.layout.get_data_addr(DataArea.FAULTY, actor_id)\n        faulty_size = self.layout.data_area_size(DataArea.FAULTY)\n        if not r:\n            self.emulator.mem_protect(faulty_base, faulty_size, UC_PROT_NONE)\n        elif not w:\n            self.emulator.mem_protect(faulty_base, faulty_size, UC_PROT_READ)\n        else:\n            self.emulator.mem_protect(faulty_base, faulty_size)\n\n    def report_coverage(self, path: str) -> None:\n        \"\"\" Write the coverage data to a file \"\"\"\n        self._dispatcher.coverage.report(path)\n\n    @abstractmethod\n    def print_registers(self, oneline: bool = False) -> None:\n        \"\"\" Print the current values of all general-purpose registers \"\"\"\n\n    # ----------------------------------------------------------------------------------------------\n    # Private Methods\n    def _execute_test_case_with_inputs(\n            self, inputs: List[InputData]) -> Tuple[List[CTrace], List[InputTaint]]:\n        \"\"\"\n        Execute the loaded test case with the given sequence of inputs\n        and collect traces and taints.\n        :param inputs: the inputs to use for the test case\n        :return: the collected traces and taints\n        \"\"\"\n        traces, taints = [], []\n        for index, input_ in enumerate(inputs):\n            self._log.dbg_header(index)\n            self.state.full_reset()\n            self._dispatcher.execution_start_dispatch(input_)\n\n            # Execute the test case with the given input\n            self._load_input(input_)\n            self._run_state_machine()\n\n            # Record traces (two options possible):\n            if not self._enable_mismatch_check_mode:  # Case 1: normal mode - store traces\n                traces.append(self.tracer.get_trace())\n            else:  # Case 2: mismatch check mode - store register values\n                register_list = self._uc_target_desc.usable_registers\n                registers = register_list[:-2]  # exclude RSP and EFLAGS\n                reg_values = [int(self.emulator.reg_read(reg)) for reg in registers]  # type: ignore\n                self.tracer.trace = [CTraceEntry(\"reg\", val) for val in reg_values]\n                traces.append(self.tracer.get_trace())\n\n            # Record taints\n            n_actors = self.state.current_test_case().n_actors()\n            taints.append(self._taint_tracker.get_taint(n_actors))\n\n        return traces, taints\n\n    def _run_state_machine(self) -> None:\n        \"\"\"\n        Execute the loaded test case on the model with the loaded input.\n\n        This method implements a state machine that repeatedly executes the test case\n        until it reaches the exit instruction while being in a non-speculative state.\n\n        The state machine ensures that:\n            - whenever the emulator exits without reaching the exit instruction,\n              the model either rolls back (if in speculation) or exits (if not in speculation)\n            - whenever a fault is triggered, the model jumps to the corresponding fault handler\n              (if not in speculation) or rolls back (if in speculation)\n        The complete state machine is shown in:\n            `docs/assets/unicorn-model-state-machine.drawio.png`.\n\n        \"\"\"\n        code_start = self.layout.code_start()\n        pc = code_start\n        while True:\n            self.state.reset_after_em_stop(pc)\n\n            # Handle re-entries after faults and rollbacks\n            if pc != code_start:\n                in_speculation = self.speculator.in_speculation()\n\n                # When entering a new loop iterations, there are the following options:\n                # 1. Re-entering after reaching the end and not in speculation\n                if self.state.is_exit_addr(pc) and not in_speculation:\n                    return\n\n                # 2. Re-entering after reaching the end and in speculation\n                if self.state.is_exit_addr(pc) and in_speculation:\n                    pc = self.speculator.rollback()\n                    self._log.dbg_rollback(pc)\n                    continue\n\n                # 3. Re-entering into a fault handler and in speculation\n                if pc == self.state.fault_handler_addr and in_speculation:\n                    # This case indicates that the rollback was supposed to terminate speculation,\n                    # so rollback again\n                    pc = self.speculator.rollback()\n                    self._log.dbg_rollback(pc)\n                    continue\n                # 4. In all other cases, continue execution as normal\n\n            # Execute the test case\n            try:\n                self.emulator.emu_start(pc, self.layout.code_end(), timeout=10 * uc.UC_SECOND_SCALE)\n            except UcError as e:\n                self.state.pending_fault = int(e.errno)  # type: ignore  # missing type annotation\n\n            # Handle faults\n            if self.state.pending_fault:\n                self._patch_context_after_fault()\n                pc = self._handle_fault()\n                if pc and pc != self.state.exit_addr:\n                    continue\n\n            # If the model is in non-speculative state, a fault terminates the execution\n            if not self.speculator.in_speculation():\n                return\n\n            # Otherwise (in a speculative state), a fault causes a speculation rollback\n            pc = self.speculator.rollback()\n            self._log.dbg_rollback(pc)\n            continue\n\n    def _handle_fault(self) -> int:\n        \"\"\"\n        Handle a fault that was triggered during the execution\n        :return: address of the next instruction to execute OR zero if the fault triggers a rollback\n        \"\"\"\n        errno = self.state.pending_fault\n        self._log.dbg_exception(errno, _err_to_str(errno))\n\n        # clear the pending fault\n        self.state.pending_fault = 0\n\n        # when a fault is triggered, CPU stores the PC and the fault type\n        # on stack - this has to be mirrored at the contract level\n        rsp = self.layout.get_data_addr(DataArea.RSP_INIT, 0)\n        self.tracer.observe_mem_access(UC_MEM_WRITE, rsp, 8, errno)\n\n        # Possible fault handling scenarios:\n        # 1. There is a registered speculation mechanism for this fault -> use it\n        next_addr = self.speculator.handle_fault(errno)\n        if next_addr:\n            return next_addr\n\n        # 2. No registered speculation mechanism, but we're already in speculation -> rollback\n        if self.speculator.in_speculation():\n            return 0\n\n        # 3. Not in speculation, and we've already had a fault before -> throw an error\n        if self.state.had_arch_fault:\n            self.print_registers()\n            error(f\"Nested fault {errno} {_err_to_str(errno)}\", print_last_tb=True)\n        self.state.had_arch_fault = True\n\n        # 4. Not-nested non-speculative fault, and it is in a list of expected faults -> handle it\n        if errno in self._handled_faults:\n            return self.state.fault_handler_addr\n\n        # 5. Non-nested non-speculative fault, and it is an unexpected fault -> throw an error\n        self.print_registers()\n        error(f\"Unexpected exception {errno} {_err_to_str(errno)}\", print_last_tb=True)\n\n    def _patch_context_after_fault(self) -> None:\n        \"\"\" Patch the context to avoid Unicorn bugs \"\"\"\n        if not self.state.previous_context:\n            error(\"Fault triggered without a previous context\")\n\n        # workaround for a Unicorn bug: after catching an exception\n        # we need to restore some pre-exception context. otherwise,\n        # the emulator becomes corrupted\n        self.emulator.context_restore(self.state.previous_context)\n        # another workaround, specifically for flags\n        flags_id = self._target_desc.uc_target_desc.reg_norm_to_constant[\"FLAGS\"]\n        self.emulator.reg_write(flags_id, self.emulator.reg_read(flags_id))\n\n    @abstractmethod\n    def _load_input(self, input_: InputData) -> None:\n        \"\"\" Load registers and memory with given input: this is architecture specific \"\"\"\n\n\n# ==================================================================================================\n# Public: x86 implementation of the Unicorn Backend\n# ==================================================================================================\nclass X86UnicornModel(UnicornModel):\n    \"\"\" Model for x86 architecture \"\"\"\n\n    def __init__(self,\n                 bases: BaseAddrTuple,\n                 target_desc: TargetDesc,\n                 speculator_cls: Type[UnicornSpeculator],\n                 tracer_cls: Type[UnicornTracer],\n                 interpreter_cls: Type[ExtraInterpreter],\n                 enable_mismatch_check_mode: bool = False) -> None:\n\n        self._architecture = (uc.UC_ARCH_X86, uc.UC_MODE_64)\n        self._flags_id = x86ucc.UC_X86_REG_EFLAGS\n\n        self.underflow_pad_values = bytes(SandboxLayout.data_area_size(DataArea.UNDERFLOW_PAD))\n        self.overflow_pad_values = bytes(SandboxLayout.data_area_size(DataArea.OVERFLOW_PAD))\n\n        super().__init__(bases, target_desc, speculator_cls, tracer_cls, interpreter_cls,\n                         enable_mismatch_check_mode)\n\n    def _load_input(self, input_: InputData) -> None:\n        \"\"\"\n        Set the memory and register values in the emulator according to the input object provided.\n        In addition, set the memory permissions for each actor.\n\n        :param input_: Input object containing the memory and register values for each actor.\n        \"\"\"\n\n        def patch_flags(flags: np.uint64) -> np.uint64:\n            return (flags & np.uint64(2263)) | np.uint64(2)\n\n        def write_area(area: DataArea, actor_id: int, data: bytes) -> None:\n            em.mem_write(self.layout.get_data_addr(area, actor_id), data)\n\n        # shortcuts to save on typing\n        em = self.emulator\n        regs = self._uc_target_desc.usable_registers\n\n        # Initialize memory for each actor:\n        n_actors = self.state.current_test_case().n_actors()\n        for actor_id in range(n_actors):\n            input_fragment = input_[actor_id]\n\n            # - initialize overflows with zeroes\n            write_area(DataArea.OVERFLOW_PAD, actor_id, self.overflow_pad_values)\n            write_area(DataArea.UNDERFLOW_PAD, actor_id, self.underflow_pad_values)\n\n            # - sandbox data pages\n            write_area(DataArea.MAIN, actor_id, input_fragment['main'].tobytes())\n            write_area(DataArea.FAULTY, actor_id, input_fragment['faulty'].tobytes())\n\n            # - GPRs\n            # Note: Executor uses the GPR area to initialize EFLAGS, so we need to patch them\n            #      before writing them to the emulator to ensure consistency.\n            input_fragment['gpr'][6] = patch_flags(input_fragment['gpr'][6])\n            # input_fragment['gpr'][7] = np.uint64(self.layout.get_data_addr(DataArea.RSP_INIT, 0))\n            write_area(DataArea.GPR, actor_id, input_fragment['gpr'].tobytes())\n\n            # - SIMD\n            write_area(DataArea.SIMD, actor_id, input_fragment['simd'].tobytes())\n\n        # Registers are initialized with the main actor's input\n        input_fragment = input_[0]\n\n        # - initialize GPRs\n        value: np.uint64\n        for i, value in enumerate(input_fragment['gpr']):\n            em.reg_write(regs[i], int(value))\n\n        # similarly to above, patch reg. values\n        em.reg_write(x86ucc.UC_X86_REG_EFLAGS, int(patch_flags(input_fragment['gpr'][6])))\n        em.reg_write(x86ucc.UC_X86_REG_RSP, self.layout.get_data_addr(DataArea.RSP_INIT, 0))\n        em.reg_write(x86ucc.UC_X86_REG_RBP, self.layout.get_data_addr(DataArea.RSP_INIT, 0))\n        em.reg_write(x86ucc.UC_X86_REG_R14, self.layout.get_data_addr(DataArea.MAIN, 0))\n\n        # - initialize SIMD\n        simd_values: List[int] = []\n        for i, val in enumerate(input_fragment['simd']):\n            if i % 4 == 0:\n                simd_values.append(int(val))\n            elif i % 4 == 1:\n                simd_values[-1] |= int(val) << 64\n            else:\n                # Unicorn doesn't properly support YMM, so the upper 128 bits are ignored\n                continue\n        for i, simd_value in enumerate(simd_values):\n            em.reg_write(self._uc_target_desc.usable_simd128_registers[i], simd_value)\n\n    def print_registers(self, oneline: bool = False) -> None:\n\n        def compressed(val: int) -> str:\n            if self.layout.is_data_addr(val):\n                return f\"base+0x{self.layout.data_addr_to_offset(val):<9x}\"\n            return f\"0x{val:016x}\"\n\n        em = self.emulator\n        rax = compressed(em.reg_read(x86ucc.UC_X86_REG_RAX))  # type: ignore\n        rbx = compressed(em.reg_read(x86ucc.UC_X86_REG_RBX))  # type: ignore\n        rcx = compressed(em.reg_read(x86ucc.UC_X86_REG_RCX))  # type: ignore\n        rdx = compressed(em.reg_read(x86ucc.UC_X86_REG_RDX))  # type: ignore\n        rsi = compressed(em.reg_read(x86ucc.UC_X86_REG_RSI))  # type: ignore\n        rdi = compressed(em.reg_read(x86ucc.UC_X86_REG_RDI))  # type: ignore\n\n        if not oneline:\n            print(\"\\n\\nRegisters:\")\n            print(f\"rax: {rax}\")\n            print(f\"rbx: {rbx}\")\n            print(f\"rcx: {rcx}\")\n            print(f\"rdx: {rdx}\")\n            print(f\"rsi: {rsi}\")\n            print(f\"rdi: {rdi}\")\n        else:\n            if CONF.color:\n                print(f\"  {BLUE}rax={COL_RESET}{rax} \"\n                      f\"{BLUE}rbx={COL_RESET}{rbx} \"\n                      f\"{BLUE}rcx={COL_RESET}{rcx}\\n\"\n                      f\"  {BLUE}rdx={COL_RESET}{rdx} \"\n                      f\"{BLUE}rsi={COL_RESET}{rsi} \"\n                      f\"{BLUE}rdi={COL_RESET}{rdi}\\n\"\n                      f\"  {BLUE}flags={COL_RESET}0b{em.reg_read(x86ucc.UC_X86_REG_EFLAGS):012b}\\n\"\n                      f\"  {BLUE}xmm0={COL_RESET}0x{em.reg_read(x86ucc.UC_X86_REG_XMM0):032x} \"\n                      f\"{BLUE}xmm1={COL_RESET}0x{em.reg_read(x86ucc.UC_X86_REG_XMM1):032x} \\n\"\n                      f\"  {BLUE}xmm2={COL_RESET}0x{em.reg_read(x86ucc.UC_X86_REG_XMM2):032x} \"\n                      f\"{BLUE}xmm3={COL_RESET}0x{em.reg_read(x86ucc.UC_X86_REG_XMM3):032x} \\n\"\n                      f\"  {BLUE}xmm4={COL_RESET}0x{em.reg_read(x86ucc.UC_X86_REG_XMM4):032x} \"\n                      f\"{BLUE}xmm5={COL_RESET}0x{em.reg_read(x86ucc.UC_X86_REG_XMM5):032x} \\n\"\n                      f\"  {BLUE}xmm6={COL_RESET}0x{em.reg_read(x86ucc.UC_X86_REG_XMM6):032x} \"\n                      f\"{BLUE}xmm7={COL_RESET}0x{em.reg_read(x86ucc.UC_X86_REG_XMM7):032x} \\n\")\n            else:\n                print(f\"  rax={rax} \"\n                      f\"rbx={rbx} \"\n                      f\"rcx={rcx} \"\n                      f\"rdx={rdx}\\n\"\n                      f\"  rsi={rsi} \"\n                      f\"rdi={rdi} \"\n                      f\"flags=0b{em.reg_read(x86ucc.UC_X86_REG_EFLAGS):012b}\\n\"\n                      f\"  xmm0=0x{em.reg_read(x86ucc.UC_X86_REG_XMM0):032x} \"\n                      f\"xmm1=0x{em.reg_read(x86ucc.UC_X86_REG_XMM1):032x} \\n\"\n                      f\"  xmm2=0x{em.reg_read(x86ucc.UC_X86_REG_XMM2):032x} \"\n                      f\"xmm3=0x{em.reg_read(x86ucc.UC_X86_REG_XMM3):032x} \\n\"\n                      f\"  xmm4=0x{em.reg_read(x86ucc.UC_X86_REG_XMM4):032x} \"\n                      f\"xmm5=0x{em.reg_read(x86ucc.UC_X86_REG_XMM5):032x} \\n\"\n                      f\"  xmm6=0x{em.reg_read(x86ucc.UC_X86_REG_XMM6):032x} \"\n                      f\"xmm7=0x{em.reg_read(x86ucc.UC_X86_REG_XMM7):032x} \\n\")\n\n\n# ==================================================================================================\n# Public: arm64 implementation of the Unicorn Backend\n# ==================================================================================================\nclass ARM64UnicornModel(UnicornModel):\n    \"\"\" Model for arm64 architecture \"\"\"\n\n    def __init__(self,\n                 bases: BaseAddrTuple,\n                 target_desc: TargetDesc,\n                 speculator_cls: Type[UnicornSpeculator],\n                 tracer_cls: Type[UnicornTracer],\n                 interpreter_cls: Type[ExtraInterpreter],\n                 enable_mismatch_check_mode: bool = False) -> None:\n\n        self._architecture = (uc.UC_ARCH_ARM64, uc.UC_MODE_ARM)\n        self._flags_id = armucc.UC_ARM64_REG_NZCV\n\n        self.underflow_pad_values = bytes(SandboxLayout.data_area_size(DataArea.UNDERFLOW_PAD))\n        self.overflow_pad_values = bytes(SandboxLayout.data_area_size(DataArea.OVERFLOW_PAD))\n\n        super().__init__(bases, target_desc, speculator_cls, tracer_cls, interpreter_cls,\n                         enable_mismatch_check_mode)\n\n    def _load_input(self, input_: InputData) -> None:\n        \"\"\"\n        Set the memory and register values in the emulator according to the input object provided.\n        In addition, set the memory permissions for each actor.\n\n        :param input_: Input object containing the memory and register values for each actor.\n        \"\"\"\n\n        # FIXME: dudup this code with x86\n\n        def patch_flags(flags: np.uint64) -> np.uint64:\n            return (flags << np.uint64(28)) % np.uint64(pow(2, 64) - 1)\n\n        def write_area(area: DataArea, actor_id: int, data: bytes) -> None:\n            em.mem_write(self.layout.get_data_addr(area, actor_id), data)\n\n        # shortcuts to save on typing\n        em = self.emulator\n        regs = self._uc_target_desc.usable_registers\n\n        # Initialize memory for each actor:\n        n_actors = self.state.current_test_case().n_actors()\n        init_gpr: List[np.uint64]\n        for actor_id in range(n_actors):\n            input_fragment = input_[actor_id].copy()\n\n            # - initialize overflows with zeroes\n            write_area(DataArea.OVERFLOW_PAD, actor_id, self.overflow_pad_values)\n            write_area(DataArea.UNDERFLOW_PAD, actor_id, self.underflow_pad_values)\n\n            # - sandbox data pages\n            write_area(DataArea.MAIN, actor_id, input_fragment['main'].tobytes())\n            write_area(DataArea.FAULTY, actor_id, input_fragment['faulty'].tobytes())\n\n            # - GPRs\n            # Note: Executor uses the GPR area to initialize EFLAGS, so we need to patch them\n            #      before writing them to the emulator to ensure consistency.\n            input_fragment['gpr'][6] = patch_flags(input_fragment['gpr'][6])\n            # input_fragment['gpr'][7] = np.uint64(self.layout.get_data_addr(DataArea.RSP_INIT, 0))\n            write_area(DataArea.GPR, actor_id, input_fragment['gpr'].tobytes())\n\n            # - SIMD\n            write_area(DataArea.SIMD, actor_id, input_fragment['simd'].tobytes())\n\n            # Save the GPR area of the main actor as it will be used to initialize registers\n            if actor_id == 0:\n                init_gpr = input_fragment['gpr']\n\n        # - initialize GPRs\n        value: np.uint64\n        for i, value in enumerate(init_gpr):\n            em.reg_write(regs[i], int(value))\n\n        # similarly to above, patch reg. values\n        em.reg_write(self._uc_target_desc.flags_register, int(init_gpr[6]))\n        em.reg_write(self._uc_target_desc.sp_register,\n                     self.layout.get_data_addr(DataArea.RSP_INIT, 0))\n        em.reg_write(self._uc_target_desc.actor_base_register,\n                     self.layout.get_data_addr(DataArea.MAIN, 0))\n\n    def print_registers(self, oneline: bool = False) -> None:\n\n        def compressed(val: int) -> str:\n            if self.layout.is_data_addr(val):\n                return f\"base+0x{self.layout.data_addr_to_offset(val):<9x}\"\n            return f\"0x{val:016x}\"\n\n        em = self.emulator\n        x0 = compressed(em.reg_read(armucc.UC_ARM64_REG_X0))  # type: ignore\n        x1 = compressed(em.reg_read(armucc.UC_ARM64_REG_X1))  # type: ignore\n        x2 = compressed(em.reg_read(armucc.UC_ARM64_REG_X2))  # type: ignore\n        x3 = compressed(em.reg_read(armucc.UC_ARM64_REG_X3))  # type: ignore\n        x4 = compressed(em.reg_read(armucc.UC_ARM64_REG_X4))  # type: ignore\n        x5 = compressed(em.reg_read(armucc.UC_ARM64_REG_X5))  # type: ignore\n        flags = f\"{em.reg_read(armucc.UC_ARM64_REG_NZCV) >> 28:04b}\"  # type: ignore\n\n        if not oneline:\n            print(\"\\n\\nRegisters:\")\n            print(f\"x0: {x0}\")\n            print(f\"x1: {x1}\")\n            print(f\"x2: {x2}\")\n            print(f\"x3: {x3}\")\n            print(f\"x4: {x4}\")\n            print(f\"x5: {x5}\")\n        else:\n            if CONF.color:\n                print(f\"  {BLUE}x0={COL_RESET}{x0} \"\n                      f\"{BLUE}x1={COL_RESET}{x1} \"\n                      f\"{BLUE}x2={COL_RESET}{x2}\\n\"\n                      f\"  {BLUE}x3={COL_RESET}{x3} \"\n                      f\"{BLUE}x4={COL_RESET}{x4} \"\n                      f\"{BLUE}x5={COL_RESET}{x5}\\n\"\n                      f\"  {BLUE}flags={COL_RESET}0b{flags}\\n\")\n            else:\n                print(f\"  x0={x0} \"\n                      f\"x1={x1} \"\n                      f\"x2={x2} \"\n                      f\"x3={x3}\\n\"\n                      f\"  x4={x4} \"\n                      f\"x5={x5} \"\n                      f\"flags=0b{flags}\\n\")\n"
  },
  {
    "path": "rvzr/model_unicorn/speculator_abc.py",
    "content": "\"\"\"\nFile: Abstract interface to be implemented by all speculators.\n      For an implementation of concrete speculators, see speculators_*.py files.\n\n      A speculator is a component that modifies the execution process of a test case when it\n      runs on the contract model (e.g., it can emulate misprediction of branches).\n      As such, speculators implement execution clauses of different contracts.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nfrom abc import ABC\nfrom typing import TYPE_CHECKING, List, Final, Tuple\n\nfrom unicorn import UC_MEM_WRITE\n\nfrom ..config import CONF\n\nif TYPE_CHECKING:\n    from unicorn import Uc\n    from .model import UnicornModel\n    from .taint_tracker import UnicornTaintTracker\n    from ..target_desc import TargetDesc, UnicornTargetDesc\n\n_UnicornContext = object\n_InstrAddress = int\n_Flags = int\n_SpecWindow = int\n_Checkpoint = Tuple[_UnicornContext, _InstrAddress, _Flags, _SpecWindow]\n\n_MemoryAddress = int\n_MemoryValue = bytes\n_StoreLogEntry = Tuple[_MemoryAddress, _MemoryValue]\n\n\nclass UnicornSpeculator(ABC):\n    \"\"\"\n    Interface definition that must be implemented by all speculators.\n    as well as implementation of common functionality.\n    \"\"\"\n\n    is_sequential: bool = False\n    \"\"\" Flag indicating if the speculator does *not* actually implement speculation. \"\"\"\n\n    # checkpointing\n    _checkpoints: List[_Checkpoint]\n    _store_logs: List[List[_StoreLogEntry]]\n\n    # speculation control\n    _max_nesting: int = 0\n    _speculation_window: int = 0\n    _max_spec_window: int = 0\n    _in_speculation: bool = False\n\n    # connections to other modules\n    _emulator: Uc\n    _model: Final[UnicornModel]\n    _target_desc: Final[TargetDesc]\n    _uc_target_desc: Final[UnicornTargetDesc]\n    _taint_tracker: UnicornTaintTracker\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__()\n        self._model = model\n        self._taint_tracker = taint_tracker\n        self._target_desc = target_desc\n        self._uc_target_desc = target_desc.uc_target_desc\n        self.reset()\n\n    # ----------------------------------------------------------------------------------------------\n    # Public Interface\n    def in_speculation(self) -> bool:\n        \"\"\" Return whether the model is currently in speculation. \"\"\"\n        return self._in_speculation\n\n    def set_max_nesting(self, max_nesting: int) -> None:\n        \"\"\" Set the maximum nesting level of the model. \"\"\"\n        self._max_nesting = max_nesting\n\n    def nesting(self) -> int:\n        \"\"\" Return the current nesting level of the model. \"\"\"\n        return len(self._checkpoints)\n\n    def reset(self) -> None:\n        \"\"\" Reset the speculator to its initial state. \"\"\"\n        self._emulator = self._model.emulator  # refresh the emulator reference\n        self._checkpoints = []\n        self._store_logs = []\n        self._in_speculation = False\n        self._speculation_window = 0\n        self._max_spec_window = CONF.model_max_spec_window\n\n    def rollback(self) -> int:\n        \"\"\" Rollback the model and its service modules to the last checkpoint. \"\"\"\n        # restore register values\n        state, next_instr, flags, spec_window = self._checkpoints.pop()\n        if not self._checkpoints:\n            self._in_speculation = False\n\n        # restore the speculation state\n        self._emulator.context_restore(state)\n        self._speculation_window = spec_window\n\n        # rollback memory changes\n        mem_changes = self._store_logs.pop()\n        while mem_changes:\n            addr, val = mem_changes.pop()\n            self._emulator.mem_write(addr, val)\n\n        # restore the flags last, to avoid corruption by other operations\n        self._emulator.reg_write(self._uc_target_desc.flags_register, flags)\n\n        # restore the taint tracking\n        self._taint_tracker.rollback()\n\n        # restart without misprediction\n        return next_instr\n\n    def handle_instruction(self, address: int, size: int) -> None:\n        \"\"\"\n        Hook function executed by the speculator on every instruction.\n        Depending on the speculator (i.e., the subclass), it may implement different speculation\n        mechanisms for some instructions (e.g., branch mispredictions).\n        :param address: address of the current instruction\n        :param size: size of the current instruction\n        :return: None\n        \"\"\"\n\n        if self._in_speculation:\n            self._speculation_window += 1\n            # rollback on a serializing instruction\n            if self._model.state.current_instruction.name in self._uc_target_desc.barriers:\n                self._emulator.emu_stop()\n\n            # and on expired speculation window\n            if self._speculation_window > self._max_spec_window:\n                self._emulator.emu_stop()\n\n        self._speculate_instruction(address, size)\n\n    def handle_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        \"\"\"\n        Hook function executed by the speculator on every memory access.\n        Depending on the speculator (i.e., the subclass), it may implement different speculation\n        mechanisms for some memory accesses (e.g., store-to-load forwarding).\n        :param access: type of the memory access (UC_MEM_READ or UC_MEM_WRITE)\n        :param address: address of the memory access\n        :param size: size of the memory access\n        :param value: value of the memory access\n        :return: None\n        \"\"\"\n        # when in speculation, log all changes to memory\n        if access == UC_MEM_WRITE and self._store_logs:\n            prev_value = bytes(self._emulator.mem_read(address, 8))\n            self._store_logs[-1].append((address, prev_value))\n\n        self._speculate_mem_access(access, address, size, value)\n\n    def handle_fault(self, errno: int) -> int:\n        \"\"\"\n        Hook function executed by the speculator on every fault.\n        Depending on the speculator (i.e., the subclass), it may implement different speculation\n        mechanisms for some faults (e.g., Meltdown).\n        :param errno: error number of the fault\n        :return: address of the next speculative instruction; 0 if no speculation\n        \"\"\"\n        return self._speculate_fault(errno)\n\n    # ----------------------------------------------------------------------------------------------\n    # Private Methods\n    def _checkpoint(self, next_instruction_addr: int, include_current_inst: bool = True) -> None:\n        \"\"\"\n        Store a checkpoint for the current state of the model and its service modules.\n        :param next_instruction_addr: address of the next instruction to execute\n        :param include_current_inst: if True, include the effects of the current instruction in the\n                                     checkpoint (used for taint tracking)\n        \"\"\"\n        flags: int = self._emulator.reg_read(self._uc_target_desc.flags_register)  # type: ignore\n        context = self._emulator.context_save()\n        spec_window = self._speculation_window\n        self._checkpoints.append((context, next_instruction_addr, flags, spec_window))\n        self._store_logs.append([])\n        self._in_speculation = True\n        self._taint_tracker.checkpoint(include_current_inst=include_current_inst)\n\n    def _max_nesting_reached(self) -> bool:\n        \"\"\" Check if the maximum nesting level has been reached. \"\"\"\n        return len(self._checkpoints) >= self._max_nesting\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        pass\n\n    def _speculate_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        pass\n\n    def _speculate_fault(self, _: int) -> int:\n        \"\"\"\n        Implement speculation upon a fault. The default implementation does not speculate.\n        :param errno: ID of the fault\n        :return: the address of the first speculative instruction\n                 OR zero if not speculation is triggered\n        \"\"\"\n        return 0\n"
  },
  {
    "path": "rvzr/model_unicorn/speculators_basic.py",
    "content": "\"\"\"\nFile: Collection of simple instruction-based speculators for the Unicorn model.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom typing import TYPE_CHECKING, Dict, Tuple, Callable, Final, Optional\nfrom unicorn import UC_MEM_WRITE\n\nimport unicorn.x86_const as ucc  # type: ignore # no type hints for this library\nimport unicorn.arm64_const as aucc  # type: ignore # no type hints for this library\n\nfrom .speculator_abc import UnicornSpeculator\nfrom ..config import CONF\n\nif TYPE_CHECKING:\n    from .model import UnicornModel\n    from .taint_tracker import UnicornTaintTracker\n    from ..target_desc import TargetDesc\n\nFLAGS_CF = 0b000000000001\nFLAGS_PF = 0b000000000100\nFLAGS_AF = 0b000000010000\nFLAGS_ZF = 0b000001000000\nFLAGS_SF = 0b000010000000\nFLAGS_TF = 0b000100000000\nFLAGS_IF = 0b001000000000\nFLAGS_DF = 0b010000000000\nFLAGS_OF = 0b100000000000\n\nFLAGS_N: Final[int] = 1 << 31\nFLAGS_Z: Final[int] = 1 << 30\nFLAGS_C: Final[int] = 1 << 29\nFLAGS_V: Final[int] = 1 << 28\n\n\nclass SeqSpeculator(UnicornSpeculator):\n    \"\"\"\n    Trivial speculator that does not implement any speculation; that is, it models\n    sequential execution of all instructions\n    \"\"\"\n\n    is_sequential: bool = True\n\n\n_CondBranchFlipper = Callable[[bytearray, int, int], Tuple[bytearray, bool, bool]]\n\n\n# ==================================================================================================\n# Conditional branch prediction (Spectre v1)\n# ==================================================================================================\nclass X86CondSpeculator(UnicornSpeculator):\n    \"\"\"\n    Speculator for conditional branch mispredicitons.\n    Forces all cond. branches to speculatively go into a wrong target\n    \"\"\"\n\n    jumps = {\n        # c - the byte code of the instruction\n        # f - the value of EFLAGS\n        0x70:\n            lambda c, f, r: (c[1:], f & FLAGS_OF != 0, False),  # JO\n        0x71:\n            lambda c, f, r: (c[1:], f & FLAGS_OF == 0, False),  # JNO\n        0x72:\n            lambda c, f, r: (c[1:], f & FLAGS_CF != 0, False),  # JB\n        0x73:\n            lambda c, f, r: (c[1:], f & FLAGS_CF == 0, False),  # JAE\n        0x74:\n            lambda c, f, r: (c[1:], f & FLAGS_ZF != 0, False),  # JZ\n        0x75:\n            lambda c, f, r: (c[1:], f & FLAGS_ZF == 0, False),  # JNZ\n        0x76:\n            lambda c, f, r: (c[1:], f & FLAGS_CF != 0 or f & FLAGS_ZF != 0, False),  # JNA\n        0x77:\n            lambda c, f, r: (c[1:], f & FLAGS_CF == 0 and f & FLAGS_ZF == 0, False),  # JNBE\n        0x78:\n            lambda c, f, r: (c[1:], f & FLAGS_SF != 0, False),  # JS\n        0x79:\n            lambda c, f, r: (c[1:], f & FLAGS_SF == 0, False),  # JNS\n        0x7A:\n            lambda c, f, r: (c[1:], f & FLAGS_PF != 0, False),  # JP\n        0x7B:\n            lambda c, f, r: (c[1:], f & FLAGS_PF == 0, False),  # JPO\n        0x7C:\n            lambda c, f, r: (c[1:], (f & FLAGS_SF == 0) != (f & FLAGS_OF == 0), False),  # JNGE\n        0x7D:\n            lambda c, f, r: (c[1:], (f & FLAGS_SF == 0) == (f & FLAGS_OF == 0), False),  # JNL\n        0x7E:\n            lambda c, f, r: (\n                c[1:],\n                f & FLAGS_ZF != 0 or (f & FLAGS_SF == 0) != (f & FLAGS_OF == 0),\n                False,\n            ),\n        0x7F:\n            lambda c, f, r: (\n                c[1:],\n                f & FLAGS_ZF == 0 and (f & FLAGS_SF == 0) == (f & FLAGS_OF == 0),\n                False,\n            ),\n        0xE0:\n            lambda c, f, r: (c[1:], r != 1 and (f & FLAGS_ZF == 0), True),  # LOOPNE\n        0xE1:\n            lambda c, f, r: (c[1:], r != 1 and (f & FLAGS_ZF != 0), True),  # LOOPE\n        0xE2:\n            lambda c, f, r: (c[1:], r != 1, True),  # LOOP\n        0xE3:\n            lambda c, f, r: (c[1:], r == 0, False),  # J*CXZ\n        0x0F:\n            lambda c, f, r: X86CondSpeculator.multibyte_jmp.get(c[1], (lambda _, __, ___:\n                                                                       ([0], False, False)))\n            (c, f, r),\n    }\n\n    multibyte_jmp: Final[Dict[int, _CondBranchFlipper]] = {\n        0x80:\n            lambda c, f, r: (c[2:], f & FLAGS_OF != 0, False),  # JO\n        0x81:\n            lambda c, f, r: (c[2:], f & FLAGS_OF == 0, False),  # JNO\n        0x82:\n            lambda c, f, r: (c[2:], f & FLAGS_CF != 0, False),  # JB\n        0x83:\n            lambda c, f, r: (c[2:], f & FLAGS_CF == 0, False),  # JAE\n        0x84:\n            lambda c, f, r: (c[2:], f & FLAGS_ZF != 0, False),  # JE\n        0x85:\n            lambda c, f, r: (c[2:], f & FLAGS_ZF == 0, False),  # JNE\n        0x86:\n            lambda c, f, r: (c[2:], f & FLAGS_CF != 0 or f & FLAGS_ZF != 0, False),  # JBE\n        0x87:\n            lambda c, f, r: (c[2:], f & FLAGS_CF == 0 and f & FLAGS_ZF == 0, False),  # JA\n        0x88:\n            lambda c, f, r: (c[2:], f & FLAGS_SF != 0, False),  # JS\n        0x89:\n            lambda c, f, r: (c[2:], f & FLAGS_SF == 0, False),  # JNS\n        0x8A:\n            lambda c, f, r: (c[2:], f & FLAGS_PF != 0, False),  # JP\n        0x8B:\n            lambda c, f, r: (c[2:], f & FLAGS_PF == 0, False),  # JPO\n        0x8C:\n            lambda c, f, r: (c[2:], (f & FLAGS_SF == 0) != (f & FLAGS_OF == 0), False),  # JNGE\n        0x8D:\n            lambda c, f, r: (c[2:], (f & FLAGS_SF == 0) == (f & FLAGS_OF == 0), False),  # JNL\n        0x8E:\n            lambda c, f, r: (\n                c[2:],\n                f & FLAGS_ZF != 0 or (f & FLAGS_SF == 0) != (f & FLAGS_OF == 0),\n                False,\n            ),\n        0x8F:\n            lambda c, f, r: (\n                c[2:],\n                f & FLAGS_ZF == 0 and (f & FLAGS_SF == 0) == (f & FLAGS_OF == 0),\n                False,\n            ),\n    }\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        assert CONF.instruction_set == \"x86-64\"\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        if self._max_nesting_reached():  # reached max spec. window? skip\n            return\n\n        # if the instruction is undefined, Unicorn will return a huge value as size\n        # skip those\n        if size > 15:  # 15 bytes is max instr size on Intel\n            return\n\n        # decode the instruction\n        code: bytearray = self._emulator.mem_read(address, size)\n        flags: int = self._emulator.reg_read(self._uc_target_desc.flags_register)  # type: ignore\n        rcx: int = self._emulator.reg_read(ucc.UC_X86_REG_RCX)  # type: ignore\n        target, will_jump, is_loop = self.decode(code, flags, rcx)\n\n        # not a a cond. jump? ignore\n        if not target:\n            return\n\n        # LOOP instructions must also decrement RCX\n        if is_loop:\n            self._emulator.reg_write(ucc.UC_X86_REG_RCX, rcx - 1)\n\n        # Take a checkpoint\n        next_instr = address + size + target if will_jump else address + size\n        self._checkpoint(next_instr)\n\n        # Simulate misprediction\n        if will_jump:\n            self._emulator.reg_write(ucc.UC_X86_REG_RIP, address + size)\n        else:\n            self._emulator.reg_write(ucc.UC_X86_REG_RIP, address + size + target)\n\n    def decode(self, code: bytearray, flags: int, rcx: int) -> Tuple[int, bool, bool]:\n        \"\"\"\n        Decodes the instruction encoded in `code` and, if it's a conditional jump,\n        returns its expected target, whether it will jump to the target (based\n        on the `flags` value), and whether it is a LOOP instruction\n        \"\"\"\n        calculate_target = \\\n            self.jumps.get(code[0], (lambda _, __, ___: ([0], False, False)))\n        target, will_jump, is_loop = calculate_target(code, flags, rcx)  # type: ignore\n        if len(target) == 1:\n            return target[0], will_jump, is_loop\n        return int.from_bytes(target, byteorder='little', signed=True), will_jump, is_loop\n\n\nclass ARM64CondSpeculator(UnicornSpeculator):\n    \"\"\"\n    Speculator for conditional branch mispredictions on ARM64.\n    Forces all cond. branches to speculatively go into a wrong target\n    \"\"\"\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        assert CONF.instruction_set == \"arm64\"\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        if self._max_nesting_reached():  # reached max spec. window? skip\n            return\n\n        # decode the instruction\n        code: bytearray = self._emulator.mem_read(address, size)\n        flags: int = self._emulator.reg_read(self._uc_target_desc.flags_register)  # type: ignore\n        target_offset, will_jump = self.decode(code, flags)\n\n        # not a a cond. jump? ignore\n        if not target_offset:\n            return\n\n        # Take a checkpoint\n        next_instr = address + size + target_offset if will_jump else address + size\n        self._checkpoint(next_instr)\n\n        # Simulate misprediction\n        target_addr = address + size if will_jump else address + size + target_offset\n        self._emulator.reg_write(self._uc_target_desc.pc_register, target_addr)\n\n    def decode(self, code: bytearray, flags: int) -> Tuple[int, bool]:\n        \"\"\"\n        Decodes the instruction encoded in `code` and, if it's a conditional jump,\n        returns its expected target and whether it will jump to the target (based\n        on the `flags` value).\n        \"\"\"\n        instruction = int.from_bytes(code, byteorder='little')\n        first_byte = instruction >> 24\n        if first_byte == 0x54 and instruction & 0x10 == 0:\n            # B.cond instruction\n            return self._decode_b_cond(instruction, flags)\n\n        if 0xb4 <= first_byte <= 0xb7 or 0x34 <= first_byte <= 0x37:\n            # CBZ/CBNZ/TBZ/TBNZ\n            return self._decode_cb_tb(instruction, first_byte)\n        return (0, False)\n\n    def _decode_b_cond(self, instruction: int, flags: int) -> Tuple[int, bool]:\n        target = self._twos_complement(instruction >> 5, 19)\n        condition = instruction & 0xf\n        n = (flags & FLAGS_N) != 0\n        z = (flags & FLAGS_Z) != 0\n        c = (flags & FLAGS_C) != 0\n        v = (flags & FLAGS_V) != 0\n        # table here is useful:\n        # https://community.arm.com/arm-community-blogs/b/\n        # architectures-and-processors-blog/posts/condition-codes-1-condition-flags-and-codes\n        will_jump = [\n            z,  # 0 = b.eq \"equal\"\n            not z,  # 1 = b.ne \"not equal\"\n            c,  # 2 = b.cs \"carry set\"\n            not c,  # 3 = b.cc \"carry clear\"\n            n,  # 4 = b.mi \"minus\"\n            not n,  # 5 = b.pl \"plus\"\n            v,  # 6 = b.vs \"overflow set\"\n            not v,  # 7 = b.vc \"overflow clear\"\n            c and not z,  # 8 = b.hi \"higher than\"\n            not c or z,  # 9 = b.ls \"lower or same\"\n            n == v,  # a = b.ge \"greater than or equal\"\n            n != v,  # b = b.lt \"less than\"\n            not z and n == v,  # c = b.gt \"greater than\"\n            z or n != v,  # d = b.le \"less than or equal\"\n            True,  # e = b.al \"always\"\n            False,  # f = b.nv \"never\"\n        ][condition]\n        return (target, will_jump)\n\n    def _decode_cb_tb(self, instruction: int, first_byte: int) -> Tuple[int, bool]:\n        # CBZ/CBNZ/TBZ/TBNZ\n        register_index = instruction & 0x1f\n        is_32bit = first_byte >> 4 == 0x3\n\n        register_value: int\n        if register_index < 31:\n            # for some reason UC_ARM64_REG_X29 != UC_ARM64_REG_X0 + 29\n            uc_reg_id = \\\n                (aucc.UC_ARM64_REG_X0 + register_index) if register_index <= 28 else \\\n                (aucc.UC_ARM64_REG_X29 + (register_index - 29))\n\n            register_value = self._emulator.reg_read(uc_reg_id)  # type: ignore\n        elif register_index == 31:\n            # xzr \"zero register\"\n            register_value = 0\n        else:\n            raise ValueError(f\"Invalid register index {register_index} in CBZ/CBNZ/TBZ/TBNZ\")\n\n        if is_32bit:\n            register_value &= 0xffff_ffff\n        if first_byte & 0xf <= 0x5:\n            # CBZ/CBNZ\n            target = self._twos_complement(instruction >> 5, 19)\n            if first_byte & 0xf == 4:\n                # CBZ\n                will_jump = register_value == 0\n            else:\n                # CBNZ\n                will_jump = register_value != 0\n        else:\n            target = self._twos_complement(instruction >> 5, 14)\n            bit_number = (instruction >> 19) & 0x1f\n            if not is_32bit:\n                bit_number += 32\n            bit = register_value & (1 << bit_number)\n            if first_byte & 0xf == 6:\n                # TBZ\n                will_jump = bit == 0\n            else:\n                # TBNZ\n                will_jump = bit != 0\n        return (target, will_jump)\n\n    @staticmethod\n    def _twos_complement(n: int, n_bits: int) -> int:\n        n &= (1 << n_bits) - 1\n        sign_bit = 1 << (n_bits - 1)\n        if n & sign_bit:\n            return n - 2 * sign_bit\n        return n\n\n\n# ==================================================================================================\n# Speculative Store Bypass (Spectre v4)\n# ==================================================================================================\nclass StoreBpasSpeculator(UnicornSpeculator):\n    \"\"\"\n    Speculator for speculative store bypasses.\n    Speculatively skips memory store if it is followed by a load from the same address.\n    \"\"\"\n    _previous_store: Optional[Tuple[int, int, int, int]] = None\n\n    def rollback(self) -> int:\n        # if there are any pending speculative store bypasses, cancel them\n        self._previous_store = None\n        return super().rollback()\n\n    def reset(self) -> None:\n        self._previous_store = None\n        super().reset()\n\n    def _speculate_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        # Since Unicorn does not have post-instruction hooks,\n        # we have to implement it in a dirty way:\n        # Save the information about the store here, but execute all the\n        # contract logic in a hook before the next instruction (see trace_instruction)\n        if access == UC_MEM_WRITE:\n            # check for duplicate calls\n            if self._previous_store is not None:\n                end_addr = address + size\n                prev_addr, prev_size = self._previous_store[0:2]\n                if address >= prev_addr and end_addr <= (prev_addr + prev_size):\n                    prev_val = self._previous_store[3].\\\n                        to_bytes(prev_size, byteorder='little', signed=self._previous_store[3] < 0)\n                    sliced = prev_val[address - prev_addr:end_addr - prev_addr][0]\n                    if sliced == value:\n                        return\n                    raise NotImplementedError(\"Self-overwriting instructions are not supported\")\n                raise NotImplementedError(\"Instructions with multiple stores are not supported\")\n\n            # it's not a duplicate - initiate speculation\n            old_val: int = self._emulator.mem_read(address, size)  # type: ignore\n            self._previous_store = (address, size, old_val, value)\n\n    def _speculate_instruction(self, address: int, _: int) -> None:\n        if self._max_nesting_reached():  # reached max spec. window? skip\n            self._previous_store = None  # clear pending speculation requests\n            return\n\n        if self._previous_store is not None:\n            store_addr = self._previous_store[0]\n            old_value = bytes(self._previous_store[2])\n            new_is_signed = self._previous_store[3] < 0\n            new_value = (self._previous_store[3]). \\\n                to_bytes(self._previous_store[1], byteorder='little', signed=new_is_signed)\n\n            # store a checkpoint (do not include the effects of the current instruction as the\n            # speculation was actually triggered by the previous instruction)\n            self._checkpoint(address, include_current_inst=False)\n\n            # cancel the previous store but preserve its value\n            self._emulator.mem_write(store_addr, old_value)\n            self._store_logs[-1].append((store_addr, new_value))\n        self._previous_store = None\n\n\nclass X86CondBpasSpeculator(X86CondSpeculator, StoreBpasSpeculator):\n    \"\"\"\n    Speculator that combines conditional branch mispredictions and speculative store bypass.\n    \"\"\"\n\n    def _speculate_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        super(StoreBpasSpeculator, self)._speculate_mem_access(access, address, size, value)\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        super(X86CondSpeculator, self)._speculate_instruction(address, size)\n"
  },
  {
    "path": "rvzr/model_unicorn/speculators_fault.py",
    "content": "\"\"\"\nFile: Collection of fault-based (i.e., Meltdown type) speculators for the Unicorn backend.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom abc import ABC, abstractmethod\nfrom typing import TYPE_CHECKING, Set, Tuple, List\nfrom copy import copy\nimport re\n\nfrom unicorn import UC_MEM_WRITE\nimport unicorn.x86_const as ucc  # type: ignore # no type hints for this library\n\nfrom .speculator_abc import UnicornSpeculator\nfrom ..tc_components.instruction import Instruction, RegisterOp, FlagsOp, MemoryOp, ImmediateOp\n\nif TYPE_CHECKING:\n    from ..target_desc import TargetDesc\n    from .model import UnicornModel\n    from .taint_tracker import UnicornTaintTracker\n    from ..tc_components.actor import ActorID\n\n\n# ==================================================================================================\n# Base class for all fault-based speculators\n# ==================================================================================================\nclass FaultSpeculator(UnicornSpeculator, ABC):\n    \"\"\"\n    Common set of functionality for all fault-based speculators.\n    Namely, it:\n    - provides a universal method for identifying if a given fault should trigger speculation\n    - provides a method for configuring the speculation rollback address\n    - records address of the current instruction,\n      which is used by subclasses to determine speculation starting points\n    \"\"\"\n\n    _errno_that_trigger_speculation: Set[int]  # set by subclasses\n    _curr_instruction_addr: int = 0\n\n    def _fault_triggers_speculation(self, errno: int) -> bool:\n        \"\"\"Check if the fault should trigger speculation\"\"\"\n        # we speculate only on a subset of faults\n        if errno not in self._errno_that_trigger_speculation:\n            return False\n\n        # no speculation after the maximum nesting level is reached\n        if self._max_nesting_reached():\n            return False\n        return True\n\n    def _get_rollback_address(self) -> int:\n        return self._model.state.fault_handler_addr\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        self._curr_instruction_addr = address\n\n    def _restore_faulty_page_permissions(self, actor_id: ActorID) -> None:\n        assert (self._model.state.page_permissions\n                is not None), \"Page permissions were not initialized\"\n        org_permissions = self._model.state.page_permissions[actor_id]\n        self._model.set_faulty_area_rw(actor_id, org_permissions[0], org_permissions[1])\n\n\n# ==================================================================================================\n# Microcode assists\n# ==================================================================================================\nclass SequentialAssistSpeculator(FaultSpeculator):\n    \"\"\"Speculator that simulates sequential handling of memory-based microcode assists\"\"\"\n\n    def __init__(\n        self,\n        target_desc: TargetDesc,\n        model: UnicornModel,\n        taint_tracker: UnicornTaintTracker,\n    ) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        self._errno_that_trigger_speculation = {12, 13}\n\n    def _speculate_fault(self, errno: int) -> int:\n        if not self._fault_triggers_speculation(errno):\n            return 0\n\n        # no speculation - simply reset the permissions to permit access\n        self._model.set_faulty_area_rw(self._model.state.current_actor.get_id(), True, True)\n        return self._curr_instruction_addr\n\n\n# ==================================================================================================\n# Simple Out-of-Order Exception Handling\n# ==================================================================================================\nclass UnicornDEH(FaultSpeculator, ABC):\n    \"\"\"\n    Base class for delayed exception handling (DEH) speculators.\n    Models delayed handling in out-of-order CPUs, where an non-data-dependent instructions may\n    be executed before a faulting instruction is retired.\n\n    Example:\n        mov rax, [faulty_addr]  ; load from faulty address (may fault)\n        mov rbx, [non-faulty_addr] ; independent load (may be executed before the fault is handled)\n        mov [some_addr], rax    ; store the loaded value (should be skipped if the load faults)\n    \"\"\"\n\n    _dependencies: Set[str]\n    _dependency_checkpoints: List[Set[str]]\n    _next_instruction_addr: int = 0\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        self._errno_that_trigger_speculation = {6, 10, 12, 13, 21}\n        self._dependencies = set()\n        self._dependency_checkpoints = []\n\n    def _checkpoint(self, next_instruction_addr: int, include_current_inst: bool = True) -> None:\n        self._dependency_checkpoints.append(copy(self._dependencies))\n        return super()._checkpoint(next_instruction_addr, include_current_inst=include_current_inst)\n\n    def rollback(self) -> int:\n        self._dependencies = self._dependency_checkpoints.pop()\n        return super().rollback()\n\n    def _speculate_fault(self, errno: int) -> int:\n        if not self._fault_triggers_speculation(errno):\n            return 0\n\n        # start speculation\n        # we set the rollback address to the end of the testcase\n        # because faults are terminating execution\n        self._checkpoint(self._get_rollback_address())\n\n        # add destinations to the dependency list\n        for op in self._model.state.current_instruction.get_dest_operands(True):\n            if isinstance(op, RegisterOp):\n                self._dependencies.add(self._target_desc.reg_normalized[op.value])\n            elif isinstance(op, FlagsOp):\n                for flag in op.get_flags_by_type(\"write\"):\n                    self._dependencies.add(flag)\n\n        # speculatively skip the faulting instruction\n        if self._model.state.is_exit_addr(self._next_instruction_addr):\n            return 0  # no need for speculation if we're at the end\n\n        self._arm64_emulate_fault_with_post_increment()\n        return self._next_instruction_addr\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        \"\"\"\n        Track instruction dependencies to skip those instructions that are dependent\n        on a faulting instruction\n        \"\"\"\n        # pylint: disable=too-many-branches\n        # FIXME: refactor this method to reduce complexity;\n        # for now, it's left as is, because this contract is not a priority\n        super()._speculate_instruction(address, size)\n\n        # check that the instruction size is correct (may be wrong for invalid instructions)\n        if self._model.state.current_instruction.size() not in [0, size]:\n            size = self._model.state.current_instruction.size()\n        self._next_instruction_addr = address + size\n\n        # reset flag\n        instruction = self._model.state.current_instruction\n\n        # track dependencies only after faults\n        if not self._in_speculation or not self._dependencies:\n            return\n\n        # check if the instruction should be skipped due to a dependency on a faulting instr\n        reg_src_operands = []\n        reg_dest_operands = []\n        address_regs = []\n        for op in instruction.get_all_operands():\n            if isinstance(op, RegisterOp):\n                if op.src:\n                    reg_src_operands.append(self._target_desc.reg_normalized[op.value])\n                if op.dest:\n                    reg_dest_operands.append(self._target_desc.reg_normalized[op.value])\n            elif isinstance(op, MemoryOp):\n                for sub_op in re.split(r\"\\+|-|\\*| \", op.value):\n                    if sub_op and sub_op in self._target_desc.reg_normalized:\n                        normalized = self._target_desc.reg_normalized[sub_op]\n                        reg_src_operands.append(normalized)\n                        address_regs.append(normalized)\n            elif isinstance(op, FlagsOp):\n                reg_src_operands.extend(op.get_flags_by_type(\"read\"))\n                reg_dest_operands.extend(op.get_flags_by_type(\"write\"))\n\n        is_dependent = False\n        is_dependent_addr = False\n        for reg in reg_src_operands:\n            if reg in self._dependencies:\n                is_dependent = True\n                break\n        for reg in address_regs:\n            if reg in self._dependencies:\n                is_dependent_addr = True\n\n        # remove overwritten values from dependencies\n        old_dependencies = list(self._dependencies)  # type cast to force copy\n        for reg in reg_dest_operands:\n            if reg not in reg_src_operands and reg in self._dependencies:\n                self._dependencies.remove(reg)\n\n        if not is_dependent:\n            return\n\n        # update dependencies\n        for reg in reg_dest_operands:\n            self._dependencies.add(reg)\n\n        # Corner cases\n        self._handle_isa_specific_corner_cases(instruction, old_dependencies, reg_dest_operands)\n\n        # special case - many memory operations are implemented as two uops,\n        # and one of them could be expected even if the other is data-dependent\n        # we approximate it by simply not skipping the dependent stores\n        if instruction.has_mem_operand(True) and not is_dependent_addr:\n            return\n\n        # this instruction is dependent on a faulting instruction -> skip it\n        self._emulator.reg_write(ucc.UC_X86_REG_RIP, address + size)\n\n    @abstractmethod\n    def _handle_isa_specific_corner_cases(self, instruction: Instruction,\n                                          old_dependencies: List[str],\n                                          reg_dest_operands: List[str]) -> None:\n        \"\"\"Handle ISA-specific corner cases in dependency tracking\"\"\"\n\n    def _arm64_emulate_fault_with_post_increment(self) -> None:\n        \"\"\" Workaround for ARM64 post-incrementing loads/stores that trigger a page fault\"\"\"\n\n\nclass X86UnicornDEH(UnicornDEH):\n    \"\"\"\n    x86-64 implementation of delayed exception handling (DEH).\n    Extends the base DEH class with x86-specific corner cases, such as:\n    - cmpxchg does not always taint RAX\n    - exchange instruction swaps dependencies\n    - XADD overrides the src taint with the dest taint\n    - zeroing and reset patterns (e.g., xor rax, rax)\n    \"\"\"\n\n    def _handle_isa_specific_corner_cases(self, instruction: Instruction,\n                                          old_dependencies: List[str],\n                                          reg_dest_operands: List[str]) -> None:\n        # pylint: disable=too-many-branches\n\n        # special case 1 - cmpxchg does not always taint RAX\n        name = instruction.name\n        if \"cmpxchg\" in name:\n            dest = instruction.operands[0]\n            if (isinstance(dest, MemoryOp)\n                    or self._target_desc.reg_normalized[dest.value] not in old_dependencies):\n                self._dependencies.remove(self._target_desc.reg_normalized[\"rax\"])\n                flags = instruction.get_flags_operand()\n                assert flags\n                for flag in flags.get_flags_by_type(\"write\"):\n                    self._dependencies.remove(flag)\n            return\n\n        # special case 2 - exchange instruction swaps dependencies\n        if \"xchg\" in name:\n            assert len(instruction.operands) == 2\n            op1, op2 = instruction.operands\n            if isinstance(op1, RegisterOp):\n                # swap dependencies\n                op1_val, op2_val = [self._target_desc.reg_normalized[op.value] for op in [op1, op2]]\n                if op1_val in old_dependencies and op2_val not in old_dependencies:\n                    self._dependencies.remove(op1_val)\n                elif op1_val not in old_dependencies and op2_val in old_dependencies:\n                    self._dependencies.remove(op2_val)\n            else:\n                # memory is never tainted -> override the src dependency\n                op2_val = self._target_desc.reg_normalized[op2.value]\n                if op2_val in old_dependencies:\n                    self._dependencies.remove(op2_val)\n            return\n\n        # special case 3 - XADD overrides the src taint with the dest taint\n        if \"xadd\" in name:\n            assert len(instruction.operands) == 2\n            op1, op2 = instruction.operands\n            if (isinstance(op1, MemoryOp)\n                    or self._target_desc.reg_normalized[op1.value] not in old_dependencies):\n                self._dependencies.remove(self._target_desc.reg_normalized[op2.value])\n            return\n\n        # special case 4 - zeroing and reset patterns\n        if name in [\"sub\", \"lock sub\", \"sbb\", \"lock sbb\", \"xor\", \"lock xor\", \"cmp\"]:\n            assert len(instruction.operands) == 2\n            op1, op2 = instruction.operands\n            if op1.value == op2.value:\n                for reg in reg_dest_operands:\n                    self._dependencies.remove(reg)\n            return\n\n\nclass ARMUnicornDEH(UnicornDEH):\n    \"\"\"\n    ARM64 implementation of delayed exception handling (DEH).\n    Currently, there are no known corner cases for ARM64.\n    \"\"\"\n\n    def _handle_isa_specific_corner_cases(self, instruction: Instruction,\n                                          old_dependencies: List[str],\n                                          reg_dest_operands: List[str]) -> None:\n        pass  # No known corner cases for ARM yet\n\n    def _arm64_emulate_fault_with_post_increment(self) -> None:\n        \"\"\"\n        Workaround for ARM64 handling of faults:\n        If a post-incrementing load/store triggers a page fault,\n        the address register is still incremented by the immediate value.\n\n        E.g., if the instruction is `ldr x0, [x1], #8` and it faults,\n        x1 is still speculatively incremented by 8, even though the load did not complete.\n        \"\"\"\n        instr = self._model.state.current_instruction\n        if \"ldr\" not in instr.name and \"str\" not in instr.name:\n            return  # instruction cannot have post-increment\n\n        # check if the instruction has a post-incrementing operand\n        operands = instr.get_all_operands()\n        if not isinstance(operands[-1], ImmediateOp):\n            return\n\n        # find the register being incremented\n        mem_addr_op = operands[-2]\n        assert isinstance(mem_addr_op, MemoryOp)\n        addr_reg = mem_addr_op.get_base_register()\n        if addr_reg is None:\n            return\n\n        # increment the register\n        increment_str = operands[-1].value\n        increment = int(increment_str[1:]) if increment_str.startswith(\"#\") else int(increment_str)\n        uc_reg = self._target_desc.uc_target_desc.reg_str_to_constant[addr_reg.value]\n        curr_value = int(self._emulator.reg_read(uc_reg))  # type: ignore\n        new_value = curr_value + increment\n        self._emulator.reg_write(uc_reg, new_value)\n\n\n# ==================================================================================================\n# Value-injection Speculation\n# ==================================================================================================\nclass X86UnicornNull(FaultSpeculator):\n    \"\"\"\n    Contract describing zero injection on faults.\n\n    Algorithm:\n    - On a faulting load:\n        * store the checkpoint\n        * overwrite the loaded value with zero\n        * change the permissions on the faulting page to RW\n        * re-execute the instruction\n    - On rollback:\n        * restore the original permissions on the faulting page\n        * rollback the memory and register values\n        * jump to the rollback address\n    \"\"\"\n\n    _curr_load: Tuple[int, int]\n    _pending_re_execution: bool = False\n    _pending_restore_permissions: bool = False\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        self._errno_that_trigger_speculation = {12, 13}\n\n    def reset(self) -> None:\n        if not getattr(self._model, \"state\", None):\n            super().reset()\n            return\n\n        # This contract handles REP instructions incorrectly (it's a known bug)\n        # Explicitly fail if a REP instruction is detected\n        for bb in self._model.state.current_test_case().iter_basic_blocks():\n            for instr in bb:\n                if \"rep\" in instr.name:\n                    raise ValueError(\n                        \"REP instructions are not supported by this contract\\n\"\n                        \"Exclude all REP instructions from the instruction set, or change contract\")\n        super().reset()\n\n    def rollback(self) -> int:\n        actor_id = self._model.state.current_actor.get_id()\n        self._model.set_faulty_area_rw(actor_id, True, True)\n        return super().rollback()\n\n    def _speculate_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        # (this method is called before _speculate_fault)\n\n        if access == UC_MEM_WRITE:\n            return\n        # save load address in case this instruction may fault\n        self._curr_load = (address, size)\n\n    def _speculate_fault(self, errno: int) -> int:\n        # (this method is called after _speculate_mem_access)\n\n        # check if the fault should trigger speculation\n        if not self._fault_triggers_speculation(errno):\n            return 0\n\n        # store a checkpoint\n        self._checkpoint(self._get_rollback_address())\n\n        # inject zero in the load\n        address, size = self._curr_load\n        if address != 0:\n            # log old value before injecting zero value\n            prev_value = bytes(self._emulator.mem_read(address, 8))\n            self._store_logs[-1].append((address, prev_value))\n\n            # inject zeros\n            self._emulator.mem_write(address, bytes([0 for _ in range(size)]))\n\n        # enable access to the faulting page and repeat the instruction\n        self._pending_re_execution = True\n        actor_id = self._model.state.current_actor.get_id()\n        self._model.set_faulty_area_rw(actor_id, True, True)\n        return self._curr_instruction_addr\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        super()._speculate_instruction(address, size)\n\n        # Case 1: this method is called after a fault (i.e., after _speculate_fault)\n        #  -> re-executed the faulting instruction\n        if self._pending_re_execution:\n            self._pending_re_execution = False\n            self._pending_restore_permissions = True\n            self._curr_load = (0, 0)\n            return\n\n        # Case 2: this method is called after the first instruction in speculation\n        # (i.e., after one call of _speculate_instruction)\n        #  -> restore the permissions of the faulting page\n        if self._pending_restore_permissions:\n            self._pending_restore_permissions = False\n            self._restore_faulty_page_permissions(self._model.state.current_actor.get_id())\n            self._curr_load = (0, 0)\n            return\n\n        # Case 3: any other case\n        #  -> Do nothing\n        self._curr_load = (0, 0)\n\n\nclass X86UnicornNullAssist(X86UnicornNull):\n    \"\"\"Variant of X86UnicornNull that does *not* terminate execution after a fault,\n    and instead rolls back to the faulting instruction after speculation, and executes\n     it without a fault.\"\"\"\n\n    def _get_rollback_address(self) -> int:\n        return self._curr_instruction_addr\n\n\nclass X86Meltdown(FaultSpeculator):\n    \"\"\"\n    Loads from the faulty region speculatively return the in-memory value\n    \"\"\"\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        self._errno_that_trigger_speculation = {12, 13}\n\n    def _speculate_fault(self, errno: int) -> int:\n        if not self._fault_triggers_speculation(errno):\n            return 0\n\n        # store a checkpoint\n        self._checkpoint(self._get_rollback_address())\n\n        # remove protection\n        self._model.set_faulty_area_rw(self._model.state.current_actor.get_id(), True, True)\n        return self._curr_instruction_addr\n\n\nclass X86NonCanonicalAddress(FaultSpeculator):\n    \"\"\"\n    Load from non-canonical address\n    \"\"\"\n\n    faulty_instruction_addr: int = -1\n    address_register: int = -1\n    register_value: int = -1\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        self._errno_that_trigger_speculation = {6, 7}\n\n    def _speculate_fault(self, errno: int) -> int:\n        if not self._fault_triggers_speculation(errno):\n            return 0\n\n        self._checkpoint(self._model.state.fault_handler_addr)\n        self.faulty_instruction_addr = self._curr_instruction_addr\n        return self._curr_instruction_addr\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        super()._speculate_instruction(address, size)\n\n        if not self._in_speculation:\n            return\n\n        model = self._model\n        if self.address_register != -1:\n            model.emulator.reg_write(self.address_register, self.register_value)\n            self.address_register = -1\n            return\n\n        if self.faulty_instruction_addr != address:\n            return\n\n        # Fix non-canonical address\n        for mem_op in model.state.current_instruction.get_mem_operands(True):\n            registers = re.split(r\"\\+|-|\\*| \", mem_op.value)\n            if len(registers) > 1:\n                continue\n\n            uc_reg = self._target_desc.uc_target_desc.reg_str_to_constant[registers[0]]\n            load_address: int = model.emulator.reg_read(uc_reg)  # type: ignore\n            is_canonical: bool = (\n                load_address > 0xFFFF800000000000 or load_address < 0x00007FFFFFFFFFFF)\n            if not is_canonical:\n                self.address_register = uc_reg\n                self.register_value = load_address\n\n                if load_address & (1 << 47):  # bit 48 is 1 => high address\n                    load_address = load_address | 0xFFFF800000000000\n                else:  # bit 48 is 0 => low address\n                    load_address = load_address & 0x00007FFFFFFFFFF\n                model.emulator.reg_write(uc_reg, load_address)\n                return\n        return\n\n    def reset(self) -> None:\n        self.faulty_instruction_addr = -1\n        self.address_register = -1\n        self.register_value = -1\n        return super().reset()\n"
  },
  {
    "path": "rvzr/model_unicorn/speculators_vs.py",
    "content": "\"\"\"\nFile: Collection of unknown value speculation speculators for the Unicorn backend.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# FIXME: pylint is disabled for this file because it is currently not maintained\n# pylint: disable=too-many-instance-attributes, too-many-locals\n# pylint: disable=too-many-branches, too-many-statements\n\nfrom __future__ import annotations\n\nfrom abc import ABC\nfrom typing import TYPE_CHECKING, Set, Tuple, List, NamedTuple, Dict, Final\n\nimport re\nfrom copy import copy\n\nfrom unicorn import UC_MEM_WRITE\n\nfrom .speculators_basic import FLAGS_CF, FLAGS_PF, FLAGS_AF, FLAGS_ZF, FLAGS_SF, FLAGS_TF, \\\n    FLAGS_IF, FLAGS_DF, FLAGS_OF\nfrom .speculators_fault import FaultSpeculator, X86NonCanonicalAddress\nfrom ..tc_components.instruction import RegisterOp, FlagsOp, MemoryOp, AgenOp\n\nif TYPE_CHECKING:\n    from ..tc_components.test_case_data import InputData\n    from ..target_desc import TargetDesc\n    from .model import UnicornModel\n    from .taint_tracker import UnicornTaintTracker\n\n\nclass _TaintedValue(NamedTuple):\n    po: int\n    label: int\n    value: int\n\n\nTaint = Set[_TaintedValue]\n\n_FLAG_NAME_TO_BITMASK: Final[Dict[str, int]] = {\n    \"CF\": FLAGS_CF,\n    \"PF\": FLAGS_PF,\n    \"AF\": FLAGS_AF,\n    \"ZF\": FLAGS_ZF,\n    \"SF\": FLAGS_SF,\n    \"TF\": FLAGS_TF,\n    \"IF\": FLAGS_IF,\n    \"DF\": FLAGS_DF,\n    \"OF\": FLAGS_OF\n}\n\n\nclass _VspecBaseSpeculator(FaultSpeculator, ABC):\n    \"\"\"\n    Base class for unknown value speculation, implementing VSOps algorithm.\n\n    The algorithm is described in Section 6 of the paper \"Speculation at Fault: Modeling and Testing\n    Microarchitectural Leakage of CPU Exceptions\" by Hofmann et al.\n    \"\"\"\n    _input_hash: int = 0\n    _full_input_taint: _TaintedValue\n    _reg_taints: Dict[str, Taint]\n    \"\"\" reg_taints: taints of registers \"\"\"\n    _reg_taints_checkpoints: List[Dict[str, Taint]]\n    _mem_taints: Dict[int, Taint]\n    \"\"\" mem_taints: taints of memory locations \"\"\"\n    _mem_taints_checkpoints: List[Dict[int, Taint]]\n    _whole_memory_tainted: bool = False\n    \"\"\" whole_memory_tainted: overapproximation recording whole memory as being corrupted/tainted\"\"\"\n    _whole_memory_tainted_checkpoints: List[bool]\n    _curr_observation: Taint = set()\n    \"\"\" _curr_observation: taints+values that need to be leaked if current instruction is\n        a memory access \"\"\"\n    _curr_mem_load: Tuple[int, int] = (-1, -1)\n    \"\"\" _curr_mem_load: address and size of last memory load (needed in case of exception) \"\"\"\n    _curr_mem_store: Tuple[int, int] = (-1, -1)\n    \"\"\" _curr_mem_store: address and size of last memory store (needed in case of exception) \"\"\"\n    _curr_dest_regs: List[str] = []\n    \"\"\" _curr_dest_regs: current destination registers \"\"\"\n    _curr_dest_regs_sizes: Dict[str, int]\n    \"\"\" curr_dest_regs_sizes: width of current destination registers, i.e., whether only part of\n        register gets overwritten \"\"\"\n    _curr_taint: Taint\n    \"\"\" curr_taint: current taint+values that are propagated from _speculate_instruction()\n        to trace_mem_access() \"\"\"\n    _curr_src_tainted: bool = False\n    \"\"\" remembers if any source operand was tainted in _speculate_instruction \"\"\"\n    _next_instruction_addr: int = 0\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        self._errno_that_trigger_speculation = {6, 7, 12, 13}\n\n        self._reg_taints = {}\n        self._reg_taints_checkpoints = []\n        self._mem_taints = {}\n        self._mem_taints_checkpoints = []\n        self._whole_memory_tainted_checkpoints = []\n        self._curr_dest_regs_sizes = {}\n        self._curr_taint = set()\n        self._full_input_taint = _TaintedValue(0, 0, self._input_hash)\\\n\n        raise NotImplementedError(\"This class and its subclasses are no longer maintained.\"\n                                  \"If you need this functionality, please contact the maintainers\")\n        # NOTE: search for FIXME comments for a list of known issues in this class\n\n    def _load_input(self, input_: InputData) -> None:\n        # FIXME:\n        # _load_input interface no longer exists; this functionality should be moved\n        #    another method (reset() is a good candidate)\n        self._input_hash = hash(input_)\n        self._full_input_taint = _TaintedValue(0, 0, self._input_hash)\n        self._curr_observation = set()\n        self._curr_dest_regs = []\n        self._curr_dest_regs_sizes = {}\n        self._curr_mem_load = (-1, -1)\n        self._curr_mem_store = (-1, -1)\n        self._curr_taint = set()\n        self._curr_src_tainted = False\n        assert len(self._reg_taints) == 0\n        assert len(self._reg_taints_checkpoints) == 0\n        assert len(self._mem_taints) == 0\n        assert len(self._mem_taints_checkpoints) == 0\n        assert not self._whole_memory_tainted\n        assert len(self._whole_memory_tainted_checkpoints) == 0\n        # super()._load_input(input_)\n\n    def _assemble_reg_values(self, regs: Set[str]) -> Tuple[Taint, bool]:\n        \"\"\"\n        Aggregate value of all registers in regs.\n        If register is tainted, use taint instead.\n        Set _curr_src_tainted to true if one of the registers was tainted.\n        Returns set of register values (usable as taints) and Boolean flag\n          to indicate if one of the registers was tainted.\n        \"\"\"\n\n        reg_values = set()\n        reg_values_tainted = False\n\n        for reg in regs:\n            if reg in self._reg_taints:\n                reg_values.update(self._reg_taints[reg])\n                # remember that one of registers was tainted\n                reg_values_tainted = True\n            else:\n                reg_id = self._uc_target_desc.reg_norm_to_constant[reg]\n                reg_value: int = self._emulator.reg_read(reg_id)  # type: ignore\n                # if register is a flag, project flags register on flag\n                if reg in {\"CF\", \"PF\", \"AF\", \"ZF\", \"SF\", \"TF\", \"IF\", \"DF\", \"OF\"}:\n                    reg_value = int((reg_value & _FLAG_NAME_TO_BITMASK[reg]) != 0)\n                pc = self._model.layout.code_addr_to_offset(self._curr_instruction_addr)\n                reg_values.add(_TaintedValue(pc, reg_id, reg_value))\n                print(f\"reg: {reg_id}, value: {reg_value}, pc: {pc}\")\n\n        return reg_values, reg_values_tainted\n\n    def _set_taint(self, reg: str, taint: Taint) -> None:\n        # sets reg to taint, only uses input hash if included in taint\n        if self._full_input_taint in taint:\n            self._reg_taints[reg] = {self._full_input_taint}\n        else:\n            self._reg_taints[reg] = taint\n\n    def _update_reg_taints(self) -> None:\n        \"\"\"\n        update current destination registers according to current taint\n        special cases:\n          1) only lower bits of register are updated, so also keep old taint\n          2) current source is not tainted, but destination is tainted,\n             so update taint of destination with current values of register\n        \"\"\"\n        for reg in self._curr_dest_regs:\n            # check if destination reg is already tainted\n            if reg in self._reg_taints:\n                # check if reg is a register, not a flag, and whether only lower bits are\n                # overwritten if this is the case, we need to keep the old taint of reg\n                if reg in self._curr_dest_regs_sizes and self._curr_dest_regs_sizes[reg] < 64:\n                    new_taint = self._reg_taints[reg] | self._curr_taint\n                    self._set_taint(reg, new_taint)\n                # else, old taint is overwritten if the source is currently tainted\n                elif self._curr_src_tainted:\n                    self._set_taint(reg, self._curr_taint)\n                # if source is not tainted and destination is overwritten, remove old taint\n                else:\n                    self._reg_taints.pop(reg, None)\n            # if destination is not tainted already, only need to propagate source taints\n            elif self._curr_src_tainted:\n                # check if reg is a register, not a flag, and whether only lower bits are\n                # overwritten if yes, then keep value currently in register as taint\n                if reg in self._curr_dest_regs_sizes and self._curr_dest_regs_sizes[reg] < 64:\n                    reg_id = self._uc_target_desc.reg_norm_to_constant[reg]\n                    reg_value: int = self._emulator.reg_read(reg_id)  # type: ignore\n                    pc = self._model.layout.code_addr_to_offset(self._curr_instruction_addr)\n                    new_taint = {_TaintedValue(pc, reg_id, reg_value)} | self._curr_taint\n                    self._set_taint(reg, new_taint)\n                # if not, just set current taint as taint of reg\n                else:\n                    self._set_taint(reg, self._curr_taint)\n\n    def _get_curr_load_taint(self) -> _TaintedValue:\n        address = self._curr_mem_load[0]\n        size = self._curr_mem_load[1]\n        mem_value = self._emulator.mem_read(address, size)\n        mem_value_int = int.from_bytes(mem_value, 'little')\n        pc = self._model.layout.code_addr_to_offset(self._curr_instruction_addr)\n        return _TaintedValue(pc, address, mem_value_int)\n\n    def _speculate_fault(self, errno: int) -> int:\n        if not self._fault_triggers_speculation(errno):\n            return 0\n\n        # start speculation\n        # set the rollback address\n        self._checkpoint(self._get_rollback_address())\n\n        # only collect new taints if none of the src operands in the faulting instruction are\n        # tainted if they are, the taints have been propagated correctly already,code_start\n        # so just ignore fault\n        if not self._curr_src_tainted:\n\n            # collect registers occurring in src and destination operands\n            # src_regs = src registers occurring outside memory load\n            # dest_regs = dest registers occurring outside memory store\n            # mem_src_regs = src registers occurring as part of address\n            # mem_dest_regs = dest registers occurring as part of store\n            src_regs = set()\n            for op in self._model.state.current_instruction.get_all_operands():\n                if isinstance(op, RegisterOp):\n                    if op.src:\n                        op_normalized = self._target_desc.reg_normalized[op.value]\n                        src_regs.add(op_normalized)\n                        # src_regs_sizes[op_normalized] = op.width\n                    if op.dest:\n                        op_normalized = self._target_desc.reg_normalized[op.value]\n                        self._curr_dest_regs.append(op_normalized)\n                        self._curr_dest_regs_sizes[op_normalized] = op.width\n                elif isinstance(op, FlagsOp):\n                    src_regs.update(op.get_flags_by_type('read'))\n                    self._curr_dest_regs.extend(op.get_flags_by_type('write'))\n\n            # source_values = evaluated load address + values of src regs\n            # these are all the values the faulting instruction depends on\n            self._curr_taint, _ = self._assemble_reg_values(src_regs)\n\n            if self._model.state.current_instruction.has_read():\n                self._curr_taint.add(self._get_curr_load_taint())\n\n            if self._model.state.current_instruction.has_write():\n                address = self._curr_mem_store[0]\n                size = self._curr_mem_store[1]\n                for i in range(size):\n                    self._mem_taints[address + i] = self._curr_taint\n\n            # need to set _curr_src_tainted to make update_reg_taints call work\n            self._curr_src_tainted = True\n            self._update_reg_taints()\n\n        return self._get_next_instruction()\n\n    def _get_next_instruction(self) -> int:\n        # speculatively skip the faulting instruction\n        if self._model.state.is_exit_addr(self._next_instruction_addr):\n            return 0  # no need for speculation if we're at the end\n        return self._next_instruction_addr\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        \"\"\"\n        Track how taints move through system and produce correct observations.\n        \"\"\"\n        # check that the instruction size is correct (may be wrong for invalid instructions)\n        if self._model.state.current_instruction.size() not in [0, size]:\n            size = self._model.state.current_instruction.size()\n        self._next_instruction_addr = address + size\n\n        # print('current taints:', self.reg_taints, self.mem_taints)\n        # print('current instruction:', self._model.state.current_instruction)\n\n        # reset observation set and rvzr/dest registers\n        # this must happen before we check if we can skip, otherwise trace_mem_access might\n        # use old values\n        self._curr_observation = set()\n        self._curr_taint = set()\n        self._curr_dest_regs = []\n        self._curr_dest_regs_sizes = {}\n        self._curr_src_tainted = False\n        # might be needed when contract is refined recording which part of register is tainted\n        # src_regs_sizes = dict()\n\n        # track taints only after faults with non-empty taints\n        if not self._in_speculation or (not self._reg_taints and not self._mem_taints):\n            return\n\n        src_regs = set()\n        mem_src_regs = set()\n        mem_dest_regs = set()\n\n        # assemble source and destination registers of instruction\n        # distinguish between normal registers and registers used in memory access\n        # some code duplication, with method _speculate_fault()\n        for op in self._model.state.current_instruction.get_all_operands():\n            if isinstance(op, RegisterOp):\n                if op.src:\n                    op_normalized = self._target_desc.reg_normalized[op.value]\n                    src_regs.add(op_normalized)\n                    # src_regs_sizes[op_normalized] = op.width\n                if op.dest:\n                    op_normalized = self._target_desc.reg_normalized[op.value]\n                    self._curr_dest_regs.append(op_normalized)\n                    self._curr_dest_regs_sizes[op_normalized] = op.width\n            elif isinstance(op, MemoryOp):\n                for sub_op in re.split(r'\\+|-|\\*| ', op.value):\n                    if sub_op and sub_op in self._target_desc.reg_normalized:\n                        normalized = self._target_desc.reg_normalized[sub_op]\n                        if op.src:\n                            mem_src_regs.add(normalized)\n                        if op.dest:\n                            mem_dest_regs.add(normalized)\n            elif isinstance(op, FlagsOp):\n                # print('read flags:', op.get_flags_by_type('read'))\n                # print('write flags:', op.get_flags_by_type('write'))\n                src_regs.update(op.get_flags_by_type('read'))\n                self._curr_dest_regs.extend(op.get_flags_by_type('write'))\n            elif isinstance(op, AgenOp):\n                assert self._model.state.current_instruction.name == \"lea\"\n                assert op.src\n                for sub_op in re.split(r'\\[|\\]|\\+|-|\\*| ', op.value):\n                    if sub_op and sub_op in self._target_desc.reg_normalized:\n                        normalized = self._target_desc.reg_normalized[sub_op]\n                        src_regs.add(normalized)\n\n        # assemble values of memory dest registers. if tainted, use taint instead\n        mem_dest_reg_values, _ = self._assemble_reg_values(mem_dest_regs)\n\n        # check if instruction attempted store using tainted register\n        #     => location of store unknown\n        tainted_mem_dest_regs = mem_dest_regs & self._reg_taints.keys()\n        if tainted_mem_dest_regs:\n            assert self._model.state.current_instruction.has_write()\n            # record observation of store\n            # leaks taint if tainted register is used\n            self._curr_observation = self._curr_observation | mem_dest_reg_values\n            # as destination is not known, whole memory is tainted (implicitly with input hash)\n            self._whole_memory_tainted = True\n            # TODO: can we write to registers and memory within one instruction? if not, return\n            # if yes, other destination registers might get tainted, so continue\n\n        # assemble values of memory src registers. if tainted, use taint instead\n        mem_src_reg_values, _ = self._assemble_reg_values(mem_src_regs)\n\n        # check if instruction attempted load using tainted register\n        #     => location of load unknown\n        tainted_mem_src_regs = mem_src_regs & self._reg_taints.keys()\n\n        if tainted_mem_src_regs and not self._model.state.current_instruction.name == \"lea\":\n            assert self._model.state.current_instruction.has_read()\n            # record observation of load\n            # leaks taint if tainted register is used\n            self._curr_observation = self._curr_observation | mem_src_reg_values\n            # load from tainted value returns content of unknown address\n            #     => taint dest registers with input hash (represents full architectural state)\n            # remember current taint in case store address needs to be tainted in trace_mem_access()\n            self._curr_taint = {self._full_input_taint}\n            for reg in self._curr_dest_regs:\n                self._reg_taints[reg] = self._curr_taint\n            # remember that instruction depended on tainted operand\n            self._curr_src_tainted = True\n            # all dest regs are tainted with maximal taint, we can return\n            return\n\n        # assemble value of all src regs, use taint if tainted\n        self._curr_taint, self._curr_src_tainted = self._assemble_reg_values(src_regs)\n        self._update_reg_taints()\n\n    def _speculate_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        # remember last address and size in case of exception\n        if access != UC_MEM_WRITE:\n            self._curr_mem_load = (address, size)\n        else:\n            self._curr_mem_store = (address, size)\n\n        if not self._in_speculation:\n            # FIXME: this branch should enable/disable tracing via self._model.tracer.enable_tracing\n            return\n\n        mem_value = self._model.emulator.mem_read(address, size)\n\n        if access != UC_MEM_WRITE:\n            # for loads, check if address is tainted\n            # Test if any address in the range of address+size is tainted\n            is_tainted: bool = False\n            taints = set()\n            for i in range(size):\n                if address + i in self._mem_taints:\n                    is_tainted = True\n                    taints.update(self._mem_taints[address + i])\n\n            # add address taint to current taint\n            if is_tainted:\n                self._curr_taint.update(taints)\n            elif self._whole_memory_tainted:\n                self._curr_taint.add(self._full_input_taint)\n\n            if is_tainted or self._whole_memory_tainted:\n                # remember that instruction used tainted src value and update taint of dest\n                # registers with address taint\n                self._curr_src_tainted = True\n                self._update_reg_taints()\n            else:\n                # if address itself is not tainted, value stored at address to current taint\n                # and potentially add to taints\n                mem_value_int = int.from_bytes(mem_value, 'little')\n                pc = self._model.layout.code_addr_to_offset(self._curr_instruction_addr)\n                self._curr_taint.add(_TaintedValue(pc, address, mem_value_int))\n                self._update_reg_taints()\n\n        if access == UC_MEM_WRITE:\n            # check if any src operand was tainted (memory location or register)\n            if not self._curr_src_tainted:\n                # if there is no current taint, remove possible taint from current address range\n                for i in range(size):\n                    self._mem_taints.pop(address + i, None)\n            # if src was tainted, add current taint to current address range\n            #     check if whole memory is already tainted, then nothing has to be done\n            elif not self._whole_memory_tainted:\n                for i in range(size):\n                    self._mem_taints[address + i] = self._curr_taint\n\n        # check if the memory access creates a tainted observation\n        if self._curr_observation:\n            # if current observation contains full architectural state info, then only leak the hash\n            if self._full_input_taint in self._curr_observation:\n                self._curr_observation = {self._full_input_taint}\n            observation_list = list(self._curr_observation)\n            observation_list.sort()\n            # print('leaking observation', observation_list)\n            # observation_hash = hash(tuple(observation_list))\n            # just append hash to trace, don't do normal memory access\n            # FIXME: this should be replaced with a public call to the tracer\n            # self._model.tracer._add_dependencies_to_trace(observation_hash)\n        # if not, do normal memory access\n        else:\n            pass\n            # FaultSpeculator.trace_mem_access(emulator, access, address, size, value, model)\n\n    def _checkpoint(self, next_instruction_addr: int, include_current_inst: bool = True) -> None:\n        self._reg_taints_checkpoints.append(copy(self._reg_taints))\n        self._mem_taints_checkpoints.append(copy(self._mem_taints))\n        self._whole_memory_tainted_checkpoints.append(copy(self._whole_memory_tainted))\n        return super()._checkpoint(next_instruction_addr, include_current_inst=include_current_inst)\n\n    def rollback(self) -> int:\n        self._reg_taints = self._reg_taints_checkpoints.pop()\n        self._mem_taints = self._mem_taints_checkpoints.pop()\n        self._whole_memory_tainted = self._whole_memory_tainted_checkpoints.pop()\n        return super().rollback()\n\n    def _get_rollback_address(self) -> int:\n        # faults end program execution\n        return self._model.state.fault_handler_addr\n\n\nclass VspecDIVSpeculator(_VspecBaseSpeculator):\n    \"\"\" Operand value speculation on division errors \"\"\"\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        # DIV exceptions only\n        self._errno_that_trigger_speculation = {21}\n\n\nclass VspecMemoryFaultsSpeculator(_VspecBaseSpeculator):\n    \"\"\" Operand value  speculation on page faults \"\"\"\n\n    pending_restore_protection: bool = False\n    pending_re_execution: bool = False\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        # Page faults and other memory errors\n        self._errno_that_trigger_speculation = {6, 7, 12, 13}\n\n    def _get_curr_load_taint(self) -> _TaintedValue:\n        # The loaded value is undefined for faulting loads,\n        # hence the memory value should not be included in dependencies\n        load_addr = self._curr_mem_load[0]\n        pc = self._model.layout.code_addr_to_offset(self._curr_instruction_addr)\n        return _TaintedValue(pc, load_addr, 0)\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        if self.pending_restore_protection:\n            self.pending_restore_protection = False\n            # FIXME: this is outdated;\n            # see speculator_faults.py:X86UnicornNull for a maintained implementation\n            # of a similar algorithm\n            # aid = self._model.state.current_actor.get_id()\n            # if self.rw_forbidden[aid]:\n            #     self._model.set_faulty_area_rw(self._model.state.current_actor.get_id(), False,\n            #                                    False)\n            # elif self.w_forbidden[aid]:\n            #     self._model.set_faulty_area_rw(self._model.state.current_actor.get_id(), True,\n            #                                    False)\n        elif self.pending_re_execution:\n            self.pending_re_execution = False\n            self.pending_restore_protection = True\n        super()._speculate_instruction(address, size)\n\n    def _get_next_instruction(self) -> int:\n        if self._model.state.is_exit_addr(self._next_instruction_addr):\n            return 0  # no need for speculation if we're at the end\n\n        # FIXME: uses outdated interfaces\n        # aid = self.current_actor.get_id()\n        # if self.pending_fault == UC_ERR_WRITE_PROT and self.w_forbidden[aid]:\n        #     # remove protection\n        #     self._model.set_faulty_area_rw(self.current_actor.get_id(), True, True)\n        #     self.pending_re_execution = True\n        #     return self._curr_instruction_addr\n        return self._next_instruction_addr\n\n\nclass VspecMemoryAssistsSpeculator(VspecMemoryFaultsSpeculator):\n    \"\"\" Operand value  speculation on page faults with memory assists \"\"\"\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        self._errno_that_trigger_speculation = {12, 13}\n\n    def rollback(self) -> int:\n        next_instruction = super().rollback()\n        if not self._in_speculation:\n            # remove protection after the assists has completed\n            self._model.set_faulty_area_rw(self._model.state.current_actor.get_id(), True, True)\n\n        return next_instruction\n\n    def _get_rollback_address(self) -> int:\n        if self._in_speculation:\n            return self._model.state.fault_handler_addr\n        return self._curr_instruction_addr\n\n\nclass VspecGPSpeculator(_VspecBaseSpeculator, X86NonCanonicalAddress):\n    \"\"\" Operand value  speculation on General Protection Faults \"\"\"\n\n    address_register: int\n    register_value: int\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        self._errno_that_trigger_speculation.update([6, 7])\n\n    # def _speculate_fault(self, errno: int) -> int:\n    #     if not self._fault_triggers_speculation(errno):\n    #         return 0\n\n    #     self._checkpoint(self._model.state.fault_handler_addr)\n    #     self.faulty_instruction_addr = self._curr_instruction_addr\n    #     return self._curr_instruction_addr\n\n    def _speculate_fault(self, errno: int) -> int:\n        if not self._fault_triggers_speculation(errno):\n            return 0\n\n        # only collect new taints if none of the src operands in the faulting instruction are\n        # tainted if they are, the taints have been propagated correctly already,code_start\n        # so just ignore fault\n        if not self._curr_src_tainted:\n\n            # collect registers occurring in src and destination operands\n            # src_regs = src registers occurring outside memory load\n            # dest_regs = dest registers occurring outside memory store\n            # mem_src_regs = src registers occurring as part of address\n            # mem_dest_regs = dest registers occurring as part of store\n            src_regs = set()\n            for op in self._model.state.current_instruction.get_all_operands():\n                if isinstance(op, RegisterOp):\n                    if op.src:\n                        op_normalized = self._target_desc.reg_normalized[op.value]\n                        src_regs.add(op_normalized)\n                        # src_regs_sizes[op_normalized] = op.width\n                    if op.dest:\n                        op_normalized = self._target_desc.reg_normalized[op.value]\n                        self._curr_dest_regs.append(op_normalized)\n                        self._curr_dest_regs_sizes[op_normalized] = op.width\n                elif isinstance(op, FlagsOp):\n                    src_regs.update(op.get_flags_by_type('read'))\n                    self._curr_dest_regs.extend(op.get_flags_by_type('write'))\n\n            # source_values = evaluated load address + values of src regs\n            # these are all the values the faulting instruction depends on\n            self._curr_taint, _ = self._assemble_reg_values(src_regs)\n\n            if self._model.state.current_instruction.has_read():\n                address = self._curr_mem_load[0]\n                address = self._noncanonical_to_canonical(address)\n                size = self._curr_mem_load[1]\n                mem_value = self._emulator.mem_read(address, size)\n                mem_value_int = int.from_bytes(mem_value, 'little')\n                pc = self._model.layout.code_addr_to_offset(self._curr_instruction_addr)\n                self._curr_taint.add(_TaintedValue(pc, address, mem_value_int))\n\n            if self._model.state.current_instruction.has_write():\n                address = self._curr_mem_store[0]\n                address = self._noncanonical_to_canonical(address)\n                size = self._curr_mem_store[1]\n                for i in range(size):\n                    self._mem_taints[address + i] = self._curr_taint\n\n            # need to set _curr_src_tainted to make update_reg_taints call work\n            self._curr_src_tainted = True\n            self._update_reg_taints()\n\n        # speculatively skip the faulting instruction\n        return self._curr_instruction_addr\n\n    def _speculate_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        if self._curr_instruction_addr == self.faulty_instruction_addr:\n            if access != UC_MEM_WRITE:\n                self._curr_mem_load = (address, size)\n            else:\n                self._curr_mem_store = (address, size)\n            self._speculate_fault(6)\n        super()._speculate_mem_access(access, address, size, value)\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        super(X86NonCanonicalAddress, self)._speculate_instruction(address, size)\n        if address != self.faulty_instruction_addr:\n            super(_VspecBaseSpeculator, self)._speculate_instruction(address, size)\n\n    def _noncanonical_to_canonical(self, address: int) -> int:\n        if address & (1 << 47):  # bit 48 is 1 => high address\n            address = address | 0xFFFF800000000000\n        else:  # bit 48 is 0 => low address\n            address = address & 0x00007FFFFFFFFFF\n        return address\n\n    def _get_rollback_address(self) -> int:\n        return self._model.state.fault_handler_addr\n\n    def reset(self) -> None:\n        self.faulty_instruction_addr = -1\n        self.address_register = -1\n        self.register_value = -1\n        return super().reset()\n\n\nclass VspecAllSpeculator(_VspecBaseSpeculator):\n    \"\"\"\n    Most permissive contract.\n    Uses vspec-unknown contract but destination operands in case of\n    exception depends on full architectural state (= on full input)\n    instead of value of src operands.\n    \"\"\"\n\n    def _speculate_fault(self, errno: int) -> int:\n        if not self._fault_triggers_speculation(errno):\n            return 0\n\n        # start speculation\n        # store a checkpoint\n        self._checkpoint(self._get_rollback_address())\n\n        # only collect new taints if none of the src operands in the faulting instruction are\n        # tainted if they are, the taints have been propagated correctly already,\n        # so just ignore fault\n        if not self._curr_src_tainted:\n\n            for op in self._model.state.current_instruction.get_all_operands():\n                if isinstance(op, RegisterOp):\n                    if op.dest:\n                        self._curr_dest_regs.append(self._target_desc.reg_normalized[op.value])\n                elif isinstance(op, FlagsOp):\n                    self._curr_dest_regs.extend(op.get_flags_by_type('write'))\n\n            if self._model.state.current_instruction.has_write():\n                address = self._curr_mem_store[0]\n                size = self._curr_mem_store[1]\n                for i in range(size):\n                    self._mem_taints[address + i] = {self._full_input_taint}\n\n            # taint destination registers with hash of full input (represents architectural state)\n            for reg in self._curr_dest_regs:\n                self._reg_taints[reg] = {self._full_input_taint}\n\n        return self._get_next_instruction()\n\n\nclass VspecAllDIVSpeculator(VspecAllSpeculator):\n    \"\"\" Any-value speculation on division errors \"\"\"\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        # DIV exceptions only\n        self._errno_that_trigger_speculation = {21}\n\n\nclass VspecAllMemoryFaultsSpeculator(VspecAllSpeculator):\n    \"\"\" Any-value speculation on page faults \"\"\"\n\n    pending_restore_protection: bool = False\n    pending_re_execution: bool = False\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        # Page faults and other memory errors\n        self._errno_that_trigger_speculation = {6, 7, 12, 13}\n\n    def _speculate_instruction(self, address: int, size: int) -> None:\n        if self.pending_restore_protection:\n            self.pending_restore_protection = False\n            # FIXME: this is outdated;\n            # see speculator_faults.py:X86UnicornNull for a maintained implementation\n            # of a similar algorithm\n            # aid = self._model.state.current_actor.get_id()\n            # if self.rw_forbidden[aid]:\n            #     self._model.set_faulty_area_rw(self._model.state.current_actor.get_id(), False,\n            #                                    False)\n            # elif self.w_forbidden[aid]:\n            #     self._model.set_faulty_area_rw(self._model.state.current_actor.get_id(), True,\n            #                                    False)\n        elif self.pending_re_execution:\n            self.pending_re_execution = False\n            self.pending_restore_protection = True\n            return\n        super()._speculate_instruction(address, size)\n\n    def _get_next_instruction(self) -> int:\n        if self._model.state.is_exit_addr(self._next_instruction_addr):\n            return 0  # no need for speculation if we're at the end\n\n        # FIXME: uses outdated interfaces\n        # aid = self.current_actor.get_id()\n        # if self.pending_fault == UC_ERR_WRITE_PROT and self.w_forbidden[aid]:\n        #     # remove protection\n        #     self._model.set_faulty_area_rw(self.current_actor.get_id(), True, True)\n        #     self.pending_re_execution = True\n        #     return self._curr_instruction_addr\n        return self._next_instruction_addr\n\n\nclass VspecAllMemoryAssistsSpeculator(VspecAllSpeculator):\n    \"\"\" Any-value speculation on A/D-bit microcode assists (MDS style) \"\"\"\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        self._errno_that_trigger_speculation = {12, 13}\n\n    def rollback(self) -> int:\n        next_instruction = super().rollback()\n        if not self._in_speculation:\n            # remove protection after the assists has completed\n            self._model.set_faulty_area_rw(self._model.state.current_actor.get_id(), True, True)\n        return next_instruction\n\n    def _get_rollback_address(self) -> int:\n        if self._in_speculation:\n            return self._model.state.fault_handler_addr\n        return self._curr_instruction_addr\n"
  },
  {
    "path": "rvzr/model_unicorn/taint_tracker.py",
    "content": "\"\"\"\nFile: Taint tracking implementation for the Unicorn model\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nimport copy\nimport re\nfrom typing import List, Optional, Set, Dict, TYPE_CHECKING, Literal, Final\nfrom typing_extensions import assert_never\n\nfrom ..tc_components.instruction import Instruction, RegisterOp, FlagsOp, \\\n    MemoryOp, AgenOp, ImmediateOp, LabelOp, CondOp\nfrom ..tc_components.test_case_data import InputTaint\nfrom ..target_desc import TargetDesc\nfrom ..sandbox import SandboxLayout, DataArea\nfrom ..config import CONF\n\nif TYPE_CHECKING:\n    from ..target_desc import UnicornTargetDesc\n    from ..sandbox import BaseAddrTuple, DataAddr\n\nTAINTED_VALUE_TYPE = Literal[\"pc\", \"mem\", \"ld_val\"]\n_ARCH_INITIAL_OBSERVATIONS_X86_64 = [\n    \"A\", \"B\", \"C\", \"D\", \"SI\", \"DI\", \"RSP\", \"CF\", \"PF\", \"AF\", \"ZF\", \"SF\", \"TF\", \"IF\", \"DF\", \"OF\",\n    \"AC\"\n]\n_ARCH_INITIAL_OBSERVATIONS_ARM64 = [\"R0\", \"R1\", \"R2\", \"R3\", \"R4\", \"R5\", \"N\", \"Z\", \"C\", \"V\"]\n\n\n# ==================================================================================================\n# Public Interface: Taint Tracker\n# ==================================================================================================\nclass UnicornTaintTracker:\n    \"\"\"\n    Tracking of the input data that impacts contract traces.\n\n    The algorithm is as follows:\n    - start_instruction: get the static source and destination operands of the instruction\n    - track_memory_access: get dynamic source and destination memory addresses\n    - taint: collect the labels (register names or mem. addresses) that are\n      exposed by this instruction in the contract trace\n    - finalize_instruction:\n      1. propagate the dependencies of the source operands to the destination operands\n      2. update the list of tainted labels with the dependencies of the labels\n         collected by taint_* methods\n    - get_taint: produce an InputTaint object based on the all tainted labels\n    \"\"\"\n    _enable_tracking: bool = True\n    _tracking_in_progress: bool = False\n\n    _initial_observations: List[str]\n    _data_start: Final[DataAddr]\n    _uc_target_desc: Final[UnicornTargetDesc]\n    _target_desc: Final[TargetDesc]\n\n    _checkpoints: List[_Dependencies]\n    _tainted_labels: Set[str]\n    _pending_taint: Set[str]\n\n    _instruction: Optional[_TrackedInstruction] = None\n    _dependencies: _Dependencies\n\n    def __init__(self, bases: BaseAddrTuple, target_desc: TargetDesc):\n        assert CONF.instruction_set in [\"x86-64\", \"arm64\"], \\\n               \"Taint tracking is only supported for x86_64 and arm64\"\n\n        self._data_start = bases[0]\n        self._target_desc = target_desc\n        self._uc_target_desc = target_desc.uc_target_desc\n\n        # Certain types of contracts have predefined observations\n        if CONF.contract_observation_clause in ('ctr', 'arch'):\n            if CONF.instruction_set == \"x86-64\":\n                self._initial_observations = _ARCH_INITIAL_OBSERVATIONS_X86_64\n            elif CONF.instruction_set == \"arm64\":\n                self._initial_observations = _ARCH_INITIAL_OBSERVATIONS_ARM64\n        else:\n            self._initial_observations = []\n\n        self.reset()\n        self._tracking_in_progress = False\n\n    # ----------------------------------------------------------------------------------------------\n    # State management methods\n    def set_enable_tracking(self, enable: bool) -> None:\n        \"\"\" Enable or disable the taint tracking \"\"\"\n        assert self._tracking_in_progress is False, \\\n            \"Cannot change tracking mode before get_taint() is called\"\n        self._enable_tracking = enable\n\n    def reset(self) -> None:\n        \"\"\" Reset the taint tracker to its initial state \"\"\"\n        self._checkpoints = []\n        self._tainted_labels = set(self._initial_observations)\n        self._pending_taint = set()\n        self._instruction = None\n        self._dependencies = _Dependencies()\n        self._tracking_in_progress = True\n\n    def checkpoint(self, include_current_inst: bool) -> None:\n        \"\"\"\n        Save the current state of the taint tracker\n        :param include_current_inst: if True, include the current instruction in the checkpoint\n        \"\"\"\n        if not self._enable_tracking:\n            return\n\n        if include_current_inst and self._instruction is not None:\n            self._finalize_instruction()\n        self._checkpoints.append(copy.deepcopy(self._dependencies))\n\n    def rollback(self) -> None:\n        \"\"\"\n        Restore the state of the taint tracker from the top-most checkpoint\n        :raises AssertionError: if there are no more checkpoints\n        \"\"\"\n        if not self._enable_tracking:\n            return\n\n        assert self._checkpoints, \"There are no more checkpoints\"\n        if self._instruction is not None:\n            self._finalize_instruction()\n        self._dependencies = copy.deepcopy(self._checkpoints.pop())\n\n    # ----------------------------------------------------------------------------------------------\n    # Dependency propagation methods\n    def track_instruction(self, instruction: Instruction) -> None:\n        \"\"\"\n        Parse instruction and record its static source and destination operands.\n        Static means the operands that we can identify without executing the instruction.\n        The remaining dynamic operands are collected by track_* methods.\n        :param instruction: the instruction to be parsed\n        \"\"\"\n        if not self._enable_tracking:\n            return\n\n        # make sure that the previous instruction is finalized\n        if self._instruction:\n            self._finalize_instruction()\n\n        # restart the tracking\n        # print(\"-----------------------------------\")\n        self._instruction = _TrackedInstruction(instruction)\n        self._instruction.parse_static_operands(self._target_desc.reg_normalized)\n        self._pending_taint = set()\n\n        # overwrite stale flag dependencies\n        # FIXME: this feels like it should be in _finalize_instruction?\n        flag_op = self._instruction.inst.get_flags_operand()\n        if flag_op:\n            for flag_label in flag_op.get_flags_by_type('overwrite'):\n                self._dependencies.flag[flag_label] = {flag_label}\n\n    def track_memory_access(self, address: int, size: int, is_write: bool) -> None:\n        \"\"\"\n        Add the address of the memory access to the list of current instruction dependencies\n        :param address: the address of the memory access\n        :param size: the size of the memory access\n        :param is_write: True if the memory access is a write (store), False if it's a read (load)\n        \"\"\"\n        if not self._enable_tracking:\n            return\n\n        assert self._instruction, \"track_memory_access called before track_instruction\"\n\n        # mask the address - we taint at the granularity of 8 bytes\n        address -= self._data_start\n        masked_start_addr = address & 0xffff_ffff_ffff_fff8\n        end_addr = address + (size - 1)\n        masked_end_addr = end_addr & 0xffff_ffff_ffff_fff8\n\n        # add all addresses to tracking\n        for i in range(masked_start_addr, masked_end_addr + 1, 8):\n            if is_write:\n                self._instruction.dest_mems.add(hex(i))\n            else:\n                self._instruction.src_mems.add(hex(i))\n\n    def _finalize_instruction(self) -> None:\n        \"\"\"\n        Propagate dependencies and record the taints of the tracked instruction\n        :raises AssertionError: if called before track_instruction\n        \"\"\"\n        assert self._instruction, \"_finalize_instruction called before track_instruction\"\n        inst = self._instruction.inst\n        inst_name = inst.name.lower()\n\n        # Extract dependencies of the tracked instruction\n        self._dependencies.add_dependencies(self._instruction)\n\n        # Workaround for REP instructions with implicit RCX dependency\n        if self._pending_taint and \"rep\" in inst_name and \"C\" in self._instruction.src_regs:\n            self._pending_taint.add('C')\n\n        # Update taints\n        # print(self._pending_taint)\n        for label in self._pending_taint:\n            if label.startswith(\"0x\"):\n                tainted_values = self._dependencies.mem.get(label, {label})\n            else:\n                tainted_values = self._dependencies.reg.get(label, {label})\n            self._tainted_labels.update(tainted_values)\n        # print(self._tainted_labels)\n\n        # Clear the dependencies of the overwritten registers\n        # NOTE: this must be done *after* the taint update, or the taints will be lost\n        self._dependencies.remove_overwritten_dependencies(self._instruction, self._target_desc)\n\n        # Reset the instruction\n        self._instruction = None\n\n    # ----------------------------------------------------------------------------------------------\n    # Tainting callback\n    def taint(self, value_type: TAINTED_VALUE_TYPE) -> None:\n        \"\"\"\n        Taint the operands of a given type for the tracked instruction\n        (tracked instruction is the last instruction on which track_instruction was called)\n\n        :param value_type: the type of the value to be tainted\n        \"\"\"\n        if not self._enable_tracking:\n            return\n\n        if not self._instruction:\n            return\n\n        # Taint the program counter\n        if value_type == \"pc\":\n            if self._instruction and self._instruction.inst.is_control_flow:\n                self._pending_taint.add(\"RIP\")\n            return\n\n        # Taint the memory addresses accessed by the instruction\n        if value_type == \"mem\":\n            for reg in self._instruction.mem_address_regs:\n                self._pending_taint.add(reg)\n            return\n\n        # Taint the loaded value\n        if value_type == \"ld_val\":\n            for addr in self._instruction.src_mems:\n                self._pending_taint.add(addr)\n            return\n        assert_never(value_type)\n\n    def taint_actors(self, actor_ids: List[int]) -> None:\n        \"\"\"\n        Taint all the memory addresses of the actors in the list\n        :param actor_ids: the list of actor IDs\n        \"\"\"\n        data_size_per_actor = SandboxLayout.data_size_per_actor()\n        for actor_id in actor_ids:\n            actor_offset = actor_id * data_size_per_actor\n            for i in range(actor_offset, actor_offset + data_size_per_actor, 8):\n                self._tainted_labels.add(hex(i))\n\n    # ----------------------------------------------------------------------------------------------\n    # Taint output\n    def get_taint(self, n_actors: int) -> InputTaint:\n        \"\"\"\n        Produce an InputTaint object based on the taints collected during\n        the model execution.\n        :param n_actors: the number of actors in the test case\n        :return: an InputTaint object\n        \"\"\"\n        # pylint: disable=too-many-locals\n        # NOTE: justified, because we have many variable that define area boundaries\n\n        if not self._enable_tracking:\n            self._tracking_in_progress = False\n            return InputTaint(n_actors)\n\n        if self._instruction:\n            self._finalize_instruction()\n\n        taint = InputTaint(n_actors)\n        tainted_sandbox_addresses: List[int] = []\n        register_start = SandboxLayout.data_area_offset(DataArea.GPR)\n        simd_start = SandboxLayout.data_area_offset(DataArea.SIMD)\n\n        for label in self._tainted_labels:\n            # Memory address\n            if label.startswith('0x'):\n                sandbox_address = int(label, 16)\n                tainted_sandbox_addresses.append(sandbox_address)\n                continue\n\n            # Register\n            reg = self._uc_target_desc.reg_norm_to_constant[label]\n            registers = self._uc_target_desc.usable_registers\n            if reg in registers:\n                sandbox_address = register_start + registers.index(reg) * 8\n                tainted_sandbox_addresses.append(sandbox_address)\n                continue\n\n            # SIMD register\n            simd_registers = self._uc_target_desc.usable_simd128_registers\n            if reg in simd_registers:\n                sandbox_address = simd_start + simd_registers.index(reg) * 16\n                tainted_sandbox_addresses.append(sandbox_address)\n                tainted_sandbox_addresses.append(sandbox_address + 1)\n            # else:\n            # print(f\"Register {label} is not tracked\")\n\n        tainted_sandbox_addresses.sort()\n        taint_offsets = [\n            InputTaint.taint_offset_from_sandbox_address(pos) for pos in tainted_sandbox_addresses\n        ]\n\n        for actor_id in range(0, n_actors):\n            actor_area_start = actor_id * InputTaint.per_actor_taint_size\n            actor_area_end = (actor_id + 1) * InputTaint.per_actor_taint_size\n            actor_taints = [\n                pos - actor_area_start\n                for pos in taint_offsets\n                if actor_area_start <= pos < actor_area_end\n            ]\n            taint.taint_actor_offsets(actor_id, actor_taints)\n\n        self._tracking_in_progress = False\n        return taint\n\n\n# ==================================================================================================\n# Private: Service Classes\n# ==================================================================================================\nclass _TrackedInstruction:\n    \"\"\"\n    A private data class that holds the source and destination operands of the tracked instruction\n    \"\"\"\n\n    def __init__(self, instruction: Instruction) -> None:\n        self.inst = instruction\n\n        self.src_regs: Set[str] = set()\n        self.dest_regs: Set[str] = set()\n\n        self.src_flags: Set[str] = set()\n        self.dest_flags: Set[str] = set()\n\n        self.src_mems: Set[str] = set()\n        self.dest_mems: Set[str] = set()\n\n        self.mem_address_regs: Set[str] = set()\n\n    def parse_static_operands(self, reg_normalizer: Dict[str, str]) -> None:\n        \"\"\"\n        Set the source and destination operands of the instruction.\n        :param reg_normalizer: a dictionary that maps register names to their normalized names\n        :return: None\n        \"\"\"\n        for op in self.inst.get_all_operands():\n            # Registers: normalize the names and record them\n            if isinstance(op, RegisterOp):\n                value = reg_normalizer[op.value]\n                if op.src:\n                    self.src_regs.add(value)\n                if op.dest:\n                    self.dest_regs.add(value)\n                continue\n\n            # Flags: record the read and write flags; also record the undefined flags\n            if isinstance(op, FlagsOp):\n                self.src_flags = set(op.get_flags_by_type('read'))\n                self.src_flags.update(op.get_flags_by_type('undef'))\n                self.dest_flags = set(op.get_flags_by_type('write'))\n                continue\n\n            # Memory: record the names of the address registers\n            if isinstance(op, MemoryOp):\n                for sub_op in re.split(r'\\+|-|\\*| ', op.value):\n                    if sub_op and sub_op in reg_normalizer:\n                        self.mem_address_regs.add(reg_normalizer[sub_op])\n                continue\n\n            if isinstance(op, AgenOp):\n                # LEA operand: record the names of the address registers\n                # Note that we record the names in self.src_regs, because it's not a memory access\n                for sub_op in re.split(r'\\+|-|\\*| ', op.value):\n                    if sub_op and sub_op in reg_normalizer:\n                        self.src_regs.add(reg_normalizer[sub_op])\n                continue\n\n            # Immediate, Label, and Condition: do nothing\n            if isinstance(op, (ImmediateOp, LabelOp, CondOp)):\n                continue\n\n            assert_never(op)\n\n\nclass _Dependencies:\n    \"\"\"\n    A private data class that tracks all dependencies collected by UnicornTaintTracker\n    \"\"\"\n    _cached_src_dependencies: Optional[Set[str]]\n\n    def __init__(self) -> None:\n        self.reg: Dict[str, Set[str]] = {}\n        self.flag: Dict[str, Set[str]] = {}\n        self.mem: Dict[str, Set[str]] = {}\n\n    def add_dependencies(self, tracked_inst: _TrackedInstruction) -> None:\n        \"\"\"\n        Update the dependencies with the source and destination operands of the tracked instruction\n        \"\"\"\n\n        # Get dependencies of the source operands\n        src_dependencies = set()\n        for reg in tracked_inst.src_regs:\n            src_dependencies.update(self.reg.get(reg, {reg}))\n        for flag in tracked_inst.src_flags:\n            src_dependencies.update(self.flag.get(flag, {flag}))\n        for addr in tracked_inst.src_mems:\n            src_dependencies.update(self.mem.get(addr, {addr}))\n        self._cached_src_dependencies = src_dependencies\n\n        # Propagate source dependencies to destination operands\n        for reg in tracked_inst.dest_regs:\n            if reg in self.reg:\n                self.reg[reg].update(src_dependencies)\n            else:\n                self.reg[reg] = copy.copy(src_dependencies)\n                self.reg[reg].add(reg)\n        for flg in tracked_inst.dest_flags:\n            if flg in self.flag:\n                self.flag[flg].update(src_dependencies)\n            else:\n                self.flag[flg] = copy.copy(src_dependencies)\n                self.flag[flg].add(flg)\n        for mem in tracked_inst.dest_mems:\n            if mem in self.mem:\n                self.mem[mem].update(src_dependencies)\n            else:\n                self.mem[mem] = copy.copy(src_dependencies)\n                self.mem[mem].add(mem)\n\n        # print(f\"reg: dest={tracked_inst.dest_regs}, src={tracked_inst.src_regs}\")\n        # print(f\"flag: dst={tracked_inst.dest_flags}, src={tracked_inst.src_flags}\")\n        # print(f\"mem: dest={tracked_inst.dest_mems}, src={tracked_inst.src_mems}\")\n        # print(f\"all reg={self.reg}\")\n        # print(f\"all flg={self.flag}\")\n        # print(f\"all mem={self.mem}\")\n        # print(\"----------------------\")\n\n    def remove_overwritten_dependencies(self, tracked_inst: _TrackedInstruction,\n                                        target_desc: TargetDesc) -> None:\n        \"\"\"\n        Remove the dependencies of the destination operands of the tracked instruction\n        \"\"\"\n        assert self._cached_src_dependencies is not None, \\\n            \"remove_overwritten_dependencies must be called after add_dependencies\"\n        src_dependencies = self._cached_src_dependencies\n        self._cached_src_dependencies = None\n\n        # Identify if the instruction overrides previous dependencies\n        # (so far we consider only two such case: MOV and LEA)\n        # FIXME: this is an x86-specific implementation and it should be moved to the x86 model\n        override: bool = False\n        inst_name = tracked_inst.inst.name.lower()\n        if (inst_name.startswith(\"mov\") or inst_name == \"lea\") \\\n           and len(tracked_inst.dest_regs) == 1:\n            reg = tracked_inst.inst.get_reg_operands(True)[0].value\n            if target_desc.register_sizes.get(reg, 0) == 64:\n                override = True\n\n        # If the instruction overrides previous dependencies, remove them\n        if override:\n            assert len(tracked_inst.dest_regs) == 1, \"MOV instruction with multiple destinations\"\n            reg = tracked_inst.dest_regs.pop()\n            for dep in list(self.reg[reg]):\n                if dep not in src_dependencies:\n                    self.reg[reg].remove(dep)\n"
  },
  {
    "path": "rvzr/model_unicorn/tracer.py",
    "content": "\"\"\"\nFile: Collection of tracers for the Unicorn backend to the contract model.\n\n      A tracer is a component that record certain events during the execution of a\n      test case on the contract model. As such, the tracers implement\n      observation clauses of different contracts.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom abc import ABC\n\nfrom typing import List, TYPE_CHECKING, Optional\nfrom unicorn import UC_MEM_READ\nimport xxhash\n\nfrom ..traces import CTrace, CTraceEntry\nfrom ..config import CONF\n\nif TYPE_CHECKING:\n    from .model import UnicornModel\n    from .taint_tracker import UnicornTaintTracker\n    from ..target_desc import TargetDesc, UnicornTargetDesc\n    from ..tc_components.test_case_data import InputData\n    from ..tc_components.test_case_code import TestCaseProgram\n\n\n# ==================================================================================================\n# Abstract Tracer Interface\n# ==================================================================================================\nclass UnicornTracer(ABC):\n    \"\"\"\n    Interface definition that must be implemented by all tracers\n    as well as implementation of common functionality.\n    \"\"\"\n    trace: List[CTraceEntry]\n    enable_tracing: bool = False\n    _model: UnicornModel\n    _taint_tracker: UnicornTaintTracker\n    _uc_target_desc: UnicornTargetDesc\n    _test_case: Optional[TestCaseProgram] = None\n    _input: Optional[InputData] = None\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__()\n        self.trace = []\n        self._model = model\n        self._taint_tracker = taint_tracker\n        self._uc_target_desc = target_desc.uc_target_desc\n\n    # ==============================================================================================\n    # Public Interface\n    def load_test_case(self, test_case: TestCaseProgram) -> None:\n        \"\"\" Load the test case into the tracer \"\"\"\n        self._test_case = test_case\n\n    def reset(self, input_: InputData) -> None:\n        \"\"\"\n        Initialize/reset the state of the tracer\n        for tracing the loaded test case with the given input\n        \"\"\"\n        self.trace = []\n        self.enable_tracing = False\n        self._input = input_\n\n    def get_trace(self) -> CTrace:\n        \"\"\" Return the collected trace in a form of a CTrace object \"\"\"\n\n        # make the trace reproducible by normalizing the addresses\n        normalized_trace: List[CTraceEntry] = []\n        layout = self._model.layout\n        for org_entry in self.trace:\n            if org_entry.type_ == \"pc\":\n                entry = CTraceEntry(\"pc\", layout.code_addr_to_offset(org_entry.value))\n            elif org_entry.type_ == \"mem\":\n                entry = CTraceEntry(\"mem\", layout.data_addr_to_offset(org_entry.value))\n            else:\n                entry = CTraceEntry(org_entry.type_, org_entry.value)\n            normalized_trace.append(entry)\n        return CTrace(normalized_trace)\n\n    def observe_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        \"\"\"\n        Trace a memory access.\n        The value may or may not be recorded on the trace, depending on the tracer implementation.\n        :param access: type of access (UC_MEM_READ, UC_MEM_WRITE)\n        :param address: address of the memory access\n        :param size: size of the memory access\n        :param value: value read or written\n        \"\"\"\n\n    def observe_instruction(self, pc: int, size: int) -> None:\n        \"\"\"\n        Trace an instruction.\n        The value may or may not be recorded on the trace, depending on the tracer implementation.\n        :param pc: program counter of the instruction\n        :param size: size of the instruction\n        \"\"\"\n\n    # ==============================================================================================\n    # Private Methods\n\n    def _add_mem_address_to_trace(self, address: int) -> None:\n        \"\"\" Record the given memory address, if tracing is enabled \"\"\"\n        if self.enable_tracing:\n            self.trace.append(CTraceEntry(\"mem\", address))\n            self._taint_tracker.taint(\"mem\")\n\n    def _add_pc_to_trace(self, address: int) -> None:\n        \"\"\" Record the given program counter, if tracing is enabled \"\"\"\n        if self.enable_tracing:\n            self.trace.append(CTraceEntry(\"pc\", address))\n            self._taint_tracker.taint(\"pc\")\n\n    def _add_dependencies_to_trace(self, dependency_hash: int) -> None:\n        \"\"\" Record the given dependency hash, if tracing is enabled \"\"\"\n        if self.enable_tracing:\n            self.trace.append(CTraceEntry(\"val\", dependency_hash))\n            self._taint_tracker.taint(\"mem\")\n\n    def _add_value_to_trace(self, val: int) -> None:\n        \"\"\" Record the given untyped value, if tracing is enabled \"\"\"\n        if self.enable_tracing:\n            self.trace.append(CTraceEntry(\"val\", val))\n\n\n# ==================================================================================================\n# Concrete Tracers\n# ==================================================================================================\nclass NoneTracer(UnicornTracer):\n    \"\"\"\n    Tracer that does not record any information.\n    Used as a placeholder when a test case has to be executed on the model without tracing.\n    \"\"\"\n\n    def observe_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        pass\n\n    def observe_instruction(self, pc: int, size: int) -> None:\n        pass\n\n    def get_trace(self) -> CTrace:\n        return CTrace.empty_trace()\n\n\nclass PCTracer(UnicornTracer):\n    \"\"\"\n    Tracer that records the program counter of all instructions executed on the model.\n\n    E.g., if the following program is executed:\n        0x0: mov eax, 0x1\n        0x4: mov ebx, 0x2\n        0x8: mov ecx, 0x3\n\n    The output trace will be [0x0, 0x4, 0x8]\n    \"\"\"\n\n    def observe_instruction(self, pc: int, size: int) -> None:\n        self._add_pc_to_trace(pc)\n        super().observe_instruction(pc, size)\n\n\nclass MemoryTracer(UnicornTracer):\n    \"\"\"\n    Tracer that records the memory addresses accessed by the model.\n\n    E.g., if the following program is executed:\n        0x0: mov eax, [0x100]\n        0x4: mov ebx, [0x200]\n        0x8: mov ecx, [0x300]\n\n    The output trace will be [0x100, 0x200, 0x300]\n    \"\"\"\n\n    def observe_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        self._add_mem_address_to_trace(address)\n        super().observe_mem_access(access, address, size, value)\n\n\nclass L1DTracer(MemoryTracer):\n    \"\"\"\n    The same as MemoryTracer, but the traces will be marked as L1D traces; that is, when\n    such traces are printed, they will be printed as L1D maps.\n    \"\"\"\n\n    def get_trace(self) -> CTrace:\n        trace = super().get_trace()\n        trace.set_printed_as_l1d(True)\n        return trace\n\n\nclass CTTracer(PCTracer):\n    \"\"\"\n    Observe address of the memory access and of the program counter.\n    \"\"\"\n\n    def observe_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        self._add_mem_address_to_trace(address)\n        super().observe_mem_access(access, address, size, value)\n\n\nclass TruncatedCTTracer(UnicornTracer):\n    \"\"\"\n    Observe address of the memory access and of the program counter at cache line granularity.\n    \"\"\"\n\n    def observe_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        self._add_mem_address_to_trace((address >> 6) << 6)\n        super().observe_mem_access(access, address, size, value)\n\n    def observe_instruction(self, pc: int, size: int) -> None:\n        self._add_pc_to_trace((pc >> 6) << 6)\n        super().observe_instruction(pc, size)\n\n\nclass TruncatedCTWithOverflowsTracer(UnicornTracer):\n    \"\"\"\n    Observe address of the memory access and of the program counter at cache line granularity +\n    observe cache line overflows.\n    \"\"\"\n\n    def observe_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        self._add_mem_address_to_trace((address >> 6) << 6)\n        if (address + size) % 64 != (address % 64):  # add overflows to the trace\n            self._add_mem_address_to_trace(((address + size) >> 6) << 6)\n        return super().observe_mem_access(access, address, size, value)\n\n    def observe_instruction(self, pc: int, size: int) -> None:\n        self._add_pc_to_trace((pc >> 6) << 6)\n        if (pc + size) // 64 != (pc // 64):  # add overflows to the trace\n            self._add_pc_to_trace(((pc + size) >> 6) << 6)\n        return super().observe_instruction(pc, size)\n\n\nclass CTNonSpecStoreTracer(PCTracer):\n    \"\"\"\n    Observe address of memory access only if not in speculation or it is a read.\n    \"\"\"\n\n    def observe_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        # trace all non-spec mem accesses and speculative loads\n        if not self._model.speculator.in_speculation() or access == UC_MEM_READ:\n            self._add_mem_address_to_trace(address)\n        super().observe_mem_access(access, address, size, value)\n\n\nclass ArchTracer(CTTracer):\n    \"\"\"\n    Similar to CTTracer, with additional exposure of:\n     - Register state at the first memory access\n     - The values loaded from memory\n\n    The main use case of this tracer is to model the guarantees provided by secure speculation\n    mechanisms, such as Speculative Taint Tracking (STT).\n    \"\"\"\n    _started: bool = False\n\n    def reset(self, input_: InputData) -> None:\n        super().reset(input_)\n        self._started = False\n\n    def observe_instruction(self, pc: int, size: int) -> None:\n        # The first instruction must exposes all register values\n        if not self._started:\n            self._started = True\n            for reg in self._uc_target_desc.usable_registers[:-1]:  # exclude stack pointer\n                val = self._model.emulator.reg_read(reg)\n                assert isinstance(val, int), f\"Expected int, got {type(val)}\"\n                self.trace.append(CTraceEntry(\"val\", val))\n\n        return super().observe_instruction(pc, size)\n\n    def observe_mem_access(self, access: int, address: int, size: int, value: int) -> None:\n        if access == UC_MEM_READ:\n            val = int.from_bytes(self._model.emulator.mem_read(address, size), byteorder='little')\n            self._add_value_to_trace(val)\n            self._taint_tracker.taint(\"ld_val\")\n        super().observe_mem_access(access, address, size, value)\n\n\n# ==================================================================================================\n# Actor-based Tracers\n# ==================================================================================================\nclass ActorNITracer(CTTracer):\n    \"\"\"\n    Tracer that exposes all data that belongs to the actors with `observer` flag set\n    + sequential traces for the non-observer actors\n    \"\"\"\n    _observer_actor_ids: List[int]\n\n    def __init__(self, target_desc: TargetDesc, model: UnicornModel,\n                 taint_tracker: UnicornTaintTracker) -> None:\n        super().__init__(target_desc, model, taint_tracker)\n        n_observers = len([desc for desc in CONF.get_actors_conf().values() if desc['observer']])\n        if n_observers == len(CONF.get_actors_conf()):\n            raise ValueError(\"ActorNITracer requires at least 1 non-observer actor\")\n        if n_observers == 0:\n            raise ValueError(\"ActorNITracer requires at least 1 observer actor\")\n\n    def reset(self, input_: InputData) -> None:\n        super().reset(input_)\n        assert self._test_case is not None, \"Test case not loaded\"\n        self._observer_actor_ids = [\n            actor.get_id() for actor in self._test_case.get_actors() if actor.observer\n        ]\n\n    def get_trace(self) -> CTrace:\n        ctrace = super().get_trace()\n        ctrace = self._add_observer_traces(ctrace)\n        self._taint_tracker.taint_actors(self._observer_actor_ids)\n        return ctrace\n\n    def _add_observer_traces(self, ctrace: CTrace) -> CTrace:\n        assert self._input is not None, \"Input not loaded\"\n        fragment_hashes: List[CTraceEntry] = []\n        for actor_id in self._observer_actor_ids:\n            input_fragment = self._input[actor_id]\n            data = input_fragment.tobytes()\n            hash_ = xxhash.xxh64(data, seed=0).intdigest()\n            fragment_hashes.append(CTraceEntry(\"val\", hash_))\n        new_trace = ctrace.get_typed() + fragment_hashes\n        return CTrace(new_trace)\n"
  },
  {
    "path": "rvzr/postprocessing/__init__.py",
    "content": ""
  },
  {
    "path": "rvzr/postprocessing/analysis_passes.py",
    "content": "\"\"\" File: Collection of minimization passes that analyse the test case without modifying it.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, List\nfrom typing_extensions import assert_never\n\nfrom ..model_unicorn import model as uc_model, speculators_basic as uc_speculator, \\\n    tracer as uc_tracer, interpreter as uc_interpreter\nfrom ..sandbox import CodeArea\nfrom ..arch.x86.target_desc import X86TargetDesc\nfrom ..arch.arm64.target_desc import ARM64TargetDesc\nfrom ..config import CONF\n\nfrom .instruction_passes import BaseInstructionMinimizationPass\n\nif TYPE_CHECKING:\n    from ..traces import Violation, CTraceEntry\n    from ..tc_components.test_case_data import InputData\n    from ..tc_components.test_case_code import TestCaseProgram\n    from ..target_desc import TargetDesc\n\n\ndef _get_seq_model(data_start: int, code_start: int) -> uc_model.UnicornModel:\n    \"\"\"\n    This is a partial duplicate of the code in factory.py,\n    but we cannot import factory.py here due to circular imports.\n    \"\"\"\n    model_cls: type[uc_model.UnicornModel]\n    target_desc: TargetDesc\n    interpreter: type[uc_interpreter.ExtraInterpreter]\n    if CONF.instruction_set == \"x86-64\":\n        model_cls = uc_model.X86UnicornModel\n        target_desc = X86TargetDesc()\n        interpreter = uc_interpreter.X86ExtraInterpreter\n    elif CONF.instruction_set == \"arm64\":\n        model_cls = uc_model.ARM64UnicornModel\n        target_desc = ARM64TargetDesc()\n        interpreter = uc_interpreter.ARMExtraInterpreter\n    else:\n        assert_never(CONF.instruction_set)\n\n    bases = (data_start, code_start)\n    model = model_cls(bases, target_desc, uc_speculator.SeqSpeculator,\n                      uc_tracer.CTTracer, interpreter)\n    return model\n\n\nclass AddViolationCommentsPass(BaseInstructionMinimizationPass):\n    \"\"\"\n    An instrumentation pass that iterates over the test case and adds comments\n    with the memory addresses of the loads and stores that caused the violation.\n    \"\"\"\n    name = \"Violation Comment Insertion\"\n    violation: Violation\n\n    def set_violation(self, violation: Violation) -> None:\n        self.violation = violation\n\n    def run(self, test_case: TestCaseProgram, inputs: List[InputData]) -> TestCaseProgram:\n        # pylint: disable=too-many-locals\n        # pylint: disable=too-many-branches\n        # FIXME: this function was written in a hurry and needs to be refactored\n\n        # reproduce the violation to get violating input IDs\n        v_inputs = [m.input_ for m in self.violation.measurements[:2]]\n        v_input_ids = [m.input_id for m in self.violation.measurements[:2]]\n\n        # create a model that will collect PC and memory traces\n        data_start, code_start = 0x2000000, 0x1000000\n        model = _get_seq_model(data_start, code_start)\n\n        # collect traces\n        model.tracer.enable_tracing = True  # start tracing from the very beginning\n        model.load_test_case(test_case)\n        ctraces_obj = model.trace_test_case(v_inputs, 30)\n        ctraces: List[List[CTraceEntry]] = [t.get_typed() for t in ctraces_obj]\n\n        # select loads and stores form the traces\n        ctrace_maps = []\n        for ctrace in ctraces:\n            ctrace_map = {}\n            for v1, v2, v3 in zip(ctrace, ctrace[1:], ctrace[2:]):\n                if v1.type_ == 'pc' and v2.type_ == 'mem':\n                    pc = v1.value\n                    ld_addr = v2.value\n                    st_addr = v3.value if v3.type_ == 'mem' else 0\n                    ctrace_map[pc] = (ld_addr, st_addr)\n            ctrace_maps.append(ctrace_map)\n\n        # get the contents of the asm file\n        lines = []\n        with open(test_case.asm_path(), \"r\") as f:\n            lines = list(enumerate(f))\n\n        # to simplify the next step, get a dictionary mapping assembly lines to PCs\n        line_num_to_pc = {}\n        for func in test_case.iter_functions():\n            actor_id = func.get_owner().get_id()\n            actor_start_pc = model.layout.get_code_addr(CodeArea.MAIN, actor_id)\n            for bb in func:\n                for inst in list(bb) + bb.terminators:\n                    pc = actor_start_pc + inst.section_offset() - code_start\n                    line_num = inst.line_num()\n                    if line_num != 0:\n                        line_num_to_pc[line_num] = pc\n\n        # add a comment with the load/store addresses to the assembly\n        with open(test_case.asm_path(), 'w') as f:\n            for i, line in lines:\n                f.write(line)\n                if i not in line_num_to_pc:\n                    continue\n                pc = line_num_to_pc[i]\n                if pc not in ctrace_maps[0] or pc not in ctrace_maps[1]:\n                    continue\n\n                ld, st, cl, of = [0, 0], [0, 0], [0, 0], [0, 0]\n                iid = v_input_ids\n                for i in range(2):\n                    ld[i], st[i] = ctrace_maps[i][pc]\n                    cl[i] = (ld[i] % 0x1000) // 64\n                    of[i] = (ld[i] % 0x1000) % 64\n\n                if st[0] != 0 or st[1] != 0:\n                    f.write(\n                        f\"{self._comment_symbol} \"\n                        f\"mem access: [{iid[0]}] {hex(ld[0])}-{hex(st[0])} CL {cl[0]}:{of[0]} | \"\n                        f\"[{iid[1]}] {hex(ld[1])}-{hex(st[1])} CL {cl[1]}:{of[1]}\\n\")\n                else:\n                    f.write(f\"{self._comment_symbol} \"\n                            f\"mem access: [{iid[0]}] {hex(ld[0])} CL {cl[0]}:{of[0]} | \"\n                            f\"[{iid[1]}] {hex(ld[1])} CL {cl[1]}:{of[1]}\\n\")\n\n                if st[0] == 0xff8 or st[1] == 0xff8:\n                    f.write(f\"{self._comment_symbol} exception?\\n\")\n\n        return test_case\n\n    def modify_instruction(self, _: List[str], __: int) -> List[str]:\n        return []  # unused\n\n    def verify_modification(self, _: TestCaseProgram, __: List[InputData]) -> bool:\n        return True  # unused\n"
  },
  {
    "path": "rvzr/postprocessing/input_passes.py",
    "content": "\"\"\"\nFile: Collection of minimization passes that operate on the test case input data.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nimport abc\nfrom copy import deepcopy\nfrom math import log2\n\nfrom typing import TYPE_CHECKING, List, Final, Optional, Tuple\n\nfrom .pass_abc import BaseMinimizationPass\nfrom ..config import CONF\n\nif TYPE_CHECKING:\n    from ..traces import Violation\n    from ..tc_components.test_case_code import TestCaseProgram\n    from ..tc_components.test_case_data import InputData\n\n_PER_ACTOR_INPUT_SIZE: Final[int] = 0x4000  # 16 KB per actor\n_PRINT_BLOCK_SIZE: Final[int] = 8  # print progress indicator in 8-byte blocks\n_PRINT_LINE_SIZE: Final[int] = 64  # print progress indicator in (64 * 8)-byte lines\n_MAX_BLOCK_SIZE: Final[int] = 64  # try to zero out up to 64 bytes at once\n\n\nclass BaseInputMinimizationPass(BaseMinimizationPass):\n    \"\"\" Base class for a minimization pass that operates on inputs. \"\"\"\n\n    @abc.abstractmethod\n    def run(self, test_case: TestCaseProgram, org_inputs: List[InputData],\n            org_violation: Violation) -> List[InputData]:\n        \"\"\" Main function that runs the minimization pass\n        :param test_case: The test case object to work on\n        :param org_inputs: List of inputs to minimize\n        :param org_violation: The original violation\n        :return: List of minimized inputs\n        \"\"\"\n\n\nclass InputSequenceMinimizationPass(BaseInputMinimizationPass):\n    \"\"\"\n    A minimization pass that iteratively removes inputs from the violating the input sequence\n    and checks if the violation is still triggered.\n    \"\"\"\n    name = \"Input Sequence Minimization\"\n\n    def run(self, test_case: TestCaseProgram, org_inputs: List[InputData],\n            org_violation: Violation) -> List[InputData]:\n        self._progress.pass_msg(\"Reducing the number of inputs by halving\")\n        org_len = len(org_inputs)\n\n        violation = org_violation\n        nonboosted_inputs = org_inputs\n        while len(nonboosted_inputs) > 5:\n            new_inputs = nonboosted_inputs[:len(nonboosted_inputs) // 2]\n            new_violation = self._fuzzer.fuzzing_round(test_case, new_inputs, [])\n            if not new_violation:\n                break\n            nonboosted_inputs = new_inputs\n            violation = new_violation\n\n        if len(nonboosted_inputs) < org_len:\n            self._progress.pass_msg(f\"Result: Reduced to {len(nonboosted_inputs)} inputs\")\n        else:\n            self._progress.pass_msg(\"Result: Could not reduce the number of inputs\")\n\n        # Get boosted inputs and disable boosting from now on\n        inputs = violation.input_sequence\n        org_ipc = CONF.inputs_per_class\n        CONF.inputs_per_class = 1  # disable boosting from now on\n\n        n_iterations = 10\n        self._progress.pass_msg(\"Reducing the input sequence iteratively\")\n        for iteration in range(n_iterations):\n            self._progress.pass_msg(f\"Iteration {iteration + 1}\")\n            org_len = len(inputs)\n            for input_id in range(org_len, 0, -1):\n                new_inputs = inputs[0:input_id] + inputs[input_id + 1:]\n                new_violation = self._fuzzer.fuzzing_round(test_case, new_inputs, [])\n                if not new_violation:\n                    self._progress.next(False)\n                    continue\n                self._progress.next(True)\n                inputs = new_inputs\n                violation = new_violation\n            self._progress.pass_finish()\n            if len(inputs) == org_len:\n                break\n        self._progress.pass_msg(f\"Result: Reduced to {len(inputs)} inputs\")\n        CONF.inputs_per_class = org_ipc\n        return violation.input_sequence\n\n\nclass DifferentialInputMinimizerPass(BaseInputMinimizationPass):\n    \"\"\"\n    A minimization pass that iteratively minimizes the difference between two violating inputs.\n    It tries to zero out blocks of decreasing size and checks if the violation is still triggered.\n    If this is not possible, it tries to copy the byte between the two inputs.\n    \"\"\"\n    name = \"Differential Input Minimizer\"\n\n    _test_case: Optional[TestCaseProgram] = None\n    _inputs: Optional[List[InputData]] = None\n    _violating_ids: Optional[Tuple[int, int]] = None\n    _local_ignore_list: List[int] = []\n    _leaked_addresses: List[int] = []\n\n    def run(self, test_case: TestCaseProgram, _: List[InputData],\n            org_violation: Violation) -> List[InputData]:\n\n        # Set the context for this pass\n        self._set_pass_context(test_case, org_violation)\n        assert self._violating_ids is not None\n        self._progress.pass_msg(\"Minimizing the difference between inputs\"\n                                f\" {self._violating_ids[0]} and {self._violating_ids[1]}\")\n\n        # Disable boosting for this pass as we already operate on the boosted inputs\n        org_conf = (CONF.inputs_per_class,)\n        CONF.inputs_per_class = 1\n\n        # Print header for progress output\n        print(f'\\n{\"Address\":<11}', end=\"\", flush=True)\n        for i in range(0, 64, 8):\n            print(f\"+0x{i * 8:<6x}\", end=\"\", flush=True)\n\n        # Start the pass\n        for actor_id in range(len(CONF.get_actors_conf())):\n            self._process_actor(actor_id)\n        print(\"\")\n\n        # Print summary\n        self._progress.pass_msg(f\"Result: Leaked {len(self._leaked_addresses)} bytes\")\n        self._progress.pass_msg(f\"Addresses: {[hex(addr) for addr in self._leaked_addresses]}\")\n\n        # Restore original configuration\n        assert self._inputs is not None\n        new_inputs = list(self._inputs)\n        CONF.inputs_per_class = org_conf[0]\n        self._reset_pass_context()\n\n        return new_inputs\n\n    def _set_pass_context(self, test_case: TestCaseProgram, org_violation: Violation) -> None:\n        \"\"\"\n        Set the context for the minimization pass.\n        :param test_case: The test case object to work on\n        :param org_violation: The original violation\n        :return: None\n        \"\"\"\n        # Store the test case and inputs\n        self._test_case = test_case\n        self._inputs = org_violation.input_sequence\n\n        # For convenience, also store the two inputs to minimize\n        violating_input_ids = [i.input_id for i in org_violation.measurements]\n        if len(violating_input_ids) > 2:\n            violating_input_ids = violating_input_ids[:2]\n        self._violating_ids = (violating_input_ids[0], violating_input_ids[1])\n\n        # Store a list of all other input IDs, which we will ignore during checks\n        self._local_ignore_list = [\n            i for i in range(len(self._inputs)) if i not in violating_input_ids\n        ]\n\n        # Finally, make a list to store all leaked addresses\n        self._leaked_addresses = []\n\n    def _reset_pass_context(self) -> None:\n        \"\"\" Reset the context for the minimization pass. \"\"\"\n        self._test_case = None\n        self._inputs = None\n        self._local_ignore_list = []\n        self._leaked_addresses = []\n\n    def _process_actor(self, actor_id: int) -> None:\n        \"\"\"\n        Process the input regions of a single actor.\n        :param actor_id: The actor ID\n        \"\"\"\n        assert self._inputs is not None and self._violating_ids is not None\n\n        # Process all input regions of the actor\n        region_offset = 0\n        for region_name in ['main', 'faulty', 'gpr', 'simd']:\n            region_size = len(self._inputs[self._violating_ids[0]][actor_id][region_name])\n\n            # Within each region, process all bytes\n            i = 0\n            while i < region_size:\n                absolute_address = actor_id * _PER_ACTOR_INPUT_SIZE + region_offset + i * 8\n\n                # Periodically break lines and print spaces for better readability\n                if i % _PRINT_LINE_SIZE == 0:\n                    print(f\"\\n0x{absolute_address:08x} \", end=\"\", flush=True)\n                elif i % _PRINT_BLOCK_SIZE == 0:\n                    print(\" \", end=\"\", flush=True)\n\n                # Process the block starting at the current index\n                processed_block_size = self._process_block(actor_id, region_name, i, region_size,\n                                                           absolute_address)\n                i += processed_block_size\n\n            region_offset += region_size * 8\n\n    def _process_block(self, actor_id: int, region_name: str, block_start: int, region_size: int,\n                       absolute_address: int) -> int:\n        \"\"\"\n        Try to minimize the difference between the two inputs at the given index.\n        \"\"\"\n        assert self._test_case is not None and self._inputs is not None \\\n               and self._violating_ids is not None\n        input_a = self._inputs[self._violating_ids[0]]\n        input_b = self._inputs[self._violating_ids[1]]\n        org_input_a = deepcopy(input_a)\n        org_input_b = deepcopy(input_b)\n\n        def _restore_addr(addr: int) -> None:\n            input_a[actor_id][region_name][addr] = org_input_a[actor_id][region_name][addr]\n            input_b[actor_id][region_name][addr] = org_input_b[actor_id][region_name][addr]\n\n        def _zero_out_block(addr: int) -> int:\n            \"\"\"\n            Try to zero out a block of memory and check if the violation is still triggered.\n            Start with the largest possible block size and iteratively decrease the block size\n            until violation is triggered or the block size is 1.\n            :return: The size of the block that was successfully zeroed out, or 1\n            \"\"\"\n            assert input_a is not None and input_b is not None and \\\n                self._test_case is not None and self._inputs is not None\n\n            # Find a suitable starting block size, fulfilling the following criteria:\n            #    * the block size is less then 512 bytes (64 * 8)\n            block_size: int = _MAX_BLOCK_SIZE - (addr % _MAX_BLOCK_SIZE)\n            #    * the block does not overlap with the next region\n            block_size = min(block_size, region_size - addr)\n            #    * the block size is a power of 2\n            block_size = 2**int(log2(block_size))\n            #    * i mod block_size == 0\n            while block_size > 1 and addr % block_size != 0:\n                block_size //= 2\n\n            # Starting from the determined block size, try to find the largest block\n            # such that zeroing out the block still triggers the violation\n            while block_size > 1:\n                # Try zeroing out the block\n                for i in range(block_size):\n                    input_a[actor_id][region_name][addr + i] = 0\n                    input_b[actor_id][region_name][addr + i] = 0\n\n                # Check if the violation is still triggered\n                if self._check_for_violation(self._test_case, self._inputs,\n                                             self._local_ignore_list):\n                    # If reproduced, we managed to zero out the block; return\n                    return block_size\n\n                # If not reproduced, restore the original values and try a smaller block\n                for i in range(block_size):\n                    _restore_addr(addr + i)\n                block_size //= 2\n\n            # If we reach here, we could not zero out a block larger than 1 byte\n            return 1\n\n        # First, try setting a large block of bytes to zero\n        block_size = _zero_out_block(block_start)\n        if block_size > 1:\n            # If reproduced, print progress and return the block size\n            n_64byte_blocks = block_size // 8\n            n_remainder_bytes = block_size % 8\n            if n_remainder_bytes > 0:\n                print(\".\" * n_remainder_bytes, end=\"\", flush=True)\n                if n_64byte_blocks > 0:\n                    print(\" \", end=\"\", flush=True)\n            if n_64byte_blocks > 0:\n                print((\".\" * 8 + \" \") * (n_64byte_blocks - 1), end=\"\", flush=True)\n                print(\".\" * 8, end=\"\", flush=True)\n            return block_size\n\n        # try zeroing out a single byte\n        input_a[actor_id][region_name][block_start] = 0\n        input_b[actor_id][region_name][block_start] = 0\n        if self._check_for_violation(self._test_case, self._inputs, self._local_ignore_list):\n            print(\".\", end=\"\", flush=True)\n            return 1\n        _restore_addr(block_start)\n\n        # check if the bytes are already equal; if so, nothing more to do here\n        if input_a[actor_id][region_name][block_start] == \\\n           input_b[actor_id][region_name][block_start]:\n            print(\"=\", end=\"\", flush=True)\n            return 1\n\n        # try copying the byte between the two inputs\n        input_a[actor_id][region_name][block_start] = \\\n            org_input_a[actor_id][region_name][block_start]\n        input_b[actor_id][region_name][block_start] = \\\n            input_a[actor_id][region_name][block_start]\n        if self._check_for_violation(self._test_case, self._inputs, self._local_ignore_list):\n            print(\"+\", end=\"\", flush=True)\n            return 1\n        _restore_addr(block_start)\n\n        # if failing, we found a leaked address\n        print(\"^\", end=\"\", flush=True)\n        self._leaked_addresses.append(absolute_address)\n        return 1\n"
  },
  {
    "path": "rvzr/postprocessing/instruction_passes.py",
    "content": "\"\"\" File: Collection of minimization passes that operate on instructions\n    (i.e., simplify test case code).\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nimport abc\nimport os\nimport re\nfrom subprocess import run\nfrom typing import TYPE_CHECKING, List, Dict, Callable\nfrom typing_extensions import assert_never\n\nfrom .pass_abc import BaseMinimizationPass\nfrom ..logs import warning\nfrom ..config import CONF\n\nif TYPE_CHECKING:\n    from ..traces import Violation\n    from ..tc_components.test_case_code import TestCaseProgram\n    from ..tc_components.test_case_data import InputData\n    from ..fuzzer import Fuzzer\n    from ..isa_spec import InstructionSet\n    from ..postprocessing.progress_printer import ProgressPrinter\n\n\nclass BaseInstructionMinimizationPass(BaseMinimizationPass):\n    \"\"\"\n    Base class for a minimization pass that operates on instructions.\n    \"\"\"\n    name: str = \"\"\n\n    # ------------------------------------------------\n    # Abstract interface\n    @abc.abstractmethod\n    def run(self, test_case: TestCaseProgram, inputs: List[InputData]) -> TestCaseProgram:\n        \"\"\" Main function that runs the minimization pass \"\"\"\n\n    @abc.abstractmethod\n    def modify_instruction(self, instructions: List[str], cursor: int) -> List[str]:\n        \"\"\"\n        Modify the instruction at the given cursor according to\n        the algorithm defined by subclass\n        \"\"\"\n\n    @abc.abstractmethod\n    def verify_modification(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        \"\"\"\n        Verify if the modification made to the test case is valid according to\n        the algorithm defined by subclass\n        \"\"\"\n\n    def minimization_loop(self,\n                          test_case: TestCaseProgram,\n                          inputs: List[InputData],\n                          skip_instrumentation_lines: bool = True) -> List[int]:\n        \"\"\"\n        Standard minimization loop that iteratively applies the modification\n        algorithm (modify_instruction) to each line of the test case and checks if the resulting\n        test case still passes the verification function (verify_modification).\n\n        :param test_case: The test case object to minimize\n        :param inputs: List of inputs to use for verification\n        :param skip_instrumentation_lines: If True, skip lines with the `instrumentation` comment\n        :return List of instruction IDs that passed the verification\n        \"\"\"\n\n        def line_is_skipped(line: str) -> bool:\n            if not line:\n                return True\n            # We skip lines that meet the following criteria:\n            is_skipped = line == \"\"  # empty line\n            is_skipped |= (line[0] == self._comment_symbol)  # comment\n            is_skipped |= (\"lfence\" in line)  # fences\n            is_skipped |= ('.' == line[0])  # labels\n            is_skipped |= ('noremove' in line)  # explicitly marked as non-removable\n            is_skipped |= (skip_instrumentation_lines and 'instrumentation' in line)\n            is_skipped |= (self._base_register in line and '[' not in line)  # sandbox updates\n            return is_skipped\n\n        # get all lines of the test case\n        with open(test_case.asm_path(), \"r\") as f:\n            instructions = f.readlines()\n\n        # Iterate over all instructions, backwards, and collect a list of instructions that\n        # can be modified while still passing the verification\n        cursor = len(instructions)\n        modifiable_ids = []\n        while True:\n            cursor -= 1\n            line = instructions[cursor].strip().lower()\n            # Check if we are done\n            if cursor == 0:\n                break\n\n            # Leave certain lines untouched\n            if line_is_skipped(line):\n                continue\n\n            # Create a modified test case\n            modified_instructions = self.modify_instruction(instructions, cursor)\n            if not modified_instructions:  # skip line if the modification failed\n                self._progress.next(False)\n                continue\n\n            # Create a test case object from the modified instructions\n            tmp_test_case = self._get_test_case_from_instructions(modified_instructions)\n\n            # Verify modification and update the list of modifiable instructions\n            check_passed = self.verify_modification(tmp_test_case, inputs)\n            if check_passed:\n                self._progress.next(True)\n                instructions = modified_instructions\n                modifiable_ids.append(cursor)\n            else:\n                self._progress.next(False)\n\n        return modifiable_ids\n\n    def set_violation(self, violation: Violation) -> None:\n        \"\"\" Set the violation that is being minimized \"\"\"\n\n\nclass InstructionRemovalPass(BaseInstructionMinimizationPass):\n    \"\"\"\n    A minimization pass that iteratively removes instructions from the test case\n    (one at a time, starting from the end) and checks if the violation is still triggered.\n    \"\"\"\n    name = \"Instruction Removal Pass\"\n\n    def run(self, test_case: TestCaseProgram, inputs: List[InputData]) -> TestCaseProgram:\n        modifiable_ids = self.minimization_loop(test_case, inputs)\n        self._progress.pass_finish()\n\n        instructions: List[str] = []\n        with open(test_case.asm_path(), \"r\") as f:\n            for i, line in enumerate(f):\n                if i in modifiable_ids:\n                    # This instruction could be removed.\n                    # Additionally, clear the instrumentation tag from the previous line\n                    if \"instrumentation\" in instructions[-1].lower():\n                        instructions[-1] = instructions[-1].replace(\"instrumentation\", \"\")\n                else:\n                    # This instruction is essential for the violation; keep it\n                    instructions.append(line)\n\n        return self._get_test_case_from_instructions(instructions)\n\n    def modify_instruction(self, instructions: List[str], cursor: int) -> List[str]:\n        return instructions[:cursor] + instructions[cursor + 1:]\n\n    def verify_modification(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        return self._check_for_violation(test_case, inputs, self._ignore_list)\n\n\nclass InstructionSimplificationPass(BaseInstructionMinimizationPass):\n    \"\"\"\n    A minimization pass that iteratively replaces instructions with simpler ones\n    (e.g., `cmov` with `mov`, `add` with `mov`, etc.) and checks\n    if the violation is still triggered.\n    \"\"\"\n    name = \"Instruction Simplification Pass\"\n\n    _instruction_replacements: Dict[str, Callable[[str], str]] = {\n        \"cmova\": lambda _: \"mov\",\n        \"cmovae\": lambda _: \"mov\",\n        \"cmovb\": lambda _: \"mov\",\n        \"cmovbe\": lambda _: \"mov\",\n        \"cmovc\": lambda _: \"mov\",\n        \"cmove\": lambda _: \"mov\",\n        \"cmovg\": lambda _: \"mov\",\n        \"cmovge\": lambda _: \"mov\",\n        \"cmovl\": lambda _: \"mov\",\n        \"cmovle\": lambda _: \"mov\",\n        \"cmovna\": lambda _: \"mov\",\n        \"cmovnae\": lambda _: \"mov\",\n        \"cmovnb\": lambda _: \"mov\",\n        \"cmovnbe\": lambda _: \"mov\",\n        \"cmovnc\": lambda _: \"mov\",\n        \"cmovne\": lambda _: \"mov\",\n        \"cmovng\": lambda _: \"mov\",\n        \"cmovnge\": lambda _: \"mov\",\n        \"cmovnl\": lambda _: \"mov\",\n        \"cmovnle\": lambda _: \"mov\",\n        \"cmovno\": lambda _: \"mov\",\n        \"cmovnp\": lambda _: \"mov\",\n        \"cmovns\": lambda _: \"mov\",\n        \"cmovnz\": lambda _: \"mov\",\n        \"cmovo\": lambda _: \"mov\",\n        \"cmovp\": lambda _: \"mov\",\n        \"cmovs\": lambda _: \"mov\",\n        \"cmovz\": lambda _: \"mov\",\n        \"xchg\": lambda _: \"mov\",\n        \"cmpxchg\": lambda _: \"xchg\",\n        \"rep\": lambda _: \"\",\n        \"lock\": lambda _: \"\",\n        \"add\": lambda _: \"mov\",\n        \"sub\": lambda _: \"add\",\n        \"or\": lambda _: \"add\",\n        \"xor\": lambda _: \"add\",\n        \"and\": lambda _: \"add\",\n        \"cmp\": lambda _: \"add\",\n        \"bsr\": lambda _: \"add\",\n        \"bsf\": lambda _: \"add\",\n        \"bt\": lambda _: \"add\",\n        \"bts\": lambda _: \"add\",\n        \"btr\": lambda _: \"add\",\n        \"btc\": lambda _: \"add\",\n        \"bzhi\": lambda _: \"add\",\n        \"bextr\": lambda _: \"add\",\n        \"blsi\": lambda _: \"add\",\n        \"blsmsk\": lambda _: \"add\",\n        \"xadd\": lambda _: \"add\",\n        \"test\": lambda _: \"add\",\n        \"adc\": lambda _: \"add\",\n        \"sbb\": lambda _: \"sub\",\n        \"mul\": lambda _: \"inc\",\n        \"div\": lambda _: \"inc\",\n        \"setb\": lambda _: \"inc\",\n        \"not\": lambda _: \"inc\",\n        \"idiv\": lambda _: \"div\",\n        \"imul\": lambda line: \"add\" if len(line.split(\",\")) == 2 else \"imul\",\n    }\n\n    def run(self, test_case: TestCaseProgram, inputs: List[InputData]) -> TestCaseProgram:\n        if CONF.instruction_set == \"arm64\":\n            warning(\"postprocessor\", \"--enable-simplification-pass has no effect on ARM64\")\n            return test_case\n\n        inst_ids = self.minimization_loop(test_case, inputs)\n        self._progress.pass_finish()\n\n        with open(test_case.asm_path(), \"r\") as f:\n            instructions = f.readlines()\n        for i in inst_ids:\n            instructions = self.modify_instruction(instructions, i)\n        return self._get_test_case_from_instructions(instructions)\n\n    def modify_instruction(self, instructions: List[str], cursor: int) -> List[str]:\n        tmp = list(instructions)  # make a copy\n        clean_line = tmp[cursor].strip().lower()\n        words = clean_line.split(\" \")\n        key = words[0]\n        replacement_func = self._instruction_replacements.get(key, None)\n        if not replacement_func:\n            return []\n        tmp[cursor] = \" \".join([replacement_func(clean_line)] + words[1:]) + \"\\n\"\n\n        return tmp\n\n    def verify_modification(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        return self._check_for_violation(test_case, inputs, self._ignore_list)\n\n\nclass ConstantSimplificationPass(BaseInstructionMinimizationPass):\n    \"\"\"\n    A minimization pass that iteratively replaces constants in the test case with zeros\n    and checks if the violation is still triggered.\n    \"\"\"\n    name = \"Constant Simplification Pass\"\n\n    def __init__(self, fuzzer: Fuzzer, instruction_set_spec: InstructionSet,\n                 progress: ProgressPrinter):\n        super().__init__(fuzzer, instruction_set_spec, progress)\n        if CONF.instruction_set == \"x86-64\":\n            self._match_dec = re.compile(r\"^-?[0-9]+$\")\n            self._match_hex = re.compile(r\"^-?0x[0-9a-f]+$\")\n            self._match_bin = re.compile(r\"^-?0b[01]+$\")\n            self._replacement = \"0\"\n        elif CONF.instruction_set == \"arm64\":\n            self._match_dec = re.compile(r\"^#-?[0-9]+$\")\n            self._match_hex = re.compile(r\"^#-?0x[0-9a-f]+$\")\n            self._match_bin = re.compile(r\"^#-?0b[01]+$\")\n            self._replacement = \"#1\"  # this value is safe as both an immediate and a bitmask\n        else:\n            assert_never(CONF.instruction_set)\n\n    def run(self, test_case: TestCaseProgram, inputs: List[InputData]) -> TestCaseProgram:\n        inst_ids = self.minimization_loop(test_case, inputs)\n        self._progress.pass_finish()\n\n        with open(test_case.asm_path(), \"r\") as f:\n            instructions = f.readlines()\n        for i in inst_ids:\n            instructions = self.modify_instruction(instructions, i)\n        return self._get_test_case_from_instructions(instructions)\n\n    def modify_instruction(self, instructions: List[str], cursor: int) -> List[str]:\n        tmp = list(instructions)  # make a copy\n        clean_line = tmp[cursor].strip().lower()\n        words = clean_line.split(\",\")\n        for word_id, word in enumerate(words):\n            word = word.strip()\n            if word == self._replacement:  # already replaced\n                break\n            if self._match_dec.match(word) or self._match_hex.match(word) \\\n               or self._match_bin.match(word):\n                tmp[cursor] = \", \".join(words[:word_id] + [self._replacement]\n                                        + words[word_id + 1:]) + \"\\n\"\n                return tmp\n        return []\n\n    def verify_modification(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        return self._check_for_violation(test_case, inputs, self._ignore_list)\n\n\nclass MaskSimplificationPass(BaseInstructionMinimizationPass):\n    \"\"\"\n    A minimization pass that iteratively replaces masks of the instrumentation\n    instructions with smaller masks and checks if the violation is still triggered.\n    E.g., `and rax, 0b1111111111111` -> `and rax, 0b1111111111110`\n    \"\"\"\n    name = \"Mask Simplification Pass\"\n\n    _mask_replacements = {\n        \"0b1111111111111\": \"0b1111111111110\",\n        \"0b1111111111110\": \"0b1111111111100\",\n        \"0b1111111111100\": \"0b1111111111000\",\n        \"0b1111111111000\": \"0b1111111110000\",\n        \"0b1111111110000\": \"0b1111111100000\",\n        \"0b1111111100000\": \"0b1111111000000\",\n        \"0b1111111000000\": \"0b1111110000000\",\n        \"0b1111110000000\": \"0b1111100000000\",\n        \"0b1111100000000\": \"0b1111000000000\",\n        \"0b1111000000000\": \"0b1110000000000\",\n        \"0b1110000000000\": \"0b1100000000000\",\n        \"0b1100000000000\": \"0b1000000000000\",\n        \"0b1000000000000\": \"0b0000000000000\",\n    }\n\n    def run(self, test_case: TestCaseProgram, inputs: List[InputData]) -> TestCaseProgram:\n        inst_ids = self.minimization_loop(test_case, inputs, skip_instrumentation_lines=False)\n        self._progress.pass_finish()\n\n        with open(test_case.asm_path(), \"r\") as f:\n            instructions = f.readlines()\n        for i in inst_ids:\n            instructions = self.modify_instruction(instructions, i)\n        return self._get_test_case_from_instructions(instructions)\n\n    def modify_instruction(self, instructions: List[str], cursor: int) -> List[str]:\n        tmp = list(instructions)  # make a copy\n\n        comment_split = tmp[cursor].split(self._comment_symbol)\n        clean_line = comment_split[0].strip().lower()\n        comment = self._comment_symbol.join(comment_split[1:]) if len(comment_split) > 1 else \"\"\n\n        words = clean_line.split(\",\")\n        for word_id, word in enumerate(words):\n            word = word.strip()\n            replacement = self._mask_replacements.get(word, None)\n            if replacement:\n                tmp[cursor] = \", \".join(words[:word_id] + [replacement] + words[word_id + 1:]) \\\n                    + \" \" + self._comment_symbol + comment\n                return tmp\n\n        return []\n\n    def verify_modification(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        return self._check_for_violation(test_case, inputs, self._ignore_list)\n\n\nclass NopReplacementPass(BaseInstructionMinimizationPass):\n    \"\"\"\n    A minimization pass that iteratively replaces instructions with NOPs\n    of the same size and checks if the violation is still triggered.\n    \"\"\"\n    name = \"NOP Replacement Pass\"\n\n    _replacements_x86 = {\n        1: \"nop  # 1 B\",\n        2: \".byte 0x66, 0x90  # 2 B\",\n        3: \"nop dword ptr [rax]  # 3 B\",\n        4: \"nop qword ptr [rax]  # 4 B\",\n        5: \"nop qword ptr [rax + 1]  # 5 B\",\n        6: \"nop qword ptr [rax + rax + 1]  # 6 B\",\n        7: \"nop dword ptr [rax + 0xff]  # 7 B\",\n        8: \"nop qword ptr [rax + 0xff]  # 8 B\",\n        9: \"nop qword ptr [rax + rax + 0xff]  # 9 B\",\n    }\n    _replacements_arm64 = {\n        4: \"nop\",  # all instructions are 4 bytes\n    }\n\n    def __init__(self, fuzzer: Fuzzer, instruction_set_spec: InstructionSet,\n                 progress: ProgressPrinter):\n        super().__init__(fuzzer, instruction_set_spec, progress)\n        if CONF.instruction_set == \"x86-64\":\n            self._replacements = self._replacements_x86\n        elif CONF.instruction_set == \"arm64\":\n            self._replacements = self._replacements_arm64\n        else:\n            assert_never(CONF.instruction_set)\n\n        self._match_jump = re.compile(r\"^j[a-z]* .*\") if CONF.instruction_set == \"x86-64\" else \\\n            re.compile(r\"^b\\.[a-z]* .*|^bl[a-z]* .*\")\n        self._match_loop = re.compile(r\"^loop[a-z]* .*\") if CONF.instruction_set == \"x86-64\" else \\\n            re.compile(r\"^cbz .*|^cbnz .*|^tbnz .*|^tbz .*\")\n\n    def run(self, test_case: TestCaseProgram, inputs: List[InputData]) -> TestCaseProgram:\n        modified_ids = self.minimization_loop(test_case, inputs, skip_instrumentation_lines=True)\n        self._progress.pass_finish()\n\n        with open(test_case.asm_path(), \"r\") as f:\n            lines = f.readlines()\n\n        instructions = []\n        for i, line in enumerate(lines):\n            # skip non-modifiable lines\n            if i not in modified_ids:\n                instructions.append(line)\n                continue\n\n            # get the NOP replacement\n            replacement = self.modify_instruction([line], 0)\n            if not replacement:\n                warning(\"postprocessor\", f\"Inconsistent NOP output: {line}\")\n                instructions.append(line)\n                continue\n\n            # This instruction could be replaced with a NOP\n            instructions.append(replacement[0])\n\n            # And the instrumentation tag from the previous line can be cleared\n            if \"instrumentation\" in instructions[-2].lower():\n                instructions[-2] = instructions[-2].replace(\"instrumentation\", \"\")\n\n        return self._get_test_case_from_instructions(instructions)\n\n    def modify_instruction(self, instructions: List[str], cursor: int) -> List[str]:\n        tmp = list(instructions)  # make a copy\n\n        line = tmp[cursor].strip().lower()\n        if \"nop\" in line:\n            return []\n\n        # skip jumps as replacing them with nops will confuse our assembly parser\n        if self._match_jump.match(line) or self._match_loop.match(line):\n            return []\n\n        # determine the instruction size\n        with open(\"tmp.asm\", \"w\") as f:\n            if CONF.instruction_set == \"x86-64\":\n                f.write(\".intel_syntax noprefix\\n\")\n            f.write(line)\n            f.write(\"\\n\")\n        run(\"as tmp.asm -o tmp.o\", shell=True, check=True)\n        run(\"objcopy -O binary --only-section=.text tmp.o tmp.o\", shell=True, check=True)\n        size = os.path.getsize(\"tmp.o\")\n        os.remove(\"tmp.asm\")\n        os.remove(\"tmp.o\")\n\n        if size not in self._replacements:\n            return []\n\n        tmp[cursor] = self._replacements[size] + \"\\n\"\n        return tmp\n\n    def verify_modification(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        return self._check_for_violation(test_case, inputs, self._ignore_list)\n\n\nclass LabelRemovalPass(BaseInstructionMinimizationPass):\n    \"\"\"\n    A minimization pass that iteratively removes unused labels from the test case.\n    Note that no verification is performed in this pass as labels are not executed.\n    \"\"\"\n    name = \"Label Removal Pass\"\n    _reserved = [\n        \".intel_syntax noprefix\", \".test_case_exit:\", \".section\", \".function\", \".macro\", \"syntax\"\n    ]\n\n    def run(self, test_case: TestCaseProgram, inputs: List[InputData]) -> TestCaseProgram:\n        with open(test_case.asm_path(), \"r\") as f:\n            instructions = f.readlines()\n            n_instructions = len(instructions)\n\n        for i in range(n_instructions):\n            line = instructions[i].strip().lower()\n\n            # skip non-labels\n            if not line.startswith(\".\"):\n                self._progress.next(False)\n                continue\n\n            # skip reserved labels\n            if any(reserved in line for reserved in self._reserved):\n                continue\n\n            # check if the label is used by other instructions\n            label = instructions[i].strip().replace(\":\", \"\")\n            used = False\n            for inst in instructions:\n                if label in inst and inst != instructions[i]:\n                    used = True\n                    break\n\n            # remove unused labels\n            if not used:\n                self._progress.next(True)\n                instructions[i] = \"\"\n            else:\n                self._progress.next(False)\n\n        self._progress.pass_finish()\n        return self._get_test_case_from_instructions(instructions)\n\n    def modify_instruction(self, instructions: List[str], cursor: int) -> List[str]:\n        return []  # unused\n\n    def verify_modification(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        return True  # unused\n\n\nclass FenceInsertionPass(BaseInstructionMinimizationPass):\n    \"\"\"\n    A minimization pass that iteratively inserts LFENCE instructions before each instruction\n    and checks if the violation is still triggered.\n    \"\"\"\n    name = \"Fence Insertion Pass\"\n\n    def __init__(self, fuzzer: Fuzzer, instruction_set_spec: InstructionSet,\n                 progress: ProgressPrinter):\n        super().__init__(fuzzer, instruction_set_spec, progress)\n        self._match_jump = re.compile(r\"^j[a-z]* .*\") if CONF.instruction_set == \"x86-64\" else \\\n            re.compile(r\"^b\\.[a-z]* .*|^bl[a-z]* .*\")\n        self._match_loop = re.compile(r\"^loop[a-z]* .*\") if CONF.instruction_set == \"x86-64\" else \\\n            re.compile(r\"^cbz .*|^cbnz .*|^tbnz .*|^tbz .*\")\n        self._fence = \"lfence\" if CONF.instruction_set == \"x86-64\" else \"dsb sy\\n isb\"\n\n    def run(self, test_case: TestCaseProgram, inputs: List[InputData]) -> TestCaseProgram:\n        inst_ids = self.minimization_loop(test_case, inputs)\n        self._progress.pass_finish()\n\n        with open(test_case.asm_path(), \"r\") as f:\n            instructions = f.readlines()\n        for i in inst_ids:\n            instructions = instructions[:i] + [self._fence + \"\\n\"] + instructions[i:]\n        return self._get_test_case_from_instructions(instructions)\n\n    def modify_instruction(self, instructions: List[str], cursor: int) -> List[str]:\n        curr_instr = instructions[cursor].lower()\n        if self._match_jump.match(curr_instr) or self._match_loop.match(curr_instr):\n            return []  # skip control-flow instructions - their target is already fenced\n        return instructions[:cursor] + [self._fence + \"\\n\"] + instructions[cursor:]\n\n    def verify_modification(self, test_case: TestCaseProgram, inputs: List[InputData]) -> bool:\n        return self._check_for_violation(test_case, inputs, self._ignore_list)\n"
  },
  {
    "path": "rvzr/postprocessing/minimizer.py",
    "content": "\"\"\" File: Entry point for the postprocessing module.\n    It selects the appropriate minimization passes based on the command-line arguments,\n    and then runs them.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nimport shutil\nimport os\n\nfrom copy import deepcopy\nfrom typing import List, NamedTuple, Dict, TYPE_CHECKING, Any, Type, Optional\nfrom ..traces import Violation\nfrom ..tc_components.test_case_code import TestCaseProgram\nfrom ..tc_components.test_case_data import InputData\nfrom ..config import CONF\nfrom ..logs import warning, error, update_logging_after_config_change\nfrom ..fuzzer import Fuzzer\n\nfrom .instruction_passes import BaseInstructionMinimizationPass, InstructionRemovalPass, \\\n    InstructionSimplificationPass, NopReplacementPass, ConstantSimplificationPass, \\\n    MaskSimplificationPass, LabelRemovalPass, FenceInsertionPass\nfrom .input_passes import BaseInputMinimizationPass, InputSequenceMinimizationPass, \\\n    DifferentialInputMinimizerPass\nfrom .analysis_passes import AddViolationCommentsPass\nfrom .progress_printer import ProgressPrinter\n\nif TYPE_CHECKING:\n    from ..isa_spec import InstructionSet\n\nTMP_DIR = \"/tmp/rvzr_minimize\"\n\n\nclass PassDesc(NamedTuple):\n    \"\"\" A named tuple to store the minimization pass description \"\"\"\n    cls_: Type[BaseInstructionMinimizationPass | BaseInputMinimizationPass]\n    is_analysis_pass: bool\n\n\nclass Minimizer:\n    \"\"\"\n    Main class for the postprocessing module. It selects the appropriate minimization passes\n    based on the command-line arguments, and then runs them.\n    \"\"\"\n\n    ignore_list: List[int]\n    \"\"\" List of input IDs that will be ignored during minimization \"\"\"\n\n    pass_map: Dict[str, PassDesc]\n    \"\"\" Mapping of pass names to their classes \"\"\"\n\n    _instruction_passes: List[Type[BaseInstructionMinimizationPass]]\n    _input_passes: List[Type[BaseInputMinimizationPass]]\n    _analysis_passes: List[Type[BaseInstructionMinimizationPass]]\n\n    def __init__(self, fuzzer: Fuzzer, instruction_set_spec: InstructionSet):\n        self._fuzzer = fuzzer\n        self._progress = ProgressPrinter()\n        self.instruction_set_spec = instruction_set_spec\n        self.ignore_list = []\n\n        # manage tmp directory\n        if not os.path.exists(TMP_DIR):\n            os.makedirs(TMP_DIR)\n\n        # initialize the pass map\n        self.pass_map = {\n            \"instruction_pass\": PassDesc(InstructionRemovalPass, False),\n            \"simplification_pass\": PassDesc(InstructionSimplificationPass, False),\n            \"nop_pass\": PassDesc(NopReplacementPass, False),\n            \"constant_pass\": PassDesc(ConstantSimplificationPass, False),\n            \"mask_pass\": PassDesc(MaskSimplificationPass, False),\n            \"label_pass\": PassDesc(LabelRemovalPass, False),\n            \"fence_pass\": PassDesc(FenceInsertionPass, True),\n            \"input_seq_pass\": PassDesc(InputSequenceMinimizationPass, False),\n            \"input_diff_pass\": PassDesc(DifferentialInputMinimizerPass, False),\n            \"comment_pass\": PassDesc(AddViolationCommentsPass, True),\n        }\n\n    def __del__(self) -> None:\n        # remove tmp directory\n        if os.path.exists(TMP_DIR):\n            shutil.rmtree(TMP_DIR)\n\n    def run(self, test_case_asm: str, n_inputs: int, test_case_outfile: str, input_outdir: str,\n            n_attempts: int, **enabled_passes: Any) -> None:\n        \"\"\"\n        Run the minimization passes based on the command-line arguments, passed as arguments\n        to this function. It first reproduces the violation, then run input passes,\n        then instruction passes, and finally the analysis passes. The resulting minimized program\n        is stored into `test_case_outfile` and the resulting minimized input sequence is stored\n        into `input_outdir`.\n\n        :param test_case_asm: Path to the test case assembly file\n        :param n_inputs: Number of inputs to use during the minimization\n        :param test_case_outfile: Path to store the minimized test case\n        :param input_outdir: Path to store the minimized inputs\n        :param n_attempts: Number of attempts to run the instruction minimization passes\n        :param enabled_passes: Dictionary of arguments to enable/disable the passes.\n               Supported keys:\n               - enable_instruction_pass\n               - enable_simplification_pass\n               - enable_nop_pass\n               - enable_constant_pass\n               - enable_mask_pass\n               - enable_label_pass\n               - enable_fence_pass\n               - enable_input_seq_pass\n               - enable_input_diff_pass\n               - enable_comment_pass\n        :return: None\n        \"\"\"\n        self._reset(enabled_passes)\n\n        # Parse the test case and inputs\n        test_case = self._fuzzer.asm_parser.parse_file(test_case_asm, self._fuzzer.code_gen,\n                                                       self._fuzzer.elf_parser)\n        inputs = self._fuzzer.data_gen.generate(n_inputs, n_actors=test_case.n_actors())\n\n        # Check if the violation can be reproduced\n        violation = self._reproduce_org_violation(test_case, inputs)\n        if not violation:\n            return\n\n        # Run the input minimization passes\n        if self._input_passes:\n            new_inputs = self._run_input_passes(test_case, inputs, violation, input_outdir)\n\n            # Check if the violation can be reproduced with the new inputs\n            new_violation = self._fuzzer.fuzzing_round(test_case, inputs, [])\n            if new_violation:\n                # Use new inputs in future passes\n                inputs = new_inputs\n                violation = new_violation\n\n                # Disable boosting from now on:\n                # The minimized input sequence is now guaranteed to be boosted\n                CONF.inputs_per_class = 1\n            else:\n                warning(\"postprocessor\", \"Non-reproducible input sequence minimization. Reverting\")\n\n        # Set the non-violating inputs as the ignore list\n        violating_ids = [m.input_id for m in violation.measurements]\n        self.ignore_list = \\\n            [i for i in range(len(violation.input_sequence)) if i not in violating_ids]\n        self._progress.pass_msg(f\"Violating input IDs: {violating_ids}\")\n\n        # Run the instruction minimization passes\n        for attempt in range(n_attempts):\n            self._progress.global_msg(f\"Minimization attempt {attempt + 1}/{n_attempts}\")\n            old_tc = deepcopy(test_case)\n            test_case = self._run_instruction_passes(test_case, inputs, violation,\n                                                     test_case_outfile)\n            if test_case == old_tc:  # break if no progress was made\n                break\n\n        # Run the analysis passes\n        test_case = self._run_analysis_passes(test_case, inputs, violation, test_case_outfile)\n\n        # Get rid of unused labels\n        if enabled_passes.get(\"enable_label_pass\", False):\n            self._instruction_passes = [LabelRemovalPass]\n            test_case = self._run_instruction_passes(test_case, inputs, violation,\n                                                     test_case_outfile)\n\n        # Store the results\n        self._progress.pass_start(\"Storing the results\")\n        test_case.save(test_case_outfile)\n\n    def _reset(self, enabled_passes: Dict[str, Any]) -> None:\n        # Get lists of enabled passes\n        self._set_passes(enabled_passes)\n\n        # Reset the ignore list\n        self.ignore_list = []\n\n        # Adjust the sample size to reduce non-reproducibility\n        CONF.executor_sample_sizes = [CONF.executor_sample_sizes[-1]]\n\n        # Make sure that fuzzing progress is not printed\n        if \"info\" in CONF.logging_modes:\n            CONF.logging_modes.remove(\"info\")\n            update_logging_after_config_change()\n\n    def _reproduce_org_violation(self, test_case: TestCaseProgram,\n                                 inputs: List[InputData]) -> Optional[Violation]:\n        self._progress.pass_start(\"Reproducing the violation\")\n        for _ in range(CONF.minimizer_retries):\n            violation = self._fuzzer.fuzzing_round(test_case, inputs, [])\n            if violation:\n                self._progress.pass_msg(\"Violation reproduced. Proceeding with minimization\")\n                return violation\n        self._progress.pass_msg(\"Could not reproduce the violation. Exiting\")\n        return None\n\n    def _set_passes(self, enabled_passes: Dict[str, Any]) -> None:\n        passes: List[PassDesc] = \\\n            [v for k, v in self.pass_map.items() if enabled_passes.get(f\"enable_{k}\", False)]\n        self._input_passes = [\n            p.cls_ for p in passes if issubclass(p.cls_, BaseInputMinimizationPass)\n        ]\n        self._instruction_passes = [\n            p.cls_\n            for p in passes\n            if issubclass(p.cls_, BaseInstructionMinimizationPass) and not p.is_analysis_pass\n        ]\n        self._analysis_passes = [\n            p.cls_\n            for p in passes\n            if issubclass(p.cls_, BaseInstructionMinimizationPass) and p.is_analysis_pass\n        ]\n\n    def _run_input_passes(self, test_case: TestCaseProgram, inputs: List[InputData],\n                          org_violation: Violation, outdir: str) -> List[InputData]:\n        violation = org_violation\n\n        for pass_cls in self._input_passes:\n            # Create the pass object\n            pass_ = pass_cls(self._fuzzer, self.instruction_set_spec, self._progress)\n            self._progress.pass_start(pass_.name)\n\n            # Run the pass\n            new_inputs = pass_.run(test_case, inputs, violation)\n\n            # Recreate the violation with the new input sequence\n            new_violation = self._fuzzer.fuzzing_round(test_case, new_inputs, [])\n            if new_violation:\n                violation = new_violation\n                inputs = new_inputs\n            else:\n                self._progress.pass_msg(\"[WARNING] Non-reproducible sequence minimization\"\n                                        \". Rolling back to the previous state\")\n\n        # Create the output directory, if not already exists\n        if outdir and not os.path.exists(outdir):\n            try:\n                os.makedirs(outdir)\n            except OSError:\n                error(f\"Creation of the directory {outdir} failed\")\n            outdir = os.path.abspath(outdir)\n\n        # Store the results\n        self._progress.pass_msg(f\"Saving new inputs in '{outdir}'\")\n        for i, input_ in enumerate(inputs):\n            input_.save(f\"{outdir}/min_input_{i:04}.bin\")\n\n        return inputs\n\n    def _run_instruction_passes(self, test_case: TestCaseProgram, inputs: List[InputData],\n                                org_violation: Violation, outfile: str) -> TestCaseProgram:\n        # create pass objects\n        passes = self._instruction_passes\n        pass_objs = [c(self._fuzzer, self.instruction_set_spec, self._progress) for c in passes]\n        for pass_obj in pass_objs:\n            pass_obj.set_ignore_list(self.ignore_list)\n            pass_obj.set_violation(org_violation)\n\n        # run passes\n        for pass_obj in pass_objs:\n            self._progress.pass_start(pass_obj.name)\n            test_case = pass_obj.run(test_case, inputs)\n            test_case.save(outfile)\n\n        return test_case\n\n    def _run_analysis_passes(self, test_case: TestCaseProgram, inputs: List[InputData],\n                             org_violation: Violation, outfile: str) -> TestCaseProgram:\n        # create pass objects\n        passes = self._analysis_passes\n        pass_objs = [c(self._fuzzer, self.instruction_set_spec, self._progress) for c in passes]\n        for pass_obj in pass_objs:\n            pass_obj.set_ignore_list(self.ignore_list)\n            pass_obj.set_violation(org_violation)\n\n        # run passes\n        for pass_obj in pass_objs:\n            self._progress.pass_start(pass_obj.name)\n            test_case = pass_obj.run(test_case, inputs)\n            test_case.save(outfile)\n\n        return test_case\n"
  },
  {
    "path": "rvzr/postprocessing/pass_abc.py",
    "content": "\"\"\" File: Abstract interfaces for minimization passes and common functionality.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nimport abc\nimport tempfile\nfrom typing import TYPE_CHECKING, List, Final\n\nfrom ..config import CONF\n\nif TYPE_CHECKING:\n    from ..fuzzer import Fuzzer\n    from ..isa_spec import InstructionSet\n    from ..tc_components.test_case_code import TestCaseProgram\n    from ..tc_components.test_case_data import InputData\n    from .progress_printer import ProgressPrinter\n\n\nclass BaseMinimizationPass(abc.ABC):\n    \"\"\" Base class for all minimization passes. Provides common functionality \"\"\"\n    name: str = \"\"\n    _fuzzer: Final[Fuzzer]\n    _instruction_set_spec: Final[InstructionSet]\n    _progress: Final[ProgressPrinter]\n    _ignore_list: List[int]\n\n    def __init__(self, fuzzer: Fuzzer, instruction_set_spec: InstructionSet,\n                 progress: ProgressPrinter):\n        self._fuzzer = fuzzer\n        self._instruction_set_spec = instruction_set_spec\n        self._progress = progress\n        self._ignore_list = []\n\n        self._comment_symbol = \"#\" if CONF.instruction_set == \"x86-64\" else \"//\"\n        self._base_register = \"r14\" if CONF.instruction_set == \"x86-64\" else \"x20\"\n\n    def set_ignore_list(self, ignore_list: List[int]) -> None:\n        \"\"\" Set the list of input IDs to ignore \"\"\"\n        self._ignore_list = ignore_list\n\n    def _get_test_case_from_instructions(self,\n                                         instructions: List[str],\n                                         path: str = \"\") -> TestCaseProgram:\n        \"\"\"\n        Create a test case object from a list of instructions.\n        The test case is stored in a file at the given path.\n        :param instructions: List of instructions\n        :param path: Path to store the test case; if empty, a temporary file is created\n        :return: Test case object\n        \"\"\"\n        # create a temporary file if no path is given\n        if not path:\n            with tempfile.NamedTemporaryFile(dir=\"/tmp/rvzr_minimize\", delete=False) as fp:\n                path = fp.name\n        # print(path)\n\n        # write the instructions to the file\n        with open(path, \"w+\") as f:\n            for line in instructions:\n                f.write(line)\n        tc = self._fuzzer.asm_parser.parse_file(path, self._fuzzer.code_gen,\n                                                self._fuzzer.elf_parser)\n        return tc\n\n    def _check_for_violation(self, test_case: TestCaseProgram, inputs: List[InputData],\n                             local_ignore_list: List[int]) -> bool:\n        \"\"\"\n        Check if the test case triggers the violation.\n        :param test_case: The test case to check\n        :param inputs: List of inputs to use for verification\n        :param ignore_list: List of input IDs to ignore\n        :return: True if the violation is triggered, False otherwise\n        \"\"\"\n        for _ in range(CONF.minimizer_retries):\n            violation = self._fuzzer.fuzzing_round(test_case, inputs, local_ignore_list)\n            if violation is not None:\n                return True\n        return False\n"
  },
  {
    "path": "rvzr/postprocessing/progress_printer.py",
    "content": "\"\"\" File: Printing of the minimization progress\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n\n\nclass ProgressPrinter():\n    \"\"\"\n    A simple class to print progress in the terminal.\n    Used to ensure that all minimization classes\n    provide a uniform output.\n    \"\"\"\n    line_width: int = 64\n    curr_width: int = 0\n    offset: int = 2\n    pass_id: int = 0\n    progress_bar_on: bool = False\n\n    def pass_start(self, label: str, offset: int = 2) -> None:\n        \"\"\" Start a new minimization pass \"\"\"\n        self.pass_id += 1\n        self.offset = offset\n        self.curr_width = 0\n        self.progress_bar_on = False\n        print(f\"[PASS {self.pass_id}] {label}\", flush=True)\n\n    def pass_finish(self) -> None:\n        \"\"\" Finish the current minimization pass \"\"\"\n        print(\"\")  # finish the line\n\n    def pass_msg(self, msg: str) -> None:\n        \"\"\" Print a message related to the current pass \"\"\"\n        print(\" \" * self.offset + \"> \" + msg)\n        self.progress_bar_on = False\n\n    def next(self, success: bool) -> None:\n        \"\"\" Print a progress bar \"\"\"\n        if not self.progress_bar_on:\n            print(\"\")\n            self.progress_bar_on = True\n\n        self.curr_width += 1\n        if self.curr_width > self.line_width:\n            print(\"\\n\", end=\"\", flush=True)\n            self.curr_width = self.offset\n\n        if success:\n            print(\".\", end=\"\", flush=True)\n        else:\n            print(\"-\", end=\"\", flush=True)\n\n    def global_msg(self, msg: str) -> None:\n        \"\"\" Print a message that is not related to the current pass \"\"\"\n        print(f\"[INFO] {msg}\")\n"
  },
  {
    "path": "rvzr/py.typed",
    "content": ""
  },
  {
    "path": "rvzr/sandbox.py",
    "content": "\"\"\"\nFile: Constants defining the memory layout for the data and code sandboxes,\n      which should be identical between the executor and the model.\n      See docs/sandbox.md for more information.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom enum import Enum\nfrom typing import Dict, List, Tuple, TYPE_CHECKING\n\nimport numpy as np\n\nif TYPE_CHECKING:\n    from .tc_components.test_case_code import TestCaseProgram\n\nPAGE_SIZE = 4096\n\nSandboxAddr = int\nDataAddr = SandboxAddr\nCodeAddr = SandboxAddr\nBaseAddrTuple = Tuple[DataAddr, CodeAddr]\n\n\n# ==================================================================================================\n# Area Enumerations\n# ==================================================================================================\nclass DataArea(Enum):\n    \"\"\"\n    Enumeration class representing data areas in the sandbox.\n    \"\"\"\n    START = 0\n    MACRO_STACK = 1\n    UNDERFLOW_PAD = 2\n    MAIN = 3\n    FAULTY = 4\n    REG_INIT = 5\n    GPR = 6\n    SIMD = 7\n    OVERFLOW_PAD = 8\n    RSP_INIT = 9\n\n\nclass CodeArea(Enum):\n    \"\"\"\n    Enumeration class representing code areas in the sandbox.\n    \"\"\"\n    START = 0\n    MAIN = 1\n    MACRO = 2\n\n\n# ==================================================================================================\n# Sandbox Layout Class\n# ==================================================================================================\nclass SandboxLayout:\n    \"\"\"\n    Layout of the data and code sandboxes. This class is responsible for ensuring\n    consistency of memory layouts between the executor, the model, and the generators.\n    \"\"\"\n    _data_start: DataAddr\n    _data_end: DataAddr\n    _code_start: CodeAddr\n    _code_end: CodeAddr\n\n    _data_addresses: List[Dict[DataArea, DataAddr]]\n    _code_addresses: List[Dict[CodeArea, CodeAddr]]\n\n    # NOTE: the constants in _DataAreaLayout and _CodeAreaLayout *must* be identical\n    # to the actor_data_t and actor_code_t in executor (rvzr/executor_km/include/sandbox_manager.h)\n    _DataAreaLayout = np.dtype(\n        [\n            ('MACRO_STACK', np.uint8, 64),\n            ('UNDERFLOW_PAD', np.uint8, PAGE_SIZE - 64),\n            ('MAIN', np.uint8, PAGE_SIZE),\n            ('FAULTY', np.uint8, PAGE_SIZE),\n            ('GPR', np.uint8, 64),  # 8 64-bit GPRs\n            ('SIMD', np.uint8, 256),  # 8 256-bit YMMs\n            ('OVERFLOW_PAD', np.uint8, PAGE_SIZE - 64 - 256),\n        ],\n        align=False,\n    )\n\n    _CodeAreaLayout = np.dtype(\n        [\n            ('MAIN', np.uint8, 2 * PAGE_SIZE),\n            ('MACRO', np.uint8, PAGE_SIZE),\n        ],\n        align=False,\n    )\n\n    # ==============================================================================================\n    # Constant Accessors\n    # ==============================================================================================\n    @classmethod\n    def data_area_size(cls, area: DataArea) -> int:\n        \"\"\"\n        Get the size of a specific area in the data sandbox.\n        :param area: The area to get the size of.\n        :return: The size of the area in bytes.\n        \"\"\"\n        return cls._DataAreaLayout[area.name].itemsize\n\n    @classmethod\n    def data_area_offset(cls, area: DataArea) -> int:\n        \"\"\"\n        Get the offset of a specific area in the data sandbox.\n        :param area: The area to get the offset of.\n        :return: The offset of the area in bytes.\n        \"\"\"\n        if area == DataArea.START:\n            return 0\n        if area == DataArea.REG_INIT:\n            return cls._DataAreaLayout.fields['GPR'][1]  # type: ignore\n        if area == DataArea.RSP_INIT:\n            return cls._DataAreaLayout.fields['FAULTY'][1] - 8  # type: ignore\n        return cls._DataAreaLayout.fields[area.name][1]  # type: ignore\n\n    @classmethod\n    def data_size_per_actor(cls) -> int:\n        \"\"\"\n        Get the size of the data sandbox for a single actor.\n        :return: The size of the data sandbox for a single actor in bytes.\n        \"\"\"\n        return cls._DataAreaLayout.itemsize\n\n    @classmethod\n    def code_area_size(cls, area: CodeArea) -> int:\n        \"\"\"\n        Get the size of a specific area in the code sandbox.\n        :param area: The area to get the size of.\n        :return: The size of the area in bytes.\n        \"\"\"\n        return cls._CodeAreaLayout[area.name].itemsize\n\n    @classmethod\n    def code_area_offset(cls, area: CodeArea) -> int:\n        \"\"\"\n        Get the offset of a specific area in the code sandbox.\n        :param area: The area to get the offset of.\n        :return: The offset of the area in bytes.\n        \"\"\"\n        if area == CodeArea.START:\n            return 0\n        return cls._CodeAreaLayout.fields[area.name][1]  # type: ignore\n\n    @classmethod\n    def code_size_per_actor(cls) -> int:\n        \"\"\"\n        Get the size of the code sandbox for a single actor.\n        :return: The size of the code sandbox for a single actor in bytes.\n        \"\"\"\n        return cls._CodeAreaLayout.itemsize\n\n    # ==============================================================================================\n    # Object Interface\n    # ==============================================================================================\n    def __init__(self, bases: BaseAddrTuple, n_actors: int):\n        # Data boundaries\n        self._data_start = bases[0]\n        self.data_size = self._DataAreaLayout.itemsize * n_actors\n        self._data_end = bases[0] + self.data_size\n        assert self.data_size % PAGE_SIZE == 0\n\n        # Code boundaries\n        self._code_start = bases[1]\n        self.code_size = self._CodeAreaLayout.itemsize * n_actors\n        self._code_end = bases[1] + self.code_size\n        assert self.code_size % PAGE_SIZE == 0\n\n        # Pre-compute data and code addresses\n        # Note: This is makes sense because we assume that the object will be initialized\n        # once and used many times.\n        self._data_addresses = []\n        for actor_id in range(n_actors):\n            actor_data_start = self._data_start + actor_id * self.data_size_per_actor()\n            self._data_addresses.append(\n                {area: actor_data_start + self.data_area_offset(area) for area in DataArea})\n        self._code_addresses = []\n        for actor_id in range(n_actors):\n            actor_code_start = self._code_start + actor_id * self.code_size_per_actor()\n            self._code_addresses.append(\n                {area: actor_code_start + self.code_area_offset(area) for area in CodeArea})\n\n    def code_start(self) -> CodeAddr:\n        \"\"\" Read-only access to the code start address \"\"\"\n        return self._code_start\n\n    def code_end(self) -> CodeAddr:\n        \"\"\" Read-only access to the code end address \"\"\"\n        return self._code_end\n\n    def data_start(self) -> DataAddr:\n        \"\"\" Read-only access to the data start address \"\"\"\n        return self._data_start\n\n    def data_end(self) -> DataAddr:\n        \"\"\" Read-only access to the data end address \"\"\"\n        return self._data_end\n\n    def get_data_addr(self, area: DataArea, actor_id: int) -> DataAddr:\n        \"\"\"\n        Get the starting address of a specific area in the data sandbox for a given actor.\n        :param area: The area to get the address of.\n        :param actor_id: The actor to get the address for.\n        :return: The starting address of the area in the data sandbox.\n        \"\"\"\n        actor_data_start = self._data_start + actor_id * self.data_size_per_actor()\n        return actor_data_start + self.data_area_offset(area)\n\n    def get_code_addr(self, area: CodeArea, actor_id: int) -> CodeAddr:\n        \"\"\"\n        Get the starting address of a specific area in the code sandbox for a given actor.\n        :param area: The area to get the address of.\n        :param actor_id: The actor to get the address for.\n        :return: The starting address of the area in the code sandbox.\n        \"\"\"\n        actor_code_start = self._code_start + actor_id * self.code_size_per_actor()\n        return actor_code_start + self.code_area_offset(area)\n\n    def get_exit_addr(self, test_case: TestCaseProgram) -> CodeAddr:\n        \"\"\"\n        Get the address of the exit instruction in the code sandbox for a given test case.\n        :param test_case: The test case to get the exit address for.\n        :return: The exit address\n        \"\"\"\n        main_section = test_case.find_section(name=\"main\")\n        main_size = main_section.get_elf_data()[\"size\"]\n        exit_offset = self._code_start + main_size - 1\n        return exit_offset\n\n    def is_data_addr(self, addr: DataAddr) -> bool:\n        \"\"\"\n        Check if the given address is within the data sandbox.\n        :param addr: The address to check.\n        :return: True if the address is within the data sandbox, False otherwise.\n        \"\"\"\n        return self._data_start <= addr < self._data_end\n\n    def is_code_addr(self, addr: CodeAddr) -> bool:\n        \"\"\"\n        Check if the given address is within the code sandbox.\n        :param addr: The address to check.\n        :return: True if the address is within the code sandbox, False otherwise.\n        \"\"\"\n        return self._code_start <= addr < self._code_end\n\n    def data_addr_to_offset(self, addr: DataAddr) -> DataAddr:\n        \"\"\"\n        Convert the given address to an offset within the data sandbox.\n        :param addr: The address to convert.\n        :return: The offset within the data sandbox.\n        \"\"\"\n        return addr - self._data_start\n\n    def code_addr_to_offset(self, addr: CodeAddr) -> CodeAddr:\n        \"\"\"\n        Convert the given address to an offset within the code sandbox.\n        :param addr: The address to convert.\n        :return: The offset within the code sandbox.\n        \"\"\"\n        return addr - self._code_start\n\n    def code_addr_to_actor_id(self, addr: CodeAddr) -> int:\n        \"\"\"\n        Given a code address, identify the actor ID that the code address belongs to.\n        :param addr: Code address\n        :return: Actor ID\n        \"\"\"\n        return (addr - self._code_start) // self.code_size_per_actor()\n\n    def data_addr_to_actor_id(self, addr: DataAddr) -> int:\n        \"\"\"\n        Given a data address, identify the actor ID that the data address belongs to.\n        :param addr: Data address\n        :return: Actor ID\n        \"\"\"\n        return (addr - self._data_start) // self.data_size_per_actor()\n"
  },
  {
    "path": "rvzr/stats.py",
    "content": "\"\"\" File: Global statistics class\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import Any, Dict\n\n\nclass FuzzingStats:\n    \"\"\"\n    Class responsible for storing and managing fuzzing statistics.\n    Implements the Borg pattern to share the state between instances.\n    \"\"\"\n    _borg_shared_state: Dict[Any, Any] = {}\n\n    test_cases: int = 0\n    num_inputs: int = 0\n    eff_classes: int = 0\n    single_entry_classes: int = 0\n    violations: int = 0\n    analysed_test_cases: int = 0\n    executor_reruns: int = 0\n\n    spec_filter: int = 0\n    observ_filter: int = 0\n    fast_path: int = 0\n    fp_nesting: int = 0\n    fp_taint_mistakes: int = 0\n    fp_early_priming: int = 0\n    fp_large_sample: int = 0\n    fp_priming: int = 0\n\n    # Implementation of Borg pattern\n    def __init__(self) -> None:\n        self.__dict__ = self._borg_shared_state\n\n    def __str__(self) -> str:\n        total_clss = self.eff_classes + self.single_entry_classes\n        total_clss_per_test_case = total_clss / self.analysed_test_cases \\\n            if self.analysed_test_cases else 0\n        effective_clss = self.eff_classes / self.analysed_test_cases \\\n            if self.analysed_test_cases else 0\n        iptc = self.num_inputs / self.test_cases if self.test_cases else 0\n\n        s = \"\"\n        s += f\"Test Cases: {self.test_cases}\\n\"\n        s += f\"Inputs per test case: {iptc:.1f}\\n\"\n        s += f\"Violations: {self.violations}\\n\"\n        s += \"Effectiveness: \\n\"\n        s += f\"  Total Cls: {total_clss_per_test_case:.1f}\\n\"\n        s += f\"  Effective Cls: {effective_clss:.1f}\\n\"\n        s += \"Discarded Test Cases:\\n\"\n        s += f\"  Speculation Filter: {self.spec_filter}\\n\"\n        s += f\"  Observation Filter: {self.observ_filter}\\n\"\n        s += f\"  Fast Path: {self.fast_path}\\n\"\n        s += f\"  Max Nesting Check: {self.fp_nesting}\\n\"\n        s += f\"  Tainting Check: {self.fp_taint_mistakes}\\n\"\n        s += f\"  Early Priming Check: {self.fp_early_priming}\\n\"\n        s += f\"  Large Sample Check: {self.fp_large_sample}\\n\"\n        s += f\"  Priming Check: {self.fp_priming}\\n\"\n        return s\n\n    def get_brief(self) -> str:\n        \"\"\" Return a brief one-line summary of the statistics \"\"\"\n\n        if self.test_cases == 0:\n            return \"\"\n\n        if self.analysed_test_cases:\n            all_cls = (self.eff_classes + self.single_entry_classes) // self.analysed_test_cases\n            eff_cls = self.eff_classes // self.analysed_test_cases\n        else:\n            all_cls = 0\n            eff_cls = 0\n        executor_reruns = self.executor_reruns // self.num_inputs\n        s = f\"Cls:{eff_cls}/{all_cls},\"\n        s += f\"In:{self.num_inputs // self.test_cases},\"\n        s += f\"R:{executor_reruns},\"\n        s += f\"SF:{self.spec_filter},\"\n        s += f\"OF:{self.observ_filter},\"\n        s += f\"Fst:{self.fast_path},\" \\\n             f\"CN:{self.fp_nesting},\" \\\n             f\"CT:{self.fp_taint_mistakes},\" \\\n             f\"P1:{self.fp_early_priming},\" \\\n             f\"CS:{self.fp_large_sample},\" \\\n             f\"P2:{self.fp_priming},\" \\\n             f\"V:{self.violations}\"\n        return s\n"
  },
  {
    "path": "rvzr/target_desc.py",
    "content": "\"\"\"\nFile: Architectural details of the target platform,\nsuch as register sizes, register names, and CPU description.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom abc import ABC, abstractmethod\nfrom typing import Dict, List, Tuple, NamedTuple, Literal, TYPE_CHECKING\nimport subprocess\n\nfrom rvzr.config import CONF, PagePropertyName\n\nif TYPE_CHECKING:\n    from .tc_components.instruction import Instruction, RegSize\n\n# ==================================================================================================\n# Custom Types\n# ==================================================================================================\nVendor = Literal[\"Intel\", \"AMD\", \"ARM\", \"Unknown\"]\nRegName = str\nRegNormalizedName = str\nRegUnicornID = int\nPTEBitName = Literal[\"present\", \"writable\", \"non_writable\", \"user\", \"write-through\",\n                     \"cache-disable\", \"accessed\", \"dirty\", \"reserved_bit\", \"executable\",\n                     \"non_executable\", \"valid\"]\nPTEBitOffset = int\nPTEBitNameMapper = Dict[PagePropertyName, Tuple[PTEBitName, bool]]\n\n\n# ==================================================================================================\n# Use-case Specific Descriptors\n# ==================================================================================================\nclass CPUDesc(NamedTuple):\n    \"\"\" CPU description. \"\"\"\n\n    vendor: Vendor\n    model: int\n    family: int\n    stepping: int\n\n\nclass MacroSpec(NamedTuple):\n    \"\"\" Macro specification. \"\"\"\n\n    type_: int\n    name: str\n    args: Tuple[str, str, str, str]\n\n\nclass UnicornTargetDesc:  # pylint: disable=too-few-public-methods\n    \"\"\" Target description in the context of a Unicorn-based model \"\"\"\n\n    usable_registers: List[RegUnicornID]\n    \"\"\" List of Unicorn register IDs that are used by test cases on the target platform. \"\"\"\n\n    usable_simd128_registers: List[RegUnicornID]\n    \"\"\" List of Unicorn SIMD register IDs that are used by test cases on the target platform. \"\"\"\n\n    reg_str_to_constant: Dict[RegName, RegUnicornID]\n    \"\"\" Mapping from register names to their Unicorn constants. \"\"\"\n\n    reg_norm_to_constant: Dict[RegNormalizedName, RegUnicornID]\n    \"\"\" Mapping from normalized register names to their Unicorn constants. \"\"\"\n\n    barriers: List[str]\n    \"\"\" List of instruction names that are considered as speculation barriers \"\"\"\n\n    flags_register: RegUnicornID\n    \"\"\" Unicorn register ID of the flags register \"\"\"\n\n    pc_register: RegUnicornID\n    \"\"\" Unicorn register ID of the program counter register \"\"\"\n\n    sp_register: RegUnicornID\n    \"\"\" Unicorn register ID of the stack pointer register \"\"\"\n\n    actor_base_register: RegUnicornID\n    \"\"\" Unicorn register ID of the register that holds the base address of the active actor \"\"\"\n\n\n# ==================================================================================================\n# Main Target Description\n# ==================================================================================================\nclass TargetDesc(ABC):\n    \"\"\" Abstract class defining the interface to target description classes. \"\"\"\n\n    cpu_desc: CPUDesc\n    \"\"\" Target CPU description. \"\"\"\n\n    # List of macro specifications. All macros are cross-platform, hence the same for all targets.\n    macro_specs: Dict[str, MacroSpec] = {\n        # macros with negative IDs are used for generation\n        # and are not supposed to reach the final binary\n        \"random_instructions\":\n            MacroSpec(-1, \"random_instructions\", (\"int\", \"int\", \"\", \"\")),\n\n        # macros with positive IDs are used for execution and can be interpreted by executor/model\n        \"function\":\n            MacroSpec(0, \"function\", (\"\", \"\", \"\", \"\")),\n        \"measurement_start\":\n            MacroSpec(1, \"measurement_start\", (\"\", \"\", \"\", \"\")),\n        \"measurement_end\":\n            MacroSpec(2, \"measurement_end\", (\"\", \"\", \"\", \"\")),\n        \"fault_handler\":\n            MacroSpec(3, \"fault_handler\", (\"\", \"\", \"\", \"\")),\n        \"switch\":\n            MacroSpec(4, \"switch\", (\"actor_id\", \"function_id\", \"\", \"\")),\n        \"set_k2u_target\":\n            MacroSpec(5, \"set_k2u_target\", (\"actor_id\", \"function_id\", \"\", \"\")),\n        \"switch_k2u\":\n            MacroSpec(6, \"switch_k2u\", (\"actor_id\", \"\", \"\", \"\")),\n        \"set_u2k_target\":\n            MacroSpec(7, \"set_u2k_target\", (\"actor_id\", \"function_id\", \"\", \"\")),\n        \"switch_u2k\":\n            MacroSpec(8, \"switch_u2k\", (\"actor_id\", \"\", \"\", \"\")),\n        \"set_h2g_target\":\n            MacroSpec(9, \"set_h2g_target\", (\"actor_id\", \"function_id\", \"\", \"\")),\n        \"switch_h2g\":\n            MacroSpec(10, \"switch_h2g\", (\"actor_id\", \"\", \"\", \"\")),\n        \"set_g2h_target\":\n            MacroSpec(11, \"set_g2h_target\", (\"actor_id\", \"function_id\", \"\", \"\")),\n        \"switch_g2h\":\n            MacroSpec(12, \"switch_g2h\", (\"actor_id\", \"\", \"\", \"\")),\n        \"landing_k2u\":\n            MacroSpec(13, \"landing_k2u\", (\"\", \"\", \"\", \"\")),\n        \"landing_u2k\":\n            MacroSpec(14, \"landing_u2k\", (\"\", \"\", \"\", \"\")),\n        \"landing_h2g\":\n            MacroSpec(15, \"landing_h2g\", (\"\", \"\", \"\", \"\")),\n        \"landing_g2h\":\n            MacroSpec(16, \"landing_g2h\", (\"\", \"\", \"\", \"\")),\n        \"set_data_permissions\":\n            MacroSpec(18, \"set_data_permissions\", (\"actor_id\", \"int\", \"int\", \"\"))\n        # FIXME: macro IDs should not be hardcoded but rather received from the executor\n        # or at least we need a test that will check that the IDs match\n    }\n\n    uc_target_desc: UnicornTargetDesc\n    \"\"\" Target description in the context of a Unicorn-based model \"\"\"\n\n    register_sizes: Dict[RegName, RegSize]\n    \"\"\" Dictionary mapping register names to their sizes in bits. \"\"\"\n\n    registers_by_size: Dict[RegSize, List[RegName]]\n    \"\"\" Dictionary with lists of all registers for a given size. \"\"\"\n\n    reg_normalized: Dict[RegName, RegNormalizedName]\n    \"\"\" Mapping from full register names to normalized size-independent names. E.g., rax -> A\"\"\"\n\n    reg_denormalized: Dict[RegNormalizedName, Dict[RegSize, RegName]]\n    \"\"\" Reverse mapping from normalized names to full register names.\n    E.g., A -> {64: rax, 32: eax, 16: ax, 8: al} \"\"\"\n\n    page_property_to_pte_bit_name: PTEBitNameMapper\n    \"\"\"\n    Dictionary mapping architecture-independent page property names to architecture-specific\n    page table entry bit names together with a bit indicating whether the property is inverted.\n    E.g.,\n        'writable' -> ('writable', False)\n        'executable' -> ('non_executable', True)\n    \"\"\"\n\n    pte_bits: Dict[PTEBitName, Tuple[PTEBitOffset, bool]]\n    \"\"\"\n    Dictionary mapping page table entry field names to their bit offsets and their default values.\n    \"\"\"\n\n    page_property_to_vm_pte_bit_name: PTEBitNameMapper\n    \"\"\"\n    Dictionary mapping architecture-independent page property names to architecture-specific\n    VM page table entry bit names. This is the unified mapping for both Intel EPT and AMD NPT.\n    \"\"\"\n\n    vm_pte_bits: Dict[PTEBitName, Tuple[PTEBitOffset, bool]]\n    \"\"\"\n    Dictionary mapping VM page table entry field names to their bit offsets\n    and their default values. This is the unified interface for various types of host-to-guest\n    page tables, such as Intel EPT and AMD NPT.\n    \"\"\"\n\n    branch_conditions: Dict[str, List[str]]\n    \"\"\" Dictionary mapping branch instructions to their condition codes. \"\"\"\n\n    mem_index_registers: List[RegName]\n    \"\"\" List of register that can be used as memory index registers. \"\"\"\n\n    @classmethod\n    def get_vendor(cls) -> Vendor:\n        \"\"\" Read the CPU vendor from lscpu \"\"\"\n        output = subprocess.check_output(\"lscpu\", shell=True)\n        if b\"Intel\" in output:\n            return \"Intel\"\n        if b\"AMD\" in output:\n            return \"AMD\"\n        if b\"ARM\" in output:\n            return \"ARM\"\n        return \"Unknown\"\n\n    @staticmethod\n    @abstractmethod\n    def is_unconditional_branch(inst: Instruction) -> bool:\n        \"\"\" Check if the instruction is an unconditional branch. \"\"\"\n\n    @staticmethod\n    @abstractmethod\n    def is_call(inst: Instruction) -> bool:\n        \"\"\" Check if the instruction is a call. \"\"\"\n\n    def get_macro_spec_from_type(self, type_: int) -> MacroSpec:\n        \"\"\"\n        Get the macro specification of a given macro type.\n        :param type_: macro type\n        :return: macro specification\n        \"\"\"\n        for macro_spec in self.macro_specs.values():\n            if macro_spec.type_ == type_:\n                return macro_spec\n        raise KeyError(f\"Unknown macro type: {type_}\")\n\n    def _filter_blocked_registers(self) -> Dict[RegSize, List[str]]:\n        \"\"\" Filter function used to remove blocked registers. Invoked by subclasses. \"\"\"\n\n        filtered_decoding: Dict[RegSize, List[str]] = {}\n        for size, registers in self.registers_by_size.items():\n            filtered_decoding[size] = []\n            for register in registers:\n                if register not in CONF.register_blocklist or register in CONF.register_allowlist:\n                    filtered_decoding[size].append(register)\n        return filtered_decoding\n"
  },
  {
    "path": "rvzr/tc_components/__init__.py",
    "content": "\"\"\"\nFile: Module containing a collection of classes that represent components\n      of a test case (both code and data).\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# flake8: noqa\n# pylint: skip-file\n\nfrom .actor import *\nfrom .instruction import *\nfrom .test_case_code import *\nfrom .test_case_data import *\n\n"
  },
  {
    "path": "rvzr/tc_components/actor.py",
    "content": "\"\"\"\nFile: Classes defining the actor abstraction.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import Dict, Tuple, Final, Optional, TYPE_CHECKING\nfrom enum import Enum\nimport random\n\nfrom ..target_desc import TargetDesc, PTEBitName, PTEBitOffset\n\nif TYPE_CHECKING:\n    from ..config import PageConf, PagePropertyName, ActorConf\n    from .test_case_code import CodeSection\n\n    _PTEBitValue = bool\n    _PTEDescriptor = Dict[PTEBitName, Tuple[PTEBitOffset, _PTEBitValue]]\n    _PropertyMap = Dict[PagePropertyName, Tuple[PTEBitName, bool]]\n\nActorID = int\nActorName = str\nPTEMask = int\n\n\nclass ActorMode(Enum):\n    \"\"\" Enumeration class representing the execution mode of an actor (host or guest). \"\"\"\n    HOST = 0\n    GUEST = 1\n\n\nclass ActorPL(Enum):\n    \"\"\" Enumeration class representing the privilege level of an actor (kernel or user). \"\"\"\n    KERNEL = 0\n    USER = 1\n\n\n# ==================================================================================================\n# Helper Functions to manage actor data properties\n# ==================================================================================================\ndef _create_pte_mask(pte_descriptor: _PTEDescriptor, page_properties_to_set: PageConf,\n                     page_property_to_pte_bit_name: _PropertyMap) -> PTEMask:\n    \"\"\"\n    Create an architecture-specific page table entry (PTE) bitmask based on the actor's\n    architecture-independent data properties. This bitmask is to be used by the executor\n    and the model to set page table properties of actors.\n\n    The function takes a dictionary `pte_descriptor` that describes each bit of the PTE for\n    the target architecture. Each entry in the dictionary maps a bit name to a tuple containing\n    the bit's offset in the PTE and its default value.\n\n    The function modifies the default values based on the `page_properties_to_set` dictionary,\n    which specifies the desired properties for the page table entry (this typically originates\n    from config.yaml).\n\n    As the names of the properties in `page_properties_to_set` may differ from the names used in\n    the `pte_descriptor`, the function uses the `page_property_to_pte_bit_name` mapping to\n    translate between the two.\n\n    Optionally, if `page_properties_to_set['randomized']` is True, the function introduces\n    randomness in the bitmask generation. Each bit has a chance of being set to its default\n    value, with the probability proportional to the number of bits that differ from their\n    default values.\n\n    :param pte_descriptor: dictionary of default values for PTE bits\n    :param page_properties_to_set: dictionary of page properties to set\n    :param page_property_to_pte_bit_name: mapping from property names to PTE bit names\n    :return: bitmask representing the PTE properties\n    :raises: AssertionError if the properties dictionary is invalid\n    \"\"\"\n    # pylint: disable=too-many-locals  # justification: function is complex but clear\n\n    is_randomized = page_properties_to_set['randomized']\n\n    # First, translate the architecture-independent properties to architecture-specific ones\n    arch_specific_properties: Dict[PTEBitName, bool] = {}\n    for property_name, value in page_properties_to_set.items():\n        if property_name == 'randomized':\n            continue\n        assert property_name in page_property_to_pte_bit_name, \\\n            f\"Actor data property {property_name} is not supported on this architecture\"\n        bit_name, is_inverted = page_property_to_pte_bit_name[property_name]\n        if is_inverted:\n            value = not value\n        arch_specific_properties[bit_name] = value\n\n    # If randomization is requested, calculate the probability of a bit being set to default value\n    probability_of_default = 0.0\n    if is_randomized:\n        # calculate the number of non-default bits\n        count_non_default = 0\n        for bit_name in pte_descriptor:\n            if pte_descriptor[bit_name][1] != arch_specific_properties[bit_name]:\n                count_non_default += 1\n\n        # the probability is proportional to the number of non-default bits\n        # we use a formula that maps the probability in the range of roughly [0.5, 0.8] to\n        # avoid having too low or too high probabilities\n        a = count_non_default\n        b = len(pte_descriptor)\n        probability_of_default = (a / (a + b)) * 0.5 + 0.5\n\n    # create the mask\n    mask: PTEMask = 0\n    for bit_name, new_value in arch_specific_properties.items():\n        # get the bit offset and default value from the PTE descriptor\n        bit_offset, default_value = pte_descriptor[bit_name]\n\n        # The new value of the bit is either directly taken from the properties dictionary,\n        # or it is randomly set to the default value based on the probability calculated above.\n        bit_value: int\n        if not is_randomized or new_value == default_value:\n            bit_value = new_value\n        else:\n            set_to_default = random.random() < probability_of_default\n            if set_to_default:\n                bit_value = default_value\n            else:\n                bit_value = new_value\n\n        # now set the bit in the mask\n        bit_value = 1 if bit_value else 0\n        mask |= bit_value << bit_offset\n    return mask\n\n\n# ==================================================================================================\n# Actor Class\n# ==================================================================================================\nclass Actor:\n    \"\"\" Class representing an actor in a test case. \"\"\"\n\n    mode: Final[ActorMode]\n    privilege_level: Final[ActorPL]\n    name: Final[ActorName]\n    data_properties: Final[PTEMask]\n    data_ept_properties: Final[PTEMask]\n    observer: Final[bool]\n    is_main: Final[bool]\n\n    _code_section: Optional[CodeSection] = None\n\n    # ==============================================================================================\n    # Constructors\n\n    def __init__(self,\n                 mode: ActorMode,\n                 pl: ActorPL,\n                 name: ActorName,\n                 data_properties: PTEMask = 0,\n                 data_ept_properties: PTEMask = 0,\n                 is_observer: bool = False) -> None:\n        self.mode = mode\n        self.privilege_level = pl\n        self.name = name\n        self.data_properties = data_properties\n        self.data_ept_properties = data_ept_properties\n        self.observer = is_observer\n        self.is_main = name == \"main\"\n\n    @classmethod\n    def from_dict(cls, actor_dict: ActorConf, target_desc: TargetDesc) -> 'Actor':\n        \"\"\"\n        Create an actor based on a dictionary of actor properties.\n        :param actor_dict: dictionary of actor properties\n        :param target_desc: target description\n        :return: Actor object\n        :raises: ValueError if actor_dict is malformed\n        \"\"\"\n        # actor mode of execution\n        if actor_dict['mode'] == \"host\":\n            mode = ActorMode.HOST\n        elif actor_dict['mode'] == \"guest\":\n            mode = ActorMode.GUEST\n        else:\n            raise ValueError(f\"Invalid actor mode: {actor_dict['mode']}\")\n\n        # privilege level\n        if actor_dict['privilege_level'] == \"kernel\":\n            pl = ActorPL.KERNEL\n        elif actor_dict['privilege_level'] == \"user\":\n            pl = ActorPL.USER\n        else:\n            raise ValueError(f\"Invalid actor privilege level: {actor_dict['privilege_level']}\")\n\n        # PTE and EPTE properties\n        data_properties = _create_pte_mask(\n            target_desc.pte_bits,\n            actor_dict[\"data_properties\"],\n            target_desc.page_property_to_pte_bit_name,\n        )\n        data_ept_properties = _create_pte_mask(\n            target_desc.vm_pte_bits,\n            actor_dict[\"data_ept_properties\"],\n            target_desc.page_property_to_vm_pte_bit_name,\n        )\n\n        # create the actor\n        return Actor(\n            mode,\n            pl,\n            actor_dict[\"name\"],\n            data_properties=data_properties,\n            data_ept_properties=data_ept_properties,\n            is_observer=actor_dict[\"observer\"],\n        )\n\n    @classmethod\n    def create_main(cls) -> 'Actor':\n        \"\"\"\n        Create the main actor with default properties.\n        :return: Actor object\n        \"\"\"\n        return Actor(ActorMode.HOST, ActorPL.KERNEL, \"main\")\n\n    # ==============================================================================================\n    # Public methods\n    def assign_code_section(self, section: CodeSection) -> None:\n        \"\"\" Assign a code section to the actor. \"\"\"\n        assert self._code_section is None, f\"Code section already assigned to actor {self.name}\"\n        self._code_section = section\n\n    def code_section(self) -> CodeSection:\n        \"\"\" Get the code section assigned to the actor. \"\"\"\n        assert self._code_section is not None, f\"Code section not assigned to actor {self.name}\"\n        return self._code_section\n\n    def get_id(self) -> ActorID:\n        \"\"\"\n        Get the actor ID.\n        :return: actor ID\n        :raises: AssertionError if the ELF section has not been assigned\n        \"\"\"\n        assert self._code_section is not None, f\"Code section not assigned to actor {self.name}\"\n        assert self._code_section.id_ is not None, \\\n            \"assign_elf_data was not called on the child CodeSection\"\n        return self._code_section.id_\n"
  },
  {
    "path": "rvzr/tc_components/instruction.py",
    "content": "\"\"\"\nFile: Collection of classes to represent instructions in a test case program and their components.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nfrom abc import ABC\nfrom dataclasses import dataclass\nfrom typing import List, Optional, Final, Literal, Union, Type, Tuple, get_args, cast\nfrom typing_extensions import assert_never\n\nfrom ..instruction_spec import OT, InstructionSpec, OperandSpec\n\nFlagType = Literal['r', 'w', 'r/w', 'r/cw', 'undef']\nRegSize = Literal[8, 16, 32, 64, 128, 256]\n\n\n# ==================================================================================================\n# Operands\n# ==================================================================================================\n@dataclass\nclass Operand(ABC):\n    \"\"\" Operand of an instruction \"\"\"\n\n    value: str\n    \"\"\" The value of the operand, e.g., name of a register, memory address, etc. \"\"\"\n\n    src: Final[bool]\n    \"\"\" If True, the operand is a source operand \"\"\"\n\n    dest: Final[bool]\n    \"\"\" If True, the operand is a destination operand \"\"\"\n\n    has_magic_value: bool = False\n    \"\"\"\n    If True, the operand value has special meaning for the parent instruction.\n    Special meaning is normally a separate opcode or encoding,\n    such as when shift by 1 is a separate opcode.\n    \"\"\"\n\n    def __init__(self, value: str, src: bool, dest: bool):\n        self.value = value.lower()\n        self.src = src\n        self.dest = dest\n        super().__init__()\n\n    @classmethod\n    def from_fixed_spec(cls, spec: OperandSpec) -> AnyOperand:  # pylint: disable=r1710,r0911\n        \"\"\"\n        Create an Operand instance from a fixed operand specification.\n        Fixed means that the specification does not have any multiple-option fields\n        (e.g., only one possible value).\n        :param spec: The operand specification\n        :return: The Operand instance of the type that corresponds to the specification\n        \"\"\"\n        # NOTE on pylint disable above:\n        # - r1710 - mitigates a false positive due to assert_never\n        # - r0911 - the large number of returns is a good design choice here\n\n        assert len(spec.values) <= 1 or spec.type == OT.FLAGS, \\\n            f\"Attempt to call from_fixed_spec with a non-fixed spec {spec.values}\"\n        value = spec.values[0] if spec.values else \"\"\n        if spec.type == OT.REG:\n            return RegisterOp(value, spec.width, spec.src, spec.dest)\n        if spec.type == OT.MEM:\n            return MemoryOp(value, spec.width, spec.src, spec.dest)\n        if spec.type == OT.IMM:\n            return ImmediateOp(value, spec.width)\n        if spec.type == OT.LABEL:\n            return LabelOp(value)\n        if spec.type == OT.AGEN:\n            return AgenOp(value, spec.width)\n        if spec.type == OT.FLAGS:\n            return FlagsOp(spec.values)\n        if spec.type == OT.COND:\n            return CondOp(value)\n        assert_never(spec.type)\n        # unreachable, hence no return\n\n\nclass RegisterOp(Operand):\n    \"\"\" Register operand of an instruction \"\"\"\n\n    width: Final[RegSize]\n\n    def __init__(self, value: str, width: int, src: bool, dest: bool):\n        assert width in get_args(RegSize), f\"Invalid register width {width} for register {value}\"\n        self.width = cast(RegSize, width)\n        super().__init__(value, src, dest)\n\n\nclass MemoryOp(Operand):\n    \"\"\" Memory operand of an instruction \"\"\"\n\n    width: Final[int]\n\n    def __init__(self, address: str, width: int, src: bool, dest: bool) -> None:\n        self.width = width\n        super().__init__(address, src, dest)\n\n    def get_base_register(self) -> Optional[RegisterOp]:\n        \"\"\"\n        Get the base register of the memory operand, if any.\n        E.g., for [rax + 8], return rax.\n        :return: The base register, or None if there is no base register\n        \"\"\"\n        addr = self.value.strip()\n\n        # Split by + and - to find base register\n        tokens = [t.strip() for t in addr.replace('-', '+').split('+')]\n\n        # Filter out numeric tokens\n        tokens = [t for t in tokens if not t.replace('0x', '').isdigit()]\n        tokens = [t for t in tokens if not t.replace('0b', '').isdigit()]\n\n        for t in tokens:\n            # the first non-numeric token is the base register\n            return RegisterOp(t.lower(), self.width, True, False)\n        return None\n\n\nclass ImmediateOp(Operand):\n    \"\"\" Immediate operand of an instruction \"\"\"\n\n    width: Final[int]\n\n    def __init__(self, value: str, width: int) -> None:\n        self.width = width\n        super().__init__(value, True, False)\n\n\nclass LabelOp(Operand):\n    \"\"\" Label operand of an instruction \"\"\"\n\n    def __init__(self, value: str) -> None:\n        super().__init__(value, True, False)\n\n\nclass AgenOp(Operand):\n    \"\"\" Address generation operand of an instruction (used by LEA instruction) \"\"\"\n\n    width: Final[int]\n\n    def __init__(self, value: str, width: int) -> None:\n        self.width = width\n        super().__init__(value, True, False)\n\n\nclass FlagsOp(Operand):\n    \"\"\" Flags operand of an instruction \"\"\"\n\n    _flag_values: Final[Tuple[str, ...]]\n    _flag_names: Final[Tuple[str, ...]] = (\"CF\", \"PF\", \"AF\", \"ZF\", \"SF\", \"TF\", \"IF\", \"DF\", \"OF\")\n\n    def __init__(self, value: Tuple[str, ...]) -> None:\n        assert len(value) == len(self._flag_names)\n        self._flag_values = value\n        super().__init__(\"FLAGS\", False, False)\n\n    def __str__(self) -> str:\n        return \"FLAGS: \" \\\n               f\"{self._flag_names[0]}{self._flag_values[0]}|\" \\\n               f\"{self._flag_names[1]}{self._flag_values[1]}|\" \\\n               f\"{self._flag_names[2]}{self._flag_values[2]}|\" \\\n               f\"{self._flag_names[3]}{self._flag_values[3]}|\" \\\n               f\"{self._flag_names[4]}{self._flag_values[4]}|\" \\\n               f\"{self._flag_names[5]}{self._flag_values[5]}|\" \\\n               f\"{self._flag_names[6]}{self._flag_values[6]}|\" \\\n               f\"{self._flag_names[7]}{self._flag_values[7]}|\" \\\n               f\"{self._flag_names[8]}{self._flag_values[8]}\"\n\n    def _get_flag_list(self, types: List[FlagType]) -> List[str]:\n        \"\"\"\n        Get a list of flags with the specified types.\n        :param types: A list of flag types to include\n        :return: A list of flags\n        \"\"\"\n        flags = []\n        for i, type_ in enumerate(self._flag_values):\n            if type_ in types:\n                flags.append(self._flag_names[i])\n        return flags\n\n    def get_flags_by_type(self, type_: Literal['read', 'write', 'overwrite', 'undef']) -> List[str]:\n        \"\"\"\n        Get a list of flags with the specified types.\n        :param types: Type of flags to include (read, write, overwrite, undef)\n        :return: A list of flags\n        \"\"\"\n        flag_types: List[FlagType]\n        if type_ == \"read\":\n            flag_types = ['r', 'r/w', 'r/cw']\n        elif type_ == \"write\":\n            flag_types = ['w', 'r/w', 'r/cw']\n        elif type_ == \"overwrite\":\n            flag_types = ['w']\n        elif type_ == \"undef\":\n            flag_types = ['undef']\n        else:\n            assert_never(type_)\n\n        return self._get_flag_list(flag_types)\n\n\n@dataclass\nclass CondOp(Operand):\n    \"\"\" Condition operand of an instruction \"\"\"\n\n    def __init__(self, value: str) -> None:\n        super().__init__(value, True, False)\n\n\n# ==================================================================================================\n# Operand Modification Interface\n# ==================================================================================================\n_ValueModifiableOperand = Union[RegisterOp, MemoryOp, ImmediateOp, LabelOp, AgenOp, CondOp]\n_SrcDestModifiableOperand = Union[RegisterOp, MemoryOp]\nAnyOperand = Union[RegisterOp, MemoryOp, ImmediateOp, LabelOp, AgenOp, CondOp, FlagsOp]\n\n\ndef copy_op_with_value_modification(op: _ValueModifiableOperand,\n                                    value: str) -> _ValueModifiableOperand:\n    \"\"\"\n    Make a copy of an operand with a modification to its value\n    :param op: The operand to copy\n    :param value: The new value of the operand\n    :return: The modified operand\n    \"\"\"\n    if isinstance(op, RegisterOp):\n        return RegisterOp(value, op.width, op.src, op.dest)\n    if isinstance(op, MemoryOp):\n        return MemoryOp(value, op.width, op.src, op.dest)\n    if isinstance(op, ImmediateOp):\n        return ImmediateOp(value, op.width)\n    if isinstance(op, LabelOp):\n        return LabelOp(value)\n    if isinstance(op, AgenOp):\n        return AgenOp(value, op.width)\n    if isinstance(op, CondOp):\n        return CondOp(value)\n    assert_never(op)\n\n\ndef copy_op_with_flow_modification(op: _SrcDestModifiableOperand,\n                                   src: Optional[bool] = None,\n                                   dest: Optional[bool] = None) -> _SrcDestModifiableOperand:\n    \"\"\"\n    Make a copy of an operand with modifications to its flow properties\n    :param op: The operand to copy\n    :param src: If not None, the new src property of the operand\n    :param dest: If not None, the new dest property of the operand\n    :return: The modified operand\n    \"\"\"\n    if src is None:\n        src = op.src\n    if dest is None:\n        dest = op.dest\n\n    if isinstance(op, RegisterOp):\n        return RegisterOp(op.value, op.width, src, dest)\n    if isinstance(op, MemoryOp):\n        return MemoryOp(op.value, op.width, src, dest)\n    assert_never(op)\n\n\n# ==================================================================================================\n# Instructions and Symbols\n# ==================================================================================================\nclass Instruction:\n    \"\"\" Instruction in a test case program \"\"\"\n\n    # pylint: disable=too-many-instance-attributes\n    # NOTE: This is a data container class, so it is expected to have many attributes\n    # pylint: disable=too-many-public-methods\n    # NOTE: This contains separate accessors for each operand type,\n    # so it is expected to have many methods\n\n    name: Final[str]\n    \"\"\" name: The name of the instruction without any operands \"\"\"\n    category: Final[str]\n    \"\"\" category: The category of the instruction, e.g., BASE-BINARY. The keyword matches\n    the category in the instruction set description file (typically called base.json)\"\"\"\n\n    is_control_flow: Final[bool]\n    \"\"\" _control_flow: If True, the instruction is a control flow instruction\n    (branch, call, return, etc.) \"\"\"\n    is_instrumentation: Final[bool]\n    \"\"\" _is_instrumentation: If True, the instruction is an instrumentation instruction,\n    which means that it was inserted by the generator to prevent faults or false positives \"\"\"\n    is_noremove: Final[bool]\n    \"\"\" is_noremove: If True, the instruction should be skipped while doing minimization passes \"\"\"\n    is_from_template: bool = False\n    \"\"\" is_from_template: If True, the instruction was directly copied from the template rather\n    then being automatically created by the generator. \"\"\"\n    is_macro_placeholder: bool = False\n    \"\"\" is_macro_placeholder: If True, this instruction is a part of a placeholder that will be\n    replaced by a macro call in the executor/model; this instruction is expected to be a NOP.\n    For most instructions, this is always False. \"\"\"\n\n    operands: Final[List[AnyOperand]]\n    \"\"\" operands: List of explicit operands of the instruction \"\"\"\n    implicit_operands: Final[List[AnyOperand]]\n    \"\"\" implicit_operands: List of implicit operands, which are not explicitly specified in the\n    instruction but are used by the instruction. For example, flags operand in x86 instructions \"\"\"\n\n    _line_num: int = -1  # line number in the source asm; access via line_num()\n    _section_id: int = -1  # section ID in the object file; access via section_id()\n    _section_offset: int = -1  # instruction offset in the section; access via section_offset()\n    _size: int = -1  # size of the instruction in bytes; access via size()\n    _inst_brief: str = \"\"  # cached brief representation of the instruction\n\n    # ----------------------------------------------------------------------------------------------\n    # Constructors\n\n    def __init__(self,\n                 name: str,\n                 category: str = \"\",\n                 is_control_flow: bool = False,\n                 is_instrumentation: bool = False,\n                 is_noremove: bool = False) -> None:\n        self.name = name\n        self.category = category\n        self.is_control_flow = is_control_flow\n        self.is_instrumentation = is_instrumentation\n        self.is_noremove = is_noremove\n\n        self.operands = []\n        self.implicit_operands = []\n\n    @classmethod\n    def from_spec(cls: Type[Instruction],\n                  sp: InstructionSpec,\n                  is_instrumentation: bool = False,\n                  is_noremove: bool = False) -> Instruction:\n        \"\"\"\n        Create an instruction with NO OPERANDS from an instruction specification.\n        :param spec: The instruction specification\n        :param is_instrumentation: If True, the instruction is an instrumentation instruction\n        :param is_noremove: If True, the instruction be kept during minimization\n        :return: The instruction\n        \"\"\"\n        obj = cls(\n            sp.name,\n            sp.category,\n            sp.is_control_flow,\n            is_instrumentation=is_instrumentation,\n            is_noremove=is_noremove)\n        return obj\n\n    # ----------------------------------------------------------------------------------------------\n    # Printing\n\n    def __str__(self) -> str:\n        op_list = [\n            \"[\" + o.value + \"]\" if isinstance(o, MemoryOp) else o.value for o in self.operands\n        ]\n        operands = ', '.join(op_list)\n        return f\"{self.name} {operands}\"\n\n    # ----------------------------------------------------------------------------------------------\n    # Operand Management\n\n    def add_op(self, op: AnyOperand, implicit: bool = False) -> Instruction:\n        \"\"\"\n        Add operand to the instruction. Returns the instruction for chaining.\n        :param op: Operand to add\n        :param implicit: If True, the operand is implicit\n        :return: The instruction\n        \"\"\"\n        if not implicit:\n            self.operands.append(op)\n        else:\n            self.implicit_operands.append(op)\n        return self\n\n    def has_mem_operand(self, include_implicit: bool) -> bool:\n        \"\"\"\n        Check if the instruction has a memory operand.\n        :param include_implicit: If True, include implicit operands in the check\n        :return: True if the instruction has a memory operand, False otherwise\n        \"\"\"\n        for o in self.operands:\n            if isinstance(o, MemoryOp):\n                return True\n        if include_implicit:\n            for o in self.implicit_operands:\n                if isinstance(o, MemoryOp):\n                    return True\n        return False\n\n    def has_write(self, include_implicit: bool = False) -> bool:\n        \"\"\"\n        Check if the instruction has a memory operand that writes to memory.\n        :param include_implicit: If True, include implicit operands in the check\n        :return: True if the instruction has a memory operand that writes to memory, False otherwise\n        \"\"\"\n        for o in self.operands:\n            if isinstance(o, MemoryOp) and o.dest:\n                return True\n        if include_implicit:\n            for o in self.implicit_operands:\n                if isinstance(o, MemoryOp) and o.dest:\n                    return True\n        return False\n\n    def has_read(self, include_implicit: bool = False) -> bool:\n        \"\"\"\n        Check if the instruction has a memory operand that reads from memory.\n        :param include_implicit: If True, include implicit operands in the check\n        :return: True if the instruction has a memory operand that reads memory, False otherwise\n        \"\"\"\n        for o in self.operands:\n            if isinstance(o, MemoryOp) and o.src:\n                return True\n        if include_implicit:\n            for o in self.implicit_operands:\n                if isinstance(o, MemoryOp) and o.src:\n                    return True\n        return False\n\n    def get_all_operands(self) -> List[AnyOperand]:\n        \"\"\"\n        Get a list of all operands of the instruction,\n        including both explicit and implicit operands.\n        :return: A list of all operands\n        \"\"\"\n        return self.operands + self.implicit_operands\n\n    def get_src_operands(self, include_implicit: bool = False) -> List[AnyOperand]:\n        \"\"\"\n        Get a list of source operands of the instruction.\n        :param include_implicit: If True, include implicit operands in the list\n        :return: A list of source operands\n        \"\"\"\n        res = []\n        for o in self.operands:\n            if o.src:\n                res.append(o)\n        if include_implicit:\n            for o in self.implicit_operands:\n                if o.src:\n                    res.append(o)\n        return res\n\n    def get_dest_operands(self, include_implicit: bool = False) -> List[AnyOperand]:\n        \"\"\"\n        Get a list of destination operands of the instruction.\n        :param include_implicit: If True, include implicit operands in the list\n        :return: A list of destination operands\n        \"\"\"\n        res = []\n        for o in self.operands:\n            if o.dest:\n                res.append(o)\n        if include_implicit:\n            for o in self.implicit_operands:\n                if o.dest:\n                    res.append(o)\n        return res\n\n    def get_mem_operands(self,\n                         include_explicit: bool = True,\n                         include_implicit: bool = False) -> List[MemoryOp]:\n        \"\"\"\n        Get a list of memory operands of the instruction.\n        :param include_implicit: If True, include implicit operands in the list\n        :return: A list of memory operands\n        \"\"\"\n        assert include_explicit or include_implicit, \"At least one of include_explicit or \" \\\n                                                     \"include_implicit must be True\"\n        res = []\n        if include_explicit:\n            for o in self.operands:\n                if isinstance(o, MemoryOp):\n                    res.append(o)\n        if include_implicit:\n            for o in self.implicit_operands:\n                if isinstance(o, MemoryOp):\n                    res.append(o)\n        return res\n\n    def get_flags_operand(self) -> Optional[FlagsOp]:\n        \"\"\"\n        Get the flags operand of the instruction.\n        :return: The flags operand, or None if the instruction does not have one\n        \"\"\"\n        for o in self.implicit_operands:\n            if isinstance(o, FlagsOp):\n                return o\n        for o in self.operands:\n            if isinstance(o, FlagsOp):\n                return o\n        return None\n\n    def get_reg_operands(self, include_implicit: bool = False) -> List[RegisterOp]:\n        \"\"\"\n        Get a list of register operands of the instruction.\n        :param include_implicit: If True, include implicit operands in the list\n        :return: A list of register operands\n        \"\"\"\n        res = []\n        for o in self.operands:\n            if isinstance(o, RegisterOp):\n                res.append(o)\n        if include_implicit:\n            for o in self.implicit_operands:\n                if isinstance(o, RegisterOp):\n                    res.append(o)\n        return res\n\n    def get_cond_operand(self) -> Optional[CondOp]:\n        \"\"\"\n        Get the condition operand of the instruction.\n        :return: The condition operand, or None if the instruction does not have one\n        \"\"\"\n        for o in self.operands:\n            if isinstance(o, CondOp):\n                return o\n        # not checking implicit operands -> conditions must be explicit\n        return None\n\n    def get_label_operand(self) -> Optional[LabelOp]:\n        \"\"\"\n        Get the label operand of the instruction.\n        :return: The label operand, or None if the instruction does not have one\n        \"\"\"\n        for o in self.operands:\n            if isinstance(o, LabelOp):\n                return o\n        # not checking implicit operands -> labels must be explicit\n        return None\n\n    def get_imm_operands(self, include_implicit: bool = False) -> List[ImmediateOp]:\n        \"\"\"\n        Get a list of immediate operands of the instruction.\n        :param include_implicit: If True, include implicit operands in the list\n        :return: A list of immediate operands\n        \"\"\"\n        res = []\n        for o in self.operands:\n            if isinstance(o, ImmediateOp):\n                res.append(o)\n        if include_implicit:\n            for o in self.implicit_operands:\n                if isinstance(o, ImmediateOp):\n                    res.append(o)\n        return res\n\n    def get_agen_operands(self) -> List[AgenOp]:\n        \"\"\"\n        Get a list of address generation operands of the instruction.\n        :return: A list of address generation operands\n        \"\"\"\n        res = []\n        for o in self.operands:\n            if isinstance(o, AgenOp):\n                res.append(o)\n        # not checking implicit operands -> agen must be explicit\n        return res\n\n    # ----------------------------------------------------------------------------------------------\n    # Instruction in Assembly\n    def assign_line_num(self, line_num: int) -> None:\n        \"\"\" Assign the line number in the source file where the instruction is located. \"\"\"\n        assert self._line_num == -1, \"Line number is already assigned\"\n        self._line_num = line_num\n\n    def line_num(self) -> int:\n        \"\"\" Get the line number in the source file where the instruction is located. \"\"\"\n        assert self._line_num != -1, \"Line number is not assigned\"\n        return self._line_num\n\n    # ----------------------------------------------------------------------------------------------\n    # Instruction in Binary\n    def assign_binary_properties(self, section_id: int, offset: int, size: int) -> None:\n        \"\"\"\n        Assign properties of the instruction in the binary file after it has been assembled.\n        :param section_id: The ID of the section in the object file where the instruction is located\n        :param offset: The section offset of the instruction in the object file\n        :param size: The size of the instruction in bytes, after it has been assembled\n        \"\"\"\n        assert self._section_id == -1, \"Instruction properties are already assigned \\n\" \\\n            \"    (assign_binary_properties() can only be called once)\"\n        self._section_id = section_id\n        self._section_offset = offset\n        self._size = size\n\n    def section_id(self) -> int:\n        \"\"\" Get the ID of the section in the object file where the instruction is located. \"\"\"\n        assert self._section_id != -1, \"Instruction properties are not assigned \\n\" \\\n            \"    (assign_binary_properties() must be called before section_id() can be used)\"\n        return self._section_id\n\n    def section_offset(self) -> int:\n        \"\"\" Get the section offset of the instruction in the object file. \"\"\"\n        assert self._section_offset != -1, \"Instruction properties are not assigned \\n\" \\\n            \"    (assign_binary_properties() must be called before section_offset() can be used)\"\n        return self._section_offset\n\n    def size(self) -> int:\n        \"\"\" Get the size of the instruction in bytes. \"\"\"\n        assert self._size != -1, \"Instruction properties are not assigned \\n\" \\\n            \"    (assign_binary_properties() must be called before size() can be used)\"\n        return self._size\n\n\ndef copy_inst_with_modification(instruction: Instruction,\n                                name: Optional[str] = None,\n                                category: Optional[str] = None,\n                                is_control_flow: Optional[bool] = None,\n                                is_instrumentation: Optional[bool] = None,\n                                is_noremove: Optional[bool] = None) -> Instruction:\n    \"\"\"\n    Make a copy of an instruction with modifications to its properties\n    :param instruction: The instruction to copy\n    :param name: If not None, the new name of the instruction\n    :param category: If not None, the new category of the instruction\n    :param is_control_flow: If not None, the new is_control_flow property of the instruction\n    :param is_instrumentation: If not None, the new is_instrumentation property of the instruction\n    :param is_noremove: If not None, the new is_noremove property of the instruction\n    :return: The new modified instruction\n    \"\"\"\n    if name is None:\n        name = instruction.name\n    if category is None:\n        category = instruction.category\n    if is_control_flow is None:\n        is_control_flow = instruction.is_control_flow\n    if is_instrumentation is None:\n        is_instrumentation = instruction.is_instrumentation\n    if is_noremove is None:\n        is_noremove = instruction.is_noremove\n\n    new_inst = Instruction(name, category, is_control_flow, is_instrumentation, is_noremove)\n    new_inst.is_from_template = instruction.is_from_template\n    new_inst.is_macro_placeholder = instruction.is_macro_placeholder\n    new_inst.operands.extend(instruction.operands.copy())\n    new_inst.implicit_operands.extend(instruction.implicit_operands.copy())\n    new_inst._section_id = instruction._section_id  # pylint: disable=protected-access\n    new_inst._section_offset = instruction._section_offset  # pylint: disable=protected-access\n    new_inst._size = instruction._size  # pylint: disable=protected-access\n    new_inst._line_num = instruction._line_num  # pylint: disable=protected-access\n\n    return new_inst\n"
  },
  {
    "path": "rvzr/tc_components/test_case_binary.py",
    "content": "\"\"\"\nFile: Classes representing assembled test case code in a binary form (ELF object file).\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import TYPE_CHECKING, Dict, List, NamedTuple, Final, Optional\n\nfrom .instruction import Instruction\nfrom ..logs import error\n\nif TYPE_CHECKING:\n    from .test_case_code import TestCaseProgram\n\nSectionID = int\nSymbolType = int\nSymbolOffset = int\nMacroArgument = int\nInstructionMap = Dict[SectionID, Dict[int, Instruction]]\n\n\nclass SymbolTableEntry(NamedTuple):\n    \"\"\" Symbol in a test case symbol table \"\"\"\n\n    sid: SectionID\n    \"\"\" The ID of the section that contains the symbol \"\"\"\n\n    offset: SymbolOffset\n    \"\"\" offset: The offset of the symbol in the actor's section \"\"\"\n\n    type_: SymbolType\n    \"\"\" type_: The type of the symbol \"\"\"\n\n    arg: MacroArgument\n    \"\"\" arg: The argument of the symbol \"\"\"\n\n\nSymbolTable = List[SymbolTableEntry]\n\n\nclass TestCaseBinary:\n    \"\"\"\n    A class representing the object ELF file (i.e., compiled assembly) of a test case program\n    \"\"\"\n\n    obj_path: Final[str]\n    \"\"\" Path to the object file generated from the asm_path \"\"\"\n\n    _symbol_table: Optional[List[SymbolTableEntry]] = None\n    \"\"\" List of symbols in the test case program \"\"\"\n\n    _instruction_map: Optional[InstructionMap] = None\n    \"\"\" Dictionary mapping section ID + offset to the corresponding Instruction object \"\"\"\n\n    _parent: TestCaseProgram  # The parent test case program\n    _obj_is_assembled: bool = False  # Flag indicating whether the object file has been assembled\n\n    def __init__(self, obj_path: str, parent: TestCaseProgram):\n        self.obj_path = obj_path\n        self._parent = parent\n\n    def mark_as_assembled(self) -> None:\n        \"\"\" Mark the object file as assembled \"\"\"\n        self._obj_is_assembled = True\n\n    def to_bytes(self, padded_section_size: int = 0, padding_byte: bytes = b'') -> bytes:\n        \"\"\" Return the full binary of the assembled object file, with sections ordered by actor ID.\n        Optionally, pad each section to a specified size with a specified padding byte.\n\n        :param pad_to_size: The size to pad each section to\n        :param padding_byte: The byte to use for padding\n        :return: A list of byte strings, each containing the full compiled binary of a section\n        \"\"\"\n        assert self._obj_is_assembled, \\\n            \"Attempting to read sections from an non-assembled object file\"\n        assert padded_section_size == 0 or len(padding_byte) == 1, \\\n            \"padding_byte must be specified as a single byte if pad_to_size is set\"\n\n        code = b''\n        with open(self.obj_path, 'rb') as bin_file:\n            for actor in self._parent.get_actors(sorted_=True):\n\n                # Read the section from the object file\n                section_data = actor.code_section().get_elf_data()\n                offset = section_data[\"offset\"]\n                size = section_data[\"size\"]\n\n                bin_file.seek(offset)\n                code += bin_file.read(size)\n\n                # Apply padding\n                assert padded_section_size >= size, \\\n                    \"Padded section size is less than to the original section size\"\n                if padded_section_size > size:\n                    padding = padded_section_size - size\n                    code += padding_byte * padding\n\n        return code\n\n    def get_macro_offset(self, macro_type: int) -> int:\n        \"\"\" Return the offset of the macro of the given type in its section.\n        If there are multiple macros of the same type, the first one is returned.\n        :param macro_id: The ID of the macro\n        :return: The offset of the macro in the object file; -1 if not found\n        \"\"\"\n        assert self._symbol_table is not None, \\\n            \"assign_elf_data() has not been called on this object\"\n        for symbol in self._symbol_table:\n            if symbol.type_ == macro_type:\n                return symbol.offset\n        return -1\n\n    def assign_elf_data(self, symbol_table: List[SymbolTableEntry],\n                        instruction_map: InstructionMap) -> None:\n        \"\"\"\n        Assign the symbol table and instruction map based on the data parsed from the ELF file\n        (normally assigned by an ELFParser instance).\n        \"\"\"\n        assert self._symbol_table is None, \"Attempting to reassign symbol table\"\n        assert self._instruction_map is None, \"Attempting to reassign instruction map\"\n        self._symbol_table = symbol_table\n        self._instruction_map = instruction_map\n\n    def symbol_table(self) -> List[SymbolTableEntry]:\n        \"\"\" Return the symbol table of the test case program \"\"\"\n        assert self._symbol_table is not None, \"Symbol table has not been populated\"\n        return self._symbol_table\n\n    def instruction_map(self) -> InstructionMap:\n        \"\"\" Return the instruction map of the test case program \"\"\"\n        assert self._instruction_map is not None, \"Instruction map has not been populated\"\n        return self._instruction_map\n\n    def save_rcbf(self, path: str) -> None:\n        \"\"\"\n        Save the test case binary in the RCBF format\n        (see docs/devel/binary-formats.md for details).\n        :param path: The path to save the RCBF file to\n        \"\"\"\n        assert self._obj_is_assembled, \"Attempting to save an un-assembled object file\"\n        actors = self._parent.get_actors(sorted_=True)\n        symbol_table = self.symbol_table()\n\n        # sanity check\n        if any(symbol.type_ < 0 for symbol in symbol_table):\n            error(\"attempt to use template as a test case\")\n\n        # write the RCBF file\n        with open(path, 'wb') as f:\n            # header\n            f.write((len(actors)).to_bytes(8, byteorder='little'))  # n_actors\n            f.write((len(symbol_table)).to_bytes(8, byteorder='little'))  # n_symbols\n\n            # actor metadata\n            for actor in actors:\n                f.write((actor.get_id()).to_bytes(8, byteorder='little'))\n                f.write((actor.mode.value).to_bytes(8, byteorder='little'))\n                f.write((actor.privilege_level.value).to_bytes(8, byteorder='little'))\n                f.write((actor.data_properties).to_bytes(8, byteorder='little'))\n                f.write((actor.data_ept_properties).to_bytes(8, byteorder='little'))\n                f.write((0).to_bytes(8, byteorder='little'))  # unused\n\n            # symbol table (first functions sorted by argument, then macros sorted by actor+offset)\n            function_symbols = [s for s in symbol_table if s[2] == 0]\n            macro_symbols = [s for s in symbol_table if s[2] != 0]\n            for aid, s_offset, s_id, arg in sorted(function_symbols, key=lambda s: s.arg):\n                # print(\"function\", s_id, aid, s_offset, arg)\n                f.write((aid).to_bytes(8, byteorder='little'))\n                f.write((s_offset).to_bytes(8, byteorder='little'))\n                f.write((s_id).to_bytes(8, byteorder='little'))\n                f.write((arg).to_bytes(8, byteorder='little'))\n            for aid, s_offset, s_id, arg in sorted(macro_symbols, key=lambda s: (s.sid, s.offset)):\n                # print(\"macro\", aid, s_offset, s_id, arg)\n                f.write((aid).to_bytes(8, byteorder='little'))\n                f.write((s_offset).to_bytes(8, byteorder='little'))\n                f.write((s_id).to_bytes(8, byteorder='little'))\n                f.write((arg).to_bytes(8, byteorder='little'))\n\n            # section metadata\n            for actor in actors:\n                section_data = actor.code_section().get_elf_data()\n                # print(\"section\\n\")\n                f.write((section_data[\"id\"]).to_bytes(8, byteorder='little'))\n                f.write((section_data[\"size\"]).to_bytes(8, byteorder='little'))\n                f.write((0).to_bytes(8, byteorder='little'))\n\n            # code\n            with open(self.obj_path, 'rb') as bin_file:\n                for actor in actors:\n                    section_data = actor.code_section().get_elf_data()\n                    bin_file.seek(section_data[\"offset\"])  # type: ignore\n                    # print(code, section.size)\n                    f.write(bin_file.read(section_data[\"size\"]))\n\n            # print(self.obj_path, f.tell())\n"
  },
  {
    "path": "rvzr/tc_components/test_case_code.py",
    "content": "\"\"\"\nFile: Class representing test case code and its components.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\n\nfrom typing import List, Dict, Optional, Final, TypedDict, Generator as GeneratorType\nfrom dataclasses import dataclass\nimport shutil\n\nfrom .actor import Actor, ActorID, ActorName, ActorPL, ActorMode\nfrom .instruction import Instruction\nfrom .test_case_binary import TestCaseBinary\n\n\n# ==================================================================================================\n# Program Structure: CodeSection -> Function -> BasicBlock -> InstructionNode -> Instruction\n# ==================================================================================================\n@dataclass\nclass InstructionNode:\n    \"\"\"\n    Wrapper class to represent an instruction as a node\n    in a double-linked list that constitutes a basic block\n    \"\"\"\n    instruction: Final[Instruction]\n    \"\"\" Wrapped instruction object \"\"\"\n\n    parent: Final[BasicBlock]\n    \"\"\" Basic block to which the instruction belongs \"\"\"\n\n    next: Optional[InstructionNode] = None\n    \"\"\" Next instruction in the basic block \"\"\"\n\n    previous: Optional[InstructionNode] = None\n    \"\"\" Previous instruction in the basic block \"\"\"\n\n    def __init__(self, instruction: Instruction, parent: BasicBlock):\n        self.instruction = instruction\n        self.parent = parent\n        self.next = None\n        self.previous = None\n\n    def __str__(self) -> str:\n        return str(self.instruction)\n\n\nclass BasicBlock:\n    \"\"\" Basic block in the test case code \"\"\"\n\n    name: Final[str]\n    \"\"\" The name (i.e., label) of the basic block \"\"\"\n\n    parent: Final[Optional[Function]]\n    \"\"\" The function that owns the basic block \"\"\"\n\n    successors: List[BasicBlock]\n    \"\"\" List of basic blocks that are successors of this basic block \"\"\"\n\n    terminators: List[Instruction]\n    \"\"\" List of terminator instructions in the basic block \"\"\"\n\n    is_exit: Final[bool]\n    \"\"\" Indicates whether the basic block should be treated as a function exit block \"\"\"\n\n    _start: Optional[InstructionNode] = None\n    _end: Optional[InstructionNode] = None\n\n    def __init__(self, name: str, parent: Optional[Function] = None, is_exit: bool = False):\n        self.name = name\n        self.parent = parent\n        self.is_exit = is_exit\n        self.successors = []\n        self.terminators = []\n\n    def __str__(self) -> str:\n        return self.name\n\n    def __len__(self) -> int:\n        \"\"\" Length of the basic block is the number of instructions in it \"\"\"\n        count = 0\n        if self._start:\n            node = self._start\n            count = 1\n            while node.next:\n                node = node.next\n                count += 1\n        return count\n\n    def __iter__(self) -> GeneratorType[Instruction, None, None]:\n        \"\"\" Default iterator over the instructions in the basic block \"\"\"\n        current_node = self._start\n        while current_node:\n            yield current_node.instruction\n            current_node = current_node.next\n\n    def iter_nodes(self) -> GeneratorType[InstructionNode, None, None]:\n        \"\"\" Non-default iterator: Iterate over the nodes in the basic block \"\"\"\n        current_node = self._start\n        while current_node:\n            yield current_node\n            current_node = current_node.next\n\n    def get_owner(self) -> Actor:\n        \"\"\" Get the actor that owns the basic block \"\"\"\n        assert self.parent is not None, \"Basic block does not have a parent function\"\n        return self.parent.parent.owner\n\n    # ----------------------------------------------------------------------------------------------\n    # Instruction insertion and deletion\n    def insert_after(self, position: Optional[InstructionNode], inst: Instruction) -> None:\n        \"\"\" Insert an instruction after a given position node in a basic block\n        :param position: If not None, the node after which to insert the new instruction;\n                         If None, insert at the _end of the basic block\n        :param inst: The instruction to insert\n        :return: None\n        :raises ValueError: If `position` is not found in the basic block\n        \"\"\"\n        inst_node = InstructionNode(inst, self)\n\n        # Position is None and the BB is empty: set the start and end to the new instruction\n        if position is None and self._end is None:\n            self._start = inst_node\n            self._end = inst_node\n            return\n\n        # Position is None and the BB is not empty: set the position to the end of the BB\n        if position is None:\n            position = self._end\n        assert position is not None\n\n        # Position is not None: ensure that `position` belongs to this BB\n        if position.parent != self:\n            raise ValueError(\"`position` not found in the basic block\")\n\n        # Insert the new instruction\n        next_ = position.next\n        position.next = inst_node\n        inst_node.previous = position\n        if next_:\n            inst_node.next = next_\n            next_.previous = inst_node\n        else:\n            self._end = inst_node\n\n    def insert_before(self, position: Optional[InstructionNode], inst: Instruction) -> None:\n        \"\"\" Insert an instruction before a given position node in a basic block\n        :param position: If not None, the node before which to insert the new instruction;\n                         If None, insert at the beginning of the basic block\n        :param inst: The instruction to insert\n        :return: None\n        :raises ValueError: If `position` is not found in the basic block\n        \"\"\"\n        inst_node = InstructionNode(inst, self)\n\n        # Position is None and the BB is empty: set the start and end to the new instruction\n        if position is None and self._start is None:\n            self._start = inst_node\n            self._end = inst_node\n            return\n\n        # Position is None and the BB is not empty: set the position to the start of the BB\n        if position is None:\n            position = self._start\n        assert position is not None\n\n        # Position is not None: ensure that `position` belongs to this BB\n        if position.parent != self:\n            raise ValueError(f\"instruction {position} belongs to {position.parent}, not {self}\")\n\n        # Insert the new instruction\n        previous = position.previous\n        position.previous = inst_node\n        inst_node.next = position\n        if previous:\n            inst_node.previous = previous\n            previous.next = inst_node\n        else:\n            self._start = inst_node\n\n    def delete(self, target: InstructionNode) -> None:\n        \"\"\"\n        Delete a node from a basic block\n        :param target: The node to delete\n        :return: None\n        :raises ValueError: If the node does not belong to the basic block\n        \"\"\"\n        # Verify that this node indeed belongs to this BB\n        if target.parent != self:\n            raise ValueError(\"Error deleting an instruction from a BB; instruction not found\")\n\n        # Patch the linked list\n        previous = target.previous\n        next_ = target.next\n        if previous is None and next_ is None:  # the only instruction in BB\n            self._end = None\n            self._start = None\n        elif previous is None:  # the first instruction\n            next_.previous = None  # type: ignore\n            self._start = next_\n        elif next_ is None:  # the last instruction\n            previous.next = None\n            self._end = previous\n        else:  # somewhere in the middle\n            previous.next = next_\n            next_.previous = previous\n\n    # ----------------------------------------------------------------------------------------------\n    # Instruction access\n    def get_first(self, exclude_macros: bool = False) -> Optional[InstructionNode]:\n        \"\"\"\n        Get the first InstructionNode in the basic block\n        :param exclude_macros: If True, return the first non-macro instruction\n        :return: The first node or None if the basic block is empty\n        \"\"\"\n        if not exclude_macros:\n            return self._start if self._start is not None else None\n\n        # Skip macro instructions\n        entry_node = self.get_first()\n        while entry_node:\n            if entry_node.instruction.name != \"macro\":\n                break\n            entry_node = entry_node.next\n        return entry_node\n\n    def get_last(self) -> Optional[InstructionNode]:\n        \"\"\" Get the last InstructionNode in the basic block\n        :return: The last node or None if the basic block is empty\n        \"\"\"\n        return self._end if self._end is not None else None\n\n    def find_instruction_node(self, inst: Instruction) -> Optional[InstructionNode]:\n        \"\"\"\n        Find a InstructionNode in the basic block that corresponds to a given instruction\n        :param inst: The instruction to find\n        :return: The node corresponding to the instruction or None if not found\n        \"\"\"\n        for node in self.iter_nodes():\n            if node.instruction == inst:\n                return node\n        return None\n\n\nclass Function:\n    \"\"\"\n    Function in the test case code.\n    This class is essentially a wrapper around a list of basic blocks, with special features:\n    * The basic blocks are ordered by their appearance in the assembly code.\n    * The last basic block has special handling: it is assumed to be the exit block of the function,\n      and it should contain little-to-no instructions. IMPORTANT: This basic block\n      is NOT included when iterating over the basic blocks in the function\n      or when calculating its length.\n    \"\"\"\n\n    name: Final[str]\n    \"\"\" The name of the function; matches the function label in the assembly code \"\"\"\n\n    parent: Final[CodeSection]\n    \"\"\" The actor that owns the function\"\"\"\n\n    _all_bb: List[BasicBlock]\n    \"\"\" List of all basic blocks in the function, ordered by their appearance in asm \"\"\"\n\n    def __init__(self, name: str, parent: CodeSection):\n        self.name = name\n        self.parent = parent\n        exit_bb = BasicBlock(f\".exit_{name.removeprefix('.function_')}\", parent=self, is_exit=True)\n        self._all_bb = [exit_bb]\n\n    def __len__(self) -> int:\n        \"\"\" Length of the function is the number of basic blocks in it, excluding the exit block \"\"\"\n        return len(self._all_bb[:-1])\n\n    def __iter__(self) -> GeneratorType[BasicBlock, None, None]:\n        \"\"\" Iterate over the basic blocks in the function, excluding the exit block \"\"\"\n        for bb in self._all_bb[:-1]:\n            yield bb\n\n    def __getitem__(self, id_: int) -> BasicBlock:\n        \"\"\" Get a basic block by its index, excluding the exit block \"\"\"\n        assert len(self._all_bb) > 1, \"Function has no non-exit basic blocks\"\n        non_exit_bbs = self._all_bb[:-1]\n        return non_exit_bbs[id_]\n\n    def append(self, bb: BasicBlock) -> None:\n        \"\"\" Append a basic block to the second-to-last position in the function\n          (the last is always exit) \"\"\"\n        exit_bb = self._all_bb.pop()\n        self._all_bb.append(bb)\n        self._all_bb.append(exit_bb)\n\n    def extend(self, bb_list: List[BasicBlock]) -> None:\n        \"\"\" Extend the function with a list of basic blocks (added to the end) \"\"\"\n        exit_bb = self._all_bb.pop()\n        self._all_bb.extend(bb_list)\n        self._all_bb.append(exit_bb)\n\n    def get_first_bb(self) -> BasicBlock:\n        \"\"\" Get the first basic block in the function.\n        If there are no basic blocks, return the default exit block.\n        \"\"\"\n        return self._all_bb[0]\n\n    def get_exit_bb(self) -> BasicBlock:\n        \"\"\" Get the last basic block in the function.\n        If there are no basic blocks, return the default exit block.\n        \"\"\"\n        exit_ = self._all_bb[-1]\n        assert exit_.is_exit, \"The last basic block is not marked as an exit block\"\n        return self._all_bb[-1]\n\n    def get_owner(self) -> Actor:\n        \"\"\" Get the actor that owns the function \"\"\"\n        return self.parent.owner\n\n\nclass _ELFSectionData(TypedDict):\n    \"\"\" Data of a section in the ELF file \"\"\"\n    offset: int\n    size: int\n    id: int\n\n\nclass CodeSection:\n    \"\"\"\n    Section in the test case code.\n    This class is essentially a wrapper around an ordered list of functions, with special features:\n    * The functions are ordered by their appearance in the assembly code.\n    \"\"\"\n\n    name: Final[str]\n    \"\"\" The name of the section \"\"\"\n\n    owner: Actor\n    \"\"\" The actor that owns the section \"\"\"\n\n    id_: Optional[int] = None\n    \"\"\" ID of the section; must match the ID in the ELF file \"\"\"\n\n    _functions: Final[List[Function]]  # List of functions in the section\n    _bin_offset: Optional[int] = None  # Offset of the section in the object file\n    _bin_size: Optional[int] = None  # Size of the section in the object file\n\n    def __init__(self, owner: Actor):\n        self.owner = owner\n        self.name = owner.name\n        owner.assign_code_section(self)\n        self._functions = []\n\n    def __iter__(self) -> GeneratorType[Function, None, None]:\n        \"\"\" Iterate over the functions in the section \"\"\"\n        for func in self._functions:\n            yield func\n\n    def __len__(self) -> int:\n        \"\"\" Length of the section is the number of functions in it \"\"\"\n        return len(self._functions)\n\n    def __getitem__(self, id_: int) -> Function:\n        \"\"\" Get a function by its index \"\"\"\n        return self._functions[id_]\n\n    def append(self, func: Function) -> None:\n        \"\"\" Append a function to the section \"\"\"\n        assert func.name not in [f.name for f in self._functions], \\\n            f\"Function {func.name} already exists in the section\"\n        self._functions.append(func)\n\n    def assign_elf_data(self, offset: int, size: int, id_: int) -> None:\n        \"\"\" Assign ELF data to the section \"\"\"\n        assert self._bin_offset is None and self._bin_size is None and self.id_ is None, \\\n            \"ELF data is already assigned\"\n        self._bin_offset = offset\n        self._bin_size = size\n        self.id_ = id_\n\n    def get_elf_data(self) -> _ELFSectionData:\n        \"\"\" Get the ELF data of the section \"\"\"\n        assert self._bin_offset is not None and self._bin_size is not None \\\n            and self.id_ is not None, \"ELF data is not assigned\"\n        return {\"offset\": self._bin_offset, \"size\": self._bin_size, \"id\": self.id_}\n\n\n# ==================================================================================================\n# All Program Information Combined\n# ==================================================================================================\nTC_EXIT_LABEL = \".test_case_exit\"\n\n\nclass TestCaseProgram:\n    \"\"\" A class representing a test case program \"\"\"\n\n    generator_seed: int\n    \"\"\" Seed used to generate the test case program \"\"\"\n\n    _asm_path: str  # Path to the assembly file containing the test case program\n    _obj: Optional[TestCaseBinary] = None  # Representation of the assembled test case program\n    _obj_is_assembled: bool = False  # Flag indicating whether the object file has been assembled\n\n    _sections: Final[List[CodeSection]]  # List of sections in the test case program\n    _actors: Dict[ActorName, Actor]  # Dictionary of actors in the test case program\n    _tc_exit_bb: Final[BasicBlock]  # Special basic block labeled that terminates the test case\n\n    def __init__(self, asm_path: str, seed: int = 0):\n        self.generator_seed = seed\n        self._asm_path = asm_path\n        self._tc_exit_bb = BasicBlock(TC_EXIT_LABEL)\n\n        self._actors = {\"main\": Actor.create_main()}\n        self._sections = [CodeSection(self._actors[\"main\"])]\n\n    def __len__(self) -> int:\n        \"\"\" Length of the test case is the number of sections \"\"\"\n        return len(self._sections)\n\n    def __getitem__(self, id_: int) -> CodeSection:\n        \"\"\" Get a section by its index \"\"\"\n        return self._sections[id_]\n\n    def get_tc_exit_bb(self) -> BasicBlock:\n        \"\"\" Get the special basic block used to terminate the test case \"\"\"\n        return self._tc_exit_bb\n\n    # ----------------------------------------------------------------------------------------------\n    # Iterators\n    def __iter__(self) -> GeneratorType[CodeSection, None, None]:\n        \"\"\" Default iterator over the sections in the test case \"\"\"\n        for sec in self._sections:\n            yield sec\n\n    def iter_functions(self) -> GeneratorType[Function, None, None]:\n        \"\"\" Non-default iterator: Iterate over all functions in the test case \"\"\"\n        for sec in self._sections:\n            for func in sec:\n                yield func\n\n    def iter_basic_blocks(self) -> GeneratorType[BasicBlock, None, None]:\n        \"\"\"\n        Non-default iterator:\n        Iterate over all basic blocks in the test case in their order of appearance in the asm file\n        \"\"\"\n        for sec in self._sections:\n            for func in sec:\n                for bb in func:\n                    yield bb\n\n    # ----------------------------------------------------------------------------------------------\n    # ELF file management\n    def assign_obj(self, obj_path: str) -> None:\n        \"\"\"\n        Assign an object file generated from the assembly file\n        :param obj_path: The path to the object file\n        :return: None\n        :raises AssertionError: If the object file is already assigned\n        \"\"\"\n        assert self._obj is None, \"Object file is already assigned\"\n        self._obj = TestCaseBinary(obj_path, self)\n\n    def mark_as_assembled(self) -> None:\n        \"\"\" Mark the object file as assembled \"\"\"\n        assert self._obj is not None, \"Object file is not assigned\"\n        self._obj_is_assembled = True\n        self._obj.mark_as_assembled()\n\n    def get_obj(self) -> TestCaseBinary:\n        \"\"\"\n        Get assigned TestCaseBinary, the container of the object file\n        generated from the test case program\n        \"\"\"\n        assert self._obj is not None, \"Object file is not assigned\"\n        return self._obj\n\n    # ----------------------------------------------------------------------------------------------\n    # ASM file management\n    def reassign_asm_file(self, asm_path: str) -> None:\n        \"\"\" Assign a new assembly file to the test case \"\"\"\n        assert not self._obj_is_assembled, \\\n            \"Attempting to reassign the asm file after it has been assembled\"\n        self._asm_path = asm_path\n\n    def asm_path(self) -> str:\n        \"\"\" Get the path to the assigned assembly file \"\"\"\n        return self._asm_path\n\n    def save(self, path: str) -> None:\n        \"\"\"\n        Save the test case assembly into a file.\n        :param path: The path to the file\n        :return: None\n        \"\"\"\n        shutil.copy2(self._asm_path, path)\n\n    # ----------------------------------------------------------------------------------------------\n    # Actor list management\n    def add_actor_with_section(self, actor: Actor, allow_overwrite: bool = False) -> None:\n        \"\"\"\n        Add an actor to the test case and assign it an empty CodeSection.\n\n        If an actor with the same name already exists and `allow_overwrite` is True,\n        the new actor will overwrite the existing one.\n        Otherwise, an error will be raised.\n        :param actor: The actor to add\n        :param allow_overwrite: Whether to allow overwriting an existing actor\n        :return: None\n        :raises ValueError: If the actor already exists in the test case\n        \"\"\"\n        if not allow_overwrite and actor.name in self._actors:\n            raise ValueError(f\"Actor {actor.name} already exists in the test case\")\n\n        # Update of the main actor\n        if actor.is_main:\n            assert actor.mode == ActorMode.HOST\n            assert actor.privilege_level == ActorPL.KERNEL\n            self._actors[actor.name] = actor\n            section = self._sections[0]\n            section.owner = actor\n            actor.assign_code_section(section)\n            return\n\n        # Update of an actor\n        if allow_overwrite and actor.name in self._actors:\n            self._actors[actor.name] = actor\n            section = self.find_section(actor.name)\n            section.owner = actor\n            actor.assign_code_section(section)\n            return\n\n        # New actor\n        self._actors[actor.name] = actor\n        section = CodeSection(actor)\n        self._sections.append(section)\n\n    def get_actors(self, sorted_: bool = False) -> List[Actor]:\n        \"\"\"\n        Get a list of actors.\n        :param sorted: Whether to sort the actors by ID\n        :return: A list of actors\n        \"\"\"\n        if sorted_:\n            return sorted(self._actors.values(), key=lambda x: x.get_id())\n        return list(self._actors.values())\n\n    def find_actor(self,\n                   name: Optional[ActorName] = None,\n                   actor_id: Optional[ActorID] = None) -> Actor:\n        \"\"\"\n        Select an actor by name or ID.\n        :param name: The name of the actor\n        :param actor_id: The ID of the actor\n        :return: The actor\n        :raises KeyError: If an actor with the given name/ID does not exist in the test case\n        :raises ValueError: If neither name nor ID is provided or if both are provided\n        \"\"\"\n        # check interface\n        assert name is not None or actor_id is not None, \"Either name or ID must be provided\"\n        assert name is None or actor_id is None, \"Only one of name or ID should be provided\"\n\n        # select by name\n        if name is not None:\n            if name not in self._actors:\n                raise KeyError(f\"Actor {name} does not exist in the test case\")\n            return self._actors[name]\n\n        # select by ID\n        for actor in self._actors.values():\n            if actor.get_id() == actor_id:\n                return actor\n        raise KeyError(f\"Actor with ID {actor_id} does not exist in the test case\")\n\n    def n_actors(self) -> int:\n        \"\"\"\n        Get the number of actors in the test case.\n        :return: The number of actors\n        \"\"\"\n        return len(self._actors)\n\n    # ==============================================================================================\n    # Function and section management\n    def get_sections(self) -> List[CodeSection]:\n        \"\"\" Get a list of sections in the test case \"\"\"\n        return self._sections\n\n    def find_section(self, name: str) -> CodeSection:\n        \"\"\"\n        Get a section by name\n        :param name: The name of the section\n        :return: The section\n        :raises KeyError: If the section does not exist in the test case\n        \"\"\"\n        for sec in self._sections:\n            if sec.name == name:\n                return sec\n        raise KeyError(f\"Section {name} does not exist in the test case\")\n\n    def find_function(self, name: str) -> Function:\n        \"\"\"\n        Get a function by name\n        :param name: The name of the function\n        :return: The function\n        :raises KeyError: If the function does not exist in the test case\n        \"\"\"\n        for sec in self._sections:\n            for func in sec:\n                if func.name == name:\n                    return func\n        raise KeyError(f\"Function {name} does not exist in the test case\")\n"
  },
  {
    "path": "rvzr/tc_components/test_case_data.py",
    "content": "\"\"\"\nFile: Classes representing test case data (aka input), as well as related and derived classes.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom typing import TYPE_CHECKING, Tuple, Optional, List\nimport numpy as np\n\nfrom ..sandbox import SandboxLayout, DataArea\n\nif TYPE_CHECKING:\n    from .actor import ActorID\n\nInputID = int\n\n# ==================================================================================================\n# Per-actor input data\n# ==================================================================================================\n\n# _ActorInput data type represents the input for a single actor\n_ActorInput = np.dtype(\n    [\n        ('main', np.uint64, SandboxLayout.data_area_size(DataArea.MAIN) // 8),\n        ('faulty', np.uint64, SandboxLayout.data_area_size(DataArea.FAULTY) // 8),\n        ('gpr', np.uint64, SandboxLayout.data_area_size(DataArea.GPR) // 8),\n        ('simd', np.uint64, SandboxLayout.data_area_size(DataArea.SIMD) // 8),\n        ('padding', np.uint64, SandboxLayout.data_area_size(DataArea.OVERFLOW_PAD) // 8),\n    ],\n    align=False,\n)\n\n_ACTOR_DATA_SIZE = _ActorInput['main'].itemsize + _ActorInput['faulty'].itemsize + \\\n    _ActorInput['gpr'].itemsize + _ActorInput['simd'].itemsize\n\n# ==================================================================================================\n# Full input data\n# ==================================================================================================\nUINT_NDARRAY = np.ndarray[Tuple[int, ...], np.dtype[np.uint64]]\n\n\nclass InputData(UINT_NDARRAY):\n    \"\"\"\n    Class representing a single data input for a test case program (TestCaseCode).\n    This data is used by the executor and the model to initialize the sandbox memory and registers.\n    The input is typically generated by the input generator or parsed from a binary file.\n\n    Internally, this class is a fixed-size numpy.ndarray of 64-bit unsigned integers,\n    with a few addition methods for convenience.\n\n    The array is organized as a 1D array of _ActorInput structures (one for each actor in the test\n    case program), sorted by actor ID. Each _ActorInput structure contains the input data for a\n    single actor, and it's layout mirrors the layout of the initialiable data regions\n    of the sandbox.py:SandboxLayout.\n    Namely, the Input array is organized as follows:\n\n    |-----|-------------------------------| main\n    | A   | sizeof(DataArea.MAIN)         |\n    | C   |-------------------------------| faulty\n    | T   | sizeof(DataArea.FAULTY)       |\n    | O   |-------------------------------| gpr\n    | R   | sizeof(DataArea.GPR)          |\n    |     |-------------------------------| simd\n    | 0   | sizeof(DataArea.SIMD)         |\n    |     |-------------------------------| padding\n    |     | sizeof(DataArea.OVERFLOW_PAD) |\n    |-----|-------------------------------|\n    | ... (repeat for n_actors)\n\n    The data in each region could be accesses via the array interface, e.g.,\n    actor0_main = Input[0]['main']\n\n    The array size is equal to the number of actors multiplied by\n    the number of elements in _ActorInput, i.e.,\n        Input.size = n_actors * _ActorInput.size\n    \"\"\"\n\n    seed: int = 0\n    \"\"\" seed: The seed value used to generate this input \"\"\"\n\n    # ==============================================================================================\n    # Constructors\n\n    def __new__(cls, n_actors: int = 1) -> InputData:\n        obj = super().__new__(cls, (n_actors,), _ActorInput, None, 0, None, None)\n        return obj\n\n    def __array_finalize__(self, obj: Optional[UINT_NDARRAY]) -> None:  # type: ignore\n        # if obj is None:\n        #     return\n        pass\n\n    # ==============================================================================================\n    # Class interface\n\n    @classmethod\n    def data_size_per_actor(cls) -> int:\n        \"\"\"\n        Get the size (in bytes) of the data area for a single actor.\n        :return: Size, in bytes\n        \"\"\"\n        return _ACTOR_DATA_SIZE\n\n    @classmethod\n    def n_data_entries_per_actor(cls) -> int:\n        \"\"\"\n        Get the number of entries in the input array for a single actor.\n\n        Note: This function is NOT equivalent to `data_size_per_actor`.\n        This is because array entries are 64-bit integers.\n        :return: Number of entries\n        \"\"\"\n        return _ACTOR_DATA_SIZE // 8\n\n    # ==============================================================================================\n    # Object interface\n\n    def __hash__(self) -> int:  # type: ignore\n        # hash of input is a hash of input data, registers and memory\n        h = hash(self.tobytes())\n        return h\n\n    def __str__(self) -> str:\n        return str(self.seed)\n\n    def __repr__(self) -> str:\n        return str(self.seed)\n\n    def set_actor_data(self, actor_id: 'ActorID', data: UINT_NDARRAY) -> None:\n        \"\"\"\n        Set the data for a single actor.\n        :param actor_id: The actor ID\n        :param data: The data to set\n        :return: None\n        :raises AssertionError: If the data array has an unexpected shape\n        \"\"\"\n        assert data.shape == (self.itemsize // 8,), \\\n            \"Data shape does not match the expected shape\"\n\n        # copy the data\n        self[actor_id] = data.view(_ActorInput)\n\n        # zero-fill the unused parts of the input\n        self[actor_id]['padding'] = 0\n\n    def save(self, path: str) -> None:\n        \"\"\"\n        Save the input to a binary file.\n        :param path: The path to the file\n        \"\"\"\n\n        with open(path, 'wb') as f:\n            f.write(self.tobytes())\n\n    def load(self, path: str) -> None:\n        \"\"\"\n        Load the input from a binary file.\n        :param path: The path to the file\n        \"\"\"\n\n        with open(path, 'rb') as f:\n            contents = np.fromfile(f, dtype=np.uint64)\n            n_actors = self.shape[0]\n            for actor_id in range(n_actors):\n                actor_start = actor_id * self.itemsize // 8\n                actor_end = actor_start + self.itemsize // 8\n                self.linear_view(actor_id)[:] = contents[actor_start:actor_end]\n\n    def linear_view(self, actor_id: ActorID) -> UINT_NDARRAY:\n        \"\"\"\n        Get a linear view of the input for a single actor;\n        that is, a 1D array of 64-bit integers.\n        :param actor_id: The actor ID\n        :return: A linear view of the input for the actor\n        \"\"\"\n        view: UINT_NDARRAY = self[actor_id].view((np.uint64, self[actor_id].itemsize // 8))\n        return view\n\n\ndef save_input_sequence_as_rdbf(inputs: List[InputData], path: str) -> None:\n    \"\"\"\n    Save the input sequences into an RDBF-formatted file.\n    (see docs/devel/binary-formats.md for details on RDBF format).\n\n    :param inputs: The input sequence\n    :param path: The path to save the RDBF file to\n    :return: None\n    \"\"\"\n    n_actors = len(inputs[0]) if len(inputs) > 0 else 1\n    with open(path, 'wb') as f:\n        # header\n        f.write((n_actors).to_bytes(8, byteorder='little'))  # number of actors\n        f.write((len(inputs)).to_bytes(8, byteorder='little'))  # number of inputs\n\n        # metadata\n        data_size_per_actor_bytes = InputData.data_size_per_actor()\n        for _ in range(n_actors):\n            f.write((data_size_per_actor_bytes).to_bytes(8, byteorder='little'))  # size\n            f.write((0).to_bytes(8, byteorder='little'))  # reserved\n\n        # data\n        for input_ in inputs:\n            f.write(input_.tobytes())\n\n\n# ==================================================================================================\n# Input taint\n# ==================================================================================================\n\nBOOL_NDARRAY = np.ndarray[Tuple[int], np.dtype[np.bool_]]\n\n_ActorInputTaint = np.dtype(\n    [\n        ('main', np.bool_, _ActorInput['main'].shape[0]),\n        ('faulty', np.bool_, _ActorInput['faulty'].shape[0]),\n        ('gpr', np.bool_, _ActorInput['gpr'].shape[0]),\n        ('simd', np.bool_, _ActorInput['simd'].shape[0]),\n        ('padding', np.bool_, _ActorInput['padding'].shape[0]),\n    ],\n    align=False,\n)\n\n\nclass InputTaint(BOOL_NDARRAY):\n    \"\"\"\n    Fixed-size boolean array that represents the taint status of a single test case input.\n    The array layout is identical to the Input class, with the same number of elements.\n    If an element is True, the corresponding element in the Input array is considered tainted.\n\n    The array is used to indicate which input elements influence contract traces.\n    \"\"\"\n\n    per_actor_taint_size: int = _ActorInputTaint.itemsize\n    \"\"\" per_actor_taint_size: The size of the taint area for a single actor \"\"\"\n\n    def __new__(cls, n_actors: int = 1) -> InputTaint:\n        obj = super().__new__(cls, (n_actors,), _ActorInputTaint, None, 0, None, None)\n        obj.fill(False)\n        return obj\n\n    def __array_finalize__(self, obj: Optional[UINT_NDARRAY]) -> None:  # type: ignore\n        # if obj is None:\n        #     return\n        pass\n\n    def linear_view(self, actor_id: 'ActorID') -> BOOL_NDARRAY:\n        \"\"\"\n        Get a linear view of the taint for a single actor;\n        that is, a 1D array of booleans.\n        :param actor_id: The actor ID\n        :return: A linear view of the taint for the actor\n        \"\"\"\n        view: BOOL_NDARRAY = self[actor_id].view((np.bool_, self[actor_id].itemsize))\n        return view\n\n    def full_linear_view(self) -> np.ndarray[Tuple[int, ...], np.dtype[np.bool_]]:\n        \"\"\"\n        Get a linear view of the taint for all actors;\n        that is, a 1D array of booleans.\n        :return: A linear view of the taint for all actors\n        \"\"\"\n        view = self.view(np.bool_)\n        return view\n\n    def taint_actor_offsets(self, actor_id: 'ActorID', offsets: List[int]) -> None:\n        \"\"\"\n        Taint a list of locations in the input of a single actor as tainted. The locations are\n        specified as offsets in the _ActorInput structure.\n        :param actor_id: The actor ID\n        :param offsets: A list of offsets\n        :return: None\n        \"\"\"\n        actor_view = self.linear_view(actor_id)\n        for offset in offsets:\n            actor_view[offset] = True\n\n    @classmethod\n    def taint_offset_from_sandbox_address(cls, sb_address: int) -> int:\n        \"\"\"\n        This function exists to cover the mismatch between the sandbox layout (sandbox.py) and\n        the InputTaint layout (this class).\n\n        The function computes the offset in the InputTaint structure from a given sandbox address\n        by subtracting the missing padding.\n\n        :param sb_address: The sandbox address\n        :return: The offset in the InputTaint structure\n        \"\"\"\n        per_actor_sandbox_size = SandboxLayout.data_size_per_actor()\n        actor_id = sb_address // per_actor_sandbox_size\n        sandbox_offset = sb_address % per_actor_sandbox_size\n        taint_offset = sandbox_offset - SandboxLayout.data_area_size(DataArea.MAIN)\n        return taint_offset // 8 + (actor_id * cls.per_actor_taint_size)\n"
  },
  {
    "path": "rvzr/traces.py",
    "content": "\"\"\"\nFile: Classes representing contract and hardware traces as well as derived containers thereof.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom collections import Counter\n\nfrom typing import List, Optional, Final, NamedTuple, Tuple, Dict, Generator, Callable, Literal\nfrom typing_extensions import assert_never\n\nimport xxhash\nimport numpy as np\nimport numpy.typing as npt\n\nfrom .tc_components.test_case_data import InputData, InputID\nfrom .tc_components.test_case_code import TestCaseProgram\nfrom .config import CONF\n\n_REG_ID_TO_NAME_X86 = {0: \"rax\", 1: \"rbx\", 2: \"rcx\", 3: \"rdx\", 4: \"rsi\", 5: \"rdi\"}\n_REG_ID_TO_NAME_ARM = {0: \"x0\", 1: \"x1\", 2: \"x2\", 3: \"x3\", 4: \"x4\", 5: \"x5\"}\n\n# ==================================================================================================\n# Contract Trace\n# ==================================================================================================\nCTraceEntryType = Literal[\"mem\", \"pc\", \"val\", \"reg\", \"ind\"]\n\n\nclass CTraceEntry(NamedTuple):\n    \"\"\"\n    Named tuple that represents a single entry in a contract trace.\n    \"\"\"\n    type_: CTraceEntryType\n    value: int\n\n\nUntypedCTrace = List[int]\n\n\nclass CTrace:\n    \"\"\"\n    Class representing a contract trace. It encapsulates a list of integers that represent a raw\n    trace collected from the model, and it provides basic comparison and hashing interfaces that\n    allow to compare traces for equality and to store them in sets or dictionaries.\n    \"\"\"\n    _trace: Final[List[CTraceEntry]]\n    _untyped: Final[UntypedCTrace]\n    _hash: Final[int]\n\n    _printed_as_l1d_map: bool = False\n    \"\"\" Flag indicating that the trace should be printed  \"\"\"\n\n    # ==============================================================================================\n    # Constructors\n\n    @classmethod\n    def empty_trace(cls) -> CTrace:\n        \"\"\" Produce a dummy CTrace object with empty raw trace \"\"\"\n        return cls([])\n\n    def __init__(self, trace: List[CTraceEntry]) -> None:\n        self._trace = trace\n        self._untyped = [entry.value for entry in trace]\n        self._hash = xxhash.xxh64(str(self._untyped), seed=0).intdigest()\n\n    # ==============================================================================================\n    # Printers\n\n    def __str__(self) -> str:\n        # For most cases, just print the hash value\n        if not self._printed_as_l1d_map:\n            return str(self._hash)\n\n        # When printing as L1D map was requested, print the trace as a 64-bit bit mask\n        # representing the cache state\n        map_value = 0\n        for address in self._untyped:\n            page_offset = (address & 0b111111000000) >> 6\n            cache_set_index = 0x8000000000000000 >> page_offset\n            map_value |= cache_set_index\n        map_str = f\"{map_value:064b}\"\n        map_str = map_str.replace(\"0\", \".\").replace(\"1\", \"^\")\n        return map_str\n\n    def full_str(self,\n                 m_col: str = \"\",\n                 pc_col: str = \"\",\n                 val_col: str = \"\",\n                 reset_col: str = \"\") -> str:\n        \"\"\"\n        Return a string representation of the complete typed trace.\n        Optionally, the colors can be specified for memory addresses, program counters, and values.\n\n        Example output: [mem: 0x100, pc: 0x200, val: 0x300]\n\n        :param m_col: color for memory addresses entries\n        :param pc_col: color for program counters entries\n        :param val_col: color for values entries\n        :param reset_col: color reset string\n        :return: colorized string representation of the trace\n        \"\"\"\n        assert reset_col or not (m_col or pc_col or val_col), \\\n            \"If any color is set, reset_col must be set as well\"\n\n        s = \"[\"\n        len_ = len(self._trace)\n        reg_names = _REG_ID_TO_NAME_X86 if CONF.instruction_set == \"x86-64\" else _REG_ID_TO_NAME_ARM\n        for i, item in enumerate(self._trace):\n            if item.type_ == \"mem\":\n                s += \"mem: \" + m_col + hex(item.value) + reset_col\n            elif item.type_ == \"pc\":\n                s += \"pc: \" + pc_col + hex(item.value) + reset_col\n            elif item.type_ == \"ind\":\n                s += \"indcall: \" + pc_col + hex(item.value) + reset_col\n            elif item.type_ == \"val\":\n                s += \"val: \" + val_col + hex(item.value) + reset_col\n            elif item.type_ == \"reg\":\n                name = reg_names[i]\n                s += name + \": \" + hex(item.value) + reset_col\n            else:\n                assert_never(item.type_)\n            if i != len_ - 1:\n                s += \", \"\n        return s + \"]\"\n\n    # ==============================================================================================\n    # Public Methods\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, CTrace):\n            raise NotImplementedError(\"Cannot compare CTrace with non-CTrace object\")\n        return self._hash == other._hash\n\n    def __lt__(self, other: CTrace) -> bool:\n        return self._hash < other._hash\n\n    def __gt__(self, other: CTrace) -> bool:\n        return self._hash > other._hash\n\n    def __len__(self) -> int:\n        return len(self._untyped)\n\n    def __hash__(self) -> int:\n        return self._hash\n\n    def is_empty(self) -> bool:\n        \"\"\" Check if the trace was created from an empty list or via `empty_trace()` \"\"\"\n        return len(self) == 0\n\n    def get_untyped(self) -> UntypedCTrace:\n        \"\"\"\n        Get a raw trace containing only integers values of the CTrace without type information\n        \"\"\"\n        return self._untyped\n\n    def get_typed(self) -> List[CTraceEntry]:\n        \"\"\" Get the full trace used to construct the CTrace object \"\"\"\n        return self._trace\n\n    def set_printed_as_l1d(self, val: bool = True) -> None:\n        \"\"\"\n        Set the flag indicating that the trace should be printed as L1D map.\n        This is normally used only for debugging purposes.\n        :param val: flag value\n        :return: None\n        \"\"\"\n        self._printed_as_l1d_map = val\n\n\n# ==================================================================================================\n# Hardware Trace\n# ==================================================================================================\nHTraceType = Literal[\"cache\", \"tsc\", \"reg\"]\n\nRawHTraceSample = np.dtype([\n    (\"trace\", np.uint64),\n    (\"pfc0\", np.uint64),\n    (\"pfc1\", np.uint64),\n    (\"pfc2\", np.uint64),\n    (\"pfc3\", np.uint64),\n    (\"pfc4\", np.uint64),\n])\nArrayOfSamples = npt.NDArray[np.void]\nPFCTuple = Tuple[int, int, int, int, int]\n\n\nclass HTrace:\n    \"\"\"\n    Class representing a sequence of hardware trace samples. The samples are normally received from\n    the executor: It executes a test case program with a given input multiple times, and each\n    execution produces a single hardware trace and a set of readings from performance counters.\n    The results of such repeated executions are collected into a single HTrace object.\n    \"\"\"\n    _raw: Final[ArrayOfSamples]\n    _hash: Final[int]\n    _is_corrupted_or_ignored: Final[bool]\n    _max_pfc: Optional[PFCTuple] = None\n    type_: Final[HTraceType]\n\n    # ==============================================================================================\n    # Constructors\n\n    @classmethod\n    def empty_trace(cls, type_: HTraceType = \"cache\") -> HTrace:\n        \"\"\" Get a dummy HTrace object with empty hardware trace and zeros for perf counters \"\"\"\n        return cls(np.ndarray(0, dtype=RawHTraceSample), type_)\n\n    @classmethod\n    def invalid_trace(cls, type_: HTraceType = \"cache\") -> HTrace:\n        \"\"\" Get a dummy HTrace object with corrupted hardware trace and zeros for perf counters \"\"\"\n        invalid_sample: npt.NDArray[np.void] = np.zeros(1, dtype=RawHTraceSample)\n        return cls(invalid_sample, type_)\n\n    def __init__(self, htrace_samples: ArrayOfSamples, type_: HTraceType = \"cache\") -> None:\n        # check that the input has the expected shape\n        assert htrace_samples.ndim == 1, \"htrace_samples must be a 1D array\"\n        assert htrace_samples.dtype == RawHTraceSample, \"htrace_samples must be of type RawHTrace\"\n\n        # store and process the samples\n        self._raw = htrace_samples\n        self._hash = xxhash.xxh64(str(htrace_samples['trace']), seed=0).intdigest()\n        self._is_corrupted_or_ignored = all(x == 0 for x in htrace_samples['trace'])\n        self.type_ = type_\n\n    # ==============================================================================================\n    # Printers\n\n    def __str__(self) -> str:\n        return str(self._hash)\n\n    def full_str(self,\n                 line_prefix: str = \"\",\n                 region1_col: str = \"\",\n                 region2_col: str = \"\",\n                 reset_col: str = \"\") -> str:\n        \"\"\"\n        Return a string (table) representation of the set of samples used to create this trace\n\n        :param line_prefix: string to prepend to each line\n        :return: string representation of the trace\n        \"\"\"\n        # Nothing to print if the trace is empty\n        if self.is_empty():\n            return line_prefix\n        if self.type_ == \"cache\":\n            return self._full_cache_str(line_prefix, region1_col, region2_col, reset_col)\n        if self.type_ == \"tsc\":\n            return self._full_tsc_str(line_prefix)\n        if self.type_ == \"reg\":\n            return self._full_arch_str(line_prefix)\n\n        assert_never(self.type_)\n        return \"\"  # pylint: disable=unreachable\n\n    def _full_arch_str(self, line_prefix: str) -> str:\n        \"\"\" Return a string representation of an architectural trace.\n        Example output:\n        [rax:0x00000000000001, rbx:0x00000000000002, rcx:0x00000000000003, rdx:0x00000000000004,\n        rsi:0x00000000000005, rdi:0x00000000000006]\n        \"\"\"\n        assert len(self._raw) == 1, \"Invalid trace shape\"\n        s = line_prefix\n        reg_names = _REG_ID_TO_NAME_X86 if CONF.instruction_set == \"x86-64\" else _REG_ID_TO_NAME_ARM\n        s += \"[\"\n        s += f\"{reg_names[0]}: 0x{self._raw[0]['trace']:x}, \"\n        s += f\"{reg_names[1]}: 0x{self._raw[0]['pfc0']:x}, \"\n        s += f\"{reg_names[2]}: 0x{self._raw[0]['pfc1']:x}, \"\n        s += f\"{reg_names[3]}: 0x{self._raw[0]['pfc2']:x}, \"\n        s += f\"{reg_names[4]}: 0x{self._raw[0]['pfc3']:x}, \"\n        s += f\"{reg_names[5]}: 0x{self._raw[0]['pfc4']:x}\"\n        s += \"]\"\n        return s\n\n    def _full_tsc_str(self, line_prefix: str) -> str:\n        \"\"\" Return a string representation of a TSC trace.\n        Example output:\n        00000001 [16]\n        00000002 [16]\n        \"\"\"\n        s = \"\"\n        mask = np.uint64(0xFFFFFFFFFFFFFF)\n        counter = Counter(self._raw['trace'])\n        trace_distribution = sorted(counter.items(), key=lambda x: x[1], reverse=True)\n        for t, c in trace_distribution:\n            t = t & mask\n            s += f\"{line_prefix}{t:08} [{c}]\\n\"\n        return s\n\n    def _full_cache_str(self, line_prefix: str, r1_col: str, r2_col: str, reset_col: str) -> str:\n        \"\"\" Return a string representation of a cache trace\n        Example output:\n            .....^..................^....................................... [16]\n            ........................^....................................... [16]\n        \"\"\"\n        s = \"\"\n        counter = Counter(self._raw['trace'])\n        trace_distribution = sorted(counter.items(), key=lambda x: x[1], reverse=True)\n        for t, c in trace_distribution:\n            line = f\"{t:064b}\"\n            line = line.replace(\"0\", \".\").replace(\"1\", \"^\")\n            line = r1_col + line[0:8] + r2_col + line[8:16] \\\n                + r1_col + line[16:24] + r2_col + line[24:32] \\\n                + r1_col + line[32:40] + r2_col + line[40:48] \\\n                + r1_col + line[48:56] + r2_col + line[56:64] \\\n                + reset_col + line[64:]\n            s += f\"{line_prefix}{line} [{c}]\\n\"\n        return s\n\n    def full_pair_str(self,\n                      other: HTrace,\n                      r1_col: str = \"\",\n                      r2_col: str = \"\",\n                      res_col: str = \"\") -> str:\n        \"\"\" Return a string representation of two sample distributions side-by-side\"\"\"\n        if self.type_ == \"cache\":\n            assert other.type_ == \"cache\"\n            return self._full_cache_pair_str(other, r1_col, r2_col, res_col)\n        if self.type_ == \"tsc\":\n            assert other.type_ == \"tsc\"\n            return self._full_tsc_pair_str(other)\n        if self.type_ == \"reg\":\n            raise NotImplementedError(\"Cannot compare architectural traces\")\n\n        assert_never(self.type_)\n        return \"\"  # pylint: disable=unreachable\n\n    def _full_tsc_pair_str(self, other: HTrace) -> str:\n        \"\"\" Return a string representation of two TSC sample distributions side-by-side\n        Example output:\n        00000001        |16     | 8      |\n        00000002        |16     | 24     |\n        \"\"\"\n        mask = np.uint64(0xFFFFFFFFFFFFFF)\n        c1 = Counter(self.get_raw_traces())\n        c2 = Counter(other.get_raw_traces())\n        keys = set(c1.keys()) | set(c2.keys())\n        traces = sorted(keys, key=lambda x: (c1[x] << 10000) + c2[x], reverse=True)\n\n        final_str = \"\"\n        for t in traces:\n            t = t & mask\n            final_str += f\"{t:08} | {c1[t]:<6} | {c2[t]:<6} |\\n\"\n        return final_str\n\n    def _full_cache_pair_str(self, other: HTrace, r1_col: str, r2_col: str, res_col: str) -> str:\n        \"\"\" Return a string representation of two cache sample distributions side-by-side\n        Example output:\n        .....^..................^....................................... |16     | 8      |\n        .....^.......................................................... |16     | 24     |\n        \"\"\"\n        c1 = Counter(self.get_raw_traces())\n        c2 = Counter(other.get_raw_traces())\n        keys = set(c1.keys()) | set(c2.keys())\n        traces = sorted(keys, key=lambda x: (c1[x] << 10000) + c2[x], reverse=True)\n\n        final_str = \"\"\n        for t in traces:\n            s = f\"{t:064b}\"\n            s = s.replace(\"0\", \".\").replace(\"1\", \"^\")\n            s = r1_col + s[0:8] + r2_col + s[8:16] \\\n                + r1_col + s[16:24] + r2_col + s[24:32] \\\n                + r1_col + s[32:40] + r2_col + s[40:48] \\\n                + r1_col + s[48:56] + r2_col + s[56:64] \\\n                + res_col + s[64:]\n            final_str += s + f\" | {c1[t]:<6} | {c2[t]:<6}|\\n\"\n        return final_str\n\n    # ==============================================================================================\n    # Public Methods\n\n    def __eq__(self, other: object) -> bool:\n        if not isinstance(other, HTrace):\n            raise NotImplementedError(\"Cannot compare HTrace with non-HTrace object\")\n        return self._hash == other._hash\n\n    def __len__(self) -> int:\n        return len(self._raw)\n\n    def __hash__(self) -> int:\n        return self._hash\n\n    def merge(self, other: HTrace) -> HTrace:\n        \"\"\"\n        Merge two HTrace objects into a single HTrace object\n        :param other: HTrace object to merge with\n        :return: A new HTrace object that contains all samples from both objects\n        \"\"\"\n        samples = np.concatenate([self._raw, other._raw])  # pylint: disable=protected-access\n        return HTrace(samples, self.type_)\n\n    def is_empty(self) -> bool:\n        \"\"\" Check if the trace was created from an empty sample or via `empty_trace()` \"\"\"\n        return len(self) == 0\n\n    def is_corrupted_or_ignored(self) -> bool:\n        \"\"\"\n        Check if the trace was created from a corrupted sample.\n        A corrupted sample is a sample were all values are zero, which is a way that executor\n        signals that the trace was not collected properly.\n        \"\"\"\n        return self._is_corrupted_or_ignored\n\n    def get_raw_readings(self) -> ArrayOfSamples:\n        \"\"\" Get all raw readings used to construct the HTrace object (including both the trace and\n        the performance counters) \"\"\"\n        return self._raw\n\n    def get_raw_traces(self) -> npt.NDArray[np.uint64]:\n        \"\"\" Get all raw traces in the HTrace object (does NOT include performance counters) \"\"\"\n        return self._raw['trace']\n\n    def sample_size(self) -> int:\n        \"\"\" Get the number of htrace samples in the HTrace object \"\"\"\n        return len(self._raw)\n\n    def get_max_pfc(self) -> PFCTuple:\n        \"\"\" Get the maximum values of performance counters in the HTrace object \"\"\"\n        if self._max_pfc is None:\n            new_max_pfc = (0, 0, 0, 0, 0)\n            for sample in self._raw:\n                if sample['pfc0'] > new_max_pfc[0]:\n                    new_max_pfc = (int(sample['pfc0']), int(sample['pfc1']), int(sample['pfc2']),\n                                   int(sample['pfc3']), int(sample['pfc4']))\n            self._max_pfc = new_max_pfc\n        return self._max_pfc\n\n\n# ==================================================================================================\n# Trace Containers\n# ==================================================================================================\nclass TraceBundle(NamedTuple):\n    \"\"\"\n    Container for a set of measurements produced by executing a test case with a given input on\n    the model and on the executor. It contains the input, the input ID, the contract trace, the\n    hardware trace.\n    \"\"\"\n    input_id: InputID\n    input_: InputData\n    ctrace: CTrace\n    htrace: HTrace\n\n\nHWEquivalenceFunction = Callable[[HTrace, HTrace], bool]\n\n\ndef _default_eq_function(htrace1: HTrace, htrace2: HTrace) -> bool:\n    \"\"\" Default equivalence function that compares hardware traces for equality \"\"\"\n    return htrace1 == htrace2\n\n\nclass HardwareEqClass:\n    \"\"\"\n    Container for a set of TraceBundles that are hardware-equivalent;\n    that is, all TraceBundles in the list have similar hardware trace.\n    Note that the notion of similarity is configurable and defined by CONF.analyser\n    \"\"\"\n\n    htrace: Final[HTrace]\n    \"\"\" hardware trace that all measurements in the equivalence class share \"\"\"\n\n    measurements: Final[List[TraceBundle]]\n    \"\"\" a list of TraceBundles that are hardware-equivalent \"\"\"\n\n    # ==============================================================================================\n    # Constructors\n\n    def __init__(self, measurements: List[TraceBundle]) -> None:\n        self.htrace = measurements[0].htrace\n        self.measurements = measurements\n\n    @classmethod\n    def build_hw_classes(\n        cls,\n        measurements: List[TraceBundle],\n        equivalence_function: HWEquivalenceFunction = _default_eq_function\n    ) -> List[HardwareEqClass]:\n        \"\"\"\n        Break down a list of measurements into hardware equivalence classes.\n        :param measurements: a list of measurements\n        :param equivalence_function: a function that compares two hardware traces and returns True\n                if they are equivalent (i.e., they are similar enough to be considered the same)\n        :return: List of hardware classes formed from the input measurements\n        \"\"\"\n        # Collect lists of measurements with equivalent hardware traces\n        hw_groups: Dict[int, List[TraceBundle]] = {}\n        diverging_htraces: List[HTrace] = []\n        for measurement in measurements:\n            htrace = measurement.htrace\n\n            # First iteration: create a new hardware equivalence class\n            if not diverging_htraces:\n                diverging_htraces.append(htrace)\n                hw_groups[hash(htrace)] = [measurement]\n                continue\n\n            # Subsequent iterations: check if the htrace is equivalent to any existing class\n            for htrace_other in diverging_htraces:\n                if equivalence_function(htrace, htrace_other):\n                    hw_groups[hash(htrace_other)].append(measurement)\n                    break\n            else:\n                diverging_htraces.append(htrace)\n                hw_groups[hash(htrace)] = [measurement]\n\n        # Create HardwareEqClass objects for each group\n        hw_classes: List[HardwareEqClass] = []\n        for group in hw_groups.values():\n            hw_classes.append(cls(group))\n        return hw_classes\n\n    # ==============================================================================================\n    # Public Methods\n\n    def __len__(self) -> int:\n        return len(self.measurements)\n\n    def __iter__(self) -> Generator[TraceBundle, None, None]:\n        yield from self.measurements\n\n    def __getitem__(self, index: int) -> TraceBundle:\n        return self.measurements[index]\n\n    def __eq__(self, other: object) -> bool:\n        \"\"\"\n        Compare two hardware equivalence classes for equality.\n        Two classes are equal if they have the same hardware trace and the same measurements.\n        \"\"\"\n        if not isinstance(other, HardwareEqClass):\n            raise NotImplementedError(\"Cannot compare HardwareEqClass with object of another type\")\n        return self.htrace == other.htrace and self.measurements == other.measurements\n\n\nclass ContractEqClass:\n    \"\"\"\n    ContractEqClass is a container for a set of TraceBundles that are contract-equivalent;\n    that is, all TraceBundles in the list have the same contract trace.\n    \"\"\"\n\n    ctrace: Final[CTrace]\n    \"\"\" contract trace that all measurements in the equivalence class share \"\"\"\n\n    measurements: Final[List[TraceBundle]]\n    \"\"\" list of TraceBundles that are contract-equivalent \"\"\"\n\n    _hw_classes: Optional[List[HardwareEqClass]] = None\n\n    # ==============================================================================================\n    # Constructors\n\n    def __init__(self, measurements: List[TraceBundle]) -> None:\n        self.ctrace = measurements[0].ctrace\n        self.measurements = measurements\n\n        # check that all measurements have the same contract trace\n        for measurement in measurements:\n            assert measurement.ctrace == self.ctrace, \"All measurements must have the same ctrace\"\n\n    @classmethod\n    def build_contract_classes(cls, measurements: List[TraceBundle]) -> List[ContractEqClass]:\n        \"\"\"\n        Break down a list of measurements into contract equivalence classes\n        :param measurements: a list of measurements\n        :return: List of contract classes formed from the input measurements\n        \"\"\"\n        # Collect lists of measurements with equivalent contract traces\n        eq_groups: Dict[int, List[TraceBundle]] = {}\n        for measurement in measurements:\n            ctrace = measurement.ctrace\n            hash_ = hash(ctrace)\n            if hash_ not in eq_groups:\n                eq_groups[hash_] = [measurement]\n            else:\n                eq_groups[hash_].append(measurement)\n\n        # Create ContractEqClass objects for each group\n        eq_classes: List[ContractEqClass] = []\n        for group in eq_groups.values():\n            eq_classes.append(cls(group))\n        return eq_classes\n\n    def __len__(self) -> int:\n        return len(self.measurements)\n\n    def set_hw_classes(self, hw_classes: List[HardwareEqClass]) -> None:\n        \"\"\"\n        Set the hardware equivalence classes for this contract equivalence class.\n        :param hw_classes: a dictionary of hardware equivalence classes indexed by htrace hash\n        \"\"\"\n        assert self._hw_classes is None, \"Attempting to set hardware equivalence classes twice\"\n        self._hw_classes = hw_classes\n\n    def set_trivial_hw_classes(self) -> None:\n        \"\"\"\n        Set the hardware equivalence classes for this contract equivalence class by directly\n        comparing hardware traces for equality.\n        \"\"\"\n        assert self._hw_classes is None, \"Attempting to set hardware equivalence classes twice\"\n        self._hw_classes = HardwareEqClass.build_hw_classes(self.measurements)\n\n    def get_hw_classes(self) -> List[HardwareEqClass]:\n        \"\"\"\n        Get a dictionary of all hardware equivalence classes\n        in the contract equivalence class; indexed by htrace hash.\n        \"\"\"\n        assert self._hw_classes is not None, \"Hardware equivalence classes not set\"\n        return self._hw_classes\n\n\nclass Violation(ContractEqClass):\n    \"\"\"\n    Violation is a special type of equivalence class that represents a violation of a contract.\n    It is a container for a list of measurements (TraceBundle) that triggered the violation\n    as well as a complete sequence of inputs that triggered the violation and the test case program.\n    \"\"\"\n\n    input_sequence: List[InputData]\n    \"\"\" complete sequence of inputs that triggered the violation \"\"\"\n\n    test_case_code: Final[TestCaseProgram]\n    \"\"\" test case program that triggered the violation \"\"\"\n\n    # ==============================================================================================\n    # Constructors\n\n    def __init__(self, measurements: List[TraceBundle], input_sequence: List[InputData],\n                 test_case_code: TestCaseProgram) -> None:\n        super().__init__(measurements)\n        self.input_sequence = input_sequence\n        self.test_case_code = test_case_code\n\n    @classmethod\n    def from_contract_eq_class(cls, eq_class: ContractEqClass, input_sequence: List[InputData],\n                               test_case_code: TestCaseProgram) -> Violation:\n        \"\"\"\n        Create a Violation object from a ContractEqClass object\n        :param eq_class: ContractEquivalenceClass object\n        :param input_sequence: complete sequence of inputs that triggered the violation\n        :return: Violation object\n        \"\"\"\n        violation = cls(eq_class.measurements, input_sequence, test_case_code)\n        violation.set_hw_classes(eq_class.get_hw_classes())\n        return violation\n\n    @classmethod\n    def pseudo_violation_from_inputs(cls, input_sequence: List[InputData],\n                                     test_case_code: TestCaseProgram) -> Violation:\n        \"\"\"\n        Create a pseudo-violation object from a list of inputs.\n\n        This interface is used by the variants of the fuzzer that rely on non-standard definition\n        of violations (e.g., ArchFuzzer). Such fuzzers may not produce traces, yet they still\n        have to return a violation object from the analyser.\n\n        :param input_: input that triggered the pseudo-violation\n        :return: Violation object\n        \"\"\"\n        measurements = []\n        hw_classes = []\n        for i, input_ in enumerate(input_sequence):\n            bundle = TraceBundle(InputID(i), input_, CTrace.empty_trace(), HTrace.empty_trace())\n            measurements.append(bundle)\n            hw_classes.append(HardwareEqClass([bundle]))\n        violation = cls(measurements, input_sequence, test_case_code)\n        violation.set_hw_classes(hw_classes)\n        return violation\n\n    # ==============================================================================================\n    # Public Methods\n    def full_str(self, region1_col: str = \"\", region2_col: str = \"\", reset_col: str = \"\") -> str:\n        \"\"\"\n        Return a string representation of the violation, including the contract and hardware\n        traces of all measurements in the violation\n        \"\"\"\n        # pylint: disable=too-many-locals  # justification: the method is clear enough as is\n\n        s = \"Violation Details:\\n\"\n\n        # Four cases to consider:\n        hw_classes = self._hw_classes\n\n        # 1. No hardware equivalence classes (set_hw_classes() was never called)\n        if hw_classes is None or not hw_classes:\n            s += f\"  Contract trace: (hash {self.ctrace})\\n\"\n            s += f\"    {self.ctrace.full_str()} \\n\"\n            s += \"  Hardware traces:\\n\"\n            for measurement in self.measurements:\n                s += measurement.htrace.full_str(\"    \") + \"\\n\"\n            return s\n\n        # 2. Only one measurement in the violation (normally the case for ArchFuzzer)\n        if len(hw_classes) == 1:\n            s += \"  Special Case: Single-input violation\\n\"\n            s += f\"  Input ID: {self.measurements[0].input_id}\\n\"\n            s += f\"  Contract trace: (hash {self.ctrace})\\n\"\n            s += f\"    {self.ctrace.full_str()} \\n\"\n            s += \"  Hardware traces:\\n\"\n            s += f\"    {hw_classes[0].htrace.full_str()} \\n\"\n            return s\n\n        # 3. If there are two HW classes, print them side by side for improved readability\n        hw_classes = self.get_hw_classes()\n        if len(hw_classes) == 2:\n            inputs1 = [m.input_id for m in hw_classes[0]]\n            inputs2 = [m.input_id for m in hw_classes[1]]\n            htrace1 = hw_classes[0][0].htrace\n            htrace2 = hw_classes[1][0].htrace\n            trace_table = htrace1.full_pair_str(htrace2, region1_col, region2_col, reset_col)\n\n            line_width = max(len(line) for line in trace_table.splitlines())\n            assert line_width > 19, \"Invalid trace table\"\n            trace_width = line_width - 19\n\n            header = \"\\n\" + \"-\" * line_width + \"\\n\"\n            header += f\"{'HTrace':^{trace_width}} | ID:{inputs1[0]:<3} | ID:{inputs2[0]:<3}|\\n\"\n            header += \"-\" * line_width + \"\\n\"\n            s += header + trace_table\n            return s\n\n        # 4. With more than two HW classes, print each HW class separately\n        for hw_class in hw_classes:\n            inputs = [measurement.input_id for measurement in hw_class]\n            s += \"  Inputs \"\n            s += f\"{inputs}\\n\" if len(inputs) < 4 else f\"{inputs[:4]} (+ {len(inputs) - 4} )\\n\"\n            s += hw_class.htrace.full_str(\"    \", region1_col, region2_col, reset_col)\n        s += \"\\n\"\n        return s\n"
  },
  {
    "path": "rvzr/unicorn.pyi",
    "content": "\"\"\"\nmypy stub for unicorn\n\"\"\"\n# pylint: disable=missing-docstring, unused-argument\nfrom typing import Optional, Callable, Any, Generator, Tuple\n\n\nclass Uc(object):\n\n    def __init__(self, arch: int, mode: int):\n        ...\n\n    def emu_start(self, begin: int, until: int, timeout: int = 0, count: int = 0) -> None:\n        ...\n\n    def emu_stop(self) -> None:\n        ...\n\n    def reg_read(self, reg_id: int, opt: Optional[int] = None) -> int:\n        ...\n\n    def reg_write(self, reg_id: int, value: int) -> None:\n        ...\n\n    def msr_read(self, msr_id: int) -> int:\n        ...\n\n    def msr_write(self, msr_id: int, value: int) -> None:\n        ...\n\n    def mem_read(self, address: int, size: int) -> bytearray:\n        ...\n\n    def mem_write(self, address: int, data: bytes) -> None:\n        ...\n\n    def mem_map(self, address: int, size: int, perms: int = ...) -> None:\n        ...\n\n    def mem_map_ptr(self, address: int, size: int, perms: int, ptr: int) -> None:\n        ...\n\n    def mem_unmap(self, address: int, size: int) -> None:\n        ...\n\n    def mem_protect(self, address: int, size: int, perms: int = ...) -> None:\n        ...\n\n    def query(self, query_mode: int) -> int:\n        ...\n\n    def hook_add(self,\n                 htype: int,\n                 callback: Callable[['Uc', int, int, int, int, Any], None]\n                 | Callable[['Uc', int, int, Any], None],\n                 user_data: Any = None,\n                 begin: int = 1,\n                 end: int = 0,\n                 arg1: int = 0) -> None:\n        ...\n\n    def hook_del(self, h: int) -> None:\n        ...\n\n    def context_save(self) -> object:\n        ...\n\n    def context_update(self, context: object) -> None:\n        ...\n\n    def context_restore(self, context: object) -> None:\n        ...\n\n    def mem_regions(self) -> Generator[Tuple[int, int], None, None]:\n        ...\n\n\nclass UcError(BaseException):\n    ...\n\n\n# Constants\nUC_API_MAJOR: int\nUC_API_MINOR: int\nUC_VERSION_MAJOR: int\nUC_VERSION_MINOR: int\nUC_VERSION_EXTRA: int\nUC_SECOND_SCALE: int\nUC_MILISECOND_SCALE: int\nUC_ARCH_ARM: int\nUC_ARCH_ARM64: int\nUC_ARCH_MIPS: int\nUC_ARCH_X86: int\nUC_ARCH_PPC: int\nUC_ARCH_SPARC: int\nUC_ARCH_M68K: int\nUC_ARCH_MAX: int\nUC_MODE_LITTLE_ENDIAN: int\nUC_MODE_BIG_ENDIAN: int\nUC_MODE_ARM: int\nUC_MODE_THUMB: int\nUC_MODE_MCLASS: int\nUC_MODE_V8: int\nUC_MODE_ARM926: int\nUC_MODE_ARM946: int\nUC_MODE_ARM1176: int\nUC_MODE_MICRO: int\nUC_MODE_MIPS3: int\nUC_MODE_MIPS32R6: int\nUC_MODE_MIPS32: int\nUC_MODE_MIPS64: int\nUC_MODE_16: int\nUC_MODE_32: int\nUC_MODE_64: int\nUC_MODE_PPC32: int\nUC_MODE_PPC64: int\nUC_MODE_QPX: int\nUC_MODE_SPARC32: int\nUC_MODE_SPARC64: int\nUC_MODE_V9: int\nUC_ERR_OK: int\nUC_ERR_NOMEM: int\nUC_ERR_ARCH: int\nUC_ERR_HANDLE: int\nUC_ERR_MODE: int\nUC_ERR_VERSION: int\nUC_ERR_READ_UNMAPPED: int\nUC_ERR_WRITE_UNMAPPED: int\nUC_ERR_FETCH_UNMAPPED: int\nUC_ERR_HOOK: int\nUC_ERR_INSN_INVALID: int\nUC_ERR_MAP: int\nUC_ERR_WRITE_PROT: int\nUC_ERR_READ_PROT: int\nUC_ERR_FETCH_PROT: int\nUC_ERR_ARG: int\nUC_ERR_READ_UNALIGNED: int\nUC_ERR_WRITE_UNALIGNED: int\nUC_ERR_FETCH_UNALIGNED: int\nUC_ERR_HOOK_EXIST: int\nUC_ERR_RESOURCE: int\nUC_ERR_EXCEPTION: int\nUC_MEM_READ: int\nUC_MEM_WRITE: int\nUC_MEM_FETCH: int\nUC_MEM_READ_UNMAPPED: int\nUC_MEM_WRITE_UNMAPPED: int\nUC_MEM_FETCH_UNMAPPED: int\nUC_MEM_WRITE_PROT: int\nUC_MEM_READ_PROT: int\nUC_MEM_FETCH_PROT: int\nUC_MEM_READ_AFTER: int\nUC_HOOK_INTR: int\nUC_HOOK_INSN: int\nUC_HOOK_CODE: int\nUC_HOOK_BLOCK: int\nUC_HOOK_MEM_READ_UNMAPPED: int\nUC_HOOK_MEM_WRITE_UNMAPPED: int\nUC_HOOK_MEM_FETCH_UNMAPPED: int\nUC_HOOK_MEM_READ_PROT: int\nUC_HOOK_MEM_WRITE_PROT: int\nUC_HOOK_MEM_FETCH_PROT: int\nUC_HOOK_MEM_READ: int\nUC_HOOK_MEM_WRITE: int\nUC_HOOK_MEM_FETCH: int\nUC_HOOK_MEM_READ_AFTER: int\nUC_HOOK_INSN_INVALID: int\nUC_HOOK_MEM_UNMAPPED: int\nUC_HOOK_MEM_PROT: int\nUC_HOOK_MEM_READ_INVALID: int\nUC_HOOK_MEM_WRITE_INVALID: int\nUC_HOOK_MEM_FETCH_INVALID: int\nUC_HOOK_MEM_INVALID: int\nUC_HOOK_MEM_VALID: int\nUC_QUERY_MODE: int\nUC_QUERY_PAGE_SIZE: int\nUC_QUERY_ARCH: int\nUC_QUERY_TIMEOUT: int\nUC_PROT_NONE: int\nUC_PROT_READ: int\nUC_PROT_WRITE: int\nUC_PROT_EXEC: int\nUC_PROT_ALL: int\n\n# X86 registers\nUC_X86_REG_INVALID: int\nUC_X86_REG_AH: int\nUC_X86_REG_AL: int\nUC_X86_REG_AX: int\nUC_X86_REG_BH: int\nUC_X86_REG_BL: int\nUC_X86_REG_BP: int\nUC_X86_REG_BPL: int\nUC_X86_REG_BX: int\nUC_X86_REG_CH: int\nUC_X86_REG_CL: int\nUC_X86_REG_CS: int\nUC_X86_REG_CX: int\nUC_X86_REG_DH: int\nUC_X86_REG_DI: int\nUC_X86_REG_DIL: int\nUC_X86_REG_DL: int\nUC_X86_REG_DS: int\nUC_X86_REG_DX: int\nUC_X86_REG_EAX: int\nUC_X86_REG_EBP: int\nUC_X86_REG_EBX: int\nUC_X86_REG_ECX: int\nUC_X86_REG_EDI: int\nUC_X86_REG_EDX: int\nUC_X86_REG_EFLAGS: int\nUC_X86_REG_EIP: int\nUC_X86_REG_EIZ: int\nUC_X86_REG_ES: int\nUC_X86_REG_ESI: int\nUC_X86_REG_ESP: int\nUC_X86_REG_FPSW: int\nUC_X86_REG_FS: int\nUC_X86_REG_GS: int\nUC_X86_REG_IP: int\nUC_X86_REG_RAX: int\nUC_X86_REG_RBP: int\nUC_X86_REG_RBX: int\nUC_X86_REG_RCX: int\nUC_X86_REG_RDI: int\nUC_X86_REG_RDX: int\nUC_X86_REG_RIP: int\nUC_X86_REG_RIZ: int\nUC_X86_REG_RSI: int\nUC_X86_REG_RSP: int\nUC_X86_REG_SI: int\nUC_X86_REG_SIL: int\nUC_X86_REG_SP: int\nUC_X86_REG_SPL: int\nUC_X86_REG_SS: int\nUC_X86_REG_CR0: int\nUC_X86_REG_CR1: int\nUC_X86_REG_CR2: int\nUC_X86_REG_CR3: int\nUC_X86_REG_CR4: int\nUC_X86_REG_CR5: int\nUC_X86_REG_CR6: int\nUC_X86_REG_CR7: int\nUC_X86_REG_CR8: int\nUC_X86_REG_CR9: int\nUC_X86_REG_CR10: int\nUC_X86_REG_CR11: int\nUC_X86_REG_CR12: int\nUC_X86_REG_CR13: int\nUC_X86_REG_CR14: int\nUC_X86_REG_CR15: int\nUC_X86_REG_DR0: int\nUC_X86_REG_DR1: int\nUC_X86_REG_DR2: int\nUC_X86_REG_DR3: int\nUC_X86_REG_DR4: int\nUC_X86_REG_DR5: int\nUC_X86_REG_DR6: int\nUC_X86_REG_DR7: int\nUC_X86_REG_DR8: int\nUC_X86_REG_DR9: int\nUC_X86_REG_DR10: int\nUC_X86_REG_DR11: int\nUC_X86_REG_DR12: int\nUC_X86_REG_DR13: int\nUC_X86_REG_DR14: int\nUC_X86_REG_DR15: int\nUC_X86_REG_FP0: int\nUC_X86_REG_FP1: int\nUC_X86_REG_FP2: int\nUC_X86_REG_FP3: int\nUC_X86_REG_FP4: int\nUC_X86_REG_FP5: int\nUC_X86_REG_FP6: int\nUC_X86_REG_FP7: int\nUC_X86_REG_K0: int\nUC_X86_REG_K1: int\nUC_X86_REG_K2: int\nUC_X86_REG_K3: int\nUC_X86_REG_K4: int\nUC_X86_REG_K5: int\nUC_X86_REG_K6: int\nUC_X86_REG_K7: int\nUC_X86_REG_MM0: int\nUC_X86_REG_MM1: int\nUC_X86_REG_MM2: int\nUC_X86_REG_MM3: int\nUC_X86_REG_MM4: int\nUC_X86_REG_MM5: int\nUC_X86_REG_MM6: int\nUC_X86_REG_MM7: int\nUC_X86_REG_R8: int\nUC_X86_REG_R9: int\nUC_X86_REG_R10: int\nUC_X86_REG_R11: int\nUC_X86_REG_R12: int\nUC_X86_REG_R13: int\nUC_X86_REG_R14: int\nUC_X86_REG_R15: int\nUC_X86_REG_ST0: int\nUC_X86_REG_ST1: int\nUC_X86_REG_ST2: int\nUC_X86_REG_ST3: int\nUC_X86_REG_ST4: int\nUC_X86_REG_ST5: int\nUC_X86_REG_ST6: int\nUC_X86_REG_ST7: int\nUC_X86_REG_XMM0: int\nUC_X86_REG_XMM1: int\nUC_X86_REG_XMM2: int\nUC_X86_REG_XMM3: int\nUC_X86_REG_XMM4: int\nUC_X86_REG_XMM5: int\nUC_X86_REG_XMM6: int\nUC_X86_REG_XMM7: int\nUC_X86_REG_XMM8: int\nUC_X86_REG_XMM9: int\nUC_X86_REG_XMM10: int\nUC_X86_REG_XMM11: int\nUC_X86_REG_XMM12: int\nUC_X86_REG_XMM13: int\nUC_X86_REG_XMM14: int\nUC_X86_REG_XMM15: int\nUC_X86_REG_XMM16: int\nUC_X86_REG_XMM17: int\nUC_X86_REG_XMM18: int\nUC_X86_REG_XMM19: int\nUC_X86_REG_XMM20: int\nUC_X86_REG_XMM21: int\nUC_X86_REG_XMM22: int\nUC_X86_REG_XMM23: int\nUC_X86_REG_XMM24: int\nUC_X86_REG_XMM25: int\nUC_X86_REG_XMM26: int\nUC_X86_REG_XMM27: int\nUC_X86_REG_XMM28: int\nUC_X86_REG_XMM29: int\nUC_X86_REG_XMM30: int\nUC_X86_REG_XMM31: int\nUC_X86_REG_YMM0: int\nUC_X86_REG_YMM1: int\nUC_X86_REG_YMM2: int\nUC_X86_REG_YMM3: int\nUC_X86_REG_YMM4: int\nUC_X86_REG_YMM5: int\nUC_X86_REG_YMM6: int\nUC_X86_REG_YMM7: int\nUC_X86_REG_YMM8: int\nUC_X86_REG_YMM9: int\nUC_X86_REG_YMM10: int\nUC_X86_REG_YMM11: int\nUC_X86_REG_YMM12: int\nUC_X86_REG_YMM13: int\nUC_X86_REG_YMM14: int\nUC_X86_REG_YMM15: int\nUC_X86_REG_YMM16: int\nUC_X86_REG_YMM17: int\nUC_X86_REG_YMM18: int\nUC_X86_REG_YMM19: int\nUC_X86_REG_YMM20: int\nUC_X86_REG_YMM21: int\nUC_X86_REG_YMM22: int\nUC_X86_REG_YMM23: int\nUC_X86_REG_YMM24: int\nUC_X86_REG_YMM25: int\nUC_X86_REG_YMM26: int\nUC_X86_REG_YMM27: int\nUC_X86_REG_YMM28: int\nUC_X86_REG_YMM29: int\nUC_X86_REG_YMM30: int\nUC_X86_REG_YMM31: int\nUC_X86_REG_ZMM0: int\nUC_X86_REG_ZMM1: int\nUC_X86_REG_ZMM2: int\nUC_X86_REG_ZMM3: int\nUC_X86_REG_ZMM4: int\nUC_X86_REG_ZMM5: int\nUC_X86_REG_ZMM6: int\nUC_X86_REG_ZMM7: int\nUC_X86_REG_ZMM8: int\nUC_X86_REG_ZMM9: int\nUC_X86_REG_ZMM10: int\nUC_X86_REG_ZMM11: int\nUC_X86_REG_ZMM12: int\nUC_X86_REG_ZMM13: int\nUC_X86_REG_ZMM14: int\nUC_X86_REG_ZMM15: int\nUC_X86_REG_ZMM16: int\nUC_X86_REG_ZMM17: int\nUC_X86_REG_ZMM18: int\nUC_X86_REG_ZMM19: int\nUC_X86_REG_ZMM20: int\nUC_X86_REG_ZMM21: int\nUC_X86_REG_ZMM22: int\nUC_X86_REG_ZMM23: int\nUC_X86_REG_ZMM24: int\nUC_X86_REG_ZMM25: int\nUC_X86_REG_ZMM26: int\nUC_X86_REG_ZMM27: int\nUC_X86_REG_ZMM28: int\nUC_X86_REG_ZMM29: int\nUC_X86_REG_ZMM30: int\nUC_X86_REG_ZMM31: int\nUC_X86_REG_R8B: int\nUC_X86_REG_R9B: int\nUC_X86_REG_R10B: int\nUC_X86_REG_R11B: int\nUC_X86_REG_R12B: int\nUC_X86_REG_R13B: int\nUC_X86_REG_R14B: int\nUC_X86_REG_R15B: int\nUC_X86_REG_R8D: int\nUC_X86_REG_R9D: int\nUC_X86_REG_R10D: int\nUC_X86_REG_R11D: int\nUC_X86_REG_R12D: int\nUC_X86_REG_R13D: int\nUC_X86_REG_R14D: int\nUC_X86_REG_R15D: int\nUC_X86_REG_R8W: int\nUC_X86_REG_R9W: int\nUC_X86_REG_R10W: int\nUC_X86_REG_R11W: int\nUC_X86_REG_R12W: int\nUC_X86_REG_R13W: int\nUC_X86_REG_R14W: int\nUC_X86_REG_R15W: int\nUC_X86_REG_IDTR: int\nUC_X86_REG_GDTR: int\nUC_X86_REG_LDTR: int\nUC_X86_REG_TR: int\nUC_X86_REG_FPCW: int\nUC_X86_REG_FPTAG: int\nUC_X86_REG_MSR: int\nUC_X86_REG_MXCSR: int\nUC_X86_REG_FS_BASE: int\nUC_X86_REG_GS_BASE: int\nUC_X86_REG_ENDING: int\n\n# X86 instructions\n\nUC_X86_INS_INVALID: int\nUC_X86_INS_AAA: int\nUC_X86_INS_AAD: int\nUC_X86_INS_AAM: int\nUC_X86_INS_AAS: int\nUC_X86_INS_FABS: int\nUC_X86_INS_ADC: int\nUC_X86_INS_ADCX: int\nUC_X86_INS_ADD: int\nUC_X86_INS_ADDPD: int\nUC_X86_INS_ADDPS: int\nUC_X86_INS_ADDSD: int\nUC_X86_INS_ADDSS: int\nUC_X86_INS_ADDSUBPD: int\nUC_X86_INS_ADDSUBPS: int\nUC_X86_INS_FADD: int\nUC_X86_INS_FIADD: int\nUC_X86_INS_FADDP: int\nUC_X86_INS_ADOX: int\nUC_X86_INS_AESDECLAST: int\nUC_X86_INS_AESDEC: int\nUC_X86_INS_AESENCLAST: int\nUC_X86_INS_AESENC: int\nUC_X86_INS_AESIMC: int\nUC_X86_INS_AESKEYGENASSIST: int\nUC_X86_INS_AND: int\nUC_X86_INS_ANDN: int\nUC_X86_INS_ANDNPD: int\nUC_X86_INS_ANDNPS: int\nUC_X86_INS_ANDPD: int\nUC_X86_INS_ANDPS: int\nUC_X86_INS_ARPL: int\nUC_X86_INS_BEXTR: int\nUC_X86_INS_BLCFILL: int\nUC_X86_INS_BLCI: int\nUC_X86_INS_BLCIC: int\nUC_X86_INS_BLCMSK: int\nUC_X86_INS_BLCS: int\nUC_X86_INS_BLENDPD: int\nUC_X86_INS_BLENDPS: int\nUC_X86_INS_BLENDVPD: int\nUC_X86_INS_BLENDVPS: int\nUC_X86_INS_BLSFILL: int\nUC_X86_INS_BLSI: int\nUC_X86_INS_BLSIC: int\nUC_X86_INS_BLSMSK: int\nUC_X86_INS_BLSR: int\nUC_X86_INS_BOUND: int\nUC_X86_INS_BSF: int\nUC_X86_INS_BSR: int\nUC_X86_INS_BSWAP: int\nUC_X86_INS_BT: int\nUC_X86_INS_BTC: int\nUC_X86_INS_BTR: int\nUC_X86_INS_BTS: int\nUC_X86_INS_BZHI: int\nUC_X86_INS_CALL: int\nUC_X86_INS_CBW: int\nUC_X86_INS_CDQ: int\nUC_X86_INS_CDQE: int\nUC_X86_INS_FCHS: int\nUC_X86_INS_CLAC: int\nUC_X86_INS_CLC: int\nUC_X86_INS_CLD: int\nUC_X86_INS_CLFLUSH: int\nUC_X86_INS_CLFLUSHOPT: int\nUC_X86_INS_CLGI: int\nUC_X86_INS_CLI: int\nUC_X86_INS_CLTS: int\nUC_X86_INS_CLWB: int\nUC_X86_INS_CMC: int\nUC_X86_INS_CMOVA: int\nUC_X86_INS_CMOVAE: int\nUC_X86_INS_CMOVB: int\nUC_X86_INS_CMOVBE: int\nUC_X86_INS_FCMOVBE: int\nUC_X86_INS_FCMOVB: int\nUC_X86_INS_CMOVE: int\nUC_X86_INS_FCMOVE: int\nUC_X86_INS_CMOVG: int\nUC_X86_INS_CMOVGE: int\nUC_X86_INS_CMOVL: int\nUC_X86_INS_CMOVLE: int\nUC_X86_INS_FCMOVNBE: int\nUC_X86_INS_FCMOVNB: int\nUC_X86_INS_CMOVNE: int\nUC_X86_INS_FCMOVNE: int\nUC_X86_INS_CMOVNO: int\nUC_X86_INS_CMOVNP: int\nUC_X86_INS_FCMOVNU: int\nUC_X86_INS_CMOVNS: int\nUC_X86_INS_CMOVO: int\nUC_X86_INS_CMOVP: int\nUC_X86_INS_FCMOVU: int\nUC_X86_INS_CMOVS: int\nUC_X86_INS_CMP: int\nUC_X86_INS_CMPPD: int\nUC_X86_INS_CMPPS: int\nUC_X86_INS_CMPSB: int\nUC_X86_INS_CMPSD: int\nUC_X86_INS_CMPSQ: int\nUC_X86_INS_CMPSS: int\nUC_X86_INS_CMPSW: int\nUC_X86_INS_CMPXCHG16B: int\nUC_X86_INS_CMPXCHG: int\nUC_X86_INS_CMPXCHG8B: int\nUC_X86_INS_COMISD: int\nUC_X86_INS_COMISS: int\nUC_X86_INS_FCOMP: int\nUC_X86_INS_FCOMPI: int\nUC_X86_INS_FCOMI: int\nUC_X86_INS_FCOM: int\nUC_X86_INS_FCOS: int\nUC_X86_INS_CPUID: int\nUC_X86_INS_CQO: int\nUC_X86_INS_CRC32: int\nUC_X86_INS_CVTDQ2PD: int\nUC_X86_INS_CVTDQ2PS: int\nUC_X86_INS_CVTPD2DQ: int\nUC_X86_INS_CVTPD2PS: int\nUC_X86_INS_CVTPS2DQ: int\nUC_X86_INS_CVTPS2PD: int\nUC_X86_INS_CVTSD2SI: int\nUC_X86_INS_CVTSD2SS: int\nUC_X86_INS_CVTSI2SD: int\nUC_X86_INS_CVTSI2SS: int\nUC_X86_INS_CVTSS2SD: int\nUC_X86_INS_CVTSS2SI: int\nUC_X86_INS_CVTTPD2DQ: int\nUC_X86_INS_CVTTPS2DQ: int\nUC_X86_INS_CVTTSD2SI: int\nUC_X86_INS_CVTTSS2SI: int\nUC_X86_INS_CWD: int\nUC_X86_INS_CWDE: int\nUC_X86_INS_DAA: int\nUC_X86_INS_DAS: int\nUC_X86_INS_DATA16: int\nUC_X86_INS_DEC: int\nUC_X86_INS_DIV: int\nUC_X86_INS_DIVPD: int\nUC_X86_INS_DIVPS: int\nUC_X86_INS_FDIVR: int\nUC_X86_INS_FIDIVR: int\nUC_X86_INS_FDIVRP: int\nUC_X86_INS_DIVSD: int\nUC_X86_INS_DIVSS: int\nUC_X86_INS_FDIV: int\nUC_X86_INS_FIDIV: int\nUC_X86_INS_FDIVP: int\nUC_X86_INS_DPPD: int\nUC_X86_INS_DPPS: int\nUC_X86_INS_RET: int\nUC_X86_INS_ENCLS: int\nUC_X86_INS_ENCLU: int\nUC_X86_INS_ENTER: int\nUC_X86_INS_EXTRACTPS: int\nUC_X86_INS_EXTRQ: int\nUC_X86_INS_F2XM1: int\nUC_X86_INS_LCALL: int\nUC_X86_INS_LJMP: int\nUC_X86_INS_FBLD: int\nUC_X86_INS_FBSTP: int\nUC_X86_INS_FCOMPP: int\nUC_X86_INS_FDECSTP: int\nUC_X86_INS_FEMMS: int\nUC_X86_INS_FFREE: int\nUC_X86_INS_FICOM: int\nUC_X86_INS_FICOMP: int\nUC_X86_INS_FINCSTP: int\nUC_X86_INS_FLDCW: int\nUC_X86_INS_FLDENV: int\nUC_X86_INS_FLDL2E: int\nUC_X86_INS_FLDL2T: int\nUC_X86_INS_FLDLG2: int\nUC_X86_INS_FLDLN2: int\nUC_X86_INS_FLDPI: int\nUC_X86_INS_FNCLEX: int\nUC_X86_INS_FNINIT: int\nUC_X86_INS_FNOP: int\nUC_X86_INS_FNSTCW: int\nUC_X86_INS_FNSTSW: int\nUC_X86_INS_FPATAN: int\nUC_X86_INS_FPREM: int\nUC_X86_INS_FPREM1: int\nUC_X86_INS_FPTAN: int\nUC_X86_INS_FFREEP: int\nUC_X86_INS_FRNDINT: int\nUC_X86_INS_FRSTOR: int\nUC_X86_INS_FNSAVE: int\nUC_X86_INS_FSCALE: int\nUC_X86_INS_FSETPM: int\nUC_X86_INS_FSINCOS: int\nUC_X86_INS_FNSTENV: int\nUC_X86_INS_FXAM: int\nUC_X86_INS_FXRSTOR: int\nUC_X86_INS_FXRSTOR64: int\nUC_X86_INS_FXSAVE: int\nUC_X86_INS_FXSAVE64: int\nUC_X86_INS_FXTRACT: int\nUC_X86_INS_FYL2X: int\nUC_X86_INS_FYL2XP1: int\nUC_X86_INS_MOVAPD: int\nUC_X86_INS_MOVAPS: int\nUC_X86_INS_ORPD: int\nUC_X86_INS_ORPS: int\nUC_X86_INS_VMOVAPD: int\nUC_X86_INS_VMOVAPS: int\nUC_X86_INS_XORPD: int\nUC_X86_INS_XORPS: int\nUC_X86_INS_GETSEC: int\nUC_X86_INS_HADDPD: int\nUC_X86_INS_HADDPS: int\nUC_X86_INS_HLT: int\nUC_X86_INS_HSUBPD: int\nUC_X86_INS_HSUBPS: int\nUC_X86_INS_IDIV: int\nUC_X86_INS_FILD: int\nUC_X86_INS_IMUL: int\nUC_X86_INS_IN: int\nUC_X86_INS_INC: int\nUC_X86_INS_INSB: int\nUC_X86_INS_INSERTPS: int\nUC_X86_INS_INSERTQ: int\nUC_X86_INS_INSD: int\nUC_X86_INS_INSW: int\nUC_X86_INS_INT: int\nUC_X86_INS_INT1: int\nUC_X86_INS_INT3: int\nUC_X86_INS_INTO: int\nUC_X86_INS_INVD: int\nUC_X86_INS_INVEPT: int\nUC_X86_INS_INVLPG: int\nUC_X86_INS_INVLPGA: int\nUC_X86_INS_INVPCID: int\nUC_X86_INS_INVVPID: int\nUC_X86_INS_IRET: int\nUC_X86_INS_IRETD: int\nUC_X86_INS_IRETQ: int\nUC_X86_INS_FISTTP: int\nUC_X86_INS_FIST: int\nUC_X86_INS_FISTP: int\nUC_X86_INS_UCOMISD: int\nUC_X86_INS_UCOMISS: int\nUC_X86_INS_VCOMISD: int\nUC_X86_INS_VCOMISS: int\nUC_X86_INS_VCVTSD2SS: int\nUC_X86_INS_VCVTSI2SD: int\nUC_X86_INS_VCVTSI2SS: int\nUC_X86_INS_VCVTSS2SD: int\nUC_X86_INS_VCVTTSD2SI: int\nUC_X86_INS_VCVTTSD2USI: int\nUC_X86_INS_VCVTTSS2SI: int\nUC_X86_INS_VCVTTSS2USI: int\nUC_X86_INS_VCVTUSI2SD: int\nUC_X86_INS_VCVTUSI2SS: int\nUC_X86_INS_VUCOMISD: int\nUC_X86_INS_VUCOMISS: int\nUC_X86_INS_JAE: int\nUC_X86_INS_JA: int\nUC_X86_INS_JBE: int\nUC_X86_INS_JB: int\nUC_X86_INS_JCXZ: int\nUC_X86_INS_JECXZ: int\nUC_X86_INS_JE: int\nUC_X86_INS_JGE: int\nUC_X86_INS_JG: int\nUC_X86_INS_JLE: int\nUC_X86_INS_JL: int\nUC_X86_INS_JMP: int\nUC_X86_INS_JNE: int\nUC_X86_INS_JNO: int\nUC_X86_INS_JNP: int\nUC_X86_INS_JNS: int\nUC_X86_INS_JO: int\nUC_X86_INS_JP: int\nUC_X86_INS_JRCXZ: int\nUC_X86_INS_JS: int\nUC_X86_INS_KANDB: int\nUC_X86_INS_KANDD: int\nUC_X86_INS_KANDNB: int\nUC_X86_INS_KANDND: int\nUC_X86_INS_KANDNQ: int\nUC_X86_INS_KANDNW: int\nUC_X86_INS_KANDQ: int\nUC_X86_INS_KANDW: int\nUC_X86_INS_KMOVB: int\nUC_X86_INS_KMOVD: int\nUC_X86_INS_KMOVQ: int\nUC_X86_INS_KMOVW: int\nUC_X86_INS_KNOTB: int\nUC_X86_INS_KNOTD: int\nUC_X86_INS_KNOTQ: int\nUC_X86_INS_KNOTW: int\nUC_X86_INS_KORB: int\nUC_X86_INS_KORD: int\nUC_X86_INS_KORQ: int\nUC_X86_INS_KORTESTB: int\nUC_X86_INS_KORTESTD: int\nUC_X86_INS_KORTESTQ: int\nUC_X86_INS_KORTESTW: int\nUC_X86_INS_KORW: int\nUC_X86_INS_KSHIFTLB: int\nUC_X86_INS_KSHIFTLD: int\nUC_X86_INS_KSHIFTLQ: int\nUC_X86_INS_KSHIFTLW: int\nUC_X86_INS_KSHIFTRB: int\nUC_X86_INS_KSHIFTRD: int\nUC_X86_INS_KSHIFTRQ: int\nUC_X86_INS_KSHIFTRW: int\nUC_X86_INS_KUNPCKBW: int\nUC_X86_INS_KXNORB: int\nUC_X86_INS_KXNORD: int\nUC_X86_INS_KXNORQ: int\nUC_X86_INS_KXNORW: int\nUC_X86_INS_KXORB: int\nUC_X86_INS_KXORD: int\nUC_X86_INS_KXORQ: int\nUC_X86_INS_KXORW: int\nUC_X86_INS_LAHF: int\nUC_X86_INS_LAR: int\nUC_X86_INS_LDDQU: int\nUC_X86_INS_LDMXCSR: int\nUC_X86_INS_LDS: int\nUC_X86_INS_FLDZ: int\nUC_X86_INS_FLD1: int\nUC_X86_INS_FLD: int\nUC_X86_INS_LEA: int\nUC_X86_INS_LEAVE: int\nUC_X86_INS_LES: int\nUC_X86_INS_LFENCE: int\nUC_X86_INS_LFS: int\nUC_X86_INS_LGDT: int\nUC_X86_INS_LGS: int\nUC_X86_INS_LIDT: int\nUC_X86_INS_LLDT: int\nUC_X86_INS_LMSW: int\nUC_X86_INS_OR: int\nUC_X86_INS_SUB: int\nUC_X86_INS_XOR: int\nUC_X86_INS_LODSB: int\nUC_X86_INS_LODSD: int\nUC_X86_INS_LODSQ: int\nUC_X86_INS_LODSW: int\nUC_X86_INS_LOOP: int\nUC_X86_INS_LOOPE: int\nUC_X86_INS_LOOPNE: int\nUC_X86_INS_RETF: int\nUC_X86_INS_RETFQ: int\nUC_X86_INS_LSL: int\nUC_X86_INS_LSS: int\nUC_X86_INS_LTR: int\nUC_X86_INS_XADD: int\nUC_X86_INS_LZCNT: int\nUC_X86_INS_MASKMOVDQU: int\nUC_X86_INS_MAXPD: int\nUC_X86_INS_MAXPS: int\nUC_X86_INS_MAXSD: int\nUC_X86_INS_MAXSS: int\nUC_X86_INS_MFENCE: int\nUC_X86_INS_MINPD: int\nUC_X86_INS_MINPS: int\nUC_X86_INS_MINSD: int\nUC_X86_INS_MINSS: int\nUC_X86_INS_CVTPD2PI: int\nUC_X86_INS_CVTPI2PD: int\nUC_X86_INS_CVTPI2PS: int\nUC_X86_INS_CVTPS2PI: int\nUC_X86_INS_CVTTPD2PI: int\nUC_X86_INS_CVTTPS2PI: int\nUC_X86_INS_EMMS: int\nUC_X86_INS_MASKMOVQ: int\nUC_X86_INS_MOVD: int\nUC_X86_INS_MOVDQ2Q: int\nUC_X86_INS_MOVNTQ: int\nUC_X86_INS_MOVQ2DQ: int\nUC_X86_INS_MOVQ: int\nUC_X86_INS_PABSB: int\nUC_X86_INS_PABSD: int\nUC_X86_INS_PABSW: int\nUC_X86_INS_PACKSSDW: int\nUC_X86_INS_PACKSSWB: int\nUC_X86_INS_PACKUSWB: int\nUC_X86_INS_PADDB: int\nUC_X86_INS_PADDD: int\nUC_X86_INS_PADDQ: int\nUC_X86_INS_PADDSB: int\nUC_X86_INS_PADDSW: int\nUC_X86_INS_PADDUSB: int\nUC_X86_INS_PADDUSW: int\nUC_X86_INS_PADDW: int\nUC_X86_INS_PALIGNR: int\nUC_X86_INS_PANDN: int\nUC_X86_INS_PAND: int\nUC_X86_INS_PAVGB: int\nUC_X86_INS_PAVGW: int\nUC_X86_INS_PCMPEQB: int\nUC_X86_INS_PCMPEQD: int\nUC_X86_INS_PCMPEQW: int\nUC_X86_INS_PCMPGTB: int\nUC_X86_INS_PCMPGTD: int\nUC_X86_INS_PCMPGTW: int\nUC_X86_INS_PEXTRW: int\nUC_X86_INS_PHADDSW: int\nUC_X86_INS_PHADDW: int\nUC_X86_INS_PHADDD: int\nUC_X86_INS_PHSUBD: int\nUC_X86_INS_PHSUBSW: int\nUC_X86_INS_PHSUBW: int\nUC_X86_INS_PINSRW: int\nUC_X86_INS_PMADDUBSW: int\nUC_X86_INS_PMADDWD: int\nUC_X86_INS_PMAXSW: int\nUC_X86_INS_PMAXUB: int\nUC_X86_INS_PMINSW: int\nUC_X86_INS_PMINUB: int\nUC_X86_INS_PMOVMSKB: int\nUC_X86_INS_PMULHRSW: int\nUC_X86_INS_PMULHUW: int\nUC_X86_INS_PMULHW: int\nUC_X86_INS_PMULLW: int\nUC_X86_INS_PMULUDQ: int\nUC_X86_INS_POR: int\nUC_X86_INS_PSADBW: int\nUC_X86_INS_PSHUFB: int\nUC_X86_INS_PSHUFW: int\nUC_X86_INS_PSIGNB: int\nUC_X86_INS_PSIGND: int\nUC_X86_INS_PSIGNW: int\nUC_X86_INS_PSLLD: int\nUC_X86_INS_PSLLQ: int\nUC_X86_INS_PSLLW: int\nUC_X86_INS_PSRAD: int\nUC_X86_INS_PSRAW: int\nUC_X86_INS_PSRLD: int\nUC_X86_INS_PSRLQ: int\nUC_X86_INS_PSRLW: int\nUC_X86_INS_PSUBB: int\nUC_X86_INS_PSUBD: int\nUC_X86_INS_PSUBQ: int\nUC_X86_INS_PSUBSB: int\nUC_X86_INS_PSUBSW: int\nUC_X86_INS_PSUBUSB: int\nUC_X86_INS_PSUBUSW: int\nUC_X86_INS_PSUBW: int\nUC_X86_INS_PUNPCKHBW: int\nUC_X86_INS_PUNPCKHDQ: int\nUC_X86_INS_PUNPCKHWD: int\nUC_X86_INS_PUNPCKLBW: int\nUC_X86_INS_PUNPCKLDQ: int\nUC_X86_INS_PUNPCKLWD: int\nUC_X86_INS_PXOR: int\nUC_X86_INS_MONITOR: int\nUC_X86_INS_MONTMUL: int\nUC_X86_INS_MOV: int\nUC_X86_INS_MOVABS: int\nUC_X86_INS_MOVBE: int\nUC_X86_INS_MOVDDUP: int\nUC_X86_INS_MOVDQA: int\nUC_X86_INS_MOVDQU: int\nUC_X86_INS_MOVHLPS: int\nUC_X86_INS_MOVHPD: int\nUC_X86_INS_MOVHPS: int\nUC_X86_INS_MOVLHPS: int\nUC_X86_INS_MOVLPD: int\nUC_X86_INS_MOVLPS: int\nUC_X86_INS_MOVMSKPD: int\nUC_X86_INS_MOVMSKPS: int\nUC_X86_INS_MOVNTDQA: int\nUC_X86_INS_MOVNTDQ: int\nUC_X86_INS_MOVNTI: int\nUC_X86_INS_MOVNTPD: int\nUC_X86_INS_MOVNTPS: int\nUC_X86_INS_MOVNTSD: int\nUC_X86_INS_MOVNTSS: int\nUC_X86_INS_MOVSB: int\nUC_X86_INS_MOVSD: int\nUC_X86_INS_MOVSHDUP: int\nUC_X86_INS_MOVSLDUP: int\nUC_X86_INS_MOVSQ: int\nUC_X86_INS_MOVSS: int\nUC_X86_INS_MOVSW: int\nUC_X86_INS_MOVSX: int\nUC_X86_INS_MOVSXD: int\nUC_X86_INS_MOVUPD: int\nUC_X86_INS_MOVUPS: int\nUC_X86_INS_MOVZX: int\nUC_X86_INS_MPSADBW: int\nUC_X86_INS_MUL: int\nUC_X86_INS_MULPD: int\nUC_X86_INS_MULPS: int\nUC_X86_INS_MULSD: int\nUC_X86_INS_MULSS: int\nUC_X86_INS_MULX: int\nUC_X86_INS_FMUL: int\nUC_X86_INS_FIMUL: int\nUC_X86_INS_FMULP: int\nUC_X86_INS_MWAIT: int\nUC_X86_INS_NEG: int\nUC_X86_INS_NOP: int\nUC_X86_INS_NOT: int\nUC_X86_INS_OUT: int\nUC_X86_INS_OUTSB: int\nUC_X86_INS_OUTSD: int\nUC_X86_INS_OUTSW: int\nUC_X86_INS_PACKUSDW: int\nUC_X86_INS_PAUSE: int\nUC_X86_INS_PAVGUSB: int\nUC_X86_INS_PBLENDVB: int\nUC_X86_INS_PBLENDW: int\nUC_X86_INS_PCLMULQDQ: int\nUC_X86_INS_PCMPEQQ: int\nUC_X86_INS_PCMPESTRI: int\nUC_X86_INS_PCMPESTRM: int\nUC_X86_INS_PCMPGTQ: int\nUC_X86_INS_PCMPISTRI: int\nUC_X86_INS_PCMPISTRM: int\nUC_X86_INS_PCOMMIT: int\nUC_X86_INS_PDEP: int\nUC_X86_INS_PEXT: int\nUC_X86_INS_PEXTRB: int\nUC_X86_INS_PEXTRD: int\nUC_X86_INS_PEXTRQ: int\nUC_X86_INS_PF2ID: int\nUC_X86_INS_PF2IW: int\nUC_X86_INS_PFACC: int\nUC_X86_INS_PFADD: int\nUC_X86_INS_PFCMPEQ: int\nUC_X86_INS_PFCMPGE: int\nUC_X86_INS_PFCMPGT: int\nUC_X86_INS_PFMAX: int\nUC_X86_INS_PFMIN: int\nUC_X86_INS_PFMUL: int\nUC_X86_INS_PFNACC: int\nUC_X86_INS_PFPNACC: int\nUC_X86_INS_PFRCPIT1: int\nUC_X86_INS_PFRCPIT2: int\nUC_X86_INS_PFRCP: int\nUC_X86_INS_PFRSQIT1: int\nUC_X86_INS_PFRSQRT: int\nUC_X86_INS_PFSUBR: int\nUC_X86_INS_PFSUB: int\nUC_X86_INS_PHMINPOSUW: int\nUC_X86_INS_PI2FD: int\nUC_X86_INS_PI2FW: int\nUC_X86_INS_PINSRB: int\nUC_X86_INS_PINSRD: int\nUC_X86_INS_PINSRQ: int\nUC_X86_INS_PMAXSB: int\nUC_X86_INS_PMAXSD: int\nUC_X86_INS_PMAXUD: int\nUC_X86_INS_PMAXUW: int\nUC_X86_INS_PMINSB: int\nUC_X86_INS_PMINSD: int\nUC_X86_INS_PMINUD: int\nUC_X86_INS_PMINUW: int\nUC_X86_INS_PMOVSXBD: int\nUC_X86_INS_PMOVSXBQ: int\nUC_X86_INS_PMOVSXBW: int\nUC_X86_INS_PMOVSXDQ: int\nUC_X86_INS_PMOVSXWD: int\nUC_X86_INS_PMOVSXWQ: int\nUC_X86_INS_PMOVZXBD: int\nUC_X86_INS_PMOVZXBQ: int\nUC_X86_INS_PMOVZXBW: int\nUC_X86_INS_PMOVZXDQ: int\nUC_X86_INS_PMOVZXWD: int\nUC_X86_INS_PMOVZXWQ: int\nUC_X86_INS_PMULDQ: int\nUC_X86_INS_PMULHRW: int\nUC_X86_INS_PMULLD: int\nUC_X86_INS_POP: int\nUC_X86_INS_POPAW: int\nUC_X86_INS_POPAL: int\nUC_X86_INS_POPCNT: int\nUC_X86_INS_POPF: int\nUC_X86_INS_POPFD: int\nUC_X86_INS_POPFQ: int\nUC_X86_INS_PREFETCH: int\nUC_X86_INS_PREFETCHNTA: int\nUC_X86_INS_PREFETCHT0: int\nUC_X86_INS_PREFETCHT1: int\nUC_X86_INS_PREFETCHT2: int\nUC_X86_INS_PREFETCHW: int\nUC_X86_INS_PSHUFD: int\nUC_X86_INS_PSHUFHW: int\nUC_X86_INS_PSHUFLW: int\nUC_X86_INS_PSLLDQ: int\nUC_X86_INS_PSRLDQ: int\nUC_X86_INS_PSWAPD: int\nUC_X86_INS_PTEST: int\nUC_X86_INS_PUNPCKHQDQ: int\nUC_X86_INS_PUNPCKLQDQ: int\nUC_X86_INS_PUSH: int\nUC_X86_INS_PUSHAW: int\nUC_X86_INS_PUSHAL: int\nUC_X86_INS_PUSHF: int\nUC_X86_INS_PUSHFD: int\nUC_X86_INS_PUSHFQ: int\nUC_X86_INS_RCL: int\nUC_X86_INS_RCPPS: int\nUC_X86_INS_RCPSS: int\nUC_X86_INS_RCR: int\nUC_X86_INS_RDFSBASE: int\nUC_X86_INS_RDGSBASE: int\nUC_X86_INS_RDMSR: int\nUC_X86_INS_RDPMC: int\nUC_X86_INS_RDRAND: int\nUC_X86_INS_RDSEED: int\nUC_X86_INS_RDTSC: int\nUC_X86_INS_RDTSCP: int\nUC_X86_INS_ROL: int\nUC_X86_INS_ROR: int\nUC_X86_INS_RORX: int\nUC_X86_INS_ROUNDPD: int\nUC_X86_INS_ROUNDPS: int\nUC_X86_INS_ROUNDSD: int\nUC_X86_INS_ROUNDSS: int\nUC_X86_INS_RSM: int\nUC_X86_INS_RSQRTPS: int\nUC_X86_INS_RSQRTSS: int\nUC_X86_INS_SAHF: int\nUC_X86_INS_SAL: int\nUC_X86_INS_SALC: int\nUC_X86_INS_SAR: int\nUC_X86_INS_SARX: int\nUC_X86_INS_SBB: int\nUC_X86_INS_SCASB: int\nUC_X86_INS_SCASD: int\nUC_X86_INS_SCASQ: int\nUC_X86_INS_SCASW: int\nUC_X86_INS_SETAE: int\nUC_X86_INS_SETA: int\nUC_X86_INS_SETBE: int\nUC_X86_INS_SETB: int\nUC_X86_INS_SETE: int\nUC_X86_INS_SETGE: int\nUC_X86_INS_SETG: int\nUC_X86_INS_SETLE: int\nUC_X86_INS_SETL: int\nUC_X86_INS_SETNE: int\nUC_X86_INS_SETNO: int\nUC_X86_INS_SETNP: int\nUC_X86_INS_SETNS: int\nUC_X86_INS_SETO: int\nUC_X86_INS_SETP: int\nUC_X86_INS_SETS: int\nUC_X86_INS_SFENCE: int\nUC_X86_INS_SGDT: int\nUC_X86_INS_SHA1MSG1: int\nUC_X86_INS_SHA1MSG2: int\nUC_X86_INS_SHA1NEXTE: int\nUC_X86_INS_SHA1RNDS4: int\nUC_X86_INS_SHA256MSG1: int\nUC_X86_INS_SHA256MSG2: int\nUC_X86_INS_SHA256RNDS2: int\nUC_X86_INS_SHL: int\nUC_X86_INS_SHLD: int\nUC_X86_INS_SHLX: int\nUC_X86_INS_SHR: int\nUC_X86_INS_SHRD: int\nUC_X86_INS_SHRX: int\nUC_X86_INS_SHUFPD: int\nUC_X86_INS_SHUFPS: int\nUC_X86_INS_SIDT: int\nUC_X86_INS_FSIN: int\nUC_X86_INS_SKINIT: int\nUC_X86_INS_SLDT: int\nUC_X86_INS_SMSW: int\nUC_X86_INS_SQRTPD: int\nUC_X86_INS_SQRTPS: int\nUC_X86_INS_SQRTSD: int\nUC_X86_INS_SQRTSS: int\nUC_X86_INS_FSQRT: int\nUC_X86_INS_STAC: int\nUC_X86_INS_STC: int\nUC_X86_INS_STD: int\nUC_X86_INS_STGI: int\nUC_X86_INS_STI: int\nUC_X86_INS_STMXCSR: int\nUC_X86_INS_STOSB: int\nUC_X86_INS_STOSD: int\nUC_X86_INS_STOSQ: int\nUC_X86_INS_STOSW: int\nUC_X86_INS_STR: int\nUC_X86_INS_FST: int\nUC_X86_INS_FSTP: int\nUC_X86_INS_FSTPNCE: int\nUC_X86_INS_FXCH: int\nUC_X86_INS_SUBPD: int\nUC_X86_INS_SUBPS: int\nUC_X86_INS_FSUBR: int\nUC_X86_INS_FISUBR: int\nUC_X86_INS_FSUBRP: int\nUC_X86_INS_SUBSD: int\nUC_X86_INS_SUBSS: int\nUC_X86_INS_FSUB: int\nUC_X86_INS_FISUB: int\nUC_X86_INS_FSUBP: int\nUC_X86_INS_SWAPGS: int\nUC_X86_INS_SYSCALL: int\nUC_X86_INS_SYSENTER: int\nUC_X86_INS_SYSEXIT: int\nUC_X86_INS_SYSRET: int\nUC_X86_INS_T1MSKC: int\nUC_X86_INS_TEST: int\nUC_X86_INS_UD2: int\nUC_X86_INS_FTST: int\nUC_X86_INS_TZCNT: int\nUC_X86_INS_TZMSK: int\nUC_X86_INS_FUCOMPI: int\nUC_X86_INS_FUCOMI: int\nUC_X86_INS_FUCOMPP: int\nUC_X86_INS_FUCOMP: int\nUC_X86_INS_FUCOM: int\nUC_X86_INS_UD2B: int\nUC_X86_INS_UNPCKHPD: int\nUC_X86_INS_UNPCKHPS: int\nUC_X86_INS_UNPCKLPD: int\nUC_X86_INS_UNPCKLPS: int\nUC_X86_INS_VADDPD: int\nUC_X86_INS_VADDPS: int\nUC_X86_INS_VADDSD: int\nUC_X86_INS_VADDSS: int\nUC_X86_INS_VADDSUBPD: int\nUC_X86_INS_VADDSUBPS: int\nUC_X86_INS_VAESDECLAST: int\nUC_X86_INS_VAESDEC: int\nUC_X86_INS_VAESENCLAST: int\nUC_X86_INS_VAESENC: int\nUC_X86_INS_VAESIMC: int\nUC_X86_INS_VAESKEYGENASSIST: int\nUC_X86_INS_VALIGND: int\nUC_X86_INS_VALIGNQ: int\nUC_X86_INS_VANDNPD: int\nUC_X86_INS_VANDNPS: int\nUC_X86_INS_VANDPD: int\nUC_X86_INS_VANDPS: int\nUC_X86_INS_VBLENDMPD: int\nUC_X86_INS_VBLENDMPS: int\nUC_X86_INS_VBLENDPD: int\nUC_X86_INS_VBLENDPS: int\nUC_X86_INS_VBLENDVPD: int\nUC_X86_INS_VBLENDVPS: int\nUC_X86_INS_VBROADCASTF128: int\nUC_X86_INS_VBROADCASTI32X4: int\nUC_X86_INS_VBROADCASTI64X4: int\nUC_X86_INS_VBROADCASTSD: int\nUC_X86_INS_VBROADCASTSS: int\nUC_X86_INS_VCMPPD: int\nUC_X86_INS_VCMPPS: int\nUC_X86_INS_VCMPSD: int\nUC_X86_INS_VCMPSS: int\nUC_X86_INS_VCOMPRESSPD: int\nUC_X86_INS_VCOMPRESSPS: int\nUC_X86_INS_VCVTDQ2PD: int\nUC_X86_INS_VCVTDQ2PS: int\nUC_X86_INS_VCVTPD2DQX: int\nUC_X86_INS_VCVTPD2DQ: int\nUC_X86_INS_VCVTPD2PSX: int\nUC_X86_INS_VCVTPD2PS: int\nUC_X86_INS_VCVTPD2UDQ: int\nUC_X86_INS_VCVTPH2PS: int\nUC_X86_INS_VCVTPS2DQ: int\nUC_X86_INS_VCVTPS2PD: int\nUC_X86_INS_VCVTPS2PH: int\nUC_X86_INS_VCVTPS2UDQ: int\nUC_X86_INS_VCVTSD2SI: int\nUC_X86_INS_VCVTSD2USI: int\nUC_X86_INS_VCVTSS2SI: int\nUC_X86_INS_VCVTSS2USI: int\nUC_X86_INS_VCVTTPD2DQX: int\nUC_X86_INS_VCVTTPD2DQ: int\nUC_X86_INS_VCVTTPD2UDQ: int\nUC_X86_INS_VCVTTPS2DQ: int\nUC_X86_INS_VCVTTPS2UDQ: int\nUC_X86_INS_VCVTUDQ2PD: int\nUC_X86_INS_VCVTUDQ2PS: int\nUC_X86_INS_VDIVPD: int\nUC_X86_INS_VDIVPS: int\nUC_X86_INS_VDIVSD: int\nUC_X86_INS_VDIVSS: int\nUC_X86_INS_VDPPD: int\nUC_X86_INS_VDPPS: int\nUC_X86_INS_VERR: int\nUC_X86_INS_VERW: int\nUC_X86_INS_VEXP2PD: int\nUC_X86_INS_VEXP2PS: int\nUC_X86_INS_VEXPANDPD: int\nUC_X86_INS_VEXPANDPS: int\nUC_X86_INS_VEXTRACTF128: int\nUC_X86_INS_VEXTRACTF32X4: int\nUC_X86_INS_VEXTRACTF64X4: int\nUC_X86_INS_VEXTRACTI128: int\nUC_X86_INS_VEXTRACTI32X4: int\nUC_X86_INS_VEXTRACTI64X4: int\nUC_X86_INS_VEXTRACTPS: int\nUC_X86_INS_VFMADD132PD: int\nUC_X86_INS_VFMADD132PS: int\nUC_X86_INS_VFMADDPD: int\nUC_X86_INS_VFMADD213PD: int\nUC_X86_INS_VFMADD231PD: int\nUC_X86_INS_VFMADDPS: int\nUC_X86_INS_VFMADD213PS: int\nUC_X86_INS_VFMADD231PS: int\nUC_X86_INS_VFMADDSD: int\nUC_X86_INS_VFMADD213SD: int\nUC_X86_INS_VFMADD132SD: int\nUC_X86_INS_VFMADD231SD: int\nUC_X86_INS_VFMADDSS: int\nUC_X86_INS_VFMADD213SS: int\nUC_X86_INS_VFMADD132SS: int\nUC_X86_INS_VFMADD231SS: int\nUC_X86_INS_VFMADDSUB132PD: int\nUC_X86_INS_VFMADDSUB132PS: int\nUC_X86_INS_VFMADDSUBPD: int\nUC_X86_INS_VFMADDSUB213PD: int\nUC_X86_INS_VFMADDSUB231PD: int\nUC_X86_INS_VFMADDSUBPS: int\nUC_X86_INS_VFMADDSUB213PS: int\nUC_X86_INS_VFMADDSUB231PS: int\nUC_X86_INS_VFMSUB132PD: int\nUC_X86_INS_VFMSUB132PS: int\nUC_X86_INS_VFMSUBADD132PD: int\nUC_X86_INS_VFMSUBADD132PS: int\nUC_X86_INS_VFMSUBADDPD: int\nUC_X86_INS_VFMSUBADD213PD: int\nUC_X86_INS_VFMSUBADD231PD: int\nUC_X86_INS_VFMSUBADDPS: int\nUC_X86_INS_VFMSUBADD213PS: int\nUC_X86_INS_VFMSUBADD231PS: int\nUC_X86_INS_VFMSUBPD: int\nUC_X86_INS_VFMSUB213PD: int\nUC_X86_INS_VFMSUB231PD: int\nUC_X86_INS_VFMSUBPS: int\nUC_X86_INS_VFMSUB213PS: int\nUC_X86_INS_VFMSUB231PS: int\nUC_X86_INS_VFMSUBSD: int\nUC_X86_INS_VFMSUB213SD: int\nUC_X86_INS_VFMSUB132SD: int\nUC_X86_INS_VFMSUB231SD: int\nUC_X86_INS_VFMSUBSS: int\nUC_X86_INS_VFMSUB213SS: int\nUC_X86_INS_VFMSUB132SS: int\nUC_X86_INS_VFMSUB231SS: int\nUC_X86_INS_VFNMADD132PD: int\nUC_X86_INS_VFNMADD132PS: int\nUC_X86_INS_VFNMADDPD: int\nUC_X86_INS_VFNMADD213PD: int\nUC_X86_INS_VFNMADD231PD: int\nUC_X86_INS_VFNMADDPS: int\nUC_X86_INS_VFNMADD213PS: int\nUC_X86_INS_VFNMADD231PS: int\nUC_X86_INS_VFNMADDSD: int\nUC_X86_INS_VFNMADD213SD: int\nUC_X86_INS_VFNMADD132SD: int\nUC_X86_INS_VFNMADD231SD: int\nUC_X86_INS_VFNMADDSS: int\nUC_X86_INS_VFNMADD213SS: int\nUC_X86_INS_VFNMADD132SS: int\nUC_X86_INS_VFNMADD231SS: int\nUC_X86_INS_VFNMSUB132PD: int\nUC_X86_INS_VFNMSUB132PS: int\nUC_X86_INS_VFNMSUBPD: int\nUC_X86_INS_VFNMSUB213PD: int\nUC_X86_INS_VFNMSUB231PD: int\nUC_X86_INS_VFNMSUBPS: int\nUC_X86_INS_VFNMSUB213PS: int\nUC_X86_INS_VFNMSUB231PS: int\nUC_X86_INS_VFNMSUBSD: int\nUC_X86_INS_VFNMSUB213SD: int\nUC_X86_INS_VFNMSUB132SD: int\nUC_X86_INS_VFNMSUB231SD: int\nUC_X86_INS_VFNMSUBSS: int\nUC_X86_INS_VFNMSUB213SS: int\nUC_X86_INS_VFNMSUB132SS: int\nUC_X86_INS_VFNMSUB231SS: int\nUC_X86_INS_VFRCZPD: int\nUC_X86_INS_VFRCZPS: int\nUC_X86_INS_VFRCZSD: int\nUC_X86_INS_VFRCZSS: int\nUC_X86_INS_VORPD: int\nUC_X86_INS_VORPS: int\nUC_X86_INS_VXORPD: int\nUC_X86_INS_VXORPS: int\nUC_X86_INS_VGATHERDPD: int\nUC_X86_INS_VGATHERDPS: int\nUC_X86_INS_VGATHERPF0DPD: int\nUC_X86_INS_VGATHERPF0DPS: int\nUC_X86_INS_VGATHERPF0QPD: int\nUC_X86_INS_VGATHERPF0QPS: int\nUC_X86_INS_VGATHERPF1DPD: int\nUC_X86_INS_VGATHERPF1DPS: int\nUC_X86_INS_VGATHERPF1QPD: int\nUC_X86_INS_VGATHERPF1QPS: int\nUC_X86_INS_VGATHERQPD: int\nUC_X86_INS_VGATHERQPS: int\nUC_X86_INS_VHADDPD: int\nUC_X86_INS_VHADDPS: int\nUC_X86_INS_VHSUBPD: int\nUC_X86_INS_VHSUBPS: int\nUC_X86_INS_VINSERTF128: int\nUC_X86_INS_VINSERTF32X4: int\nUC_X86_INS_VINSERTF32X8: int\nUC_X86_INS_VINSERTF64X2: int\nUC_X86_INS_VINSERTF64X4: int\nUC_X86_INS_VINSERTI128: int\nUC_X86_INS_VINSERTI32X4: int\nUC_X86_INS_VINSERTI32X8: int\nUC_X86_INS_VINSERTI64X2: int\nUC_X86_INS_VINSERTI64X4: int\nUC_X86_INS_VINSERTPS: int\nUC_X86_INS_VLDDQU: int\nUC_X86_INS_VLDMXCSR: int\nUC_X86_INS_VMASKMOVDQU: int\nUC_X86_INS_VMASKMOVPD: int\nUC_X86_INS_VMASKMOVPS: int\nUC_X86_INS_VMAXPD: int\nUC_X86_INS_VMAXPS: int\nUC_X86_INS_VMAXSD: int\nUC_X86_INS_VMAXSS: int\nUC_X86_INS_VMCALL: int\nUC_X86_INS_VMCLEAR: int\nUC_X86_INS_VMFUNC: int\nUC_X86_INS_VMINPD: int\nUC_X86_INS_VMINPS: int\nUC_X86_INS_VMINSD: int\nUC_X86_INS_VMINSS: int\nUC_X86_INS_VMLAUNCH: int\nUC_X86_INS_VMLOAD: int\nUC_X86_INS_VMMCALL: int\nUC_X86_INS_VMOVQ: int\nUC_X86_INS_VMOVDDUP: int\nUC_X86_INS_VMOVD: int\nUC_X86_INS_VMOVDQA32: int\nUC_X86_INS_VMOVDQA64: int\nUC_X86_INS_VMOVDQA: int\nUC_X86_INS_VMOVDQU16: int\nUC_X86_INS_VMOVDQU32: int\nUC_X86_INS_VMOVDQU64: int\nUC_X86_INS_VMOVDQU8: int\nUC_X86_INS_VMOVDQU: int\nUC_X86_INS_VMOVHLPS: int\nUC_X86_INS_VMOVHPD: int\nUC_X86_INS_VMOVHPS: int\nUC_X86_INS_VMOVLHPS: int\nUC_X86_INS_VMOVLPD: int\nUC_X86_INS_VMOVLPS: int\nUC_X86_INS_VMOVMSKPD: int\nUC_X86_INS_VMOVMSKPS: int\nUC_X86_INS_VMOVNTDQA: int\nUC_X86_INS_VMOVNTDQ: int\nUC_X86_INS_VMOVNTPD: int\nUC_X86_INS_VMOVNTPS: int\nUC_X86_INS_VMOVSD: int\nUC_X86_INS_VMOVSHDUP: int\nUC_X86_INS_VMOVSLDUP: int\nUC_X86_INS_VMOVSS: int\nUC_X86_INS_VMOVUPD: int\nUC_X86_INS_VMOVUPS: int\nUC_X86_INS_VMPSADBW: int\nUC_X86_INS_VMPTRLD: int\nUC_X86_INS_VMPTRST: int\nUC_X86_INS_VMREAD: int\nUC_X86_INS_VMRESUME: int\nUC_X86_INS_VMRUN: int\nUC_X86_INS_VMSAVE: int\nUC_X86_INS_VMULPD: int\nUC_X86_INS_VMULPS: int\nUC_X86_INS_VMULSD: int\nUC_X86_INS_VMULSS: int\nUC_X86_INS_VMWRITE: int\nUC_X86_INS_VMXOFF: int\nUC_X86_INS_VMXON: int\nUC_X86_INS_VPABSB: int\nUC_X86_INS_VPABSD: int\nUC_X86_INS_VPABSQ: int\nUC_X86_INS_VPABSW: int\nUC_X86_INS_VPACKSSDW: int\nUC_X86_INS_VPACKSSWB: int\nUC_X86_INS_VPACKUSDW: int\nUC_X86_INS_VPACKUSWB: int\nUC_X86_INS_VPADDB: int\nUC_X86_INS_VPADDD: int\nUC_X86_INS_VPADDQ: int\nUC_X86_INS_VPADDSB: int\nUC_X86_INS_VPADDSW: int\nUC_X86_INS_VPADDUSB: int\nUC_X86_INS_VPADDUSW: int\nUC_X86_INS_VPADDW: int\nUC_X86_INS_VPALIGNR: int\nUC_X86_INS_VPANDD: int\nUC_X86_INS_VPANDND: int\nUC_X86_INS_VPANDNQ: int\nUC_X86_INS_VPANDN: int\nUC_X86_INS_VPANDQ: int\nUC_X86_INS_VPAND: int\nUC_X86_INS_VPAVGB: int\nUC_X86_INS_VPAVGW: int\nUC_X86_INS_VPBLENDD: int\nUC_X86_INS_VPBLENDMB: int\nUC_X86_INS_VPBLENDMD: int\nUC_X86_INS_VPBLENDMQ: int\nUC_X86_INS_VPBLENDMW: int\nUC_X86_INS_VPBLENDVB: int\nUC_X86_INS_VPBLENDW: int\nUC_X86_INS_VPBROADCASTB: int\nUC_X86_INS_VPBROADCASTD: int\nUC_X86_INS_VPBROADCASTMB2Q: int\nUC_X86_INS_VPBROADCASTMW2D: int\nUC_X86_INS_VPBROADCASTQ: int\nUC_X86_INS_VPBROADCASTW: int\nUC_X86_INS_VPCLMULQDQ: int\nUC_X86_INS_VPCMOV: int\nUC_X86_INS_VPCMPB: int\nUC_X86_INS_VPCMPD: int\nUC_X86_INS_VPCMPEQB: int\nUC_X86_INS_VPCMPEQD: int\nUC_X86_INS_VPCMPEQQ: int\nUC_X86_INS_VPCMPEQW: int\nUC_X86_INS_VPCMPESTRI: int\nUC_X86_INS_VPCMPESTRM: int\nUC_X86_INS_VPCMPGTB: int\nUC_X86_INS_VPCMPGTD: int\nUC_X86_INS_VPCMPGTQ: int\nUC_X86_INS_VPCMPGTW: int\nUC_X86_INS_VPCMPISTRI: int\nUC_X86_INS_VPCMPISTRM: int\nUC_X86_INS_VPCMPQ: int\nUC_X86_INS_VPCMPUB: int\nUC_X86_INS_VPCMPUD: int\nUC_X86_INS_VPCMPUQ: int\nUC_X86_INS_VPCMPUW: int\nUC_X86_INS_VPCMPW: int\nUC_X86_INS_VPCOMB: int\nUC_X86_INS_VPCOMD: int\nUC_X86_INS_VPCOMPRESSD: int\nUC_X86_INS_VPCOMPRESSQ: int\nUC_X86_INS_VPCOMQ: int\nUC_X86_INS_VPCOMUB: int\nUC_X86_INS_VPCOMUD: int\nUC_X86_INS_VPCOMUQ: int\nUC_X86_INS_VPCOMUW: int\nUC_X86_INS_VPCOMW: int\nUC_X86_INS_VPCONFLICTD: int\nUC_X86_INS_VPCONFLICTQ: int\nUC_X86_INS_VPERM2F128: int\nUC_X86_INS_VPERM2I128: int\nUC_X86_INS_VPERMD: int\nUC_X86_INS_VPERMI2D: int\nUC_X86_INS_VPERMI2PD: int\nUC_X86_INS_VPERMI2PS: int\nUC_X86_INS_VPERMI2Q: int\nUC_X86_INS_VPERMIL2PD: int\nUC_X86_INS_VPERMIL2PS: int\nUC_X86_INS_VPERMILPD: int\nUC_X86_INS_VPERMILPS: int\nUC_X86_INS_VPERMPD: int\nUC_X86_INS_VPERMPS: int\nUC_X86_INS_VPERMQ: int\nUC_X86_INS_VPERMT2D: int\nUC_X86_INS_VPERMT2PD: int\nUC_X86_INS_VPERMT2PS: int\nUC_X86_INS_VPERMT2Q: int\nUC_X86_INS_VPEXPANDD: int\nUC_X86_INS_VPEXPANDQ: int\nUC_X86_INS_VPEXTRB: int\nUC_X86_INS_VPEXTRD: int\nUC_X86_INS_VPEXTRQ: int\nUC_X86_INS_VPEXTRW: int\nUC_X86_INS_VPGATHERDD: int\nUC_X86_INS_VPGATHERDQ: int\nUC_X86_INS_VPGATHERQD: int\nUC_X86_INS_VPGATHERQQ: int\nUC_X86_INS_VPHADDBD: int\nUC_X86_INS_VPHADDBQ: int\nUC_X86_INS_VPHADDBW: int\nUC_X86_INS_VPHADDDQ: int\nUC_X86_INS_VPHADDD: int\nUC_X86_INS_VPHADDSW: int\nUC_X86_INS_VPHADDUBD: int\nUC_X86_INS_VPHADDUBQ: int\nUC_X86_INS_VPHADDUBW: int\nUC_X86_INS_VPHADDUDQ: int\nUC_X86_INS_VPHADDUWD: int\nUC_X86_INS_VPHADDUWQ: int\nUC_X86_INS_VPHADDWD: int\nUC_X86_INS_VPHADDWQ: int\nUC_X86_INS_VPHADDW: int\nUC_X86_INS_VPHMINPOSUW: int\nUC_X86_INS_VPHSUBBW: int\nUC_X86_INS_VPHSUBDQ: int\nUC_X86_INS_VPHSUBD: int\nUC_X86_INS_VPHSUBSW: int\nUC_X86_INS_VPHSUBWD: int\nUC_X86_INS_VPHSUBW: int\nUC_X86_INS_VPINSRB: int\nUC_X86_INS_VPINSRD: int\nUC_X86_INS_VPINSRQ: int\nUC_X86_INS_VPINSRW: int\nUC_X86_INS_VPLZCNTD: int\nUC_X86_INS_VPLZCNTQ: int\nUC_X86_INS_VPMACSDD: int\nUC_X86_INS_VPMACSDQH: int\nUC_X86_INS_VPMACSDQL: int\nUC_X86_INS_VPMACSSDD: int\nUC_X86_INS_VPMACSSDQH: int\nUC_X86_INS_VPMACSSDQL: int\nUC_X86_INS_VPMACSSWD: int\nUC_X86_INS_VPMACSSWW: int\nUC_X86_INS_VPMACSWD: int\nUC_X86_INS_VPMACSWW: int\nUC_X86_INS_VPMADCSSWD: int\nUC_X86_INS_VPMADCSWD: int\nUC_X86_INS_VPMADDUBSW: int\nUC_X86_INS_VPMADDWD: int\nUC_X86_INS_VPMASKMOVD: int\nUC_X86_INS_VPMASKMOVQ: int\nUC_X86_INS_VPMAXSB: int\nUC_X86_INS_VPMAXSD: int\nUC_X86_INS_VPMAXSQ: int\nUC_X86_INS_VPMAXSW: int\nUC_X86_INS_VPMAXUB: int\nUC_X86_INS_VPMAXUD: int\nUC_X86_INS_VPMAXUQ: int\nUC_X86_INS_VPMAXUW: int\nUC_X86_INS_VPMINSB: int\nUC_X86_INS_VPMINSD: int\nUC_X86_INS_VPMINSQ: int\nUC_X86_INS_VPMINSW: int\nUC_X86_INS_VPMINUB: int\nUC_X86_INS_VPMINUD: int\nUC_X86_INS_VPMINUQ: int\nUC_X86_INS_VPMINUW: int\nUC_X86_INS_VPMOVDB: int\nUC_X86_INS_VPMOVDW: int\nUC_X86_INS_VPMOVM2B: int\nUC_X86_INS_VPMOVM2D: int\nUC_X86_INS_VPMOVM2Q: int\nUC_X86_INS_VPMOVM2W: int\nUC_X86_INS_VPMOVMSKB: int\nUC_X86_INS_VPMOVQB: int\nUC_X86_INS_VPMOVQD: int\nUC_X86_INS_VPMOVQW: int\nUC_X86_INS_VPMOVSDB: int\nUC_X86_INS_VPMOVSDW: int\nUC_X86_INS_VPMOVSQB: int\nUC_X86_INS_VPMOVSQD: int\nUC_X86_INS_VPMOVSQW: int\nUC_X86_INS_VPMOVSXBD: int\nUC_X86_INS_VPMOVSXBQ: int\nUC_X86_INS_VPMOVSXBW: int\nUC_X86_INS_VPMOVSXDQ: int\nUC_X86_INS_VPMOVSXWD: int\nUC_X86_INS_VPMOVSXWQ: int\nUC_X86_INS_VPMOVUSDB: int\nUC_X86_INS_VPMOVUSDW: int\nUC_X86_INS_VPMOVUSQB: int\nUC_X86_INS_VPMOVUSQD: int\nUC_X86_INS_VPMOVUSQW: int\nUC_X86_INS_VPMOVZXBD: int\nUC_X86_INS_VPMOVZXBQ: int\nUC_X86_INS_VPMOVZXBW: int\nUC_X86_INS_VPMOVZXDQ: int\nUC_X86_INS_VPMOVZXWD: int\nUC_X86_INS_VPMOVZXWQ: int\nUC_X86_INS_VPMULDQ: int\nUC_X86_INS_VPMULHRSW: int\nUC_X86_INS_VPMULHUW: int\nUC_X86_INS_VPMULHW: int\nUC_X86_INS_VPMULLD: int\nUC_X86_INS_VPMULLQ: int\nUC_X86_INS_VPMULLW: int\nUC_X86_INS_VPMULUDQ: int\nUC_X86_INS_VPORD: int\nUC_X86_INS_VPORQ: int\nUC_X86_INS_VPOR: int\nUC_X86_INS_VPPERM: int\nUC_X86_INS_VPROTB: int\nUC_X86_INS_VPROTD: int\nUC_X86_INS_VPROTQ: int\nUC_X86_INS_VPROTW: int\nUC_X86_INS_VPSADBW: int\nUC_X86_INS_VPSCATTERDD: int\nUC_X86_INS_VPSCATTERDQ: int\nUC_X86_INS_VPSCATTERQD: int\nUC_X86_INS_VPSCATTERQQ: int\nUC_X86_INS_VPSHAB: int\nUC_X86_INS_VPSHAD: int\nUC_X86_INS_VPSHAQ: int\nUC_X86_INS_VPSHAW: int\nUC_X86_INS_VPSHLB: int\nUC_X86_INS_VPSHLD: int\nUC_X86_INS_VPSHLQ: int\nUC_X86_INS_VPSHLW: int\nUC_X86_INS_VPSHUFB: int\nUC_X86_INS_VPSHUFD: int\nUC_X86_INS_VPSHUFHW: int\nUC_X86_INS_VPSHUFLW: int\nUC_X86_INS_VPSIGNB: int\nUC_X86_INS_VPSIGND: int\nUC_X86_INS_VPSIGNW: int\nUC_X86_INS_VPSLLDQ: int\nUC_X86_INS_VPSLLD: int\nUC_X86_INS_VPSLLQ: int\nUC_X86_INS_VPSLLVD: int\nUC_X86_INS_VPSLLVQ: int\nUC_X86_INS_VPSLLW: int\nUC_X86_INS_VPSRAD: int\nUC_X86_INS_VPSRAQ: int\nUC_X86_INS_VPSRAVD: int\nUC_X86_INS_VPSRAVQ: int\nUC_X86_INS_VPSRAW: int\nUC_X86_INS_VPSRLDQ: int\nUC_X86_INS_VPSRLD: int\nUC_X86_INS_VPSRLQ: int\nUC_X86_INS_VPSRLVD: int\nUC_X86_INS_VPSRLVQ: int\nUC_X86_INS_VPSRLW: int\nUC_X86_INS_VPSUBB: int\nUC_X86_INS_VPSUBD: int\nUC_X86_INS_VPSUBQ: int\nUC_X86_INS_VPSUBSB: int\nUC_X86_INS_VPSUBSW: int\nUC_X86_INS_VPSUBUSB: int\nUC_X86_INS_VPSUBUSW: int\nUC_X86_INS_VPSUBW: int\nUC_X86_INS_VPTESTMD: int\nUC_X86_INS_VPTESTMQ: int\nUC_X86_INS_VPTESTNMD: int\nUC_X86_INS_VPTESTNMQ: int\nUC_X86_INS_VPTEST: int\nUC_X86_INS_VPUNPCKHBW: int\nUC_X86_INS_VPUNPCKHDQ: int\nUC_X86_INS_VPUNPCKHQDQ: int\nUC_X86_INS_VPUNPCKHWD: int\nUC_X86_INS_VPUNPCKLBW: int\nUC_X86_INS_VPUNPCKLDQ: int\nUC_X86_INS_VPUNPCKLQDQ: int\nUC_X86_INS_VPUNPCKLWD: int\nUC_X86_INS_VPXORD: int\nUC_X86_INS_VPXORQ: int\nUC_X86_INS_VPXOR: int\nUC_X86_INS_VRCP14PD: int\nUC_X86_INS_VRCP14PS: int\nUC_X86_INS_VRCP14SD: int\nUC_X86_INS_VRCP14SS: int\nUC_X86_INS_VRCP28PD: int\nUC_X86_INS_VRCP28PS: int\nUC_X86_INS_VRCP28SD: int\nUC_X86_INS_VRCP28SS: int\nUC_X86_INS_VRCPPS: int\nUC_X86_INS_VRCPSS: int\nUC_X86_INS_VRNDSCALEPD: int\nUC_X86_INS_VRNDSCALEPS: int\nUC_X86_INS_VRNDSCALESD: int\nUC_X86_INS_VRNDSCALESS: int\nUC_X86_INS_VROUNDPD: int\nUC_X86_INS_VROUNDPS: int\nUC_X86_INS_VROUNDSD: int\nUC_X86_INS_VROUNDSS: int\nUC_X86_INS_VRSQRT14PD: int\nUC_X86_INS_VRSQRT14PS: int\nUC_X86_INS_VRSQRT14SD: int\nUC_X86_INS_VRSQRT14SS: int\nUC_X86_INS_VRSQRT28PD: int\nUC_X86_INS_VRSQRT28PS: int\nUC_X86_INS_VRSQRT28SD: int\nUC_X86_INS_VRSQRT28SS: int\nUC_X86_INS_VRSQRTPS: int\nUC_X86_INS_VRSQRTSS: int\nUC_X86_INS_VSCATTERDPD: int\nUC_X86_INS_VSCATTERDPS: int\nUC_X86_INS_VSCATTERPF0DPD: int\nUC_X86_INS_VSCATTERPF0DPS: int\nUC_X86_INS_VSCATTERPF0QPD: int\nUC_X86_INS_VSCATTERPF0QPS: int\nUC_X86_INS_VSCATTERPF1DPD: int\nUC_X86_INS_VSCATTERPF1DPS: int\nUC_X86_INS_VSCATTERPF1QPD: int\nUC_X86_INS_VSCATTERPF1QPS: int\nUC_X86_INS_VSCATTERQPD: int\nUC_X86_INS_VSCATTERQPS: int\nUC_X86_INS_VSHUFPD: int\nUC_X86_INS_VSHUFPS: int\nUC_X86_INS_VSQRTPD: int\nUC_X86_INS_VSQRTPS: int\nUC_X86_INS_VSQRTSD: int\nUC_X86_INS_VSQRTSS: int\nUC_X86_INS_VSTMXCSR: int\nUC_X86_INS_VSUBPD: int\nUC_X86_INS_VSUBPS: int\nUC_X86_INS_VSUBSD: int\nUC_X86_INS_VSUBSS: int\nUC_X86_INS_VTESTPD: int\nUC_X86_INS_VTESTPS: int\nUC_X86_INS_VUNPCKHPD: int\nUC_X86_INS_VUNPCKHPS: int\nUC_X86_INS_VUNPCKLPD: int\nUC_X86_INS_VUNPCKLPS: int\nUC_X86_INS_VZEROALL: int\nUC_X86_INS_VZEROUPPER: int\nUC_X86_INS_WAIT: int\nUC_X86_INS_WBINVD: int\nUC_X86_INS_WRFSBASE: int\nUC_X86_INS_WRGSBASE: int\nUC_X86_INS_WRMSR: int\nUC_X86_INS_XABORT: int\nUC_X86_INS_XACQUIRE: int\nUC_X86_INS_XBEGIN: int\nUC_X86_INS_XCHG: int\nUC_X86_INS_XCRYPTCBC: int\nUC_X86_INS_XCRYPTCFB: int\nUC_X86_INS_XCRYPTCTR: int\nUC_X86_INS_XCRYPTECB: int\nUC_X86_INS_XCRYPTOFB: int\nUC_X86_INS_XEND: int\nUC_X86_INS_XGETBV: int\nUC_X86_INS_XLATB: int\nUC_X86_INS_XRELEASE: int\nUC_X86_INS_XRSTOR: int\nUC_X86_INS_XRSTOR64: int\nUC_X86_INS_XRSTORS: int\nUC_X86_INS_XRSTORS64: int\nUC_X86_INS_XSAVE: int\nUC_X86_INS_XSAVE64: int\nUC_X86_INS_XSAVEC: int\nUC_X86_INS_XSAVEC64: int\nUC_X86_INS_XSAVEOPT: int\nUC_X86_INS_XSAVEOPT64: int\nUC_X86_INS_XSAVES: int\nUC_X86_INS_XSAVES64: int\nUC_X86_INS_XSETBV: int\nUC_X86_INS_XSHA1: int\nUC_X86_INS_XSHA256: int\nUC_X86_INS_XSTORE: int\nUC_X86_INS_XTEST: int\nUC_X86_INS_FDISI8087_NOP: int\nUC_X86_INS_FENI8087_NOP: int\nUC_X86_INS_ENDING: int\n"
  },
  {
    "path": "tests/.coveragerc",
    "content": "[run]\nomit =\n    */__init__.py\n    cli.py\n\n[report]\nexclude_lines =\n    pragma: no cover\n    assert_never\n\nexclude_also =\n    if TYPE_CHECKING:\n    def _parse_args\n    def _km_write\n    def _is_smt_enabled\n    def _can_set_reserved\n    def _is_kernel_module_installed\n    def _configure_kernel_module\n    def _read_trace\n    def _rewind_km_output_to_end\n    if not skip_setup:\n    # unit_model_dr\n    def _check_if_installed\n    if type_id not in _TRACE_ID_TO_NAME\n    .*# pylint: disable=unreachable\n    .*NotImplementedError\n"
  },
  {
    "path": "tests/.gitignore",
    "content": "*.o\n*.patched.asm\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/acceptance.bats",
    "content": "#!/usr/bin/env bats\n# set -o errexit -o pipefail -o nounset\n\nPRESERVE_TMP=${PRESERVE_TMP:-0}\nCPU_MODEL=$(cat /proc/cpuinfo | grep \"model\" | head -n 1 | cut -d: -f2 | tr -d ' ')\n\n# ------------------------------------------------------------------------------\n# Helper functions\n# ------------------------------------------------------------------------------\nfunction setup() {\n    VENDOR=\"$(lscpu | grep Vendor | awk '{print $3}')\"\n    ARCH=\"$(lscpu | grep Architecture | awk '{print $2}')\"\n\n    PROJECT_ROOT=\"$(cd \"$(dirname \"$BATS_TEST_FILENAME\")/../\" >/dev/null 2>&1 && pwd)\"\n\n    if [ \"$ARCH\" == \"x86_64\" ]; then\n        ASM_DIR=\"$PROJECT_ROOT/tests/x86_tests/asm\"\n        CONF_DIR=\"$PROJECT_ROOT/tests/x86_tests/configs\"\n    elif [ \"$ARCH\" == \"aarch64\" ]; then\n        ASM_DIR=\"$PROJECT_ROOT/tests/arm64/asm\"\n        CONF_DIR=\"$PROJECT_ROOT/tests/arm64/configs\"\n    fi\n\n    if [ \"$ARCH\" == \"x86_64\" ]; then\n        ISA=\"$PROJECT_ROOT/base.json\"\n        if [ ! -f \"$ISA\" ]; then\n            echo \"Could not find 'base.json' in $ISA\"\n            echo \"Follow the instructions in README.md to download it, and copy into this directory.\"\n            false\n        fi\n    elif [ \"$ARCH\" == \"aarch64\" ]; then\n        ISA=\"$PROJECT_ROOT/tests/arm64/min_arm64.json\"\n    fi\n\n    cli=\"$PROJECT_ROOT/revizor.py\"\n    cli_opt=\"python3 -OO $PROJECT_ROOT/revizor.py\"\n    fuzz_opt=\"$cli fuzz -s $ISA --save-violations f -I $CONF_DIR\"\n\n    # tmp directory for tests\n    TEST_DIR=$(mktemp -d)\n}\n\nfunction teardown() {\n    if [ \"$PRESERVE_TMP\" -eq 0 ]; then\n        rm -rf $TEST_DIR\n    fi\n}\n\nfunction assert_violation() {\n    # Check if the given test produces a contract violation\n    local cmd=\"$@\"\n\n    run bash -c \"$cmd\"\n    echo \"Command: $cmd\"\n    echo \"Exit code: $status\"\n    echo \"Output: '$output'\"\n    [[ \"$status\" -eq 1 && \"$output\" = *\"=== Violations detected ===\"* ]]\n}\n\nfunction assert_no_violation() {\n    local cmd=\"$@\"\n\n    run bash -c \"$cmd\"\n    echo \"Command: $cmd\"\n    echo \"Exit code: $status\"\n    echo \"Output: '$output'\"\n    [[ \"$status\" -eq 0 && \"$output\" != *\"=== Violations detected ===\"* ]]\n}\n\nfunction assert_violation_or_arch_fail() {\n    # Check if the given test produces a contract violation OR an architectural failure\n    local cmd=\"$@\"\n\n    run bash -c \"$cmd\"\n    echo \"Command: $cmd\"\n    echo \"Exit code: $status\"\n    echo \"Output: '$output'\"\n    if [[ \"$output\" == *\" Architectural mismatch \"* ]]; then\n        return\n    fi\n\n    [[ \"$status\" -eq 1 && \"$output\" = *\"=== Violations detected ===\"* ]]\n}\n\nfunction intel_only() {\n    if [ \"$VENDOR\" != \"GenuineIntel\" ]; then\n        skip \"Intel-specific test\"\n    fi\n}\n\nfunction amd_only() {\n    if [ \"$VENDOR\" != \"AuthenticAMD\" ]; then\n        skip \"AMD-specific test\"\n    fi\n}\n\nfunction x86_only() {\n    if [ \"$ARCH\" != \"x86_64\" ]; then\n        skip \"x86-specific test\"\n    fi\n}\n\nfunction arm_only() {\n    if [ \"$ARCH\" != \"aarch64\" ]; then\n        skip \"ARM-specific test\"\n    fi\n}\n\n# ------------------------------------------------------------------------------\n# Tests\n# ------------------------------------------------------------------------------\n\n@test \"Architectural Test: Model and Executor are initialized with the same values (registers)\" {\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/model_match.asm -c $CONF_DIR/arch.yaml -i 20\"\n}\n\n@test \"Architectural Test: Model and Executor are initialized with the same values (memory)\" {\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/model_match_memory.asm -c $CONF_DIR/arch.yaml -i 20\"\n}\n\n@test \"Architectural Test: Model and Executor are initialized with the same values (flags)\" {\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/model_flags_match.asm -c $CONF_DIR/arch.yaml -i 20\"\n}\n\n@test \"Architectural Test: Model and Executor are initialized with the same values (SIMD registers)\" {\n    x86_only\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/model_match_xmm.asm -c $CONF_DIR/arch.yaml -i 20\"\n}\n\n@test \"Architectural Test/Unicorn: 100 Random Test Cases\" {\n    assert_no_violation \"$fuzz_opt -c $CONF_DIR/arch.yaml -n 100 -i 10\"\n}\n\n@test \"Architectural Test/DR: 100 Random Test Cases\" {\n    x86_only\n    if ! ~/.local/dynamorio/drrun -c ~/.local/dynamorio/libdr_model.so -- ls /dev/null; then\n        skip \"DynamoRIO is not installed\"\n    fi\n    assert_no_violation \"$fuzz_opt -c $CONF_DIR/arch-dr.yaml -n 100 -i 10\"\n}\n\n@test \"ArchDiff Test: 10 Random Test Cases\" {\n    assert_no_violation \"$fuzz_opt -c $CONF_DIR/archdiff.yaml -n 10 -i 10\"\n}\n\n@test \"Test Basics: Sequence of direct jumps\" {\n    assert_no_violation \"$fuzz_opt -c $CONF_DIR/ct-seq.yaml -t $ASM_DIR/direct_jumps.asm -i 100\"\n}\n\n@test \"Test Basics: Sequence of calls\" {\n    assert_no_violation \"$fuzz_opt -c $CONF_DIR/ct-seq.yaml -t $ASM_DIR/calls.asm -i 100\"\n}\n\n@test \"Detection [spectre-type]: Spectre V1; load variant\" {\n    assert_violation \"$fuzz_opt -t $ASM_DIR/spectre_v1.asm -c $CONF_DIR/ct-seq.yaml  -i 20\"\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/spectre_v1.asm -c $CONF_DIR/ct-cond.yaml -i 20\"\n}\n\n@test \"Detection [spectre-type]: Spectre V1; store variant\" {\n    intel_only\n    assert_violation \"$fuzz_opt -t $ASM_DIR/spectre_v1.1.asm -c $CONF_DIR/ct-seq.yaml -i 20\"\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/spectre_v1.1.asm -c $CONF_DIR/ct-cond.yaml -i 20\"\n}\n\n@test \"Detection [spectre-type]: Spectre V1; nested variant\" {\n    x86_only\n    assert_violation \"$fuzz_opt -t $ASM_DIR/spectre_v1_n2.asm -c $CONF_DIR/ct-seq.yaml -i 20\"\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/spectre_v1_n2.asm -c $CONF_DIR/ct-cond.yaml -i 20\"\n}\n\n@test \"Detection [spectre-type]: Spectre V2 (BTI)\" {\n    x86_only\n    assert_violation \"$fuzz_opt -t $ASM_DIR/spectre_v2.asm -c $CONF_DIR/ct-seq.yaml -i 10 -n 100\"\n}\n\n@test \"Detection [spectre-type]: Spectre V4 (SSBP)\" {\n    x86_only\n    assert_violation \"$fuzz_opt -t $ASM_DIR/spectre_v4.asm -c $CONF_DIR/ssbp-detect.yaml -i 100\"\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/spectre_v4.asm -c $CONF_DIR/ssbp-verif.yaml  -i 100\"\n\n    # used default config to test SSBP patch (it is enabled by default)\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/spectre_v4.asm -c $CONF_DIR/ct-seq.yaml -i 100\"\n}\n\n@test \"Detection [spectre-type]: Spectre V5 (return misprediction)\" {\n    x86_only\n    assert_violation \"$fuzz_opt -t $ASM_DIR/spectre_ret.asm -c $CONF_DIR/ct-seq.yaml -i 10\"\n}\n\n@test \"Detection [meltdown-type]: #DE-zero speculation\" {\n    x86_only\n    assert_violation \"$fuzz_opt -t $ASM_DIR/fault-div-zero-speculation.asm -c $CONF_DIR/div-detect.yaml -i 3\"\n    # assert_no_violation \"$fuzz_opt -t $ASM_DIR/fault-div-zero-speculation.asm -c $CONF_DIR/div-verif.yaml -i 3\"\n}\n\n@test \"Detection [meltdown-type]: #DE-overflow speculation\" {\n    x86_only\n    assert_violation \"$fuzz_opt -t $ASM_DIR/fault-div-overflow-speculation.asm -c $CONF_DIR/div-detect.yaml -i 3\"\n    # assert_no_violation \"$fuzz_opt -t $ASM_DIR/fault-div-overflow-speculation.asm -c $CONF_DIR/div-verif.yaml -i 3\"\n}\n\n@test \"Detection [meltdown-type]: #PF-present speculation\" {\n    intel_only\n    if [ $CPU_MODEL -ge 140 ]; then\n        skip \"Meltdown is patched on Intel CPU models >= 140\"\n    fi\n    assert_violation \"$fuzz_opt -t $ASM_DIR/fault_load.asm -c $CONF_DIR/l1tf-p.yaml -i 5\"\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/fault_load.asm -c $CONF_DIR/l1tf-p-verif.yaml -i 5\"\n}\n\n@test \"Detection [meltdown-type]: #PF-writable speculation\" {\n    intel_only\n    if [ $CPU_MODEL -ge 140 ]; then\n        skip \"Meltdown is patched on Intel CPU models >= 140\"\n    fi\n    assert_violation \"$fuzz_opt -t $ASM_DIR/fault_rmw.asm -c $CONF_DIR/l1tf-p.yaml -i 5\"\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/fault_rmw.asm -c $CONF_DIR/l1tf-p-verif.yaml -i 5\"\n}\n\n@test \"Detection [meltdown-type]: #PF-smap speculation\" {\n    intel_only\n    if ! grep \"smap\" /proc/cpuinfo >/dev/null; then\n        skip\n    fi\n    # Note: an arch. violation is expected here if SMAP is disabled in the kernel\n    assert_violation_or_arch_fail \"$fuzz_opt -t $ASM_DIR/fault_load.asm -c $CONF_DIR/meltdown.yaml -i 5\"\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/fault_load.asm -c $CONF_DIR/meltdown-verif.yaml -i 5\"\n}\n\n@test \"Sequential handling: #DB-instruction\" {\n    x86_only\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/fault_INT1.asm -c $CONF_DIR/exceptions.yaml -i 100\"\n}\n\n@test \"Sequential handling: #BP\" {\n    x86_only\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/fault_INT3.asm -c $CONF_DIR/exceptions.yaml -i 100\"\n}\n\n@test \"Sequential handling: #UD\" {\n    x86_only\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/fault_UD.asm -c $CONF_DIR/exceptions.yaml -i 100\"\n}\n\n@test \"Detection [meltdown-type]: Out-of-order Undefined Instruction Exception\" {\n    arm_only\n    assert_violation \"$fuzz_opt -t $ASM_DIR/fault_undefined_opcode.asm -c $CONF_DIR/ct-seq.yaml -i 20\"\n}\n\n@test \"Feature: Storing and loading test cases\" {\n    x86_only\n    assert_no_violation \"$cli_opt generate -s $ISA -c $CONF_DIR/ct-seq.yaml -w $TEST_DIR -n 1 -i 2\"\n    assert_no_violation \"$cli_opt reproduce -s $ISA -c $CONF_DIR/ct-seq.yaml -t $TEST_DIR/tc0/program.asm -i $TEST_DIR/tc0/input*.bin\"\n}\n\n@test \"Architectural Test: Multi-actor test case\" {\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/actor_switch.asm -c $CONF_DIR/arch-actors.yaml -i 20\"\n}\n\n@test \"Architectural Test: Fault Handling\" {\n    assert_no_violation \"$fuzz_opt -t $ASM_DIR/macro_fault_handler.asm -c $CONF_DIR/arch-faults.yaml -i 20\"\n}\n\n@test \"Feature: Fault Handling\" {\n    local cmd=\"$fuzz_opt -t $ASM_DIR/macro_fault_handler.asm -c $CONF_DIR/fault-handler.yaml -i 1\"\n    run bash -c \"$cmd\"\n    echo \"Command: $cmd\"\n    echo \"Exit code: $status\"\n    echo \"Output: '$output'\"\n    [[ \"$status\" -eq 0 && \"$output\" = *\"^.......^...^..................................................^\"* ]]\n}\n\n@test \"Feature: VMX/SVM\" {\n    x86_only\n    amd_only  # see https://github.com/microsoft/side-channel-fuzzer/issues/122\n    if cat /proc/cpuinfo | grep -e \"vmx\" -e \"svm\" >/dev/null; then\n        echo \"1\" >/sys/rvzr_executor/enable_hpa_gpa_collisions\n        assert_no_violation \"$fuzz_opt -t $ASM_DIR/vm_switch.asm -c $CONF_DIR/vm-switch.yaml -i 20\"\n\n        # echo \"Testing page table allocation...\"\n        # run cat /sys/rvzr_executor/dbg_guest_page_tables\n        # if [ $status -ne 0 ]; then\n        #     echo \"Page table allocation test failed: $output\"\n        # fi\n        [[ $status -eq 0 ]]\n    else\n        skip\n    fi\n}\n\n"
  },
  {
    "path": "tests/arm64/asm/actor_switch.asm",
    "content": ".section .data.main\n\n.function_start:\n    mov x0, #1\n\n    .macro.switch.actor2.function_1:\n# end of function_start\n# --------------------------------------------------------------------------------------------------\n\n.function_fin:\n    .bb0:\n    nop\n# end of function_fin\n# --------------------------------------------------------------------------------------------------\n\n.section .data.actor2\n.function_1:\n    mov x1, #2\n\n    .macro.switch.main.function_fin:\n# end of function_1\n# --------------------------------------------------------------------------------------------------\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/asm_basic.asm",
    "content": ".section .data.main\n\n.function_0:\n.bb_0:\n\nisb  // instrumentation\n\n  // line with a comment\n\nadc w11, w20, w10  // register operands\nand x13, x13, #0b1111111000000     // immediate operand\nadd x13, x13, x20 // instrumentation\nldrh w23, [x13], #-115 // memory operand\n\nb.ne .bb_main.1\n  .bb_main.1:\n    adc w1, w2, w3  // indentation\n    and x20, x20, #0b1111111000000\n    add x20, x20, x20\n        ldrh w28, [ x20],     #-143    // extra spaces\n\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/asm_multiactor.asm",
    "content": ".section .data.main\n\n.function_0:\nnop\nnop\n\n.section .data.guest_1\n.function_1:\nnop\n\n.section .data.main\n.function_2:\n.bb0:\nnop\n\n// .section exit\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/asm_symbol.asm",
    "content": ".section .data.main\n\n.macro.measurement_start: nop; nop; nop\n\nnop\n\n.macro.measurement_end: nop; nop; nop\n\nand x0, x0, #1\n\n.function_1:\n\nnop\n\n.section .data.guest_1\n.function_2:\nnop\n\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/calls.asm",
    "content": ".section .data.main\n.function_0:\nbl .function_1\n.function_1:\nbl .function_2\n.function_2:\nbl .function_3\n.function_3:\nbl .function_4\n.function_4:\nbl .function_5\n.function_5:\nbl .function_6\n.function_6:\nbl .function_7\n.function_7:\nbl .function_8\n.function_8:\nbl .function_9\n.function_9:\nbl .function_10\n.function_10:\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/direct_jumps.asm",
    "content": ".section .data.main\nb .1\n.1:\nb .2\n.2:\nb .3\n.3:\nb .4\n.4:\nb .5\n.5:\nb .6\n.6:\nb .7\n.7:\nb .8\n.8:\nb .9\n.9:\nb .10\n.10:\nb .11\n.11:\nb .12\n.12:\nb .13\n.13:\nb .14\n.14:\nb .15\n.15:\nb .16\n.16:\nb .17\n.17:\nb .18\n.18:\nb .19\n.19:\nb .20\n.20:\nb .21\n.21:\nb .22\n.22:\nb .23\n.23:\nb .24\n.24:\nb .25\n.25:\nb .26\n.26:\nb .27\n.27:\nb .28\n.28:\nb .29\n.29:\nb .30\n.30:\nb .31\n.31:\nb .32\n.32:\nb .33\n.33:\nb .34\n.34:\nb .35\n.35:\nb .36\n.36:\nb .37\n.37:\nb .38\n.38:\nb .39\n.39:\nb .40\n.40:\nb .41\n.41:\nb .42\n.42:\nb .43\n.43:\nb .44\n.44:\nb .45\n.45:\nb .46\n.46:\nb .47\n.47:\nb .48\n.48:\nb .49\n.49:\nb .50\n.50:\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/fault-div-zero-speculation.asm",
    "content": ".section .data.main\n\nmov x2, #1\nmov x1, #0\nudiv x3, x2, x1\n\nand x3, x3, #0b1111111111111\nadd x3, x3, #0x100\nldr x0, [x20, x3]\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/fault_undefined_opcode.asm",
    "content": ".section .data.main\n\n// instrumentation to prevent page faults\nand x0, x0, #0b1111111111111\n\n// undefined instruction to trigger Undefined Instruction exception\nudf #0\n\n// this instruction should not be executed architecturally but may be executed transiently\nldr x1, [x20, x0]\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/macro_fault_handler.asm",
    "content": ".section .data.main\n.function_main_0:\n\nldr x0, [x20, #0x300]\nmov x0, #0\nbrk #0x20\nmov x1, #1\n\n.macro.fault_handler:\nldr x2, [x20, #0x200]\nldr x2, [x20, #0xff8]\nmov x2, #2\n\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/model_flags_match.asm",
    "content": ".section .data.main\n\n// base values\nmov x4, #1\nmov x5, #0\n\n// flag check\ncsel x0, x4, x5, mi  // n == 1\ncsel x1, x4, x5, eq  // z == 1\ncsel x2, x4, x5, cs  // c == 1\ncsel x3, x4, x5, vs  // v == 1\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/model_match.asm",
    "content": ".section .data.main\n\n// empty - leaving initial reg values unchanged\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/model_match_memory.asm",
    "content": ".section .data.main\n\nldr x0, [x2]  // main page\nldr x1, [x20, 4096]  // faulty page\nadd x0, x0, x1\n\nldr x1, [x20, -8]  // underflow pad\nldr x2, [x20, 4096 + 4096 + 320]  // overflow pad\nadd x1, x1, x2\n\nldr x2, [x20, 4096 + 4096]  // reg init\nldr x3, [x20, 4096 + 4096 + 48]  // patched flags\nldr x4, [x20, 4096 + 4096 + 64]  // simd init\nmov x5, x20\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/model_match_xmm.asm",
    "content": ".section .data.main\nmov x0, v0.d[0]\nmov x1, v1.d[0]\nmov x2, v2.d[0]\nmov x3, v3.d[0]\nmov x4, v4.d[0]\nmov x5, v5.d[0]\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/asm/spectre_v1.asm",
    "content": ".section .data.main\n.function_main:\n\n# reduce the entropy of x0\nand x0, x0, #0b111111000000\n\n# delay the cond. jump\nadd x1, x1, x0\nadd x1, x1, #1\nadd x1, x1, x0\nadd x1, x1, #1\nadd x1, x1, x0\nadd x1, x1, #1\nadd x1, x1, x0\nadd x1, x1, #1\nadd x1, x1, x0\nadd x1, x1, #1\nadd x1, x1, x0\nadd x1, x1, #1\nadd x1, x1, x0\nadd x1, x1, #1\nadd x1, x1, x0\nadd x1, x1, #1\n\n# reduce the entropy in x1\nand x1, x1, #0b1000000\n\n# misprediction\ncmp x1, #0\nb.eq .l1\n\n.l0:\n# x1 != 0\n    add x2, x20, x0\n    ldr x0, [x2], #0\n    b .l2\n.l1:\n# x1 == 0\n.l2:\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/arm64/configs/arch-actors.yaml",
    "content": "file: !include arch.yaml\n\nactors:\n  - actor2:\n      - mode: \"host\"\n"
  },
  {
    "path": "tests/arm64/configs/arch-faults.yaml",
    "content": "file: !include common.yaml\nfile: !include base-and-simd-categories.yaml\n\nfuzzer: architectural\nenable_priming: false\ninputs_per_class: 1\nlogging_modes:\n  - info\n  - dbg_violation\n  # - dbg_dump_htraces\n  # - dbg_dump_ctraces\n\nprogram_size: 300\navg_mem_accesses: 150\nmax_bb_per_function: 3\nmin_bb_per_function: 3\n\nfaults_allowlist:\n  - opcode-undefined\n"
  },
  {
    "path": "tests/arm64/configs/arch.yaml",
    "content": "file: !include common.yaml\nfile: !include base-and-simd-categories.yaml\n\nfuzzer: architectural\nenable_priming: false\ninputs_per_class: 1\nlogging_modes:\n  - info\n  - dbg_violation\n  # - dbg_dump_htraces\n  # - dbg_dump_ctraces\n\nprogram_size: 300\navg_mem_accesses: 150\nmax_bb_per_function: 3\nmin_bb_per_function: 3\n"
  },
  {
    "path": "tests/arm64/configs/archdiff.yaml",
    "content": "file: !include common.yaml\nfile: !include base-and-simd-categories.yaml\n\nfuzzer: archdiff\nenable_priming: false\ninputs_per_class: 1\nlogging_modes:\n - info\n#  - dbg_violation\n\nprogram_size: 100\navg_mem_accesses: 50\nmax_bb_per_function: 3\nmin_bb_per_function: 3\n"
  },
  {
    "path": "tests/arm64/configs/base-and-simd-categories.yaml",
    "content": "instruction_categories:\n  - general-arithmetic\n  - general-barrier\n  - general-bitwise\n  - general-uncond_branch\n  - general-cond_branch\n  - general-comparison\n  - general-condsel\n  - general-dataxfer\n  - general-misc\n"
  },
  {
    "path": "tests/arm64/configs/common.yaml",
    "content": "data_generator_seed: 1234567\nprogram_generator_seed: 1234567\n\n# Acceptance tests do not require a large sample size\nexecutor_sample_sizes:\n  - 10\n\n# No logging\nlogging_modes:\n  -\n  # - dbg_model\n  # - dbg_dump_htraces\n  # - dbg_dump_ctraces\n"
  },
  {
    "path": "tests/arm64/configs/ct-cond.yaml",
    "content": "file: !include common.yaml\n\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - cond\n"
  },
  {
    "path": "tests/arm64/configs/ct-seq.yaml",
    "content": "file: !include common.yaml\n\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - seq"
  },
  {
    "path": "tests/arm64/configs/exceptions.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-seq.yaml\n\nfaults_allowlist:\n  - debug-register\n  - breakpoint\n  - opcode-undefined\n"
  },
  {
    "path": "tests/arm64/configs/fault-handler.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-seq.yaml\n\nactors:\n  - main:\n    - data_properties:\n      - present: false\n\n\nexecutor_mode: F+R\nlogging_modes:\n  - dbg_dump_htraces\n"
  },
  {
    "path": "tests/arm64/min_arm64.json",
    "content": "[\n  {\"name\": \"csel\", \"category\": \"general-condsel\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rm\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": false, \"comment\": \"standard condition (field \\\"cond\\\")\", \"type_\": \"COND\", \"values\": [], \"width\": 0}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"general-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"general-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose register to be transferred (field \\\"rt\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"signed immediate byte offset [-256-255] (field \\\"imm9\\\")\", \"type_\": \"IMM\", \"values\": [\"[-256-255]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ldr\", \"category\": \"general-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose register to be transferred (field \\\"rt\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": true, \"src\": true, \"comment\": \"64-bit general-purpose base register or sp (field \\\"rn\\\")\", \"type_\": \"MEM\", \"width\": 64},\n      {\"dest\": false, \"src\": true, \"comment\": \"signed immediate byte offset [-256-255] (field \\\"imm9\\\")\", \"type_\": \"IMM\", \"values\": [\"[-256-255]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ldr\", \"category\": \"general-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose register to be transferred (field \\\"rt\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose base register or sp (field \\\"rn\\\")\", \"type_\": \"MEM\", \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"str\", \"category\": \"general-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose register to be transferred (field \\\"rt\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose base register or sp (field \\\"rn\\\")\", \"type_\": \"MEM\", \"width\": 64},\n      {\"dest\": false, \"src\": true, \"comment\": \"signed immediate byte offset [-256-255] (field \\\"imm9\\\")\", \"type_\": \"IMM\", \"values\": [\"[-256-255]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"str\", \"category\": \"general-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose register to be transferred (field \\\"rt\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose base register or sp (field \\\"rn\\\")\", \"type_\": \"MEM\", \"width\": 64},\n      {\"dest\": false, \"src\": true, \"comment\": \"signed immediate byte offset [-256-255] (field \\\"imm9\\\")\", \"type_\": \"IMM\", \"values\": [\"[-256-255]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"str\", \"category\": \"general-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose register to be transferred (field \\\"rt\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose base register or sp (field \\\"rn\\\")\", \"type_\": \"MEM\", \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"str\", \"category\": \"general-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose register to be transferred (field \\\"rt\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose base register or sp (field \\\"rn\\\")\", \"type_\": \"MEM\", \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sub\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit destination general-purpose register or sp (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\", \"sp\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit source general-purpose register or sp (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\", \"sp\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"unsigned immediate [0-4095] (field \\\"imm12\\\")\", \"type_\": \"IMM\", \"values\": [\"[0-4095]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"add\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"32-bit destination general-purpose register or wsp (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\", \"sp\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit source general-purpose register or wsp (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\", \"sp\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"unsigned immediate [0-4095] (field \\\"imm12\\\")\", \"type_\": \"IMM\", \"values\": [\"[0-4095]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"add\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit destination general-purpose register or sp (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\", \"sp\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit source general-purpose register or sp (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\", \"sp\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"unsigned immediate [0-4095] (field \\\"imm12\\\")\", \"type_\": \"IMM\", \"values\": [\"[0-4095]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"add\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"32-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose source register (field \\\"rm\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"add\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rm\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"32-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose source register (field \\\"rm\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"adc\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rm\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sdiv\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"rd\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"rn\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"rm\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sdiv\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"rd\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"rn\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"rm\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"udiv\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"rd\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"rn\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"rm\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"udiv\", \"category\": \"general-arithmetic\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"rd\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"rn\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"rm\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"and\", \"category\": \"general-bitwise\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"32-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose source register (field \\\"rm\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"and\", \"category\": \"general-bitwise\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rm\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"and\", \"category\": \"general-bitwise\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"32-bit destination general-purpose register or wsp (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\", \"sp\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"bitmask immediate (field \\\"imms:immr\\\")\", \"type_\": \"IMM\", \"values\": [\"bitmask\"], \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"and\", \"category\": \"general-bitwise\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit destination general-purpose register or sp (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\", \"sp\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"bitmask immediate (field \\\"n:imms:immr\\\")\", \"type_\": \"IMM\", \"values\": [\"bitmask\"], \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"eor\", \"category\": \"general-bitwise\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"32-bit destination general-purpose register or wsp (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\", \"sp\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"bitmask immediate (field \\\"imms:immr\\\")\", \"type_\": \"IMM\", \"values\": [\"bitmask\"], \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"eor\", \"category\": \"general-bitwise\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit destination general-purpose register or sp (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\", \"sp\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"bitmask immediate (field \\\"n:imms:immr\\\")\", \"type_\": \"IMM\", \"values\": [\"bitmask\"], \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"eor\", \"category\": \"general-bitwise\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"32-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"32-bit general-purpose source register (field \\\"rm\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"eor\", \"category\": \"general-bitwise\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rm\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"general-comparison\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": true, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"general-comparison\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"type_\": \"IMM\", \"values\": [\"[0-4095]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": true, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"dsb\", \"category\": \"general-barrier\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": false, \"src\": false, \"comment\": \"\", \"type_\": \"IMM\", \"values\": [\"sy\"], \"width\": 1}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"isb\", \"category\": \"general-barrier\", \"is_control_flow\": false,\n    \"operands\": [],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"nop\", \"category\": \"general-misc\", \"is_control_flow\": false,\n    \"operands\": [],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ldrh\", \"category\": \"general-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"32-bit general-purpose register to be transferred (field \\\"rt\\\")\", \"type_\": \"REG\", \"width\": 32, \"values\": [\"w0\", \"w1\", \"w2\", \"w3\", \"w4\", \"w5\", \"w6\", \"w7\", \"w8\", \"w9\", \"w10\", \"w11\", \"w12\", \"w13\", \"w14\", \"w15\", \"w16\", \"w17\", \"w18\", \"w19\", \"w20\", \"w21\", \"w22\", \"w23\", \"w24\", \"w25\", \"w26\", \"w27\", \"w28\", \"w29\", \"w30\", \"w31\"]},\n      {\"dest\": true, \"src\": true, \"comment\": \"64-bit general-purpose base register or sp (field \\\"rn\\\")\", \"type_\": \"MEM\", \"width\": 64},\n      {\"dest\": false, \"src\": true, \"comment\": \"signed immediate byte offset [-256-255] (field \\\"imm9\\\")\", \"type_\": \"IMM\", \"values\": [\"[-256-255]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"b\", \"category\": \"general-uncond_branch\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"comment\": \"label to be branched to (field imm19)\", \"type_\": \"LABEL\", \"values\": [], \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"values\": [\"pc\"], \"type_\": \"REG\", \"width\": 64, \"src\": true, \"dest\": false}\n    ]\n  },\n  {\"name\": \"b.\", \"category\": \"general-cond_branch\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"dest\": false, \"src\": false, \"comment\": \"standard condition (field \\\"cond\\\")\", \"type_\": \"COND\", \"values\": [], \"width\": 0},\n      {\"dest\": false, \"src\": true, \"comment\": \"label to be conditionally branched to (field imm19)\", \"type_\": \"LABEL\", \"values\": [], \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"values\": [\"pc\"], \"type_\": \"REG\", \"width\": 64, \"src\": true, \"dest\": false}\n    ]\n  },\n  {\"name\": \"br\", \"category\": \"general-indirect_branch\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"comment\": \"rn\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"bl\", \"category\": \"general-call\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"comment\": \"addr_pcrel26\", \"type_\": \"LABEL\", \"width\": 0, \"values\": []}\n    ],\n    \"implicit_operands\": [\n      {\"dest\": false, \"src\": true, \"type_\": \"REG\", \"width\": 64, \"values\": [\"pc\"]},\n      {\"dest\": true, \"src\": false, \"type_\": \"REG\", \"width\": 64, \"values\": [\"x30\"]}\n    ]\n  },\n  {\"name\": \"blr\", \"category\": \"general-call\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"comment\": \"rn\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mrs\", \"category\": \"system-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"comment\": \"64-bit general-purpose source register (field \\\"rn\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"nzcv\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mrs\", \"category\": \"system-dataxfer\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": true, \"src\": false, \"comment\": \"64-bit general-purpose destination register (field \\\"rd\\\")\", \"type_\": \"REG\", \"width\": 64, \"values\": [\"x0\", \"x1\", \"x2\", \"x3\", \"x4\", \"x5\", \"x6\", \"x7\", \"x8\", \"x9\", \"x10\", \"x11\", \"x12\", \"x13\", \"x14\", \"x15\", \"x16\", \"x17\", \"x18\", \"x19\", \"x20\", \"x21\", \"x22\", \"x23\", \"x24\", \"x25\", \"x26\", \"x27\", \"x28\", \"x29\", \"x30\", \"x31\"]},\n      {\"dest\": false, \"src\": true, \"type_\": \"REG\", \"width\": 64, \"values\": [\"pmevcntr0_el0\", \"pmevcntr1_el0\", \"pmevcntr2_el0\", \"pmevcntr3_el0\"]}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"brk\", \"category\": \"system-exception\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"type_\": \"IMM\", \"values\": [\"[0-65535]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"w\", \"\", \"\", \"w\", \"w\", \"\", \"\", \"\", \"w\"]}\n    ]\n  },\n  {\"name\": \"udf\", \"category\": \"system-exception\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"dest\": false, \"src\": true, \"type_\": \"IMM\", \"values\": [\"[0-65535]\"], \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"width\": 0, \"src\": false, \"dest\": false, \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"]}\n    ]\n  }\n]\n"
  },
  {
    "path": "tests/arm64/model_common.py",
    "content": "\"\"\"\nFile: Collection of helper classes for arm64 model tests.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom typing import List, Generator\n\nimport os\nimport tempfile\nfrom pathlib import Path\n\nfrom rvzr.tc_components.test_case_code import TestCaseProgram\nfrom rvzr.tc_components.test_case_data import InputData\nfrom rvzr.isa_spec import InstructionSet\nfrom rvzr.elf_parser import ELFParser\nfrom rvzr.arch.arm64.target_desc import ARM64TargetDesc\nfrom rvzr.arch.arm64.asm_parser import ARM64AsmParser\nfrom rvzr.arch.arm64.generator import ARM64Generator\nfrom rvzr.config import CONF\n\ntest_path = Path(__file__).resolve()\ntest_dir = test_path.parent\n\nASM_HEADER = \"\"\"\n.section .data.main\n\"\"\"\n\n# base addresses for calculating expected contract traces\nPC0 = 0x8\nMEM_BASE = 0x1000000\nCODE_BASE = 0x8000\nMAIN_OFFSET = 0x1000\nFAULTY_OFFSET = 0x2000\n\nMEM_DEFAULT_VALUE = 1\nREG_DEFAULT_VALUE = 2\nMEM_FAULTY_DEFAULT_VALUE = 3\nRSP_DEFAULT_VALUE = FAULTY_OFFSET - 8\n\n\nclass Inst:\n    \"\"\" Instruction with its size and memory address \"\"\"\n    text: str\n    size: int\n    mem_address: int\n    mem_value: int\n    pc_offset: int\n\n    def __init__(self, text: str, size: int, mem_address: int, mem_value: int):\n        self.text = text\n        self.size = size\n        self.mem_address = mem_address\n        self.mem_value = mem_value\n        self.pc_offset = 0\n\n\nclass InstList:\n    \"\"\" List of instructions with their memory addresses \"\"\"\n    instructions: List[Inst]\n\n    def __init__(self, instructions: List[Inst]):\n        # measurement_end macro is inserted automatically at the end\n        instructions.append(Inst(\".macro.measurement_end:\", 0, 0, 0))\n\n        # set the pc_offset for each instruction\n        self.set_offsets(instructions)\n        self.instructions = instructions\n\n    def __iter__(self) -> Generator[Inst, None, None]:\n        yield from self.instructions\n\n    def __getitem__(self, index: int) -> Inst:\n        return self.instructions[index]\n\n    @staticmethod\n    def set_offsets(instructions: List[Inst]) -> None:\n        \"\"\" Set the pc_offset for each instruction in a list \"\"\"\n        pc = 0x8\n        for inst in instructions:\n            inst.pc_offset = pc\n            pc += inst.size\n\n    def to_test_case(self) -> TestCaseProgram:\n        \"\"\" Load a test case from the assembly string \"\"\"\n        min_arm64_path = test_dir / \"min_arm64.json\"\n\n        asm_str = ASM_HEADER\n        asm_str += \"\\n\".join([x.text for x in self.instructions])\n        asm_str += \"\\n.test_case_exit:\\n\"\n\n        instruction_set = InstructionSet(min_arm64_path.absolute().as_posix())\n        target_desc = ARM64TargetDesc()\n        elf_parser = ELFParser(target_desc)\n        asm_parser = ARM64AsmParser(instruction_set, target_desc)\n        generator = ARM64Generator(CONF.program_generator_seed, instruction_set, target_desc,\n                                   asm_parser, elf_parser)\n\n        asm_file = tempfile.NamedTemporaryFile(delete=False)\n        with open(asm_file.name, \"w\", encoding=\"utf-8\") as f:\n            f.write(asm_str)\n        tc: TestCaseProgram = asm_parser.parse_file(asm_file.name, generator, elf_parser)\n        asm_file.close()\n        os.unlink(asm_file.name)\n        return tc\n\n\ndef get_default_input() -> InputData:\n    input_ = InputData()\n    input_[0]['main'][0] = MEM_DEFAULT_VALUE\n    input_[0]['main'][1] = MEM_DEFAULT_VALUE\n    input_[0]['faulty'][0] = MEM_FAULTY_DEFAULT_VALUE\n    input_[0]['faulty'][1] = MEM_FAULTY_DEFAULT_VALUE\n    for i in range(0, 7):\n        input_[0]['gpr'][i] = REG_DEFAULT_VALUE\n    return input_\n"
  },
  {
    "path": "tests/arm64/unit_generators.py",
    "content": "\"\"\"\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# pylint: disable=missing-function-docstring\n# pylint: disable=missing-class-docstring\n\nimport unittest\nimport tempfile\nimport subprocess\nimport os\nfrom pathlib import Path\nfrom copy import deepcopy\n\nfrom rvzr.arch.arm64.generator import ARM64Generator, _ARM64Printer\nfrom rvzr.arch.arm64.target_desc import ARM64TargetDesc\nfrom rvzr.elf_parser import ELFParser\nfrom rvzr.factory import get_program_generator, get_asm_parser\nfrom rvzr.isa_spec import InstructionSet\nfrom rvzr.tc_components.actor import ActorMode\nfrom rvzr.tc_components.test_case_code import TestCaseProgram\nfrom rvzr.tc_components.test_case_binary import SymbolTableEntry\nfrom rvzr.code_generator import assemble\nfrom rvzr.config import CONF\nfrom rvzr.logs import update_logging_after_config_change\n\nCONF.instruction_set = \"arm64\"\ntest_path = Path(__file__).resolve()\ntest_dir = test_path.parent\n\nASM_OPCODE = \"\"\"\n.section .data.main\n.long 0xd503201f\n.test_case_exit:\n\"\"\"\n\n\nclass ARM64GeneratorTest(unittest.TestCase):\n\n    @classmethod\n    def setUpClass(cls) -> None:\n        CONF.logging_modes = []\n        update_logging_after_config_change()\n\n    @staticmethod\n    def load_tc(asm_str: str) -> TestCaseProgram:\n\n        instruction_set = InstructionSet((test_dir / \"min_arm64.json\").absolute().as_posix())\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        asm_parser = get_asm_parser(instruction_set)\n        elf_parser = ELFParser(ARM64TargetDesc())\n\n        asm_file = tempfile.NamedTemporaryFile(delete=False)\n        with open(asm_file.name, \"w\") as f:\n            f.write(asm_str)\n        tc: TestCaseProgram = asm_parser.parse_file(asm_file.name, generator, elf_parser)\n        asm_file.close()\n        os.unlink(asm_file.name)\n        return tc\n\n    def test_arm64_configuration(self) -> None:\n        CONF.generator = \"random\"\n        instruction_set = InstructionSet((test_dir / \"min_arm64.json\").absolute().as_posix(),\n                                         CONF.instruction_categories)\n        gen = get_program_generator(CONF.program_generator_seed, instruction_set)\n        self.assertEqual(gen.__class__, ARM64Generator)\n\n    def test_arm64_all_instructions(self) -> None:\n        # pylint: disable=protected-access\n        # Note: This function tests internals of the generator, which is why we\n        # have to disable the protected-access warning.\n\n        asm_file = tempfile.NamedTemporaryFile(\"w\", delete=False)\n        obj_file = tempfile.NamedTemporaryFile(\"w\", delete=False)\n\n        instruction_set = InstructionSet((test_dir / \"min_arm64.json\").absolute().as_posix())\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        function_generator = generator._function_generator\n        tc = TestCaseProgram(asm_file.name)\n        tc.assign_obj(obj_file.name)\n\n        func = function_generator.generate_empty(\".function_0\", tc.find_section(name=\"main\"))\n        tc.find_section(name=\"main\").append(func)\n        printer = _ARM64Printer(ARM64TargetDesc())\n        all_instructions = ['']\n\n        # try generating instruction strings\n        for bb in func:\n            for instruction_spec in instruction_set.non_control_flow_specs:\n                # fill up with random operand, following the spec\n                inst = generator.generate_instruction(instruction_spec)\n                bb.insert_after(bb.get_last(), inst)\n\n            for instr in bb:\n                instr_str = printer._instruction_to_str(instr)\n                self.assertTrue(instr_str, f'Instruction {instr} was not generated.')\n                all_instructions.append(instr_str + \"\\n\")\n\n        # apply generator passes\n        for p in generator._passes:\n            p.run_on_test_case(tc)\n\n        # write the instructions to the asm file\n        generator._printer.print(tc)\n\n        # check if the generated instructions are valid\n        assembly_failed = False\n        try:\n            assemble(tc)\n        except subprocess.CalledProcessError:\n            assembly_failed = True\n        else:\n            obj_file.close()\n            os.unlink(obj_file.name)\n        os.unlink(asm_file.name)\n\n        if assembly_failed:\n            self.fail(\"Generated invalid instruction(s)\")\n\n    def test_arm64_asm_parsing_basic(self) -> None:\n        instruction_set = InstructionSet((test_dir / \"min_arm64.json\").absolute().as_posix())\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        asm_parser = get_asm_parser(instruction_set)\n        elf_parser = ELFParser(ARM64TargetDesc())\n\n        asm_name = (test_dir / \"asm/asm_basic.asm\").absolute().as_posix()\n        tc: TestCaseProgram = asm_parser.parse_file(asm_name, generator, elf_parser)\n        section = tc[0]\n        functions = list(section)\n\n        self.assertEqual(len(functions), 2)\n\n        main = functions[0]\n        self.assertEqual(main.name, \".function_0\")\n\n        self.assertEqual(len(main), 3)\n\n        bb0 = main[1]\n        bb1 = main[2]\n        exit_ = main.get_exit_bb()\n\n        self.assertEqual(bb0.successors[0], bb1)\n        self.assertEqual(bb1.successors[0], exit_)\n\n        self.assertEqual(functions[1].name, \".function_end\")\n\n    def test_arm64_asm_parsing_opcode(self) -> None:\n\n        tc = self.load_tc(ASM_OPCODE)\n        functions = list(tc[0])\n\n        main_iter = iter(functions[0])\n        bb0 = next(main_iter)\n        insts = list(bb0)\n        self.assertEqual(insts[0].name, \"macro\")\n        self.assertEqual(insts[1].name, \"opcode\")\n\n    def test_arm64_asm_parsing_section(self) -> None:\n        prev_actors = deepcopy(CONF.get_actors_conf())\n        CONF.get_actors_conf()[\"guest_1\"] = deepcopy(CONF._actor_default)\n        CONF.get_actors_conf()[\"guest_1\"][\"name\"] = \"guest_1\"\n        CONF.get_actors_conf()[\"guest_1\"][\"mode\"] = \"guest\"\n        CONF.get_actors_conf()[\"guest_1\"][\"privilege_level\"] = \"kernel\"\n\n        instruction_set = InstructionSet((test_dir / \"min_arm64.json\").absolute().as_posix())\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        asm_parser = get_asm_parser(instruction_set)\n        elf_parser = ELFParser(ARM64TargetDesc())\n        name = (test_dir / \"asm/asm_multiactor.asm\").absolute().as_posix()\n        tc: TestCaseProgram = asm_parser.parse_file(name, generator, elf_parser)\n\n        self.assertEqual(tc.n_actors(), 2)\n        self.assertEqual(tc.find_actor(name=\"main\").mode, ActorMode.HOST)\n        self.assertEqual(tc.find_actor(name=\"main\").get_id(), 0)\n        self.assertEqual(tc.find_actor(name=\"guest_1\").mode, ActorMode.GUEST)\n        self.assertEqual(tc.find_actor(name=\"guest_1\").get_id(), 1)\n\n        self.assertEqual(len(tc), 2)\n\n        sec1 = tc[0]\n        self.assertEqual(len(sec1), 3)\n        self.assertEqual(sec1.owner.get_id(), 0)\n        self.assertTrue(sec1.owner.is_main)\n\n        f1 = sec1[0]\n        self.assertEqual(f1.name, \".function_0\")\n        self.assertEqual(len(f1[0]), 3)\n\n        f2 = sec1[1]\n        self.assertEqual(f2.name, \".function_2\")\n        self.assertEqual(len(f2[0]), 1)\n\n        sec2 = tc[1]\n        self.assertEqual(len(sec2), 1)\n        self.assertEqual(sec2.owner.get_id(), 1)\n        self.assertFalse(sec2.owner.is_main)\n\n        f1 = sec2[0]\n        self.assertEqual(f1.name, \".function_1\")\n        self.assertEqual(len(f1[0]), 1)\n\n        CONF._actors = prev_actors\n\n    def test_arm64_asm_parsing_symbols(self) -> None:\n        prev_actors = deepcopy(CONF.get_actors_conf())\n        CONF.get_actors_conf()[\"guest_1\"] = deepcopy(CONF._actor_default)\n        CONF.get_actors_conf()[\"guest_1\"][\"name\"] = \"guest_1\"\n        CONF.get_actors_conf()[\"guest_1\"][\"mode\"] = \"guest\"\n        CONF.get_actors_conf()[\"guest_1\"][\"privilege_level\"] = \"kernel\"\n\n        instruction_set = InstructionSet((test_dir / \"min_arm64.json\").absolute().as_posix())\n\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        asm_parser = get_asm_parser(instruction_set)\n        elf_parser = ELFParser(ARM64TargetDesc())\n        name = (test_dir / \"asm/asm_symbol.asm\").absolute().as_posix()\n        tc: TestCaseProgram = asm_parser.parse_file(name, generator, elf_parser)\n        obj = tc.get_obj()\n        symbol_table = obj.symbol_table()\n\n        self.assertEqual(symbol_table[0], SymbolTableEntry(0, 0, 0, 0))  # function_0\n        self.assertEqual(symbol_table[1], SymbolTableEntry(0, 0, 1, 0))  # measurement_start\n        self.assertEqual(symbol_table[2], SymbolTableEntry(0, 0x10, 2, 0))  # measurement_end\n        self.assertEqual(symbol_table[3], SymbolTableEntry(0, 0x20, 0, 1))  # function_1\n        self.assertEqual(symbol_table[4], SymbolTableEntry(1, 0, 0, 2))  # function_2\n\n        CONF._actors = prev_actors\n"
  },
  {
    "path": "tests/arm64/unit_isa_loader.py",
    "content": "\"\"\"\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nimport unittest\nfrom pathlib import Path\n\nfrom rvzr.isa_spec import InstructionSet\nfrom rvzr.config import CONF\n\ntest_path = Path(__file__).resolve()\ntest_dir = test_path.parent\nCONF.instruction_set = \"arm64\"\n\n\nclass ARM64ISALoaderTest(unittest.TestCase):\n\n    def test_loading(self) -> None:\n        instruction_set = InstructionSet((test_dir / \"min_arm64.json\").absolute().as_posix(),\n                                         [\"general-dataxfer\"])\n        inst_names = [i.name for i in instruction_set.instructions]\n        self.assertIn(\"mov\", inst_names)\n"
  },
  {
    "path": "tests/kernel_module.bats",
    "content": "#!/usr/bin/env bats\n\nINPUT_SIZE=$((4096 * 3))\n\nINPUT_META='\\x00\\x03\\x00\\x00\\x00\\x00\\x00\\x00''\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'\n\nsetup() {\n    # get the containing directory of this file\n    DIR=\"$(cd \"$(dirname \"$BATS_TEST_FILENAME\")\" >/dev/null 2>&1 && pwd)\"\n    VENDOR=\"$(lscpu | grep Vendor | awk '{print $3}')\"\n    ARCH=\"$(lscpu | grep Architecture | awk '{print $2}')\"\n}\n\nfunction x86_only() {\n    if [ \"$ARCH\" != \"x86_64\" ]; then\n        skip \"x86-only test\"\n    fi\n}\n\nhex2bin32() {\n    local v=$1\n    printf \"\\\\\\x%02x\" $((v & 255)) $((v >> 8 & 255)) $((v >> 16 & 255)) $((v >> 24 & 255))\n}\n\nfunction load_input() {\n    local create_only=$1\n    local nruns=$2\n    local dest_file=$3\n    local header=\"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00$(hex2bin32 $nruns)\\x00\\x00\\x00\\x00\"\n\n    printf $header >$dest_file\n    printf $INPUT_META >>$dest_file\n    printf '%0.s\\x00' $(seq 1 $INPUT_SIZE) >>$dest_file\n\n    if [ $create_only = false ]; then\n        cat $dest_file >/sys/rvzr_executor/inputs\n        rm $dest_file\n    fi\n}\n\nfunction load_test_case() {\n    local create_only=$1\n    local asm_file=$2\n    local dest_file=$3\n\n    if [ $ARCH == \"x86_64\" ]; then\n        ${DIR}/scripts/create_rcbf_file.py $asm_file $dest_file x86\n    else\n        ${DIR}/scripts/create_rcbf_file.py $asm_file $dest_file arm64\n    fi\n\n    if [ $create_only = false ]; then\n        cat $dest_file >/sys/rvzr_executor/test_case\n        rm $dest_file\n    fi\n}\n\nfunction set_default_config() {\n    echo \"0\" >/sys/rvzr_executor/enable_dbg_gpr_mode\n    echo \"1\" >/sys/rvzr_executor/enable_ssbp_patch\n    echo \"0\" >/sys/rvzr_executor/enable_prefetcher\n}\n\n@test \"Executor: Loading a test case\" {\n    printf \"nop\\n\" >tmp.asm\n    load_test_case true tmp.asm tmp.bin\n\n    run bash -c 'cat tmp.bin > /sys/rvzr_executor/test_case'\n    [ \"$status\" -eq 0 ]\n    rm tmp.bin\n\n    load_input true 1 input.bin\n    run bash -c 'cat input.bin > /sys/rvzr_executor/inputs'\n    [ \"$status\" -eq 0 ]\n    rm input.bin\n\n    run cat /sys/rvzr_executor/inputs\n    [ \"$status\" -eq 0 ]\n    echo \"Output: $output\"\n    [[ \"$output\" -eq \"1\" ]]\n}\n\n@test \"Executor: Tracing\" {\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    [ \"$status\" -eq 0 ]\n}\n\n@test \"Executor: Printing base addresses\" {\n    run cat /sys/rvzr_executor/print_data_base\n    echo \"Output: $output\"\n    [[ \"$output\" != \"0\" ]]\n    run cat /sys/rvzr_executor/print_code_base\n    echo \"Output: $output\"\n    [[ \"$output\" != \"0\" ]]\n}\n\n@test \"Executor: Controlling warmups\" {\n    echo \"50\" >/sys/rvzr_executor/warmups\n    run cat /sys/rvzr_executor/warmups\n    [[ \"$output\" -eq \"50\" ]]\n}\n\n@test \"Executor: Controlling patches\" {\n    tmpasm=$(mktemp /tmp/revizor-test.XXXXXX.asm)\n    tmpbin=$(mktemp /tmp/revizor-test.XXXXXX.o)\n    tmpinput=$(mktemp /tmp/revizor-test.XXXXXX.bin)\n    echo \"nop\" >$tmpasm\n    load_test_case false $tmpasm $tmpbin\n    load_input false 1 $tmpinput\n\n    run bash -c 'echo \"1\" > /sys/rvzr_executor/enable_ssbp_patch'\n    [ \"$status\" -eq 0 ]\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    [ \"$status\" -eq 0 ]\n\n    run bash -c 'echo \"0\" > /sys/rvzr_executor/enable_ssbp_patch'\n    [ \"$status\" -eq 0 ]\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    [ \"$status\" -eq 0 ]\n\n    run bash -c 'echo \"1\" > /sys/rvzr_executor/enable_prefetcher'\n    [ \"$status\" -eq 0 ]\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    [ \"$status\" -eq 0 ]\n\n    run bash -c 'echo \"0\" > /sys/rvzr_executor/enable_prefetcher'\n    [ \"$status\" -eq 0 ]\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    [ \"$status\" -eq 0 ]\n}\n\n@test \"Executor: Hardware tracing with GPR\" {\n    set_default_config\n    echo \"1\" >/sys/rvzr_executor/enable_dbg_gpr_mode\n\n    tmpasm=$(mktemp /tmp/revizor-test.XXXXXX.asm)\n    tmpbin=$(mktemp /tmp/revizor-test.XXXXXX.o)\n    tmpinput=$(mktemp /tmp/revizor-test.XXXXXX.bin)\n\n    if [ $ARCH == \"x86_64\" ]; then\n        echo \"mov \\$1, %rax; mov \\$2, %rbx; mov \\$3, %rcx; mov \\$4, %rdx; mov \\$5, %rsi; mov \\$6, %rdi;\" >$tmpasm\n    else\n        echo \"mov x0, 1; mov x1, 2; mov x2, 3; mov x3, 4; mov x4, 5; mov x5, 6;\" >$tmpasm\n    fi\n\n    load_test_case false $tmpasm $tmpbin\n    load_input false 1 $tmpinput\n\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    echo \"Output: $output\"\n    [[ \"$output\" == *\"1,2,3,4,5,6\"* ]]\n}\n\n@test \"Executor: Hardware tracing with P+P\" {\n    x86_only\n    set_default_config\n    echo \"P+P\" >/sys/rvzr_executor/measurement_mode\n\n    tmpasm=$(mktemp /tmp/revizor-test.XXXXXX.asm)\n    tmpbin=$(mktemp /tmp/revizor-test.XXXXXX.o)\n    tmpinput=$(mktemp /tmp/revizor-test.XXXXXX.bin)\n    echo \"nop\" >$tmpasm\n    load_test_case false $tmpasm $tmpbin\n    load_input false 1 $tmpinput\n\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    echo \"Output: $output\"\n    [[ \"$output\" == *\"9223372036854775808,\"* ]]\n\n    echo \"movq %r14, %rax; add \\$512, %rax; movq (%rax), %rax\" >$tmpasm\n    load_test_case false $tmpasm $tmpbin\n    load_input false 1 $tmpinput\n\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    echo \"Output: $output\"\n    [[ \"$output\" == *\"9259400833873739776,\"* ]]\n}\n\n@test \"Executor: Hardware tracing with F+R\" {\n    x86_only\n    set_default_config\n    echo \"F+R\" >/sys/rvzr_executor/measurement_mode\n\n    tmpasm=$(mktemp /tmp/revizor-test.XXXXXX.asm)\n    tmpbin=$(mktemp /tmp/revizor-test.XXXXXX.o)\n    tmpinput=$(mktemp /tmp/revizor-test.XXXXXX.bin)\n    echo \"nop\" >$tmpasm\n    load_test_case false $tmpasm $tmpbin\n    load_input false 1 $tmpinput\n\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    echo \"Output: $output\"\n    [[ \"$output\" == *\"9223372036854775808,\"* ]]\n\n    echo \"movq %r14, %rax; add \\$512, %rax; movq (%rax), %rax\" >$tmpasm\n    load_test_case false $tmpasm $tmpbin\n    load_input false 1 $tmpinput\n\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    echo \"Output: $output\"\n    [[ \"$output\" == *\"9259400833873739776,\"* ]]\n}\n\n@test \"Executor: Hardware tracing with E+R\" {\n    x86_only\n    set_default_config\n    echo \"E+R\" >/sys/rvzr_executor/measurement_mode\n\n    tmpasm=$(mktemp /tmp/revizor-test.XXXXXX.asm)\n    tmpbin=$(mktemp /tmp/revizor-test.XXXXXX.o)\n    tmpinput=$(mktemp /tmp/revizor-test.XXXXXX.bin)\n    echo \"nop\" >$tmpasm\n    load_test_case false $tmpasm $tmpbin\n    load_input false 1 $tmpinput\n\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    echo \"Output: $output\"\n    [[ \"$output\" == *\"9223372036854775808,\"* ]]\n\n    echo \"movq %r14, %rax; add \\$512, %rax; movq (%rax), %rax\" >$tmpasm\n    load_test_case false $tmpasm $tmpbin\n    load_input false 1 $tmpinput\n\n    run taskset -c 0 cat /sys/rvzr_executor/trace\n    echo \"Output: $output\"\n    [[ \"$output\" == *\"9259400833873739776,\"* ]]\n}\n\n@test \"Executor: Noise Level\" {\n    x86_only\n    set_default_config\n\n    # execute one dummy run to set Executor into the default config and to load the test case\n    nruns=1000\n    threshold=900\n\n    tmpasm=$(mktemp /tmp/revizor-test.XXXXXX.asm)\n    tmpbin=$(mktemp /tmp/revizor-test.XXXXXX.o)\n    tmpinput=$(mktemp /tmp/revizor-test.XXXXXX.bin)\n    tmpresult=$(mktemp /tmp/revizor-test.XXXXXX.txt)\n\n    echo \"movq (%r14), %rax\" >$tmpasm\n    load_test_case true \"$tmpasm\" \"$tmpbin\"\n\n    printf \"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00$(hex2bin32 $nruns)\\x00\\x00\\x00\\x00\" >/sys/rvzr_executor/inputs\n    printf $INPUT_META >>/sys/rvzr_executor/inputs\n    dd if=/dev/zero of=\"$tmpinput\" bs=$INPUT_SIZE count=$nruns status=none\n    cat $tmpinput >/sys/rvzr_executor/inputs\n\n    run cat /sys/rvzr_executor/inputs\n    [[ \"$output\" -eq \"1\" ]]\n\n    for mode in \"P+P\" \"F+R\" \"E+R\"; do\n        # echo $mode\n        echo $mode >/sys/rvzr_executor/measurement_mode\n        cat \"$tmpbin\" >/sys/rvzr_executor/test_case\n\n        echo \"\" >$tmpresult\n\n        # START=$(date +%s.%N)\n        while true; do\n            run taskset -c 0 cat /sys/rvzr_executor/trace\n            [ \"$status\" -eq 0 ]\n            echo \"$output\" >>$tmpresult\n            if [[ \"$output\" == *\"done\"* ]]; then\n                break\n            fi\n        done\n        # END=$(date +%s.%N)\n        # echo \"$END - $START\" | bc\n\n        # cat $tmpresult | awk -F, '/,/{print $1}' | sort | uniq -c | sort -r -b -n\n        run bash -c \"cat $tmpresult | awk -F, '/,/{print \\$1}' | sort | uniq -c | sort -r -b -n | awk '//{print \\$1}' | head -n1\"\n        echo \"$mode: $output\"\n        [ $output -ge $threshold ]\n    done\n    rm $tmpasm\n    rm \"$tmpbin\"\n    rm \"$tmpinput\"\n    rm \"$tmpresult\"\n}\n\n@test \"Executor: Noisy stores\" {\n    x86_only\n    set_default_config\n\n    # execute one dummy run to set Executor into the default config and to load the test case\n    nruns=1000\n    threshold=900\n\n    tmpasm=$(mktemp /tmp/revizor-test.XXXXXX.asm)\n    tmpbin=$(mktemp /tmp/revizor-test.XXXXXX.o)\n    tmpinput=$(mktemp /tmp/revizor-test.XXXXXX.bin)\n    tmpresult=$(mktemp /tmp/revizor-test.XXXXXX.txt)\n\n    echo \"movq %r14, %rax; add \\$512, %rax; movq \\$128, (%rax)\" >$tmpasm\n    load_test_case true $tmpasm $tmpbin\n\n    printf \"\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00$(hex2bin32 $nruns)\\x00\\x00\\x00\\x00\" >/sys/rvzr_executor/inputs\n    printf $INPUT_META >>/sys/rvzr_executor/inputs\n    dd if=/dev/zero of=\"$tmpinput\" bs=$INPUT_SIZE count=$nruns status=none\n    cat $tmpinput >/sys/rvzr_executor/inputs\n\n    mode=\"P+P\"\n    echo $mode >/sys/rvzr_executor/measurement_mode\n    cat $tmpbin >/sys/rvzr_executor/test_case\n\n    run cat /sys/rvzr_executor/inputs\n    [[ \"$output\" -eq \"1\" ]]\n\n    echo \"\" >$tmpresult\n\n    while true; do\n        run taskset -c 0 cat /sys/rvzr_executor/trace\n        [ \"$status\" -eq 0 ]\n        echo \"$output\" >>$tmpresult\n        if [[ \"$output\" == *\"done\"* ]]; then\n            break\n        fi\n    done\n\n    run bash -c \"cat $tmpresult | awk -F, '/,/{print \\$1}' | sort | uniq -c | sort -r | awk '//{print \\$1}' | head -n1\"\n    echo \"$mode: $output\"\n    [ $output -ge $threshold ]\n\n    rm $tmpasm\n    rm \"$tmpbin\"\n    rm \"$tmpinput\"\n    rm \"$tmpresult\"\n}\n"
  },
  {
    "path": "tests/pre-release.sh",
    "content": "#!/usr/bin/env bash\n# FILE: tests/pre-release.sh\n#       Run all available demos to ensure that regressions on all known vulnerabilities on\n#       the CPU under test\n\nset -o errexit -o pipefail -o noclobber -o nounset\ntrap exit INT\n\nSCRIPT=$(realpath $0)\nSCRIPT_DIR=$(dirname $SCRIPT)\n\nred='\\033[0;31m'\ngreen='\\033[0;32m'\nyellow='\\033[0;33m'\nreset='\\033[0m'\n\nverbose=0\nwork_dir=\"\"\nrevizor_dir=${revizor_dir:-\"$SCRIPT_DIR/..\"}\n\n# ==================================================================================================\n# Command-line argument parsing\n# ==================================================================================================\nfunction print_help() {\n    echo \"Usage: $0 [-v] -w <work_dir>\"\n    echo \"\"\n    echo \"Options:\"\n    echo \"  -v, --verbose        Enable verbose output\"\n    echo \"  -w, --workdir        Working directory for temporary files\"\n}\n\nfunction read_args() {\n    # check for availability of getopt\n    getopt --test >/dev/null && true\n    if [[ $? -ne 4 ]]; then\n        echo 'ERROR: getopt is not available'\n        exit 1\n    fi\n\n    # List arguments\n    LONGOPTS=workdir:,verbose\n    OPTIONS=w:v\n\n    # Parse output\n    PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name \"$0\" -- \"$@\") || exit 2\n    eval set -- \"$PARSED\"\n\n    while true; do\n        case \"$1\" in\n        -v | --verbose)\n            verbose=0\n            ;;\n        -w | --workdir)\n            work_dir=$2\n            shift\n            ;;\n        --)\n            shift\n            break\n            ;;\n        esac\n        shift\n    done\n\n    # check usage\n    if [ -z \"$work_dir\" ]; then\n        print_help\n        exit 1\n    fi\n\n    # make sure that the directories and required files exist\n    if [ ! -d \"$work_dir\" ]; then\n        echo \"ERROR: Could not find '$work_dir'\"\n    fi\n    if [ ! -f \"$revizor_dir/revizor.py\" ]; then\n        echo \"ERROR: Could not find '$revizor_dir/revizor.py'\"\n    fi\n    if [ ! -f \"$revizor_dir/base.json\" ]; then\n        echo \"ERROR: Could not find '$revizor_dir/base.json'\"\n    fi\n\n    # Globals\n    work_dir=$(realpath $work_dir)\n    revizor=\"$revizor_dir/revizor.py\"\n    instructions=\"$revizor_dir/base.json\"\n    conf_dir=\"$revizor_dir/demo/\"\n\n}\n\n# ==================================================================================================\n# Patching and manipulation of configuration files\n# ==================================================================================================\n\n# array of patches to the configuration file that makes a repro config into a verification config\n# the array is a map from the name of the vulnerability to the patch function\ndeclare -A verif_patches\nverif_patches[\"detect-v1\"]=\"contract_execution_clause:\\n  - cond\"\nverif_patches[\"detect-v1-store\"]=\"contract_observation_clause: ct\"\nverif_patches[\"detect-v4\"]=\"x86_executor_enable_ssbp_patch: true\"\n\nfunction make_verification_conf() {\n    # create a version of the reproduce file that should NOT trigger a violation if\n    # the violation has an expected root cause. E.g., for Spectre V1, we change the contract\n    # execution clause to COND instead of SEQ, which means that violations caused by conditional\n    # branches should no longer be reported by the fuzzer (i.e., become non-reproducible).\n\n    local name=$1\n    local repro_conf=$2\n    local verif_conf=$3\n\n    cp $repro_conf $verif_conf\n    if [[ ! -v verif_patches[$name] ]]; then\n        printf \"${yellow}NO VERIFICATION PATCH AVAILABLE${reset}\\n\"\n        return 1\n    fi\n    local patch=${verif_patches[$name]}\n    echo -e \"$patch\" >>$verif_conf\n    return 0\n}\n\nfunction disable_stat_logging() {\n    local config=$1\n\n    # disable statistics logging to avoid polluting the output\n    echo \"logging_modes:\" >>$config\n    echo \"  - info\" >>$config\n}\n\n\nfunction disable_all_logging() {\n    local config=$1\n\n    # disable all logging to avoid polluting the output\n    echo \"logging_modes: []\" >>$config\n}\n\n# ==================================================================================================\n# Functions\n# ==================================================================================================\nfunction prep_files_for_run() {\n    local name=$1\n\n    # remove leftovers from previous runs\n    rm -rf $work_dir &>/dev/null || true\n    mkdir -p $work_dir\n\n    # check that the configuration file exists\n    org_config=\"$conf_dir/${name}.yaml\"\n    if [ ! -f \"$org_config\" ]; then\n        # templated demos have a different naming scheme\n        org_config=\"$conf_dir/$name/config.yaml\"\n        if [ ! -f \"$org_config\" ]; then\n            echo \"ERROR: Could not find '$org_config'\"\n            exit 1\n        fi\n    fi\n\n    # make a copy of the configuration file and patch it\n    config=\"$work_dir/conf.yaml\"\n    cp $org_config $config\n    disable_stat_logging $config\n\n    # create a log file\n    log=\"$work_dir/${name}-log.txt\"\n    rm $log &>/dev/null || true\n}\n\nfunction check_results() {\n    # Check the output of the experiment for errors and parse the results\n\n    # arguments\n    local log=$1\n    local exit_code=$2\n    local expected=$3\n\n    # output messages\n    fail=\"${red}FAIL${reset}\"\n    error=\"${red}ERROR${reset}\"\n    ok=\"${green}PASSED${reset}\"\n\n    # check for errors\n    if grep \"ERROR\" $log &>/dev/null; then\n        printf \"$error\\n\"\n        return 1\n    fi\n    if grep \"Error\" $log &>/dev/null; then\n        printf \"$error\\n\"\n        return 1\n    fi\n    if grep \"Errno\" $log &>/dev/null; then\n        printf \"$error\\n\"\n        return 1\n    fi\n\n    # if no violations were found, the test failed\n    if [ $exit_code -ne $expected ]; then\n        printf \"$fail [exit code %s != %s]\\n\" \"$exit_code\" \"$expected\"\n        return 1\n    fi\n\n    # parse the output\n    duration=$(awk '/Duration/{print $2}' $log)\n    printf \"$ok [%s sec]\\n\" \"$duration\"\n    return 0\n}\n\n\nfunction run() {\n    local name=$1\n    local templated=${2:-0}\n\n    prep_files_for_run $name\n\n    # Print the header\n    echo \"\"\n    printf \"${yellow}============================= $name =============================${reset}\\n\"\n\n    # run the test\n    printf \"${green}+ Detect ...  ${reset}\\n\"\n    set +e\n    if [ $verbose -eq 1 ]; then set -x; fi\n    if [ $templated -eq 0 ]; then\n        python ${revizor} fuzz -s $instructions -c $config -I $conf_dir -i $NUM_INPUTS -n $NUM_PROGS --timeout $TIMEOUT -w \"$work_dir\" 2>&1 | tee \"$log\"\n    else\n        template=\"$conf_dir/$name/template.asm\"\n        python ${revizor} tfuzz -s $instructions -t $template -c $config -I $conf_dir -i $NUM_INPUTS -n $NUM_PROGS --timeout $TIMEOUT -w \"$work_dir\" 2>&1 | tee \"$log\"\n    fi\n    exit_code=$?\n    if [ $verbose -eq 1 ]; then set +x; fi\n    check_results $log $exit_code 1\n    if [ $? -ne 0 ]; then return 0; fi\n    set -e\n\n    # move the violation into a dedicated dir\n    vdir=\"$work_dir/violation*\"\n    if [ -d \"$vdir\" ]; then\n        echo \"ERROR: Could not find a violation directory: '$vdir'\"\n        exit 1\n    fi\n\n    # reproduce the violations\n    printf \"${green}+ Reproduce ...  ${reset}\\n\"\n    repro_conf=\"$vdir/reproduce.yaml\"\n    disable_all_logging $repro_conf\n    set +e\n    if [ $verbose -eq 1 ]; then set -x; fi\n    python ${revizor} reproduce -s $instructions -c $repro_conf -I $conf_dir -t $vdir/program.asm -i $(ls $vdir/input*.bin) 2>&1 | tee \"$log\"\n    exit_code=$?\n    if [ $verbose -eq 1 ]; then set +x; fi\n    check_results $log $exit_code 1\n    if [ $? -ne 0 ]; then return 0; fi\n    set -e\n\n    # verify that the violation has the expected root cause\n    printf \"${green}+ Verify ...  ${reset}\\n\"\n    verif_conf=\"$work_dir/verif.yaml\"\n    set +e\n    make_verification_conf $name $repro_conf $verif_conf\n    if [ $? -ne 0 ]; then return 0; fi\n    if [ $verbose -eq 1 ]; then set -x; fi\n    python ${revizor} reproduce -s $instructions -c $verif_conf -I $conf_dir -t $vdir/program.asm -i $(ls $vdir/input*.bin) 2>&1 | tee \"$log\"\n    exit_code=$?\n    if [ $verbose -eq 1 ]; then set +x; fi\n    check_results $log $exit_code 0\n    if [ $? -ne 0 ]; then return 0; fi\n    set -e\n}\n\n# ==================================================================================================\n# Test configuration\nNUM_INPUTS=25\nNUM_PROGS=1000000000 # some large number that is never reached before the timeout\nTIMEOUT=$((10 * 60 * 60))         # seconds\n\nread_args \"$@\"\n\n# Measurements\nprintf \"Starting at $(date '+%H:%M:%S on %d.%m.%Y')\\n\"\n\nrun \"detect-v1\"\nrun \"detect-v1-store\"\nrun \"detect-v4\"\n\nif grep -q 'E-2288G' /proc/cpuinfo; then\n    run \"detect-mds\"\n    run \"detect-foreshadow\"\n    run \"detect-zdi\"\nfi\n\nif grep -q 'AMD' /proc/cpuinfo; then\n    run \"tsa-sq\" 1\n    run \"tsa-l1d\" 1\nfi\n\n# these two are slow to detect, thus run them last\nrun \"detect-sco\"\nrun \"detect-v1-var\"\n"
  },
  {
    "path": "tests/quick-test.sh",
    "content": "#!/usr/bin/env bash\n\nfunction assert_violation() {\n    local cmd=\"$@\"\n    log=$(mktemp)\n\n    bash -c \"$cmd\" > $log\n    status=$?\n    output=$(cat $log)\n    if [[ \"$status\" -eq 1 && \"$output\" = *\"=== Violations detected ===\"* ]]; then\n        echo \"Detection: OK\"\n    else\n        echo \"Detection: FAIL\"\n        echo \"Command: $cmd\"\n        echo \"Exit code: $status\"\n        echo \"Output: '$output'\"\n        exit 1\n    fi\n}\n\nfunction assert_no_violation() {\n    local cmd=\"$@\"\n\n    log=$(mktemp)\n\n    bash -c \"$cmd\" > $log\n    status=$?\n    output=$(cat $log)\n    if [[ \"$status\" -eq 0 && \"$output\" != *\"=== Violations detected ===\"* ]]; then\n        echo \"Filtering: OK\"\n    else\n        echo \"Filtering: FAIL\"\n        echo \"Command: $cmd\"\n        echo \"Exit code: $status\"\n        echo \"Output: '$output'\"\n        exit 1\n    fi\n}\n\nSCRIPT_DIR=$(dirname $(realpath $0))\n\ncmd=\"./revizor.py fuzz -s $SCRIPT_DIR/../base.json --save-violations f -I $SCRIPT_DIR/x86_tests/configs -t $SCRIPT_DIR/x86_tests/asm/spectre_v1.asm -c $SCRIPT_DIR/x86_tests/configs/ct-seq.yaml -i 20\"\nassert_violation \"$cmd\"\n\ncmd=\"./revizor.py fuzz -s $SCRIPT_DIR/../base.json --save-violations f -I $SCRIPT_DIR/x86_tests/configs -t $SCRIPT_DIR/x86_tests/asm/spectre_v1.asm -c $SCRIPT_DIR/x86_tests/configs/ct-cond.yaml -i 20\"\nassert_no_violation \"$cmd\"\n"
  },
  {
    "path": "tests/runtests.sh",
    "content": "#!/usr/bin/env bash\n\nAVAILABLE_STAGES=(\"type_check\" \"code_style_check\" \"core_unit_tests\" \"package_install_test\"\n    \"km_tests\" \"arch_unit_tests\" \"acceptance_tests\")\n\nfunction parse_args() {\n    POSITIONAL_ARGS=()\n    IGNORE_ERRORS=false\n    STRICT=false\n    SKIP_KM_TESTS=false\n    STAGE=\"\"\n\n    while [[ $# -gt 0 ]]; do\n        case $1 in\n        --strict)\n            STRICT=true\n            shift\n            ;;\n        --ignore-errors)\n            IGNORE_ERRORS=true\n            shift\n            ;;\n        --skip-km-tests)\n            SKIP_KM_TESTS=true\n            shift\n            ;;\n        --stage)\n            if [ -z \"$2\" ]; then\n                echo \"Error: --stage requires an argument\"\n                exit 1\n            fi\n            STAGE=\"$2\"\n            shift 2\n            ;;\n        -* | --*)\n            echo \"Unknown option $1\"\n            exit 1\n            ;;\n        esac\n    done\n\n    if [[ -n \"$STAGE\" && ! \" ${AVAILABLE_STAGES[@]} \" =~ \" ${STAGE} \" ]]; then\n        echo \"Invalid stage: $STAGE\"\n        echo \"Available stages: ${AVAILABLE_STAGES[*]}\"\n        exit 1\n    fi\n\n}\n\n# ==================================================================================================\n# Testing Stages\n# ==================================================================================================\nfunction type_check() {\n    local enable_strict=$1\n\n    echo \"\"\n    echo \"===== MyPy =====\"\n    cd $SCRIPT_DIR/.. || exit\n    MYPYPATH=rvzr/ python3 -m mypy --strict $ALL_PY --no-warn-unused-ignores --untyped-calls-exclude=elftools\n    cd - >/dev/null || exit\n\n    if [ \"$enable_strict\" = true ]; then\n        echo \"\"\n        cd $SCRIPT_DIR/.. || exit\n        echo \"===== STRICT CHECK: MyPy (Unit Tests) =====\"\n        MYPYPATH=rvzr/ python3 -m mypy --strict tests/unit_*.py --no-warn-unused-ignores --untyped-calls-exclude=elftools\n        MYPYPATH=rvzr/ python3 -m mypy --strict tests/x86_tests/unit_*.py --no-warn-unused-ignores --untyped-calls-exclude=elftools\n        MYPYPATH=rvzr/ python3 -m mypy --strict tests/arm64/unit_*.py --no-warn-unused-ignores --untyped-calls-exclude=elftools\n        cd - >/dev/null || exit\n    fi\n\n}\n\nfunction code_style_check() {\n    local enable_strict=$1\n\n    echo \"\"\n    echo \"===== Code Style Checking with flake8 =====\"\n    cd $SCRIPT_DIR/.. || exit\n    python3 -m flake8 --max-line-length 100 --ignore E402,W503 . --count --show-source --statistics\n    cd - >/dev/null || exit\n\n    if [ \"$enable_strict\" = true ]; then\n        echo \"\"\n        cd $SCRIPT_DIR/.. || exit\n        echo \"===== STRICT CHECK: PyLint =====\"\n        python3 -m pylint --rcfile=.pylintrc $ALL_PY\n        cd - >/dev/null || exit\n    fi\n\n    echo \"\"\n    echo \"===== [DR] Code Style & Linting with clang-tidy =====\"\n    cd $SCRIPT_DIR/../rvzr/model_dynamorio || exit\n\n    if [ -d \"adapter/build\" ] || [ -d \"backend/build\" ]; then\n        # this test requires that libstd++-*-dev is installed on the system;\n        versions=($(dpkg -l | grep libstdc++- | grep dev | awk '{print $2}' | sed 's/libstdc++-//;s/-dev//'))\n        if [ ${#versions[@]} -eq 0 ]; then\n            echo \"[DR] No libstdc++-*-dev package found; skipping clang-tidy check\"\n            cd - >/dev/null || exit\n            return\n        fi\n\n        if [ -d \"adapter/build\" ]; then\n            find . -name \"*.c\" -or -name \"*.h\" | grep -v \"CMakeFiles\" | xargs clang-tidy --quiet -p adapter/build/ --config-file=adapter/.clang-tidy\n        fi\n        if [ -d \"backend/build\" ]; then\n            find backend -name \"*.cpp\" -or -name \"*.hpp\" | grep -v \"CMakeFiles\" | xargs clang-tidy --quiet --use-color -p backend/build --config-file=backend/.clang-tidy\n        fi\n    else\n        echo \"[DR] No build directory for DR backend found; skipping clang-tidy check\"\n    fi\n\n    cd - >/dev/null || exit\n}\n\nfunction core_unit_tests() {\n    echo \"\"\n    echo \"===== Core Unit Tests =====\"\n    cd $SCRIPT_DIR/.. || exit\n    python3 -m unittest tests.unit_fuzzer -v\n    echo \"-------------\"\n    python3 -m unittest tests.unit_analyser -v\n    echo \"-------------\"\n    python3 -m unittest tests.unit_docs -v\n    echo \"-------------\"\n    python3 -m unittest tests.unit_isa_loader\n    echo \"-------------\"\n    python3 -m unittest tests.unit_stats\n    echo \"-------------\"\n    python3 -m unittest tests.unit_tc_components\n    echo \"-------------\"\n    python3 -m unittest tests.unit_traces\n    cd - >/dev/null || exit\n}\n\nfunction package_install_test() {\n    echo \"\"\n    echo \"===== Package installation =====\"\n\n    # skip if no internet connection\n    if ! ping -c 1 8.8.8.8 &>/dev/null; then\n        echo \"No internet connection, skipping package installation test\"\n        return\n    fi\n\n    cd $SCRIPT_DIR/.. || exit\n    python3 -m pip uninstall revizor-fuzzer -y\n    python3 -m build\n    python3 -m pip install dist/*.whl\n    cd - >/dev/null || exit\n    cd $SCRIPT_DIR/ || exit\n    set +e\n    out=$(python3 -c \"import rvzr; rvzr.cli.main()\" 2>&1)\n    set -e\n    if [[ \"$out\" != *\"usage: \"* ]]; then\n        echo \"> ERROR: Package installation test failed\"\n        exit 1\n    else\n        echo \"> Package installation test passed\"\n    fi\n    cd - >/dev/null || exit\n}\n\nfunction km_tests() {\n    if [ \"$SKIP_KM_TESTS\" != true ]; then\n        echo \"\"\n        echo \"===== Executor kernel module =====\"\n        cd $SCRIPT_DIR || exit\n        ./kernel_module.bats\n        cd - >/dev/null || exit\n    fi\n}\n\nfunction arch_unit_tests() {\n    # Note: we intentionally do not use the 'discover' option because it causes cross-contamination\n    # of config options between unit tests\n\n    if [ \"$ARCH\" == \"x86_64\" ]; then\n        echo \"\"\n        echo \"===== x86 unit tests =====\"\n        cd $SCRIPT_DIR/.. || exit\n        python3 -m unittest tests.x86_tests.unit_isa_loader -v\n        echo \"-------------\"\n        python3 -m unittest tests.x86_tests.unit_generators -v\n        echo \"-------------\"\n        python3 -m unittest tests.x86_tests.unit_model -v\n        echo \"-------------\"\n        python3 -m unittest tests.x86_tests.unit_taint_tracker -v\n        echo \"-------------\"\n        python3 -m unittest tests.x86_tests.unit_dr_decoder -v\n        echo \"-------------\"\n        cd - >/dev/null || exit\n        # exit\n    else\n        echo \"\"\n        echo \"===== arm64 unit tests =====\"\n        cd $SCRIPT_DIR/.. || exit\n        cd $SCRIPT_DIR/.. || exit\n        python3 -m unittest tests.arm64.unit_isa_loader -v\n        echo \"-------------\"\n        python3 -m unittest tests.arm64.unit_generators -v\n        echo \"-------------\"\n        # python3 -m unittest tests.arm64.unit_model -v\n        # echo \"-------------\"\n        # python3 -m unittest tests.arm64.unit_taint_tracker -v\n        # echo \"-------------\"\n        cd - >/dev/null || exit\n        # exit\n    fi\n}\n\nfunction acceptance_tests() {\n    if [ \"$SKIP_KM_TESTS\" != true ]; then\n        echo \"\"\n        echo \"===== Acceptance tests =====\"\n        cd $SCRIPT_DIR || exit\n        ./acceptance.bats\n        cd - >/dev/null || exit\n    fi\n}\n\n# ==================================================================================================\n# Runners\n# ==================================================================================================\nfunction run_one_stage() {\n    local stage=$1\n\n    case $stage in\n    type_check)\n        type_check $STRICT\n        ;;\n    code_style_check)\n        code_style_check $STRICT\n        ;;\n    core_unit_tests)\n        core_unit_tests\n        ;;\n    package_install_test)\n        package_install_test\n        ;;\n    km_tests)\n        km_tests\n        ;;\n    arch_unit_tests)\n        arch_unit_tests\n        ;;\n    acceptance_tests)\n        acceptance_tests\n        ;;\n    *)\n        echo \"Unknown stage: $stage\"\n        exit 1\n        ;;\n    esac\n}\n\nfunction main() {\n    parse_args $@\n\n    if [ \"$IGNORE_ERRORS\" != \"true\" ]; then\n        set -e\n    fi\n\n    if [ \"$STRICT\" = true ]; then\n        echo \"Including optional tests\"\n    fi\n\n    VENDOR=\"$(lscpu | grep Vendor | awk '{print $3}')\"\n    ARCH=\"$(lscpu | grep Architecture | awk '{print $2}')\"\n\n    SCRIPT_DIR=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &>/dev/null && pwd)\"\n    ALL_PY=$(find rvzr/ -name \"*.py\" | grep -v \"config\" | grep -v \"fuzzer\")\n\n    # if STAGE is set, run only that stage\n    if [[ -n \"$STAGE\" ]]; then\n        run_one_stage \"$STAGE\"\n        exit 0\n    fi\n\n    type_check $STRICT\n    code_style_check $STRICT\n    package_install_test\n    core_unit_tests\n    km_tests\n    arch_unit_tests\n    acceptance_tests\n}\n\nmain $@\n"
  },
  {
    "path": "tests/scripts/create_rcbf_file.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nFile: create_rcbf_file.py\n      Creates an RCBF test case file from an assembly source file.\n      On details of the RCBF file format, see docs/devel/binary-formats.md\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n\nimport sys\nimport os\nimport subprocess\n\n\ndef write_actor_metadata(f, entry):\n    f.write((entry[0]).to_bytes(8, byteorder='little'))  # id\n    f.write((entry[1]).to_bytes(8, byteorder='little'))  # mode\n    f.write((entry[2]).to_bytes(8, byteorder='little'))  # pl\n    f.write((entry[3]).to_bytes(8, byteorder='little'))  # data permissions\n    f.write((entry[4]).to_bytes(8, byteorder='little'))  # data ept properties\n    f.write((entry[5]).to_bytes(8, byteorder='little'))  # code permissions\n\n\ndef write_st_entry(f, entry):\n    f.write((entry[0]).to_bytes(8, byteorder='little'))  # owner\n    f.write((entry[1]).to_bytes(8, byteorder='little'))  # offset\n    f.write((entry[2]).to_bytes(8, byteorder='little'))  # id\n    f.write((entry[3]).to_bytes(8, byteorder='little'))  # args\n\n\ndef write_metadata_entry(f, entry):\n    f.write((entry[0]).to_bytes(8, byteorder='little'))  # owner\n    f.write((entry[1]).to_bytes(8, byteorder='little'))  # size\n    f.write((entry[2]).to_bytes(8, byteorder='little'))  # reserved\n\n\ndef write_nop(f, arch: str):\n    if arch == 'x86':\n        f.write(b'\\x0f\\x1f\\x84\\x00\\xff\\x00\\x00\\x00')\n    elif arch == 'arm64':\n        f.write(b'\\x1f\\x20\\x03\\xd5\\x1f\\x20\\x03\\xd5\\x1f\\x20\\x03\\xd5')\n\n\ndef get_macro_placeholder_size(arch: str):\n    if arch == 'x86':\n        return 8\n    return 12\n\n\ndef main(asm_file: str, obj_file: str, arch: str):\n    n_actors = 1\n    n_symbols = 3\n\n    # compile the assembly file\n    tmpbin = asm_file + '.o'\n    subprocess.run(['as', asm_file, '-o', tmpbin])\n    subprocess.run(['strip', '--remove-section=.note.gnu.property', tmpbin])\n    subprocess.run(['objcopy', tmpbin, '-O', 'binary', tmpbin])\n    main_size = os.path.getsize(tmpbin)\n\n    # create the test case file\n    with open(obj_file, 'wb') as f:\n        # write the test case header\n        f.write((n_actors).to_bytes(8, byteorder='little'))\n        f.write((n_symbols).to_bytes(8, byteorder='little'))\n\n        # write actor metadata\n        write_actor_metadata(f, (0, 0, 0, 0x8000000000000063, 0, 0))\n\n        # write the symbol table\n        # - symbol 1: main\n        write_st_entry(f, (0, 0, 0, 0))\n        # - symbol 2: MACRO_MEASUREMENT_START\n        write_st_entry(f, (0, 0, 1, 0))\n        main_size += get_macro_placeholder_size(arch)\n        # - symbol 3: MACRO_MEASUREMENT_END\n        write_st_entry(f, (0, main_size, 2, 0))\n        main_size += get_macro_placeholder_size(arch)\n\n        # write the section metadata\n        write_metadata_entry(f, (0, main_size, 0))\n\n        # write the code\n        write_nop(f, arch)  # nop - MACRO_MEASUREMENT_START\n        with open(tmpbin, 'rb') as bin_file:\n            code = bin_file.read()\n            f.write(code)\n        write_nop(f, arch)\n\n\nif __name__ == '__main__':\n    if len(sys.argv) != 4:\n        print(\"Usage: %s <asm_file> <dest_file> <x86|arm64>\" % sys.argv[0])\n        sys.exit(1)\n\n    if sys.argv[3] not in ['x86', 'arm64']:\n        print(\"Invalid architecture: %s\" % sys.argv[3])\n        sys.exit(1)\n\n    sys.exit(main(sys.argv[1], sys.argv[2], sys.argv[3]))\n"
  },
  {
    "path": "tests/scripts/create_rdbf_file.py",
    "content": "#!/usr/bin/env python3\n\"\"\"\nFile: create_rdbf_file.py\n      Creates an RDBF test case file from an assembly source file.\n      On details of the RDBF file format, see docs/devel/binary-formats.md\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nimport sys\n\n\ndef main(dest_file: str, n_inputs: int, n_actors: int):\n    # Open the destination file for writing\n    with open(dest_file, 'wb') as f:\n        # Write the header\n        f.write(n_actors.to_bytes(8, byteorder='little'))  # Number of actors\n        f.write(n_inputs.to_bytes(8, byteorder='little'))  # Number of inputs\n\n        # Write metadata for each actor and input\n        for _ in range(n_actors * n_inputs):\n            f.write((4096 * 3).to_bytes(8, byteorder='little'))  # Section size\n            f.write((0).to_bytes(8, byteorder='little'))  # Reserved\n\n        # Write data sections for each actor and input\n        for _ in range(n_inputs):\n            for _ in range(n_actors):\n                # Write main_area, faulty_area, and reg_init_region (each 4096 bytes)\n                f.write(b'\\x00' * 4096)  # main_area\n                f.write(b'\\x00' * 4096)  # faulty_area\n                f.write(b'\\x00' * 4096)  # reg_init_region\n\n    return 0\n\n\nif __name__ == '__main__':\n    if len(sys.argv) != 4:\n        print(f\"Usage: {sys.argv[0]} <dest_file> <n_inputs> <n_actors>\")\n        sys.exit(1)\n    try:\n        n_inputs = int(sys.argv[2])\n    except ValueError:\n        print(f\"Invalid number of inputs: {sys.argv[2]}\")\n        sys.exit(1)\n    try:\n        n_actors = int(sys.argv[3])\n    except ValueError:\n        print(f\"Invalid number of actors: {sys.argv[3]}\")\n        sys.exit(1)\n\n    sys.exit(main(sys.argv[1], n_inputs, n_actors))\n"
  },
  {
    "path": "tests/unit_analyser.py",
    "content": "\"\"\"\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n\nimport unittest\nfrom typing import List\n\nimport numpy as np\nimport numpy.typing as npt\n\nfrom rvzr.analyser import MergedBitmapAnalyser, SetAnalyser, ChiSquaredAnalyser\nfrom rvzr.tc_components.test_case_data import InputData\nfrom rvzr.tc_components.test_case_code import TestCaseProgram\nfrom rvzr.traces import CTrace, HTrace, RawHTraceSample, CTraceEntry\nfrom rvzr.config import CONF\n\n\ndef _htrace_from_trace(trace_list: List[int]) -> HTrace:\n    samples: npt.NDArray[np.void] = np.ndarray(len(trace_list), dtype=RawHTraceSample)\n    for i, trace in enumerate(trace_list):\n        samples[i] = (trace, 0, 0, 0, 0, 0)\n    return HTrace(samples)\n\n\ndef _ctrace_from_int(trace: int) -> CTrace:\n    return CTrace([CTraceEntry(\"val\", trace)])\n\n\nclass AnalyserTest(unittest.TestCase):\n\n    def test_merged_bitmap_analyser(self) -> None:\n        analyser = MergedBitmapAnalyser()\n        dummy_input = InputData()\n        dummy_tc = TestCaseProgram(\"\")\n        inputs = [dummy_input] * 4\n\n        htraces_int = [[0b1101, 0b1101], [0b1011, 0b1011], [0b1000, 0b1000], [0b1000, 0b1000]]\n        htraces = [_htrace_from_trace(trace) for trace in htraces_int]\n\n        ctraces_int = [1, 1, 2, 2]\n        ctraces = [_ctrace_from_int(trace) for trace in ctraces_int]\n\n        violations = analyser.filter_violations(ctraces, htraces, dummy_tc, inputs)\n        self.assertEqual(len(violations), 1)\n        self.assertEqual(violations[0].ctrace, ctraces[0])\n\n    def test_set_analyser(self) -> None:\n        analyser = SetAnalyser()\n        dummy_input = InputData()\n        dummy_tc = TestCaseProgram(\"\")\n        inputs = [dummy_input] * 4\n\n        htraces_int = [[1, 2, 2, 1], [1, 3, 3, 1], [1, 1, 1, 1], [1, 1, 1, 1]]\n        htraces = [_htrace_from_trace(trace) for trace in htraces_int]\n\n        ctraces_int = [1, 1, 2, 2]\n        ctraces = [_ctrace_from_int(trace) for trace in ctraces_int]\n\n        violations = analyser.filter_violations(ctraces, htraces, dummy_tc, inputs)\n        self.assertEqual(len(violations), 1)\n        self.assertEqual(violations[0].ctrace, ctraces[0])\n\n    def test_chi2_analyser(self) -> None:\n        analyser = ChiSquaredAnalyser()\n        dummy_input = InputData()\n        dummy_tc = TestCaseProgram(\"\")\n        inputs = [dummy_input] * 4\n\n        h1 = [1] * CONF.executor_sample_sizes[0]\n        h2 = [2] * CONF.executor_sample_sizes[0]\n        h2[0] = 1\n        h2[1] = 1\n        htraces = [\n            _htrace_from_trace(h1),\n            _htrace_from_trace(h2),\n            _htrace_from_trace(h2),\n            _htrace_from_trace(h2)\n        ]\n\n        ctraces_int = [1, 1, 2, 2]\n        ctraces = [_ctrace_from_int(trace) for trace in ctraces_int]\n\n        violations = analyser.filter_violations(ctraces, htraces, dummy_tc, inputs)\n        self.assertEqual(len(violations), 1)\n        self.assertEqual(violations[0].ctrace, ctraces[0])\n"
  },
  {
    "path": "tests/unit_docs.py",
    "content": "\"\"\"\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom typing import Dict, List, Union\n\nimport unittest\nimport inspect\nimport pathlib\nfrom rvzr.config import CONF\n\nFILE_DIR = pathlib.Path(__file__).parent.resolve()\nDOC_DIR = FILE_DIR.parent / \"docs\"\n\nACTOR_SECTION_HEADER = \"## <a name=\\\"actor\\\"></a> Actor Configuration\"\nAVAILABLE_OPTIONS_HEADER = '=== \"Available Options\"'\nDEFAULT_VALUE_PREFIX = \":material-water:\"\nAUTODETECTED_PREFIX = \":octicons-cpu-24:\"\n\n\ndef _is_option_name(line: str) -> bool:\n    # format: #### `option_name`\n    return line.startswith(\"#### `\") and \"`\" in line[6:]\n\n\ndef parse_config_options_from_docs(doc_lines: List[str]) -> Dict[str, List[Union[str, List[str]]]]:\n    \"\"\"\n    Parse configuration options from the documentation.\n\n    :param doc_lines: Lines from the config.md file\n    :return: Dictionary mapping option names to [default_value, [available_options]]\n    \"\"\"\n    doc_options: Dict[str, List[Union[str, List[str]]]] = {}\n    curr_name: str = \"\"\n    in_actor_section: bool = False\n    in_available_options: bool = False\n\n    for line in doc_lines:\n        # Detect actor section\n        if ACTOR_SECTION_HEADER in line:\n            in_actor_section = True\n        elif line.startswith(\"## \"):\n            in_actor_section = False\n\n        # Parse option name from heading\n        if _is_option_name(line):\n            end_idx = line.index(\"`\", 6)\n            option_name = line[6:end_idx]\n\n            # Add actor_ prefix if in actor section\n            if in_actor_section:\n                curr_name = f\"actor_{option_name}\"\n            else:\n                curr_name = option_name\n\n            doc_options[curr_name] = [\"\", []]\n            in_available_options = False\n\n        # Parse default value (e.g., :material-water: `value`)\n        elif curr_name and doc_options[curr_name][0] == \"\":\n            if DEFAULT_VALUE_PREFIX in line and \"`\" in line:\n                # Find backtick-quoted value after :material-water:\n                parts = line.split(\"`\")\n                for j in range(1, len(parts), 2):\n                    if j > 0 and DEFAULT_VALUE_PREFIX in parts[j - 1]:\n                        doc_options[curr_name][0] = parts[j]\n                        break\n            elif AUTODETECTED_PREFIX in line:\n                # Auto-detected default value\n                doc_options[curr_name][0] = \"(auto-detected)\"\n\n        # Detect Available Options section\n        elif AVAILABLE_OPTIONS_HEADER in line:\n            in_available_options = True\n\n        # Parse available options (e.g., `opt1` | `opt2` | `opt3`)\n        elif in_available_options and curr_name and \"`\" in line and \"|\" in line:\n            options_line = line.strip()\n            if options_line.startswith(\"`\"):\n                # Split by | and extract values between backticks\n                parts = options_line.split(\"|\")\n                options = []\n                for part in parts:\n                    part = part.strip()\n                    if part.startswith(\"`\") and part.endswith(\"`\"):\n                        options.append(part.strip(\"`\"))\n                doc_options[curr_name][1] = options\n            in_available_options = False\n\n    return doc_options\n\n\nclass DocumentationTest(unittest.TestCase):\n    \"\"\"\n    A class for testing if the documentation is up to date.\n    \"\"\"\n    longMessage = False\n\n    def test_conf_docs(self) -> None:\n        # Test if the documentation contains all the config options.\n\n        # get the text of the config documentation\n        with open(DOC_DIR / \"ref/config.md\", \"r\") as f:\n            doc_text = f.read()\n\n        # get a list of config options\n        options = [\n            k[0]\n            for k in inspect.getmembers(CONF, lambda x: not inspect.isroutine(x))\n            if not k[0].startswith(\"_\")\n        ]\n\n        # check if each option is in the documentation\n        for option in options:\n            self.assertTrue(option in doc_text, msg=f\"{option} not found in documentation\")\n\n    def test_conf_options_docs(self) -> None:\n        # Test if the documentation contains all possible values for the config options.\n\n        # get the text of the config documentation\n        with open(DOC_DIR / \"ref/config.md\", \"r\") as f:\n            doc_text = f.readlines()\n\n        # build a map of config options to their possible values in the doc\n        doc_options = parse_config_options_from_docs(doc_text)\n\n        # get a list of config options\n        options = [\n            k for k in inspect.getmembers(CONF, lambda x: not inspect.isroutine(x))\n            if not k[0].startswith(\"_\")\n        ]\n        alternatives = CONF._option_values  # pylint: disable=protected-access\n\n        # check if all alternatives and defaults are documented\n        for name, default_ in options:\n            doc_default = doc_options[name][0]\n            assert isinstance(doc_default, str)\n            if not doc_default.startswith(\"(\"):\n                self.assertEqual(\n                    str(default_),\n                    doc_default,\n                    msg=f\"Default for `{name}` is incorrect: {default_} != {doc_default}\")\n\n            if doc_options[name][1]:\n                doc_values = doc_options[name][1]\n                self.assertSetEqual(\n                    set(alternatives[name]),\n                    set(doc_values),\n                    msg=f\"Options for `{name}` are incorrect: {alternatives[name]} != {doc_values}\")\n"
  },
  {
    "path": "tests/unit_fuzzer.py",
    "content": "\"\"\"\nFile: Unit tests for rvzr/fuzzer.py\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# pylint: disable=missing-function-docstring,missing-class-docstring,protected-access\n# pylint: disable=too-many-instance-attributes,too-many-public-methods\n\nfrom __future__ import annotations\n\nimport unittest\nfrom unittest.mock import MagicMock, patch\nfrom typing import List, Any, Iterator\nfrom contextlib import contextmanager\nimport numpy as np\n\nfrom rvzr.fuzzer import Fuzzer, _RoundManager, _RoundState\nfrom rvzr.tc_components.test_case_code import TestCaseProgram\nfrom rvzr.tc_components.test_case_data import InputData\nfrom rvzr.traces import CTrace, HTrace, CTraceEntry, RawHTraceSample, TraceBundle, Violation, \\\n    ArrayOfSamples\nfrom rvzr.config import CONF\nfrom rvzr.logs import update_logging_after_config_change\n\n\ndef _mk_ctrace(value: int) -> CTrace:\n    \"\"\"Helper to create a simple contract trace\"\"\"\n    return CTrace([CTraceEntry(\"val\", value)])\n\n\ndef _mk_htrace(trace_value: int, sample_size: int = 100) -> HTrace:\n    \"\"\"Helper to create a hardware trace with repeated samples\"\"\"\n    samples: ArrayOfSamples = np.ndarray(sample_size, dtype=RawHTraceSample)\n    for i in range(sample_size):\n        samples[i] = (trace_value, 0, 0, 0, 0, 0)\n    return HTrace(samples, \"cache\")\n\n\ndef _mk_violation(n_inputs: int = 2) -> Violation:\n    \"\"\"Helper to create a mock violation\"\"\"\n    inputs = [InputData() for _ in range(n_inputs)]\n    measurements = [\n        TraceBundle(\n            input_id=i,\n            input_=inputs[i],\n            ctrace=_mk_ctrace(1),  # same ctrace\n            htrace=_mk_htrace(0x100 + i)  # different htraces\n        ) for i in range(n_inputs)\n    ]\n    test_case = TestCaseProgram(\"test.asm\")\n    violation = Violation(measurements, inputs, test_case)\n    violation.set_trivial_hw_classes()\n    return violation\n\n\n@contextmanager\ndef _temp_conf_override(**kwargs: Any) -> Iterator[None]:\n    \"\"\"Context manager to temporarily override CONF settings\"\"\"\n    original_values = {}\n    for key, value in kwargs.items():\n        if hasattr(CONF, key):\n            original_values[key] = getattr(CONF, key)\n            setattr(CONF, key, value)\n\n    try:\n        yield\n    finally:\n        for key, value in original_values.items():\n            setattr(CONF, key, value)\n        update_logging_after_config_change()\n\n\nclass _MockSetup:\n    \"\"\"Helper class to setup standard mocks for fuzzing tests\"\"\"\n\n    def __init__(self, inputs: List[InputData]) -> None:\n        self.inputs = inputs\n        self.boosted_inputs = inputs * 2\n        self.ctraces = [_mk_ctrace(1), _mk_ctrace(1), _mk_ctrace(1), _mk_ctrace(1)]\n        self.htraces = [\n            _mk_htrace(0x100), _mk_htrace(0x100), _mk_htrace(0x200), _mk_htrace(0x200)\n        ]\n\n    def configure_mocks(\n        self,\n        data_gen: MagicMock,\n        model: MagicMock,\n        executor: MagicMock,\n        analyser: MagicMock,\n        violations: List[Violation] | None = None\n    ) -> None:\n        \"\"\"Configure standard mock returns for a typical fuzzing round\"\"\"\n        data_gen.generate_boosted.return_value = self.boosted_inputs\n        model.trace_test_case_with_taints.return_value = (self.ctraces[:2], [None, None])\n        model.trace_test_case.return_value = self.ctraces\n        executor.trace_test_case.return_value = self.htraces\n        analyser.filter_violations.return_value = violations if violations is not None else []\n\n\nclass FuzzerRoundTest(unittest.TestCase):\n    \"\"\"\n    Comprehensive tests for the fuzzing_round method and its multi-stage violation detection.\n    This test exercises the main fuzzing loop which has the lowest coverage in fuzzer.py.\n    \"\"\"\n\n    def setUp(self) -> None:\n        \"\"\"Set up mock objects for fuzzer components\"\"\"\n        # Save original config state\n        self.orig_logging = CONF.logging_modes\n        self.orig_sample_sizes = CONF.executor_sample_sizes\n        self.orig_inputs_per_class = CONF.inputs_per_class\n        self.orig_fast_path = CONF.enable_fast_path_model\n        self.orig_priming = CONF.enable_priming\n\n        # Configure for testing\n        CONF.logging_modes = []\n        CONF.executor_sample_sizes = [100, 200, 500]\n        CONF.inputs_per_class = 2\n        CONF.enable_fast_path_model = True\n        CONF.enable_priming = True\n        CONF.model_min_nesting = 1\n        CONF.model_max_nesting = 3\n        update_logging_after_config_change()\n\n        # Create test data\n        self.test_case = TestCaseProgram(\"test.asm\")\n        self.inputs = [InputData(), InputData()]\n\n        # Mock components\n        self.mock_model = MagicMock()\n        self.mock_model.is_speculative = True\n\n        self.mock_executor = MagicMock()\n        self.mock_arch_model = MagicMock()\n        self.mock_arch_executor = MagicMock()\n\n        self.mock_analyser = MagicMock()\n        self.mock_code_gen = MagicMock()\n        self.mock_data_gen = MagicMock()\n        self.mock_elf_parser = MagicMock()\n        self.mock_asm_parser = MagicMock()\n\n        # Create a minimal fuzzer with mocked components\n        # Using __new__ bypasses __init__, so no factory is called\n        self.fuzzer = Fuzzer.__new__(Fuzzer)\n        self.fuzzer.model = self.mock_model\n        self.fuzzer.executor = self.mock_executor\n        self.fuzzer.arch_model = self.mock_arch_model\n        self.fuzzer.arch_executor = self.mock_arch_executor\n        self.fuzzer.analyser = self.mock_analyser\n        self.fuzzer.code_gen = self.mock_code_gen\n        self.fuzzer.data_gen = self.mock_data_gen\n        self.fuzzer.elf_parser = self.mock_elf_parser\n        self.fuzzer.asm_parser = self.mock_asm_parser\n        self.fuzzer.log = MagicMock()\n        self.fuzzer._work_dir = \"/tmp/test\"\n\n    def tearDown(self) -> None:\n        \"\"\"Restore original config state\"\"\"\n        CONF.logging_modes = self.orig_logging\n        CONF.executor_sample_sizes = self.orig_sample_sizes\n        CONF.inputs_per_class = self.orig_inputs_per_class\n        CONF.enable_fast_path_model = self.orig_fast_path\n        CONF.enable_priming = self.orig_priming\n        update_logging_after_config_change()\n\n    def test_fuzzing_round_no_violation_fast_path(self) -> None:\n        # Test fuzzing_round when no violations are found in the fast path\n\n        boosted_inputs = self.inputs * 2  # inputs_per_class=2\n        ctraces = [_mk_ctrace(1), _mk_ctrace(1), _mk_ctrace(2), _mk_ctrace(2)]\n        htraces = [_mk_htrace(0x100), _mk_htrace(0x100), _mk_htrace(0x200), _mk_htrace(0x200)]\n\n        self.mock_data_gen.generate_boosted.return_value = boosted_inputs\n        self.mock_model.trace_test_case_with_taints.return_value = (ctraces[:2], [None, None])\n        self.mock_model.trace_test_case.return_value = ctraces\n        self.mock_executor.trace_test_case.return_value = htraces\n        self.mock_analyser.filter_violations.return_value = []\n\n        result = self.fuzzer.fuzzing_round(self.test_case, self.inputs, [])\n        self.assertIsNone(result)\n\n    def test_fuzzing_round_violation_detected_survives_all_stages(self) -> None:\n        # Test fuzzing_round when a genuine violation survives all false positive filters\n\n        with _temp_conf_override(enable_priming=False):\n            # Setup: return violations through all stages\n            violation = _mk_violation(4)\n            mock_setup = _MockSetup(self.inputs)\n            mock_setup.configure_mocks(\n                self.mock_data_gen, self.mock_model, self.mock_executor,\n                self.mock_analyser, [violation]\n            )\n\n            # Architectural mismatch check should pass (no mismatch) - same register values\n            # get_untyped() returns the first 6 values for comparison\n            arch_htrace_data: ArrayOfSamples = np.ndarray(1, dtype=RawHTraceSample)\n            arch_htrace_data[0] = (1, 2, 3, 4, 5, 6)\n            arch_htraces = [HTrace(arch_htrace_data, \"reg\") for _ in range(4)]\n            arch_ctraces = [\n                CTrace([CTraceEntry(\"val\", v) for v in [1, 2, 3, 4, 5, 6]]) for _ in range(4)\n            ]\n            self.mock_arch_executor.trace_test_case.return_value = arch_htraces\n            self.mock_arch_model.trace_test_case.return_value = arch_ctraces\n\n            # Execute\n            result = self.fuzzer.fuzzing_round(self.test_case, self.inputs, [])\n\n            # Verify: violation should survive all stages\n            self.assertIsNotNone(result)\n            self.assertEqual(result, violation)\n\n    def test_fuzzing_round_fp_filtered_by_nesting(self) -> None:\n        # Test that false positives due to insufficient nesting are filtered out\n        violation = _mk_violation(4)\n        mock_setup = _MockSetup(self.inputs)\n        mock_setup.configure_mocks(\n            self.mock_data_gen, self.mock_model, self.mock_executor, self.mock_analyser\n        )\n\n        # First call (fast path) returns violation, second call (nesting) returns no violation\n        self.mock_analyser.filter_violations.side_effect = [[violation], []]\n\n        # Execute\n        result = self.fuzzer.fuzzing_round(self.test_case, self.inputs, [])\n\n        # Verify: violation should be filtered out after nesting stage\n        self.assertIsNone(result)\n\n    def test_fuzzing_round_fp_filtered_by_taint_mistake(self) -> None:\n        # Test that false positives due to taint tracking errors are filtered out\n        boosted_inputs = self.inputs * 2\n        ctraces_fast = [_mk_ctrace(1), _mk_ctrace(1), _mk_ctrace(1), _mk_ctrace(1)]\n        ctraces_full = [_mk_ctrace(1), _mk_ctrace(2), _mk_ctrace(1), _mk_ctrace(2)]\n        htraces = [_mk_htrace(0x100), _mk_htrace(0x100), _mk_htrace(0x200), _mk_htrace(0x200)]\n\n        violation = _mk_violation(4)\n\n        self.mock_data_gen.generate_boosted.return_value = boosted_inputs\n        self.mock_model.trace_test_case_with_taints.return_value = (ctraces_fast[:2], [None, None])\n\n        # Return different ctraces when called with full tracing (taint_mistake stage)\n        self.mock_model.trace_test_case.side_effect = [ctraces_fast, ctraces_fast, ctraces_full]\n        self.mock_executor.trace_test_case.return_value = htraces\n\n        # Violation appears with fast ctraces, disappears with full ctraces\n        self.mock_analyser.filter_violations.side_effect = [[violation], [violation], []]\n\n        # Execute\n        result = self.fuzzer.fuzzing_round(self.test_case, self.inputs, [])\n\n        # Verify: violation should be filtered out after taint_mistake stage\n        self.assertIsNone(result)\n\n    def test_fuzzing_round_fp_filtered_by_priming(self) -> None:\n        # Test that false positives due to cross-talk between inputs are filtered by priming\"\"\"\n        violation = _mk_violation(4)\n        mock_setup = _MockSetup(self.inputs)\n        mock_setup.configure_mocks(\n            self.mock_data_gen, self.mock_model, self.mock_executor,\n            self.mock_analyser, [violation]\n        )\n\n        # Priming check: traces are NOT equivalent (false positive)\n        self.mock_analyser.htraces_are_equivalent.return_value = False\n\n        # Execute\n        result = self.fuzzer.fuzzing_round(self.test_case, self.inputs, [])\n\n        # Verify: violation should be filtered out by priming\n        self.assertIsNone(result)\n\n    def test_fuzzing_round_noise_stage_extends_htraces(self) -> None:\n        # Test that noise stage extends htraces with larger sample sizes\"\"\"\n        # Configure for single sample size to skip noise stage initially\n        orig_sample_sizes = CONF.executor_sample_sizes\n        CONF.executor_sample_sizes = [100]  # Single sample size - skips noise stage\n\n        try:\n            boosted_inputs = self.inputs * 2\n            ctraces = [_mk_ctrace(1), _mk_ctrace(1), _mk_ctrace(2), _mk_ctrace(2)]\n            htraces = [\n                _mk_htrace(0x100, 100),\n                _mk_htrace(0x100, 100),\n                _mk_htrace(0x200, 100),\n                _mk_htrace(0x200, 100)\n            ]\n\n            self.mock_data_gen.generate_boosted.return_value = boosted_inputs\n            self.mock_model.trace_test_case_with_taints.return_value = (ctraces[:2], [None, None])\n            self.mock_model.trace_test_case.return_value = ctraces\n            self.mock_executor.trace_test_case.return_value = htraces\n            self.mock_analyser.filter_violations.return_value = []\n\n            # Execute\n            result = self.fuzzer.fuzzing_round(self.test_case, self.inputs, [])\n\n            # Verify: no violation, noise stage was skipped\n            self.assertIsNone(result)\n            # Only fast path should execute\n            self.assertEqual(self.mock_executor.trace_test_case.call_count, 1)\n        finally:\n            CONF.executor_sample_sizes = orig_sample_sizes\n\n    def test_fuzzing_round_architectural_mismatch_detected(self) -> None:\n        # Test detection of architectural mismatches between model and executor\"\"\"\n        # Temporarily disable priming to simplify test, and set work_dir to None\n        # to prevent file writing on architectural mismatch\n        self.fuzzer._work_dir = \"\"  # Disable file writing\n\n        with _temp_conf_override(enable_priming=False):\n            violation = _mk_violation(4)\n            mock_setup = _MockSetup(self.inputs)\n            mock_setup.configure_mocks(\n                self.mock_data_gen, self.mock_model, self.mock_executor,\n                self.mock_analyser, [violation]\n            )\n\n            # Architectural mismatch: model and executor return different register values\n            # Hardware returns specific register values\n            arch_htrace_data: ArrayOfSamples = np.ndarray(1, dtype=RawHTraceSample)\n            arch_htrace_data[0] = (1, 2, 3, 4, 5, 6)\n            arch_htraces = [HTrace(arch_htrace_data, \"reg\") for _ in range(4)]\n\n            # Model returns different values (mismatch!) - get_untyped()[:6] is compared\n            arch_ctraces = [\n                CTrace([CTraceEntry(\"val\", v) for v in [999, 2, 3, 4, 5, 6]]) for _ in range(4)\n            ]\n\n            self.mock_arch_executor.trace_test_case.return_value = arch_htraces\n            self.mock_arch_model.trace_test_case.return_value = arch_ctraces\n\n            # Execute - suppress warning by mocking the report method\n            with patch.object(self.fuzzer, '_report_bug_arch'):\n                result = self.fuzzer.fuzzing_round(self.test_case, self.inputs, [])\n\n            # Verify: architectural mismatch should filter the violation\n            self.assertIsNone(result)\n\n        self.fuzzer._work_dir = \"/tmp/test\"\n\n    def test_fuzzing_round_executor_error_handled(self) -> None:\n        # Test that IOErrors from executor are handled gracefully\"\"\"\n        mock_setup = _MockSetup(self.inputs)\n        self.mock_data_gen.generate_boosted.return_value = mock_setup.boosted_inputs\n        self.mock_model.trace_test_case_with_taints.return_value = (\n            mock_setup.ctraces[:2], [None, None]\n        )\n        self.mock_model.trace_test_case.return_value = mock_setup.ctraces\n\n        # Executor raises IOError\n        self.mock_executor.trace_test_case.side_effect = IOError(\"Trace collection failed\")\n\n        # Execute\n        result = self.fuzzer.fuzzing_round(self.test_case, self.inputs, [])\n\n        # Verify: should handle error and return None\n        self.assertIsNone(result)\n\n    def test_fuzzing_round_with_ignore_list(self) -> None:\n        # Test that starting ignore list is properly set in executor\"\"\"\n        mock_setup = _MockSetup(self.inputs)\n        mock_setup.configure_mocks(\n            self.mock_data_gen, self.mock_model, self.mock_executor, self.mock_analyser\n        )\n\n        # Execute with ignore list\n        ignore_list = [0, 1]\n        result = self.fuzzer.fuzzing_round(self.test_case, self.inputs, ignore_list)\n\n        # Verify\n        self.assertIsNone(result)\n\n    def test_fuzzing_round_empty_inputs(self) -> None:\n        # Test fuzzing_round with empty input list\"\"\"\n        empty_inputs: List[InputData] = []\n\n        # With empty inputs, no violations should be checked\n        self.mock_data_gen.generate_boosted.return_value = []\n        self.mock_model.trace_test_case_with_taints.return_value = ([], [])\n        self.mock_model.trace_test_case.return_value = []\n        self.mock_executor.trace_test_case.return_value = []\n\n        # Execute\n        result = self.fuzzer.fuzzing_round(self.test_case, empty_inputs, [])\n\n        # Verify: should complete without errors\n        self.assertIsNone(result)\n        # filter_violations should not be called with empty inputs\n        self.mock_analyser.filter_violations.assert_not_called()\n\n    def test_round_state_configuration(self) -> None:\n        # Test _RoundState initialization with different model types\"\"\"\n        # Test with speculative model\n        state_spec = _RoundState(is_speculative=True)\n        self.assertEqual(state_spec.model_nesting, CONF.model_min_nesting)\n        self.assertEqual(state_spec.max_nesting, CONF.model_max_nesting)\n        self.assertTrue(state_spec.enable_priming)\n        self.assertTrue(state_spec.enable_fast_contract_tracing)\n\n        # Test with non-speculative model\n        state_non_spec = _RoundState(is_speculative=False)\n        self.assertEqual(state_non_spec.model_nesting, 1)\n        self.assertEqual(state_non_spec.max_nesting, 1)\n\n    def test_round_manager_stage_execution_order(self) -> None:\n        # Test that round manager executes stages in the correct order\"\"\"\n        mock_setup = _MockSetup(self.inputs)\n        mock_setup.configure_mocks(\n            self.mock_data_gen, self.mock_model, self.mock_executor, self.mock_analyser\n        )\n\n        # Create round manager\n        round_mgr = _RoundManager(self.fuzzer, self.test_case, self.inputs)\n\n        # Execute stages\n        round_mgr.execute_stage(\"fast\")\n        self.assertFalse(round_mgr.conf.is_initial)\n        self.assertFalse(round_mgr.conf.record_stats)\n\n        round_mgr.execute_stage(\"nesting\")\n        self.assertTrue(round_mgr.conf.reuse_boosts)\n        self.assertTrue(round_mgr.conf.update_ignore_list)\n\n        round_mgr.execute_stage(\"taint_mistake\")\n        self.assertTrue(round_mgr.conf.reuse_ctraces)\n\n        round_mgr.finalize()\n\n\nclass FuzzerStartTest(unittest.TestCase):\n    \"\"\"Tests for the main fuzzing loop start() method\"\"\"\n\n    def setUp(self) -> None:\n        \"\"\"Set up minimal mock fuzzer for start() tests\"\"\"\n        self.orig_logging = CONF.logging_modes\n        CONF.logging_modes = []\n        update_logging_after_config_change()\n\n        # Create minimal fuzzer using __new__ to bypass __init__ and factory\n        self.fuzzer = Fuzzer.__new__(Fuzzer)\n        self.fuzzer.log = MagicMock()\n        self.fuzzer.code_gen = MagicMock()\n        self.fuzzer.data_gen = MagicMock()\n        self.fuzzer.model = MagicMock()\n        self.fuzzer.executor = MagicMock()\n        self.fuzzer.analyser = MagicMock()\n        self.fuzzer._work_dir = \"/tmp/test\"\n        self.fuzzer._input_paths = []\n        self.fuzzer._existing_test_case = \"\"\n\n        # Mock generation\n        self.test_case = TestCaseProgram(\"test.asm\")\n        self.fuzzer.code_gen.create_test_case.return_value = self.test_case\n\n        # Mock data generation\n        self.inputs = [InputData(), InputData()]\n        self.fuzzer.data_gen.generate.return_value = self.inputs\n\n    def tearDown(self) -> None:\n        CONF.logging_modes = self.orig_logging\n        update_logging_after_config_change()\n\n    def test_start_no_violations_found(self) -> None:\n        # Test start() when no violations are found\"\"\"\n        # Mock fuzzing_round to return no violations\n        with patch.object(self.fuzzer, 'fuzzing_round', return_value=None):\n            result = self.fuzzer.start(\n                num_test_cases=5,\n                num_inputs=2,\n                timeout=0,\n                nonstop=False,\n                save_violations=False,\n                type_=\"random\")\n\n        # Verify\n        self.assertFalse(result)\n\n    def test_start_violation_found_stop(self) -> None:\n        # Test start() stops after finding first violation when nonstop=False\"\"\"\n        violation = _mk_violation()\n\n        # Mock fuzzing_round to return violation on second iteration\n        with patch.object(self.fuzzer, 'fuzzing_round', side_effect=[None, violation]):\n            result = self.fuzzer.start(\n                num_test_cases=5,\n                num_inputs=2,\n                timeout=0,\n                nonstop=False,\n                save_violations=False,\n                type_=\"random\")\n\n        # Verify: should stop after violation\n        self.assertTrue(result)\n\n    def test_start_violation_found_nonstop(self) -> None:\n        # Test start() continues after finding violation when nonstop=True\"\"\"\n        violation = _mk_violation()\n\n        # Mock fuzzing_round to return violations multiple times\n        with patch.object(\n                self.fuzzer, 'fuzzing_round', side_effect=[violation, None, violation, None]):\n            result = self.fuzzer.start(\n                num_test_cases=4,\n                num_inputs=2,\n                timeout=0,\n                nonstop=True,\n                save_violations=False,\n                type_=\"random\")\n\n        # Verify: should continue through all test cases\n        self.assertTrue(result)\n\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "tests/unit_isa_loader.py",
    "content": "\"\"\"\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nimport unittest\n\nimport os\nimport tempfile\n\nfrom rvzr.isa_spec import InstructionSet\nfrom rvzr.instruction_spec import OT, InstructionSpec\n\nbasic = \"\"\"\n[\n{\"name\": \"test\", \"category\": \"CATEGORY\", \"is_control_flow\": true,\n  \"operands\": [\n    {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n    {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16}\n  ],\n  \"implicit_operands\": [\n    {\"type_\": \"FLAGS\", \"values\": [\"w\", \"r\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"],\n     \"src\": false, \"dest\": false, \"width\": 0}\n  ]\n},\n{\"name\": \"test2\", \"category\": \"CATEGORY\", \"is_control_flow\": false,\n  \"operands\": [\n    {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n  ],\n  \"implicit_operands\": []\n}\n]\n\"\"\"\n\nduplicate = \"\"\"\n[\n{\"name\": \"test\", \"category\": \"CATEGORY\", \"is_control_flow\": false,\n  \"operands\": [\n    {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n  ],\n  \"implicit_operands\": []\n},\n{\"name\": \"test\", \"category\": \"CATEGORY\", \"is_control_flow\": false,\n  \"operands\": [\n    {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n  ],\n  \"implicit_operands\": []\n}\n]\n\"\"\"\n\n\nclass InstructionSetParserTest(unittest.TestCase):\n\n    def test_parsing(self) -> None:\n        spec_file = tempfile.NamedTemporaryFile(\"w\", delete=False)\n        with open(spec_file.name, \"w\") as f:\n            f.write(basic)\n\n        instruction_set = InstructionSet(spec_file.name)\n        spec_file.close()\n        os.unlink(spec_file.name)\n\n        spec: InstructionSpec = instruction_set.instructions[0]\n        self.assertEqual(spec.name, \"test\")\n        self.assertEqual(spec.category, \"CATEGORY\")\n        self.assertEqual(spec.has_mem_operand, True)\n        self.assertEqual(spec.has_write, True)\n        self.assertEqual(spec.is_control_flow, True)\n\n        self.assertEqual(len(spec.operands), 2)\n        op1 = spec.operands[0]\n        self.assertEqual(op1.type, OT.MEM)\n        self.assertEqual(op1.width, 16)\n        self.assertEqual(op1.src, True)\n        self.assertEqual(op1.dest, True)\n\n        op2 = spec.operands[1]\n        self.assertEqual(op2.type, OT.REG)\n        self.assertEqual(op2.values, (\"ax\",))\n        self.assertEqual(op2.src, True)\n        self.assertEqual(op2.dest, False)\n\n        self.assertEqual(len(spec.implicit_operands), 1)\n        flags = spec.implicit_operands[0]\n        self.assertEqual(flags.type, OT.FLAGS)\n        self.assertEqual(flags.values, ('w', 'r', 'undef', 'w', 'w', '', '', '', 'w'))\n\n    def test_dedup_identical(self) -> None:\n        spec_file = tempfile.NamedTemporaryFile(\"w\", delete=False)\n        with open(spec_file.name, \"w\") as f:\n            f.write(duplicate)\n\n        instruction_set = InstructionSet(spec_file.name)\n        spec_file.close()\n        os.unlink(spec_file.name)\n\n        self.assertEqual(len(instruction_set.instructions), 1, \"No deduplication\")\n"
  },
  {
    "path": "tests/unit_stats.py",
    "content": "\"\"\"\nFile: Unit tests for stats.py\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# pylint: disable=missing-function-docstring  # no need in tests\n# pylint: disable=missing-class-docstring  # no need in tests\n\nimport unittest\n\nfrom rvzr.stats import FuzzingStats\n\n\nclass StatsTest(unittest.TestCase):\n\n    def test_borg(self) -> None:\n        stats1 = FuzzingStats()\n        stats1.test_cases = 1\n        stats2 = FuzzingStats()\n        self.assertEqual(stats2.test_cases, 1)\n\n    def test_str(self) -> None:\n        stats = FuzzingStats()\n        stats.test_cases = 1\n        str_ = str(stats)\n        self.assertIn(\"Test Cases: 1\", str_)\n\n        stats.num_inputs = 2\n        str_ = str(stats)\n        self.assertIn(\"Inputs per test case: 2.0\", str_)\n\n        stats.violations = 3\n        str_ = str(stats)\n        self.assertIn(\"Violations: 3\", str_)\n\n    def test_get_brief(self) -> None:\n        stats = FuzzingStats()\n        stats.test_cases = 0\n        brief = stats.get_brief()\n        self.assertEqual(brief, \"\")\n\n        stats.test_cases = 1\n        stats.eff_classes = 2\n        stats.single_entry_classes = 3\n        stats.analysed_test_cases = 4\n        stats.num_inputs = 5\n        stats.executor_reruns = 6\n        stats.spec_filter = 7\n        stats.observ_filter = 8\n        stats.fast_path = 9\n        stats.fp_nesting = 10\n        stats.fp_taint_mistakes = 11\n        stats.fp_early_priming = 12\n        stats.fp_large_sample = 13\n        stats.fp_priming = 14\n        stats.violations = 15\n\n        brief = stats.get_brief()\n        self.assertEqual(brief,\n                         \"Cls:0/1,In:5,R:1,SF:7,OF:8,Fst:9,CN:10,CT:11,P1:12,CS:13,P2:14,V:15\")\n\n        stats.analysed_test_cases = 0\n        brief = stats.get_brief()\n        self.assertEqual(brief,\n                         \"Cls:0/0,In:5,R:1,SF:7,OF:8,Fst:9,CN:10,CT:11,P1:12,CS:13,P2:14,V:15\")\n"
  },
  {
    "path": "tests/unit_tc_components.py",
    "content": "\"\"\"\nFile: Selection of unit tests for the data container classes\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# pylint: disable=missing-function-docstring  # no need in tests\n# pylint: disable=missing-class-docstring  # no need in tests\n\nimport os\nimport random\nimport unittest\nfrom unittest.mock import MagicMock\n\nimport tempfile\nimport numpy as np\n\nfrom rvzr.tc_components.actor import Actor, ActorMode, ActorPL\nfrom rvzr.tc_components.test_case_code import CodeSection, TestCaseProgram, BasicBlock, Function\nfrom rvzr.tc_components.test_case_binary import TestCaseBinary, SymbolTableEntry\nfrom rvzr.tc_components.test_case_data import InputData, _ACTOR_DATA_SIZE\nfrom rvzr.instruction_spec import InstructionSpec, OperandSpec, OT\nfrom rvzr.tc_components.instruction import Instruction, Operand, \\\n    copy_op_with_flow_modification, copy_op_with_value_modification, copy_inst_with_modification, \\\n    RegisterOp, MemoryOp, ImmediateOp, LabelOp, AgenOp, CondOp, \\\n    FlagsOp\nfrom rvzr.config import ActorConf\n\n\ndef _get_dummy_actor_dict() -> ActorConf:\n    actor_dict: ActorConf = {\n        'mode': 'host',\n        'privilege_level': 'kernel',\n        'name': 'test_actor',\n        'data_properties': {\n            'randomized': False,\n            'user': True\n        },\n        'data_ept_properties': {\n            'randomized': False,\n            'user': True,\n        },\n        'observer': False,\n        \"instruction_blocklist\": set(),\n        \"fault_blocklist\": set(),\n    }\n    return actor_dict\n\n\ndef _get_mock_target_desc() -> MagicMock:\n    mock_td = MagicMock()\n    mock_td.pte_bits = {'user': (0, False)}\n    mock_td.vm_pte_bits = {'user': (0, False)}\n    mock_td.page_property_to_pte_bit_name = {\n        \"user\": (\"user\", False),\n    }\n    mock_td.page_property_to_vm_pte_bit_name = {\n        \"user\": (\"user\", False),\n    }\n    return mock_td\n\n\nclass ActorTest(unittest.TestCase):\n\n    def test_dict_constructor(self) -> None:\n        # Mock actor_dict\n        actor_dict = _get_dummy_actor_dict()\n\n        # Create Actor from dict\n        actor = Actor.from_dict(actor_dict, _get_mock_target_desc())\n\n        # Assertions\n        self.assertEqual(actor.mode, ActorMode.HOST)\n        self.assertEqual(actor.privilege_level, ActorPL.KERNEL)\n        self.assertEqual(actor.name, 'test_actor')\n        self.assertFalse(actor.observer)\n\n        # Guest/User Actor\n        actor_dict['mode'] = 'guest'\n        actor_dict['privilege_level'] = 'user'\n        actor = Actor.from_dict(actor_dict, _get_mock_target_desc())\n        self.assertEqual(actor.mode, ActorMode.GUEST)\n        self.assertEqual(actor.privilege_level, ActorPL.USER)\n\n        # Invalid privilege level\n        actor_dict['privilege_level'] = 'invalid_pl'\n        with self.assertRaises(ValueError):\n            _ = Actor.from_dict(actor_dict, _get_mock_target_desc())\n\n        # Invalid mode\n        actor_dict['mode'] = 'invalid_mode'\n        with self.assertRaises(ValueError):\n            _ = Actor.from_dict(actor_dict, _get_mock_target_desc())\n\n    def test_create_main(self) -> None:\n        # Call the create_main method\n        main_actor = Actor.create_main()\n\n        # Assert the properties of the returned Actor object\n        self.assertEqual(main_actor.mode, ActorMode.HOST)\n        self.assertEqual(main_actor.privilege_level, ActorPL.KERNEL)\n        self.assertEqual(main_actor.name, \"main\")\n\n    def test_get_id(self) -> None:\n        # Create an Actor instance\n        actor_dict = _get_dummy_actor_dict()\n        actor = Actor.from_dict(actor_dict, _get_mock_target_desc())\n        section = CodeSection(actor)\n\n        # Call get_id without assigning an ElfSection and assert it raises an AssertionError\n        with self.assertRaises(AssertionError):\n            actor.get_id()\n\n        # Create and assign an elf data\n        section.assign_elf_data(offset=0, size=0, id_=42)\n\n        # Call get_id and assert the returned ID\n        self.assertEqual(actor.get_id(), 42)\n\n    def test_is_main(self) -> None:\n        actor_dict = _get_dummy_actor_dict()\n        actor_dict['name'] = 'main'\n        target_desc = _get_mock_target_desc()\n\n        main_actor = Actor.from_dict(actor_dict, target_desc)\n        self.assertTrue(main_actor.is_main)\n\n        actor_dict['name'] = 'non_main_actor'\n        non_main_actor = Actor.from_dict(actor_dict, target_desc)\n        self.assertFalse(non_main_actor.is_main)\n\n    def test_pte_constructor(self) -> None:\n        actor_dict = _get_dummy_actor_dict()\n        actor_dict['data_properties'] = {'randomized': False, 'user': False}\n        actor_dict['data_ept_properties'] = {'randomized': False, 'user': False}\n\n        # Mock target_desc\n        target_desc = _get_mock_target_desc()\n\n        # Create Actor from dict\n        actor = Actor.from_dict(actor_dict, target_desc)\n        self.assertEqual(actor.data_properties, 0)\n        self.assertEqual(actor.data_ept_properties, 0)\n\n        actor_dict['data_properties'] = {'randomized': True, 'user': True}\n        actor_dict['data_ept_properties'] = {'randomized': True, 'user': True}\n        random.seed(52)\n\n        actor = Actor.from_dict(actor_dict, target_desc)\n        self.assertEqual(actor.data_properties, 1)\n        self.assertEqual(actor.data_ept_properties, 0)\n\n\nclass InstructionSpecTest(unittest.TestCase):\n\n    def test_OT_str(self) -> None:\n        self.assertEqual(str(OT.REG), \"REG\")\n        self.assertEqual(str(OT.MEM), \"MEM\")\n        self.assertEqual(str(OT.IMM), \"IMM\")\n        self.assertEqual(str(OT.LABEL), \"LABEL\")\n        self.assertEqual(str(OT.AGEN), \"AGEN\")\n        self.assertEqual(str(OT.FLAGS), \"FLAGS\")\n        self.assertEqual(str(OT.COND), \"COND\")\n\n    def test_operand_str(self) -> None:\n        # Create an OperandSpec object\n        operand = OperandSpec(values=['rax', 'rbx'], type_=OT.REG, src=True, dest=False)\n\n        # Assert the string representation of the OperandSpec object\n        self.assertEqual(str(operand), \"(rax, rbx)\")\n\n        # Check the string representation of an OperandSpec object with no values\n        operand = OperandSpec(values=[], type_=OT.REG, src=True, dest=False)\n        self.assertEqual(str(operand), \"()\")\n\n    def test_instruction_spec_str(self) -> None:\n        # Create an InstructionSpec object with operands\n        operand = OperandSpec(values=['rax', 'rbx'], type_=OT.REG, src=True, dest=False)\n        instruction = InstructionSpec(name='MOV', category='MOV')\n        instruction.operands.append(operand)\n\n        # Assert the string representation of the InstructionSpec object\n        self.assertEqual(str(instruction), \"MOV (rax, rbx) \")\n\n    def test_instruction_spec_hash(self) -> None:\n        # Create an InstructionSpec object\n        operand = OperandSpec(values=['rax', 'rbx'], type_=OT.REG, src=True, dest=False)\n        instruction = InstructionSpec(name='MOV', category='MOV')\n        instruction.operands.append(operand)\n\n        # Assert the hash of the InstructionSpec object\n        self.assertEqual(hash(instruction), hash(str(instruction)))\n\n\nclass OperandTest(unittest.TestCase):\n\n    def test_operand_from_spec(self) -> None:\n        for type_ in [OT.REG, OT.MEM, OT.IMM, OT.LABEL, OT.AGEN, OT.FLAGS, OT.COND]:\n            values = [\"val\"] if type_ != OT.FLAGS else [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"]\n            val = \"val\" if type_ != OT.FLAGS else \"flags\"\n            src = True if type_ != OT.FLAGS else False  # pylint: disable=simplifiable-if-expression\n\n            # Create an OperandSpec object\n            operand_spec = OperandSpec(values=values, type_=type_, src=src, dest=False, width=8)\n\n            # Create an Operand object from the OperandSpec object\n            operand = Operand.from_fixed_spec(operand_spec)\n\n            # Assert the properties of the Operand object\n            self.assertEqual(operand.value, val)\n            self.assertEqual(operand.src, src)\n            self.assertFalse(operand.dest)\n\n    def test_flag_print(self) -> None:\n        operand = FlagsOp((\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"))\n        self.assertEqual(str(operand), \"FLAGS: CF|PF|AF|ZF|SF|TF|IF|DF|OF\")\n\n    def test_flag_accessors(self) -> None:\n        # (\"CF\", \"PF\", \"AF\", \"ZF\", \"SF\", \"TF\", \"IF\", \"DF\", \"OF\")\n        operand = FlagsOp((\"w\", \"r\", \"r/w\", \"r/cw\", \"undef\", \"\", \"\", \"\", \"\"))\n\n        self.assertEqual(operand.get_flags_by_type(\"read\"), [\"PF\", \"AF\", \"ZF\"])\n        self.assertEqual(operand.get_flags_by_type(\"write\"), [\"CF\", \"AF\", \"ZF\"])\n        self.assertEqual(operand.get_flags_by_type(\"overwrite\"), [\"CF\"])\n        self.assertEqual(operand.get_flags_by_type(\"undef\"), [\"SF\"])\n\n    def test_operand_copy_methods(self) -> None:\n        reg_op = RegisterOp(\"rax\", 64, True, False)\n        mem_op = MemoryOp(\"0x0\", 64, True, False)\n        imm_op = ImmediateOp(\"0x0\", 64)\n        label_op = LabelOp(\"label\")\n        agen_op = AgenOp(\"0x0\", 64)\n        cond_op = CondOp(\"cond\")\n\n        # Test copy_op_with_value_modification\n        for op in [reg_op, mem_op, imm_op, label_op, agen_op, cond_op]:\n            new_op = copy_op_with_value_modification(op, \"new_val\")  # type: ignore\n            self.assertEqual(new_op.value, \"new_val\")\n            self.assertEqual(new_op.src, op.src)\n            self.assertEqual(new_op.dest, op.dest)\n\n        # Test copy_op_with_flow_modification - src\n        for op in [reg_op, mem_op]:\n            new_op = copy_op_with_flow_modification(op, src=False)  # type: ignore\n            self.assertEqual(new_op.value, op.value)\n            self.assertEqual(new_op.width, op.width)  # type: ignore\n            self.assertEqual(new_op.dest, op.dest)\n            self.assertFalse(new_op.src)\n\n        # Test copy_op_with_flow_modification - dest\n        for op in [reg_op, mem_op]:\n            new_op = copy_op_with_flow_modification(op, dest=True)  # type: ignore\n            self.assertEqual(new_op.value, op.value)\n            self.assertEqual(new_op.width, op.width)  # type: ignore\n            self.assertEqual(new_op.src, op.src)\n            self.assertTrue(new_op.dest)\n\n\nclass InstructionTest(unittest.TestCase):\n\n    def test_instruction_from_spec(self) -> None:\n        # Create an InstructionSpec object\n        operand = OperandSpec(values=['rax', 'rbx'], type_=OT.REG, src=True, dest=False)\n        instruction_spec = InstructionSpec(name='MOV', category='MOV')\n        instruction_spec.operands.append(operand)\n\n        # Create an Instruction object from the InstructionSpec object\n        instruction = Instruction.from_spec(instruction_spec)\n\n        # Assert the properties of the Instruction object\n        self.assertEqual(instruction.name, 'MOV')\n        self.assertEqual(instruction.category, 'MOV')\n        self.assertFalse(instruction.is_control_flow)\n        self.assertFalse(instruction.is_instrumentation)\n        self.assertFalse(instruction.is_noremove)\n        self.assertFalse(instruction.is_from_template)\n        self.assertEqual(len(instruction.operands), 0)  # Operands are generated separately!\n        self.assertEqual(len(instruction.implicit_operands), 0)\n\n    def test_instruction_str(self) -> None:\n        # Create an Instruction object\n        operand = RegisterOp(\"rax\", 64, True, False)\n        instruction = Instruction(\"MOV\", \"MOV\")\n        instruction.operands.append(operand)\n\n        # Assert the string representation of the Instruction object\n        self.assertEqual(str(instruction), \"MOV rax\")\n\n    def test_instr_add_op(self) -> None:\n        instruction = Instruction(\"MOV\", \"MOV\")\n        operand = RegisterOp(\"rax\", 64, True, False)\n        implicit_operand = RegisterOp(\"rbx\", 64, True, False)\n\n        # Add explicit operand\n        instruction = instruction.add_op(operand)\n        self.assertEqual(len(instruction.operands), 1)\n        self.assertEqual(instruction.operands[0], operand)\n\n        # Add implicit operand\n        instruction = instruction.add_op(implicit_operand, implicit=True)\n        self.assertEqual(len(instruction.implicit_operands), 1)\n        self.assertEqual(instruction.implicit_operands[0], implicit_operand)\n\n    def test_instr_properties(self) -> None:\n        # Case 1: Instruction with no memory operands\n        instruction = Instruction(\"MOV\", \"MOV\")\n        op_reg = RegisterOp(\"rax\", 64, True, False)\n        instruction.operands.append(op_reg)\n\n        self.assertFalse(instruction.has_mem_operand(include_implicit=True))\n        self.assertFalse(instruction.has_write(include_implicit=True))\n        self.assertFalse(instruction.has_read(include_implicit=True))\n\n        # Case 2: Instruction with an explicit read\n        instruction = Instruction(\"MOV\", \"MOV\")\n        op = MemoryOp(\"0x0\", 64, True, False)\n        instruction.operands.append(op)\n\n        self.assertTrue(instruction.has_mem_operand(include_implicit=False))\n        self.assertTrue(instruction.has_mem_operand(include_implicit=True))\n        self.assertFalse(instruction.has_write(include_implicit=True))\n        self.assertTrue(instruction.has_read(include_implicit=True))\n\n        # Case 3: Instruction with an explicit write\n        instruction = Instruction(\"MOV\", \"MOV\")\n        op = MemoryOp(\"0x0\", 64, False, True)\n        instruction.operands.append(op)\n\n        self.assertTrue(instruction.has_mem_operand(include_implicit=False))\n        self.assertTrue(instruction.has_mem_operand(include_implicit=True))\n        self.assertTrue(instruction.has_write(include_implicit=True))\n        self.assertFalse(instruction.has_read(include_implicit=True))\n\n        # Case 4: Instruction with an implicit read\n        instruction = Instruction(\"MOV\", \"MOV\")\n        op = MemoryOp(\"0x0\", 64, True, False)\n        instruction.implicit_operands.append(op)\n\n        self.assertFalse(instruction.has_mem_operand(include_implicit=False))\n        self.assertTrue(instruction.has_mem_operand(include_implicit=True))\n        self.assertFalse(instruction.has_write(include_implicit=True))\n        self.assertTrue(instruction.has_read(include_implicit=True))\n\n        # Case 5: Instruction with an implicit write\n        instruction = Instruction(\"MOV\", \"MOV\")\n        op = MemoryOp(\"0x0\", 64, False, True)\n        instruction.implicit_operands.append(op)\n\n        self.assertFalse(instruction.has_mem_operand(include_implicit=False))\n        self.assertTrue(instruction.has_mem_operand(include_implicit=True))\n        self.assertTrue(instruction.has_write(include_implicit=True))\n        self.assertFalse(instruction.has_read(include_implicit=True))\n\n    def test_operand_accessors(self) -> None:\n        reg_op = RegisterOp(\"rax\", 64, True, False)\n        mem_op = MemoryOp(\"0x0\", 64, True, True)\n        imm_op = ImmediateOp(\"0x0\", 64)\n        label_op = LabelOp(\"label\")\n        agen_op = AgenOp(\"0x0\", 64)\n        cond_op = CondOp(\"cond\")\n        flags_op = FlagsOp((\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"))\n\n        # Case 1: Explicit operands\n        instruction = Instruction(\"MOV\", \"MOV\")\n        instruction.operands.extend([reg_op, mem_op, imm_op, label_op, agen_op, cond_op, flags_op])\n\n        self.assertEqual(instruction.get_all_operands(),\n                         [reg_op, mem_op, imm_op, label_op, agen_op, cond_op, flags_op])\n        self.assertEqual(instruction.get_src_operands(),\n                         [reg_op, mem_op, imm_op, label_op, agen_op, cond_op])\n        self.assertEqual(instruction.get_dest_operands(), [mem_op])\n        self.assertEqual(instruction.get_mem_operands(), [mem_op])\n        self.assertEqual(instruction.get_flags_operand(), flags_op)\n        self.assertEqual(instruction.get_reg_operands(), [reg_op])\n        self.assertEqual(instruction.get_cond_operand(), cond_op)\n        self.assertEqual(instruction.get_label_operand(), label_op)\n        self.assertEqual(instruction.get_imm_operands(), [imm_op])\n\n        # Case 2: Implicit operands\n        instruction = Instruction(\"MOV\", \"MOV\")\n        instruction.implicit_operands.extend([reg_op, mem_op, imm_op, agen_op, flags_op])\n\n        self.assertEqual(instruction.get_all_operands(),\n                         [reg_op, mem_op, imm_op, agen_op, flags_op])\n        self.assertEqual(instruction.get_src_operands(), [])\n        self.assertEqual(\n            instruction.get_src_operands(include_implicit=True), [reg_op, mem_op, imm_op, agen_op])\n        self.assertEqual(instruction.get_dest_operands(), [])\n        self.assertEqual(instruction.get_dest_operands(include_implicit=True), [mem_op])\n        self.assertEqual(instruction.get_mem_operands(), [])\n        self.assertEqual(instruction.get_mem_operands(include_implicit=True), [mem_op])\n        self.assertEqual(instruction.get_flags_operand(), flags_op)\n        self.assertEqual(instruction.get_reg_operands(), [])\n        self.assertEqual(instruction.get_reg_operands(include_implicit=True), [reg_op])\n        self.assertEqual(instruction.get_cond_operand(), None)\n        self.assertEqual(instruction.get_label_operand(), None)\n        self.assertEqual(instruction.get_imm_operands(), [])\n        self.assertEqual(instruction.get_imm_operands(include_implicit=True), [imm_op])\n\n        # Case 3: No operands\n        instruction = Instruction(\"MOV\", \"MOV\")\n\n        self.assertEqual(instruction.get_all_operands(), [])\n        self.assertEqual(instruction.get_src_operands(True), [])\n        self.assertEqual(instruction.get_dest_operands(True), [])\n        self.assertEqual(instruction.get_mem_operands(True), [])\n        self.assertEqual(instruction.get_flags_operand(), None)\n        self.assertEqual(instruction.get_reg_operands(True), [])\n\n    def test_copy_with_modification(self) -> None:\n        # pylint: disable=protected-access\n\n        # Create an Instruction object\n        org_instruction = Instruction(\n            \"MOV\", \"MOV\", is_control_flow=False, is_instrumentation=False, is_noremove=False)\n        org_instruction.is_from_template = True\n        org_instruction._section_id = 1\n        org_instruction._section_offset = 1\n        org_instruction._line_num = 1\n        org_instruction._size = 1\n\n        # Add operands\n        reg_op = RegisterOp(\"rax\", 64, True, False)\n        org_instruction.operands.append(reg_op)\n\n        # Copy with no modification\n        new_instruction = copy_inst_with_modification(org_instruction)\n        self.assertEqual(new_instruction.name, \"MOV\")\n        self.assertEqual(new_instruction.category, \"MOV\")\n        self.assertFalse(new_instruction.is_control_flow)\n        self.assertFalse(new_instruction.is_instrumentation)\n        self.assertFalse(new_instruction.is_noremove)\n        self.assertTrue(new_instruction.is_from_template)\n        self.assertEqual(new_instruction._section_id, 1)\n        self.assertEqual(new_instruction._section_offset, 1)\n        self.assertEqual(new_instruction._line_num, 1)\n        self.assertEqual(new_instruction._size, 1)\n        self.assertEqual(new_instruction.operands, [reg_op])\n\n        # Copy with modifications\n        new_instruction = copy_inst_with_modification(\n            org_instruction,\n            name=\"NOP\",\n            category=\"NOP\",\n            is_control_flow=True,\n            is_instrumentation=True,\n            is_noremove=True)\n        self.assertEqual(new_instruction.name, \"NOP\")\n        self.assertEqual(new_instruction.category, \"NOP\")\n        self.assertTrue(new_instruction.is_control_flow)\n        self.assertTrue(new_instruction.is_instrumentation)\n        self.assertTrue(new_instruction.is_noremove)\n        self.assertTrue(new_instruction.is_from_template)\n        self.assertEqual(new_instruction._section_id, 1)\n        self.assertEqual(new_instruction._section_offset, 1)\n        self.assertEqual(new_instruction._line_num, 1)\n        self.assertEqual(new_instruction._size, 1)\n        self.assertEqual(new_instruction.operands, [reg_op])\n\n    def test_line_num_interface(self) -> None:\n        # Check unassigned line number\n        instruction = Instruction(\"MOV\", \"MOV\")\n        with self.assertRaises(AssertionError):\n            instruction.line_num()\n\n        # Check valid line number\n        instruction._line_num = 10  # Assuming _line_num is a protected attribute\n        self.assertEqual(instruction.line_num(), 10)\n\n\nclass TestCaseBinaryTest(unittest.TestCase):\n\n    def test_get_macro_offset(self) -> None:\n        # Create an instance of TestCaseBinary\n        test_case_code = TestCaseProgram(\"\")\n        test_case_bin = TestCaseBinary(\"\", test_case_code)\n\n        # Handling of non-assigned macro table\n        with self.assertRaises(AssertionError):\n            test_case_bin.get_macro_offset(1)\n\n        # Mock the _symbol_table with symbols containing the desired macro_type\n        symbol1 = SymbolTableEntry(sid=0, offset=1, type_=1, arg=0)\n        symbol2 = SymbolTableEntry(sid=0, offset=10, type_=2, arg=0)\n        symbol_table = [symbol1, symbol2]\n        test_case_bin.assign_elf_data(symbol_table, MagicMock())\n\n        # Call get_macro_offset and assert the returned offset\n        self.assertEqual(test_case_bin.get_macro_offset(1), 1)\n        self.assertEqual(test_case_bin.get_macro_offset(2), 10)\n\n        # Check non-existing macro type\n        self.assertEqual(test_case_bin.get_macro_offset(3), -1)\n\n\nclass TestCaseCodeTest(unittest.TestCase):\n\n    def test_basic_block_str(self) -> None:\n        bb = BasicBlock(\"bb1\", MagicMock())\n        self.assertEqual(str(bb), \"bb1\")\n\n    def test_basic_block_get_owner(self) -> None:\n        # Normal case\n        actor = Actor.create_main()\n        section = CodeSection(actor)\n        func = Function(\"func1\", section)\n        bb = BasicBlock(\"bb1\", func)\n\n        self.assertEqual(bb.get_owner(), actor)\n\n        # No-parent BB\n        bb = BasicBlock(\"bb1\", None)\n        with self.assertRaises(AssertionError):\n            bb.get_owner()\n\n\nclass InputDataTest(unittest.TestCase):\n\n    def test_data_sizes(self) -> None:\n        # Test data_size_per_actor\n        self.assertEqual(InputData.data_size_per_actor(), _ACTOR_DATA_SIZE)\n\n        # Test n_data_entries_per_actor\n        self.assertEqual(InputData.n_data_entries_per_actor(), _ACTOR_DATA_SIZE // 8)\n\n    def test_hash(self) -> None:\n        input_data = InputData(1)\n        self.assertEqual(hash(input_data), hash(input_data.tobytes()))\n\n    def test_str(self) -> None:\n        input_data = InputData(1)\n        input_data.seed = 42\n        self.assertEqual(str(input_data), \"42\")\n        self.assertEqual(repr(input_data), \"42\")\n\n    def test_set_actor_data(self) -> None:\n        # Create an instance of InputData\n        input_data = InputData(1)\n\n        # Attempt setting data with invalid shape\n        data = np.zeros((1,), dtype=np.uint64)\n        with self.assertRaises(AssertionError):\n            input_data.set_actor_data(0, data)\n\n        # Set the actor data with a valid shape\n        size = input_data.itemsize // 8\n        data = np.ndarray((size,), dtype=np.uint64)\n        data.fill(42)\n        input_data.set_actor_data(0, data)\n        self.assertEqual(input_data[0][\"main\"][0], 42)\n\n    def test_save(self) -> None:\n        # Create an instance of InputData\n        input_data = InputData(1)\n        data = np.array([42 for _ in range(input_data.itemsize // 8)], dtype=np.uint64)\n        input_data.set_actor_data(0, data)\n\n        # Create a temporary binary file\n        with tempfile.NamedTemporaryFile(delete=False) as f:\n            path = f.name\n\n        # Save the input data to the binary file\n        input_data.save(path)\n\n        # Check the contents of the binary file\n        with open(path, 'rb') as f:\n            contents = np.fromfile(f, dtype=np.uint64)\n            self.assertEqual(contents[0], 42)\n\n        # Remove the temporary binary file\n        os.unlink(path)\n\n    def test_load(self) -> None:\n        input_data = InputData(1)\n\n        # Create a temporary binary file\n        with tempfile.NamedTemporaryFile(delete=False) as f:\n            path = f.name\n        data = np.array([42 for _ in range(input_data.itemsize // 8)], dtype=np.uint64)\n        with open(path, 'wb') as f:\n            f.write(data.tobytes())\n\n        # Load the input data from the binary file\n        input_data.load(path)\n        self.assertEqual(input_data[0][\"main\"][0], 42)\n\n        # Remove the temporary binary file\n        os.unlink(path)\n\n    def test_linear_view(self) -> None:\n        input_data = InputData(1)\n        data = np.array([42 for _ in range(input_data.itemsize // 8)], dtype=np.uint64)\n        input_data.set_actor_data(0, data)\n\n        # Get the linear view of the input for the actor\n        linear_view = input_data.linear_view(0)\n\n        # Assert the shape of the linear view\n        self.assertEqual(linear_view.shape, (input_data.itemsize // 8,))\n\n        # Assert the contents of the linear view\n        self.assertEqual(linear_view[0], 42)\n"
  },
  {
    "path": "tests/unit_traces.py",
    "content": "\"\"\"\nFile: Collection of unit tests for rvzr/traces.py\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n\n# Note: We relax the pylint rules in unit tests to allow for more introspection, and also\n#       because test readability is less critical.\n# pylint: disable=missing-function-docstring,missing-class-docstring,too-many-public-methods\n\nfrom __future__ import annotations\n\nimport unittest\nimport unittest.mock\nimport xxhash\nimport numpy as np\n\nfrom rvzr.traces import CTraceEntry, CTrace, HTrace, RawHTraceSample, HardwareEqClass, \\\n    TraceBundle, ContractEqClass, Violation\nfrom rvzr.tc_components.test_case_data import InputData, InputID\nfrom rvzr.tc_components.test_case_code import TestCaseProgram\n\n\nclass TestCTrace(unittest.TestCase):\n    \"\"\" Unit tests for CTrace and related classes \"\"\"\n\n    def test_empty_constructor(self) -> None:\n        # Test `empty_trace` interface\n        trace = CTrace.empty_trace()\n        self.assertIsInstance(trace, CTrace)\n        self.assertTrue(trace.is_empty())\n        self.assertEqual(trace, CTrace([]))\n\n    def test_str(self) -> None:\n        # Test `__str__` interface\n        # 1. Normal case\n        values = [0x0, 0x100]\n        entries = [CTraceEntry(\"val\", i) for i in values]\n        trace = CTrace(entries)\n        hash_ = xxhash.xxh64(str(values), seed=0).intdigest()\n        self.assertEqual(str(trace), str(hash_))\n\n        # 2. Special case - L1D map\n        trace.set_printed_as_l1d(True)\n        l1d_map = '^...^...........................................................'\n        self.assertEqual(str(trace), l1d_map)\n\n    def test_full_str(self) -> None:\n        # Test `full_str` interface\n        entries = [\n            CTraceEntry(\"mem\", 0),\n            CTraceEntry(\"pc\", 0),\n            CTraceEntry(\"ind\", 0),\n            CTraceEntry(\"val\", 0),\n            CTraceEntry(\"reg\", 0),\n        ]\n        trace = CTrace(entries)\n        m_col, pc_col, val_col, reset_col = \"m|\", \"p|\", \"v|\", \"r|\"\n\n        # 1. Normal case\n        expected_x86 = \"[mem: m|0x0r|, pc: p|0x0r|, indcall: p|0x0r|, val: v|0x0r|, rsi: 0x0r|]\"\n        expected_arm = \"[mem: m|0x0r|, pc: p|0x0r|, indcall: p|0x0r|, val: v|0x0r|, x4: 0x0r|]\"\n        self.assertIn(\n            trace.full_str(m_col, pc_col, val_col, reset_col), [expected_x86, expected_arm])\n\n        # 2. Default colors\n        expected_x86 = \"[mem: 0x0, pc: 0x0, indcall: 0x0, val: 0x0, rsi: 0x0]\"\n        expected_arm = \"[mem: 0x0, pc: 0x0, indcall: 0x0, val: 0x0, x4: 0x0]\"\n        self.assertIn(trace.full_str(), [expected_x86, expected_arm])\n\n        # 3. Invalid color combination\n        with self.assertRaises(AssertionError):\n            trace.full_str(\"m|\", \"p|\", \"v|\")\n\n    def test_default_methods(self) -> None:\n        # Test default methods: `__eq__`, `__lt__`, `__gt__`, `__len__`, `__hash__`\n        entries = [CTraceEntry(\"val\", i) for i in range(5)]\n        trace1 = CTrace(entries)\n        trace2 = CTrace(entries)\n        trace3 = CTrace([CTraceEntry(\"val\", 10)])\n\n        # __eq__\n        self.assertEqual(trace1, trace2)\n        self.assertNotEqual(trace1, trace3)\n        with self.assertRaises(NotImplementedError):\n            _ = trace1 == \"not a trace\"\n\n        # __lt__ and __gt__\n        self.assertFalse(trace1 < trace2)\n        self.assertFalse(trace1 > trace2)\n\n        # __len__\n        self.assertEqual(len(trace1), 5)\n\n        # __hash__\n        self.assertEqual(hash(trace1), hash(trace2))\n        self.assertNotEqual(hash(trace1), hash(trace3))\n\n    def test_accessors(self) -> None:\n        # Test accessors: `get_untyped`, `get_typed`, `is_empty`\n        entries = [CTraceEntry(\"val\", i) for i in range(5)]\n        trace = CTrace(entries)\n\n        # get_untyped\n        untyped = trace.get_untyped()\n        self.assertEqual(untyped, [0, 1, 2, 3, 4])\n\n        # get_typed\n        typed = trace.get_typed()\n        self.assertEqual(typed, entries)\n\n        # is_empty\n        self.assertFalse(trace.is_empty())\n        empty_trace = CTrace.empty_trace()\n        self.assertTrue(empty_trace.is_empty())\n\n\nclass TestHTrace(unittest.TestCase):\n    \"\"\" Unit tests for HTrace and related classes \"\"\"\n\n    def test_empty_constructor(self) -> None:\n        # Test `empty_trace` interface\n        trace = HTrace.empty_trace()\n        self.assertIsInstance(trace, HTrace)\n        self.assertTrue(trace.is_empty())\n        self.assertEqual(trace, HTrace(np.ndarray(0, dtype=RawHTraceSample)))\n\n    def test_invalid_constructor(self) -> None:\n        # Test `invalid_trace` interface\n        trace = HTrace.invalid_trace()\n        self.assertIsInstance(trace, HTrace)\n        self.assertTrue(trace.is_corrupted_or_ignored())\n\n    def test_printers(self) -> None:\n        # Test `__str__`, `full_str` interfaces\n        # __str__\n        entries = np.array([(0x100, 0, 0, 0, 0, 0)], dtype=RawHTraceSample)\n        trace = HTrace(entries)\n        hash_ = xxhash.xxh64(str(entries['trace']), seed=0).intdigest()\n        self.assertEqual(str(trace), str(hash_))\n\n        # full_str: empty trace\n        empty_trace = HTrace.empty_trace()\n        self.assertEqual(empty_trace.full_str(), \"\")\n\n        # full_str: cache trace\n        trace = HTrace(np.array([(0b10001, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\")\n        expected = \"...........................................................^...^ [1]\\n\"\n        self.assertEqual(trace.full_str(), expected)\n\n        # full_str: TSC trace\n        trace = HTrace(np.array([(256, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"tsc\")\n        expected = \"00000256 [1]\\n\"\n        self.assertEqual(trace.full_str(), expected)\n\n        # full_str: architectural trace\n        trace = HTrace(np.array([(0x1, 0x2, 0x3, 0x4, 0x5, 0x6)], dtype=RawHTraceSample), \"reg\")\n        expected_x86 = \"[rax: 0x1, rbx: 0x2, rcx: 0x3, rdx: 0x4, rsi: 0x5, rdi: 0x6]\"\n        expected_arm = \"[x0: 0x1, x1: 0x2, x2: 0x3, x3: 0x4, x4: 0x5, x5: 0x6]\"\n        self.assertIn(trace.full_str(), [expected_x86, expected_arm])\n\n    def test_pair_printers(self) -> None:\n        # Test `full_pair_str` interface\n        # cache traces\n        trace1 = HTrace(np.array([(0b10001, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\")\n        trace2 = HTrace(np.array([(0b10010, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\")\n        expected = \\\n            \"...........................................................^...^\" \\\n            \" | 1      | 0     |\\n\" \\\n            \"...........................................................^..^.\" \\\n            \" | 0      | 1     |\\n\"\n        self.assertEqual(trace1.full_pair_str(trace2), expected)\n\n        # TSC traces\n        trace1 = HTrace(np.array([(256, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"tsc\")\n        trace2 = HTrace(np.array([(512, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"tsc\")\n        expected = \"00000256 | 1      | 0      |\\n\"\\\n                   \"00000512 | 0      | 1      |\\n\"\n        self.assertEqual(trace1.full_pair_str(trace2), expected)\n\n        # architectural traces\n        trace1 = HTrace(np.array([(0x1, 0x2, 0x3, 0x4, 0x5, 0x6)], dtype=RawHTraceSample), \"reg\")\n        trace2 = HTrace(np.array([(0x7, 0x8, 0x9, 0xa, 0xb, 0xc)], dtype=RawHTraceSample), \"reg\")\n        # FIXME: the below assert is nonsensical, and it exists only to satisfy the coverage tool\n        with self.assertRaises(NotImplementedError):\n            trace1.full_pair_str(trace2)\n\n    def test_default_methods(self) -> None:\n        # Test default methods: `__eq__`, `__len__`, `__hash__`\n        trace1 = HTrace(np.array([(0x100, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\")\n        trace2 = HTrace(np.array([(0x100, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\")\n        trace3 = HTrace(np.array([(0x200, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\")\n\n        # __eq__\n        self.assertEqual(trace1, trace2)\n        self.assertNotEqual(trace1, trace3)\n\n        # __len__\n        self.assertEqual(len(trace1), 1)\n        self.assertEqual(len(trace3), 1)\n        self.assertEqual(len(HTrace.empty_trace()), 0)\n\n        # __hash__\n        self.assertEqual(hash(trace1), hash(trace2))\n        self.assertNotEqual(hash(trace1), hash(trace3))\n\n    def test_merge(self) -> None:\n        # Test `merge` method\n        trace1 = HTrace(np.array([(0x100, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\")\n        trace2 = HTrace(np.array([(0x200, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\")\n        merged_trace = trace1.merge(trace2)\n\n        self.assertEqual(len(merged_trace), 2)\n        self.assertEqual(merged_trace.get_raw_traces().tolist(), [0x100, 0x200])\n\n    def test_accessors(self) -> None:\n        # Test accessors: `get_raw_readings`, `get_raw_traces`, `sample_size`, `get_max_pfc`\n        entries = np.array([(0x100, 1, 2, 3, 4, 5)], dtype=RawHTraceSample)\n        trace = HTrace(entries)\n\n        # get_raw_readings\n        raw_readings = trace.get_raw_readings()\n        self.assertTrue(np.array_equal(raw_readings, entries))\n\n        # get_raw_traces\n        raw_traces = trace.get_raw_traces()\n        self.assertTrue(np.array_equal(raw_traces, entries['trace']))\n\n        # sample_size\n        self.assertEqual(trace.sample_size(), 1)\n        self.assertEqual(HTrace.empty_trace().sample_size(), 0)\n\n        # get_max_pfc\n        self.assertEqual(trace.get_max_pfc(), (1, 2, 3, 4, 5))\n\n\ndef _get_bundle_set() -> list[TraceBundle]:\n    trace_bundle1 = TraceBundle(\n        input_id=InputID(1),\n        input_=InputData(),\n        ctrace=CTrace([CTraceEntry(\"val\", 0)]),\n        htrace=HTrace(np.array([(0x100, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\"))\n    trace_bundle2 = TraceBundle(\n        input_id=InputID(2),\n        input_=InputData(),\n        ctrace=CTrace([CTraceEntry(\"val\", 0)]),\n        htrace=HTrace(np.array([(0x100, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\"))\n    trace_bundle3 = TraceBundle(\n        input_id=InputID(3),\n        input_=InputData(),\n        ctrace=CTrace([CTraceEntry(\"val\", 1)]),\n        htrace=HTrace(np.array([(0x200, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\"))\n    trace_bundle4 = TraceBundle(\n        input_id=InputID(3),\n        input_=InputData(),\n        ctrace=CTrace([CTraceEntry(\"val\", 1)]),\n        htrace=HTrace(np.array([(0x300, 0, 0, 0, 0, 0)], dtype=RawHTraceSample), \"cache\"))\n    return [trace_bundle1, trace_bundle2, trace_bundle3, trace_bundle4]\n\n\nclass TestHardwareEqClass(unittest.TestCase):\n    \"\"\" Unit tests for HardwareEqClass\"\"\"\n\n    def test_hw_class_builder(self) -> None:\n        # Test `build_hw_classes`\n        # Create hardware equivalence classes\n        trace_bundles = _get_bundle_set()\n        hw_classes = HardwareEqClass.build_hw_classes(trace_bundles)\n\n        # Test grouping\n        self.assertEqual(len(hw_classes), 3)\n        self.assertEqual(hw_classes[0].htrace, trace_bundles[0].htrace)\n        self.assertEqual(hw_classes[1].htrace, trace_bundles[2].htrace)\n        self.assertEqual(hw_classes[0].measurements, [trace_bundles[0], trace_bundles[1]])\n        self.assertEqual(hw_classes[1].measurements, [trace_bundles[2]])\n\n    def test_default_methods(self) -> None:\n        # Test default methods\n        trace_bundles = _get_bundle_set()\n        hw_classes = HardwareEqClass.build_hw_classes(trace_bundles)\n\n        # __len__\n        self.assertEqual(len(hw_classes[0]), 2)\n\n        # __iter__\n        for i, bundle in enumerate(hw_classes[0]):\n            self.assertEqual(bundle, trace_bundles[i])\n\n        # __getitem__\n        self.assertEqual(hw_classes[0][0], trace_bundles[0])\n\n        # __eq__\n        self.assertNotEqual(hw_classes[0], hw_classes[1])\n        with self.assertRaises(NotImplementedError):\n            _ = hw_classes[0] == \"not a HardwareEqClass\"\n\n\nclass TestContractEqClass(unittest.TestCase):\n    \"\"\" Unit tests for ContractEqClass \"\"\"\n\n    def test_contract_class_builder(self) -> None:\n        # Test `build_contract_classes`\n        trace_bundles = _get_bundle_set()\n        contract_classes = ContractEqClass.build_contract_classes(trace_bundles)\n\n        # Test grouping\n        self.assertEqual(len(contract_classes), 2)\n        self.assertEqual(contract_classes[0].ctrace, trace_bundles[0].ctrace)\n        self.assertEqual(len(contract_classes[0]), 2)\n        self.assertEqual(contract_classes[0].measurements, trace_bundles[:2])\n        self.assertEqual(contract_classes[1].ctrace, trace_bundles[2].ctrace)\n        self.assertEqual(contract_classes[1].measurements, trace_bundles[2:])\n\n    def test_accessors(self) -> None:\n        # Test accessors: `set_hw_classes`, `set_trivial_hw_classes`, `get_hw_classes`\n        trace_bundles = _get_bundle_set()\n        contract_classes = ContractEqClass.build_contract_classes(trace_bundles)\n\n        # get_hw_classes - failing case\n        with self.assertRaises(AssertionError):\n            _ = contract_classes[0].get_hw_classes()\n\n        # set_hw_classes\n        hw_classes = HardwareEqClass.build_hw_classes(trace_bundles)\n        contract_classes[0].set_hw_classes(hw_classes)\n        self.assertEqual(contract_classes[0].get_hw_classes(), hw_classes)\n        with self.assertRaises(AssertionError):  # repeated setting forbidden\n            contract_classes[0].set_hw_classes([])\n\n        # set_trivial_hw_classes\n        with self.assertRaises(AssertionError):\n            contract_classes[0].set_trivial_hw_classes()\n        contract_classes = ContractEqClass.build_contract_classes(trace_bundles)\n        contract_classes[0].set_trivial_hw_classes()\n        self.assertEqual(contract_classes[0].get_hw_classes()[0].htrace, hw_classes[0].htrace)\n        self.assertEqual(contract_classes[0].get_hw_classes()[0], hw_classes[0])\n\n\nclass TestViolation(unittest.TestCase):\n    \"\"\" Unit tests for Violation class \"\"\"\n\n    def test_constructors(self) -> None:\n        # __init__\n        measurements = _get_bundle_set()[2:]\n        input_sequence = [m.input_ for m in measurements]\n        test_case_code = unittest.mock.MagicMock(spec=TestCaseProgram)\n        violation = Violation(measurements, input_sequence, test_case_code)\n        violation.set_trivial_hw_classes()\n        self.assertEqual(violation.input_sequence, input_sequence)\n        self.assertEqual(violation.test_case_code, test_case_code)\n\n        # from_contract_eq_class\n        contract_class = ContractEqClass.build_contract_classes(measurements)[0]\n        contract_class.set_trivial_hw_classes()\n        violation_from_class = Violation.from_contract_eq_class(contract_class, input_sequence,\n                                                                test_case_code)\n        self.assertEqual(violation_from_class.input_sequence, input_sequence)\n        self.assertEqual(violation_from_class.test_case_code, test_case_code)\n        self.assertListEqual(violation_from_class.measurements, violation.measurements)\n        self.assertEqual(violation_from_class.input_sequence, input_sequence)\n\n        # pseudo_violation_from_inputs\n        pseudo_violation = Violation.pseudo_violation_from_inputs(input_sequence, test_case_code)\n        self.assertEqual(pseudo_violation.input_sequence, input_sequence)\n        self.assertEqual(pseudo_violation.test_case_code, test_case_code)\n        self.assertTrue(pseudo_violation.measurements[0].ctrace.is_empty())\n        self.assertTrue(pseudo_violation.measurements[0].htrace.is_empty())\n"
  },
  {
    "path": "tests/x86_tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/x86_tests/asm/actor_switch.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n\n.function_start:\n    # delay on rbx\n    lea rbx, qword ptr [rbx + rcx + 1]\n    lea rbx, qword ptr [rbx + rcx + 1]\n    lea rbx, qword ptr [rbx + rcx + 1]\n    lea rbx, qword ptr [rbx + rcx + 1]\n    lea rbx, qword ptr [rbx + rcx + 1]\n    lea rbx, qword ptr [rbx + rcx + 1]\n    lea rbx, qword ptr [rbx + rcx + 1]\n    lea rbx, qword ptr [rbx + rcx + 1]\n    lea rbx, qword ptr [rbx + rcx + 1]\n    lea rbx, qword ptr [rbx + rcx + 1]\n    and rbx, 0b1\n\n    .macro.switch.actor2.function_1:\n# end of function_start\n# --------------------------------------------------------------------------------------------------\n\n.function_fin:\n    .bb0:\n    nop\n# end of function_fin\n# --------------------------------------------------------------------------------------------------\n\n.section .data.actor2\n.function_1:\n    # a typical spectre v1 gadget\n    jz .l3\n    .l1:\n        # mask the memory access\n        and rax, 0b111111000000\n        mov rax, qword ptr [r14 + rax]\n    jmp .l3\n    .l2:\n        # mov rax, qword ptr [r14 + 0x100]\n    .l3:\n\n    and rdx, 0b111111000000\n    mov rax, qword ptr [r14 + rdx]\n    mov rsi, 0x42\n\n    .macro.switch.main.function_fin:\n# end of function_1\n# --------------------------------------------------------------------------------------------------\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/asm_basic.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n\n.function_0:\n.bb_0:\n\n  # line with a comment\nnop # no operands\ndiv rbx  # one operand\nand rax, rax # two operands\nand rax, 0b0111111000000 # immediate value - binary\nand rax, 42 # immediate value - decimal\nand rax, 0xfa # immediate value - hex\nand rax, -1 # immediate value - negative\nand rdi, r14  # reserved register\nneg rax  # lowercase\nmov rax, qword ptr [r14]  # load - simple addressing\nmov rax, qword ptr [r14 + rbx]  # load - two parts\nmov rax, qword ptr [r14 + rbx + 8]  # load - three parts\nmov rax, qword ptr [r14 + rbx]  # store\nlock adc dword ptr [r14 + rbx], eax  # lock prefix\nand rax, rax # instrumentation\n\nmov rdi, rdi # multiple matches\n\n\njmp .bb_1\n  .bb_1:\n      and rdi, 0b0111111000000 # indentation\n     cmp qword ptr [ r14 + rdi ] , 59   # extra spaces\n    and rdi, 0b0111111000000 # instrumentation\n    cmpxchg byte ptr [r14 + rsi], sil\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/asm_multiactor.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n\n.function_0:\nnop\nnop\n\n.section .data.guest_1\n.function_1:\nnop\n\n.section .data.main\n.function_2:\n.bb0:\nnop\n\n# .section exit\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/asm_symbol.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n\n.macro.measurement_start: nop qword ptr [rax + 0xff]\n\nnop\n\n.macro.measurement_end: nop qword ptr [rax + 0xff]\n\nand rax, rax\n\n.function_1:\n\nnop\n\n.section .data.guest_1\n.function_2:\nnop\n\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/calls.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n.function_0:\ncall .function_1\n.function_1:\nadd rsp, 8\ncall .function_2\n.function_2:\nadd rsp, 8\ncall .function_3\n.function_3:\nadd rsp, 8\ncall .function_4\n.function_4:\nadd rsp, 8\ncall .function_5\n.function_5:\nadd rsp, 8\ncall .function_6\n.function_6:\nadd rsp, 8\ncall .function_7\n.function_7:\nadd rsp, 8\ncall .function_8\n.function_8:\nadd rsp, 8\ncall .function_9\n.function_9:\nadd rsp, 8\ncall .function_10\n.function_10:\nadd rsp, 8\ncall .function_11\n.function_11:\nadd rsp, 8\ncall .function_12\n.function_12:\nadd rsp, 8\ncall .function_13\n.function_13:\nadd rsp, 8\ncall .function_14\n.function_14:\nadd rsp, 8\ncall .function_15\n.function_15:\nadd rsp, 8\ncall .function_16\n.function_16:\nadd rsp, 8\ncall .function_17\n.function_17:\nadd rsp, 8\ncall .function_18\n.function_18:\nadd rsp, 8\ncall .function_19\n.function_19:\nadd rsp, 8\ncall .function_20\n.function_20:\nadd rsp, 8\ncall .function_21\n.function_21:\nadd rsp, 8\ncall .function_22\n.function_22:\nadd rsp, 8\ncall .function_23\n.function_23:\nadd rsp, 8\ncall .function_24\n.function_24:\nadd rsp, 8\ncall .function_25\n.function_25:\nadd rsp, 8\ncall .function_26\n.function_26:\nadd rsp, 8\ncall .function_27\n.function_27:\nadd rsp, 8\ncall .function_28\n.function_28:\nadd rsp, 8\ncall .function_29\n.function_29:\nadd rsp, 8\ncall .function_30\n.function_30:\nadd rsp, 8\ncall .function_31\n.function_31:\nadd rsp, 8\ncall .function_32\n.function_32:\nadd rsp, 8\ncall .function_33\n.function_33:\nadd rsp, 8\ncall .function_34\n.function_34:\nadd rsp, 8\ncall .function_35\n.function_35:\nadd rsp, 8\ncall .function_36\n.function_36:\nadd rsp, 8\ncall .function_37\n.function_37:\nadd rsp, 8\ncall .function_38\n.function_38:\nadd rsp, 8\ncall .function_39\n.function_39:\nadd rsp, 8\ncall .function_40\n.function_40:\nadd rsp, 8\ncall .function_41\n.function_41:\nadd rsp, 8\ncall .function_42\n.function_42:\nadd rsp, 8\ncall .function_43\n.function_43:\nadd rsp, 8\ncall .function_44\n.function_44:\nadd rsp, 8\ncall .function_45\n.function_45:\nadd rsp, 8\ncall .function_46\n.function_46:\nadd rsp, 8\ncall .function_47\n.function_47:\nadd rsp, 8\ncall .function_48\n.function_48:\nadd rsp, 8\ncall .function_49\n.function_49:\nadd rsp, 8\ncall .function_50\n.function_50:\nadd rsp, 8\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/direct_jumps.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\njmp .1\n.1:\njmp .2\n.2:\njmp .3\n.3:\njmp .4\n.4:\njmp .5\n.5:\njmp .6\n.6:\njmp .7\n.7:\njmp .8\n.8:\njmp .9\n.9:\njmp .10\n.10:\njmp .11\n.11:\njmp .12\n.12:\njmp .13\n.13:\njmp .14\n.14:\njmp .15\n.15:\njmp .16\n.16:\njmp .17\n.17:\njmp .18\n.18:\njmp .19\n.19:\njmp .20\n.20:\njmp .21\n.21:\njmp .22\n.22:\njmp .23\n.23:\njmp .24\n.24:\njmp .25\n.25:\njmp .26\n.26:\njmp .27\n.27:\njmp .28\n.28:\njmp .29\n.29:\njmp .30\n.30:\njmp .31\n.31:\njmp .32\n.32:\njmp .33\n.33:\njmp .34\n.34:\njmp .35\n.35:\njmp .36\n.36:\njmp .37\n.37:\njmp .38\n.38:\njmp .39\n.39:\njmp .40\n.40:\njmp .41\n.41:\njmp .42\n.42:\njmp .43\n.43:\njmp .44\n.44:\njmp .45\n.45:\njmp .46\n.46:\njmp .47\n.47:\njmp .48\n.48:\njmp .49\n.49:\njmp .50\n.50:\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/fault-div-overflow-speculation.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n\nMOV ebx, 1\nlfence\nDIV bx\n\nXOR rax, rcx\nAND rax, 0b111111111111 # instrumentation\nMOV rax, qword ptr [r14 + rax + 128]\n\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/fault-div-zero-speculation.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nmov edx, 0\nmov ebx, 0\ndiv ebx\nxor rax, rcx\nand rax, 0b111111111111 # instrumentation\nmov rax, qword ptr [r14 + rax + 128]\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/fault_INT1.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n.byte 0xf1  # int1\nmov rax, qword ptr [r14 + 256]\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/fault_INT3.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nint3\nmov rax, qword ptr [r14 + 256]\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/fault_UD.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nud2\nand rax, 0b111111111111 # instrumentation\nmov rax, qword ptr [r14 + rax + 128]\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/fault_load.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nmov rax, qword ptr [r14 + 4096]\nxor rax, rcx\nand rax, 0b111111111111 # instrumentation\nmov rax, qword ptr [r14 + rax]\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/fault_ooo_mem_access.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nmov rax, qword ptr [r14 + 4096 + 64]\n\n# dependent memory access\nand rbx, 0b111111111000 # instrumentation\nmov rax, qword ptr [r14 + rbx]\n\n# independent memory access\nand rbx, 0b111111111000 # instrumentation\nmov rax, qword ptr [r14 + rbx]\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/fault_rmw.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nxadd qword ptr [r14 + 4096], rax\nadd rax, rbx\nand rax, 0b111111111111 # instrumentation\nmov rax, qword ptr [r14 + rax + 128]\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/macro_fault_handler.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n.function_main_0:\n\nmov rbx, qword ptr [r14 + 0x300]\nud2\nlfence\n\n.macro.fault_handler:\nmov rax, qword ptr [r14 + 0x200]\n\n\n# ----------------------------- Exit    ------------------------------------------------------------\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/minimization-after.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n.function_main:\n.macro.measurement_start: nop qword ptr [rax + 0xff]\nand rax, 0b1111111111111 # instrumentation\nor ebx, dword ptr [r14 + rax]  # speculation source ?\nmov al, bl\nxor ax, -2067\ntest al, -117 # instrumentation\nand rax, 0b1111111111111 # instrumentation\nmov qword ptr [r14 + rax], rcx  # speculation sink ?\n.section .data.main\n.function_end:\n.macro.measurement_end: nop qword ptr [rax + 0xff]\n.section .data.main\n.test_case_exit:nop\n"
  },
  {
    "path": "tests/x86_tests/asm/minimization-before.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n.function_main:\n.bb_main.entry:\njmp .bb_main.0\n.bb_main.0:\ntest dil, 51\nadc ax, -49\nxor eax, ecx\nand rax, 0b1111111111111 # instrumentation\nor ebx, dword ptr [r14 + rax]\nor al, bl\nand rsi, 0b1111111111111 # instrumentation\nxor byte ptr [r14 + rsi], al\nsetl bl\nxor ax, -2067\nlea si, qword ptr [rsi + rbx]\nsbb cl, cl\nand rdx, 0b1111111111111 # instrumentation\nlock and dword ptr [r14 + rdx], -37\ndec al\ntest al, -117 # instrumentation\nand rax, 0b1111111111111 # instrumentation\nxchg qword ptr [r14 + rax], rcx\nmovsx esi, cl\nxadd rdi, rdi\n.bb_main.exit:\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/model_flags_match.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nlfence\nmov rax, r14\n\nmov rbx, 0\nmov rcx, 64\ncmovb rbx, rcx\nmov rcx, qword ptr [rax + rbx]\n\nmov rbx, 0\nmov rcx, 128\ncmovbe rbx, rcx\nmov rcx, qword ptr [rax + rbx]\n\nmov rbx, 0\nmov rcx, 192\ncmovl rbx, rcx\nmov rcx, qword ptr [rax + rbx]\n\nmov rbx, 0\nmov rcx, 256\ncmovle rbx, rcx\nmov rcx, qword ptr [rax + rbx]\n\nmov rbx, 0\nmov rcx, 320\ncmovo rbx, rcx\nmov rcx, qword ptr [rax + rbx]\n\nmov rbx, 0\nmov rcx, 384\ncmovp rbx, rcx\nmov rcx, qword ptr [rax + rbx]\n\nmov rbx, 0\nmov rcx, 448\ncmovs rbx, rcx\nmov rcx, qword ptr [rax + rbx]\n\nmov rbx, 0\nmov rcx, 512\ncmovz rbx, rcx\nmov rcx, qword ptr [rax + rbx]\n\n// cmovnb\n// cmovnbe\n// cmovnl\n// cmovnle\n// cmovno\n// cmovnp\n// cmovns\n// cmovnz\nmfence\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/model_match.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n\n# empty - leaving initial reg values unchanged\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/model_match_memory.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n\nmov rax, qword ptr [r14]  # main page\nadd rax, qword ptr [r14 + 4096]  # faulty page\n\nmov rbx, qword ptr [r14 - 8]  # underflow pad\nadd rbx, qword ptr [r14 + 4096 + 4096 + 320]  # overflow pad\n\nmov rcx, qword ptr [r14 + 4096 + 4096]  # reg init\nmov rdx, qword ptr [r14 + 4096 + 4096 + 48]  # patched flags\nmov rsi, qword ptr [r14 + 4096 + 4096 + 64]  # simd init\nmov rdi, r14\n\n\n# uncomment the following to test the complete sandbox contents\n# xor rax, rax\n# xor rbx, rbx\n# xor rcx, rcx\n# xor rdx, rcx\n# mov rdi, 0xff8\n# .l1:\n# add rax, qword ptr [r14 + rdi]\n# add rbx, qword ptr [r14 + rdi + 0x1000]\n# add rcx, qword ptr [r14 + rdi + 0x2000]\n# sub rdi, 8\n# jnz .l1\n# .l1_exit:\n\n# mov rdi, 0xef8\n# .l2:\n# add rdx, qword ptr [r14 + rdi - 0xf00]\n# sub rdi, 8\n# jnz .l2\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/model_match_xmm.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nmovq rax, xmm0\nmovq rbx, xmm1\nmovq rdx, xmm2\nmovq rcx, xmm3\nmovq rsi, xmm4\nmovq rdi, xmm5\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/spectre_ret.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n\n# speculative offset:\n# these shifts generate a random page offset, 64-bit aligned\nand rax, 0b111111000000\nlfence\n\nmov rcx, r14\nadd rsp, 8  # ensure that the call and ret use the first cache set\n\ncall .function_1\n\n.unreachable:\n// lfence  # if you uncomment this line, the speculation will stop\nand rax, 0b110000000  # reduce the number of possibilities\nmov rax, qword ptr [rcx + rax]  # speculative access\nlfence\n\n.function_1:\nlea rdx, qword ptr [rip + .function_2]\nmov qword ptr [rsp], rdx\nret\n\n.function_2:\nmov rdx, qword ptr [rcx + 64]\nmfence\n\n# clear to avoid failing the arch check\nmov rcx, 0\nmov rdx, 0\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/spectre_v1.1.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nlfence\n\n# reduce the entropy of rax\nand rax, 0b111111000000\n\n# delay the cond. jump\nmov rcx, 0\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\n\n# reduce the entropy in rbx\nand rbx, 0b1000000\n\ncmp rbx, 0\nje .l1  # misprediction\n.l0:\n    # rbx != 0\n    mov qword ptr [r14], rax\n    mov rdx, qword ptr [r14]\n    mov rbx, qword ptr [r14 + rdx]\n.l1:\nmfence\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/spectre_v1.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nlfence\n\n# reduce the entropy of rax\nand rax, 0b111111000000\n\n# delay the cond. jump\nmov rcx, 0\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\n\n# reduce the entropy in rbx\nand rbx, 0b1000000\n\ncmp rbx, 0\nje .l1  # misprediction\n.l0:\n    # rbx != 0\n    mov rax, qword ptr [r14 + rax]\njmp .l2\n.l1:\n    # rbx == 0\n    #mov rax, qword ptr [r14 + 64]\n.l2:\nmfence\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/spectre_v1_arch.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nlfence\n\n# delay the cond. jump\nmov rax, 0\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\n\n# reduce the entropy in rbx\nand rbx, 0b1000000\n\ncmp rbx, 0\nje .l1  # misprediction\n.l0:\n    # rbx != 0\n    mov rax, qword ptr [r14 + 1024]\n    shl rax, 2\n    and rax, 0b111111000000\n    mov rax, qword ptr [r14 + rax] # leakage happens here\n.l1:\n\nmfence\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/spectre_v1_independent.asm",
    "content": "# This test case is identical to spectre_v1 except the offset of the speculative mem. access\n# is input-independent. Therefore, this test case must not be flagged.\n\n.intel_syntax noprefix\n.section .data.main\nMOV rcx, r14\n\n# input: ebx - a random value, eax - fixed value\nMOV rax, 128\nlfence\n\n# no delay to increase the likelihood of a false positive\nSHL rbx, 63\nSHR rbx, 63\n\n# speculation\nCMP rbx, 0\nJE .l1\n.l0:\n    # rbx != 0\n    MOV rcx, qword ptr [rcx + rax]\nJMP .l2\n.l1:\n    # rbx == 0\n    MOV rcx, qword ptr [rcx]\n.l2:\nMFENCE\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/spectre_v1_n2.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\nlfence\n\n# reduce the entropy of rax\nand rax, 0b111111000000\n\n# delay the cond. jump\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\n\n# reduce the entropy in rbx\nand rbx, 0b1000000\n\ncmp rbx, 0\nje .l1  # misprediction\nje .l1\n.l0:\n    # rbx != 0\n    mov rax, qword ptr [r14 + rax]\njmp .l2\n.l1:\n    # rbx == 0\n    #mov rax, qword ptr [r14 + 64]\n.l2:\nmfence\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/spectre_v2.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n\n# reduce the entropy of rax\nand rax, 0b111111000000\n\n# prepare jump targets\nlea rdx, qword ptr [rip + .l1]\nlea rsi, qword ptr [rip + .l2]\n\n# delay the jump\nmov rcx, 0\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\nlea rbx, qword ptr [rbx + rcx + 1]\nlea rbx, qword ptr [rbx + rcx - 1]\n\n# reduce the entropy in rbx\nand rbx, 0b11000000\n\n# select a target based on the random value in rbx\ncmp rbx, 0\ncmove rsi, rdx\n\njmp rsi   # misprediction\n.l0:\nlfence\n.l1:\n    # rbx = 0\n    mov rdx, qword ptr [r14 + rax]\n.l2:\nmfence\n\n# override the targets to avoid failing the arch. check\nmov rdx, 0\nmov rsi, 0\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/spectre_v4.asm",
    "content": ".intel_syntax noprefix\n.section .data.main\n\n# the leaked value - rcx\n# construct a page offset in the range [0x200; 0x900]\nand rcx, 0b11100000000\nadd rcx, 0x200\n\n# save the offset into [r14 + 0]\nmov qword ptr [r14], rcx\nmfence\n\n# create a delay on rbx\nmov rax, 0\nand rbx, 0b111000\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\nlea rbx, qword ptr [rbx + rax + 1]\nlea rbx, qword ptr [rbx + rax - 1]\n\n# sequence of potentially aliasing store-load\n# if rbx == 0, they alias and rdx = 0x40\n# if rbx != 0, they do not alias and rdx = offset saved above\nmov qword ptr [r14 + rbx], 0x40  # store offset 0x40\nmov rdx, qword ptr [r14]  # load the offset; misprediction happens here\n\n# dependent load with the offset\nand rdx, 0b111111000000\nmov rdx, qword ptr [r14 + rdx]\nmfence\n\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/asm/vm_switch.asm",
    "content": ".intel_syntax noprefix\n# ----------------------------- actor 1 ------------------------------------------------------------\n.section .data.main\n.function_main1:\n\n.function_main2:\n    .macro.set_h2g_target.actor2.function_a2:\n    .macro.set_g2h_target.main.function_fin:\n    .macro.switch_h2g.actor2:\n\n.function_fin:\n    .macro.landing_g2h:\n    nop\n\n# ----------------------------- actor 2 ------------------------------------------------------------\n.section .data.actor2\n.function_a2:\n    .macro.landing_h2g:\n    .macro.measurement_start:\n    mov rax, qword ptr [r14 + 0x100]\n    .macro.measurement_end:\n\n    .macro.switch_g2h.main:\n\n# ----------------------------- exit    ------------------------------------------------------------\n.test_case_exit:\n"
  },
  {
    "path": "tests/x86_tests/configs/arch-actors.yaml",
    "content": "file: !include arch.yaml\n\nactors:\n  - actor2:\n      - mode: \"host\"\n"
  },
  {
    "path": "tests/x86_tests/configs/arch-dr.yaml",
    "content": "file: !include common.yaml\nfile: !include base-and-simd-categories.yaml\n\nfuzzer: architectural\nmodel_backend: dynamorio\nenable_priming: false\ninputs_per_class: 1\nlogging_modes:\n - info\n - dbg_violation\n\nprogram_size: 150\navg_mem_accesses: 50\nmax_bb_per_function: 3\nmin_bb_per_function: 3\n"
  },
  {
    "path": "tests/x86_tests/configs/arch-faults.yaml",
    "content": "file: !include common.yaml\nfile: !include base-and-simd-categories.yaml\n\nfuzzer: architectural\nenable_priming: false\ninputs_per_class: 1\nlogging_modes:\n - info\n - dbg_violation\n\nprogram_size: 300\navg_mem_accesses: 150\nmax_bb_per_function: 3\nmin_bb_per_function: 3\n\nfaults_allowlist:\n  - opcode-undefined\n"
  },
  {
    "path": "tests/x86_tests/configs/arch.yaml",
    "content": "file: !include common.yaml\nfile: !include base-and-simd-categories.yaml\n\nfuzzer: architectural\nenable_priming: false\ninputs_per_class: 1\nlogging_modes:\n - info\n - dbg_violation\n\nprogram_size: 300\navg_mem_accesses: 150\nmax_bb_per_function: 3\nmin_bb_per_function: 3\n"
  },
  {
    "path": "tests/x86_tests/configs/archdiff.yaml",
    "content": "file: !include common.yaml\nfile: !include base-and-simd-categories.yaml\n\nfuzzer: archdiff\nenable_priming: false\ninputs_per_class: 1\nlogging_modes:\n - info\n#  - dbg_violation\n\nprogram_size: 150\navg_mem_accesses: 100\nmax_bb_per_function: 3\nmin_bb_per_function: 3\n"
  },
  {
    "path": "tests/x86_tests/configs/base-and-simd-categories.yaml",
    "content": "instruction_categories:\n- BASE-BINARY\n- BASE-BITBYTE\n- BASE-CMOV\n- BASE-COND_BR\n- BASE-CONVERT\n- BASE-DATAXFER\n- BASE-FLAGOP\n- BASE-LOGICAL\n- BASE-MISC\n- BASE-NOP\n- BASE-POP\n- BASE-PUSH\n- BASE-SEMAPHORE\n- BASE-SETCC\n- BASE-STRINGOP\n- BASE-WIDENOP\n- SSE-SSE\n- SSE-DATAXFER\n- SSE-MISC\n- SSE2-DATAXFER\n- SSE2-MISC\n- CLFLUSHOPT-CLFLUSHOPT\n- CLFSH-MISC\n"
  },
  {
    "path": "tests/x86_tests/configs/base-categories.yaml",
    "content": "instruction_categories:\n- BASE-BINARY\n- BASE-BITBYTE\n- BASE-CMOV\n- BASE-COND_BR\n- BASE-CONVERT\n- BASE-DATAXFER\n- BASE-FLAGOP\n- BASE-LOGICAL\n- BASE-MISC\n- BASE-NOP\n- BASE-POP\n- BASE-PUSH\n- BASE-SEMAPHORE\n- BASE-SETCC\n- BASE-STRINGOP\n- BASE-WIDENOP\n"
  },
  {
    "path": "tests/x86_tests/configs/common.yaml",
    "content": "data_generator_seed: 1234567\nprogram_generator_seed: 1234567\n\n# Acceptance tests do not require a large sample size\nexecutor_sample_sizes:\n  - 10\n\n# No logging\nlogging_modes:\n  -\n  # - dbg_model\n  # - dbg_dump_htraces\n  # - dbg_dump_ctraces\n"
  },
  {
    "path": "tests/x86_tests/configs/copy.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-deh.yaml\n\n"
  },
  {
    "path": "tests/x86_tests/configs/ct-cond.yaml",
    "content": "file: !include common.yaml\n\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - cond\n"
  },
  {
    "path": "tests/x86_tests/configs/ct-deh.yaml",
    "content": "contract_observation_clause: ct\ncontract_execution_clause:\n  - delayed-exception-handling\n"
  },
  {
    "path": "tests/x86_tests/configs/ct-seq.yaml",
    "content": "file: !include common.yaml\n\ncontract_observation_clause: ct\ncontract_execution_clause:\n  - seq"
  },
  {
    "path": "tests/x86_tests/configs/div-detect.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-deh.yaml\n\nfaults_allowlist:\n  - div-by-zero\n  - div-overflow\n"
  },
  {
    "path": "tests/x86_tests/configs/div-verif.yaml",
    "content": "file: !include common.yaml\n\ncontract_execution_clause:\n  - vspec-ops-div\n\nfaults_allowlist:\n  - div-by-zero\n  - div-overflow\n"
  },
  {
    "path": "tests/x86_tests/configs/exceptions.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-seq.yaml\n\nfaults_allowlist:\n  - debug-register\n  - breakpoint\n  - opcode-undefined\n"
  },
  {
    "path": "tests/x86_tests/configs/fault-handler.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-seq.yaml\n\nactors:\n  - main:\n    - data_properties:\n      - present: false\n\nexecutor_mode: F+R\nlogging_modes:\n  - dbg_dump_htraces\n\nfaults_allowlist:\n  - opcode-undefined\n"
  },
  {
    "path": "tests/x86_tests/configs/l1tf-p-verif.yaml",
    "content": "file: !include common.yaml\n\ncontract_execution_clause:\n  - nullinj-fault\n\nactors:\n  - main:\n    - data_properties:\n      - present: false\n\n"
  },
  {
    "path": "tests/x86_tests/configs/l1tf-p.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-deh.yaml\n\nactors:\n  - main:\n    - data_properties:\n      - present: false\n"
  },
  {
    "path": "tests/x86_tests/configs/l1tf-w-verif.yaml",
    "content": "file: !include common.yaml\n\ncontract_execution_clause:\n  - nullinj-fault\n\nactors:\n  - main:\n    - data_properties:\n      - writable: false\n\n"
  },
  {
    "path": "tests/x86_tests/configs/l1tf-w.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-deh.yaml\n\nactors:\n  - main:\n    - data_properties:\n      - writable: false\n"
  },
  {
    "path": "tests/x86_tests/configs/meltdown-verif.yaml",
    "content": "file: !include common.yaml\n\ncontract_execution_clause:\n  - nullinj-fault\n\nactors:\n  - main:\n    - data_properties:\n      - user: true\n"
  },
  {
    "path": "tests/x86_tests/configs/meltdown.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-deh.yaml\n\nactors:\n  - main:\n    - data_properties:\n      - user: true\n"
  },
  {
    "path": "tests/x86_tests/configs/mpx-verif.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-deh.yaml\n\nfaults_allowlist:\n  - bounds-range-exceeded\n"
  },
  {
    "path": "tests/x86_tests/configs/mpx.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-seq.yaml\n\nfaults_allowlist:\n  - bounds-range-exceeded\n"
  },
  {
    "path": "tests/x86_tests/configs/ssbp-detect.yaml",
    "content": "file: !include common.yaml\n\ndata_generator_seed: 400\nx86_executor_enable_ssbp_patch: false\n"
  },
  {
    "path": "tests/x86_tests/configs/ssbp-verif.yaml",
    "content": "file: !include common.yaml\n\ndata_generator_seed: 400\nx86_executor_enable_ssbp_patch: false\n\ncontract_execution_clause:\n  - bpas\n"
  },
  {
    "path": "tests/x86_tests/configs/vm-switch.yaml",
    "content": "file: !include common.yaml\nfile: !include ct-seq.yaml\n\n\nactors:\n  - actor2:\n      - mode: \"guest\"\n"
  },
  {
    "path": "tests/x86_tests/min_x86.json",
    "content": "[\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock adc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"add\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"add\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"add\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n  \"operands\": [\n    {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n    {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64}\n  ],\n  \"implicit_operands\": [\n    {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n  ]\n},\n  {\"name\": \"and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock and\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bsf\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"w\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bsf\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"w\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bsf\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"w\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bsf\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"w\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bsf\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"w\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bsf\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"w\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bsr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"w\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bsr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"w\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bt\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bt\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0, \"is_signed\": false}\n    ]\n  },\n  {\"name\": \"lock btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock btc\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"btr\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"bts\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock bts\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock bts\", \"category\": \"BASE-BITBYTE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"call\", \"category\": \"BASE-CALL\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"call\", \"category\": \"BASE-CALL\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"call\", \"category\": \"BASE-CALL\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"{disp32} call\", \"category\": \"BASE-CALL\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"cbw\", \"category\": \"BASE-CONVERT\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ]\n  },\n  {\"name\": \"cdq\", \"category\": \"BASE-CONVERT\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ]\n  },\n  {\"name\": \"clc\", \"category\": \"BASE-FLAGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cld\", \"category\": \"BASE-FLAGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cli\", \"category\": \"BASE-FLAGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"w\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmc\", \"category\": \"BASE-FLAGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmovbe\", \"category\": \"BASE-CMOV\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmovno\", \"category\": \"BASE-CMOV\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmovp\", \"category\": \"BASE-CMOV\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmovz\", \"category\": \"BASE-CMOV\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": false, \"width\": 32, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmp\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": false, \"width\": 64, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpsb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"r\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpsd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"r\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpsw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"r\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmpxchg8b\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ecx\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ebx\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"w\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock cmpxchg8b\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ecx\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ebx\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"w\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock cmpxchg\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cpuid\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ebx\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ecx\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": false, \"dest\": true, \"width\": 32}\n    ]\n  },\n  {\"name\": \"cwd\", \"category\": \"BASE-CONVERT\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"dx\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ]\n  },\n  {\"name\": \"cwde\", \"category\": \"BASE-CONVERT\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ]\n  },\n  {\"name\": \"dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock dec\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"div\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"div\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"div\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"div\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"dx\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"div\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"div\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rdx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"div\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"dx\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"div\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"div\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rdx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"enterw\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"bp\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"enter\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rbp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"idiv\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"idiv\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"idiv\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"idiv\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"dx\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"idiv\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"idiv\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rdx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"idiv\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"dx\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"idiv\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"idiv\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rdx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"undef\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"dx\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rdx\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"dx\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rdx\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"imul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n\n  {\"name\": \"inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock inc\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"iretd\", \"category\": \"BASE-RET\", \"is_control_flow\": true,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 160},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"w\", \"w\", \"w\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jb\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jb\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jbe\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jbe\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jl\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jl\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jle\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jle\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jmp\", \"category\": \"BASE-UNCOND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"jmp\", \"category\": \"BASE-UNCOND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"{disp32} jmp\", \"category\": \"BASE-UNCOND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"jmp\", \"category\": \"BASE-UNCOND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"jmp far\", \"category\": \"BASE-UNCOND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"jmp far\", \"category\": \"BASE-UNCOND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 48}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"jmp far\", \"category\": \"BASE-UNCOND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 80}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"jnb\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jnb\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jnbe\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jnbe\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jnl\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jnl\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jnle\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jnle\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jno\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jno\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jnp\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jnp\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jns\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jns\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jnz\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jnz\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jo\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jo\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jp\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jp\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jrcxz\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"js\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} js\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"jz\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{disp32} jz\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lahf\", \"category\": \"BASE-FLAGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"r\", \"r\", \"r\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lea\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"AGEN\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"leavew\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rbp\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"bp\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"sp\"], \"src\": true, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"leave\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rbp\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rbp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"lfs\", \"category\": \"BASE-SEGOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"fs\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"lfs\", \"category\": \"BASE-SEGOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 48}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"fs\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"lfs\", \"category\": \"BASE-SEGOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 80}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"fs\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"lgs\", \"category\": \"BASE-SEGOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"gs\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"lgs\", \"category\": \"BASE-SEGOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 48}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"gs\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"lgs\", \"category\": \"BASE-SEGOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 80}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"gs\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"lodsb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lodsd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lodsw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"loop\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"loope\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"loopne\", \"category\": \"BASE-COND_BR\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"LABEL\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 0}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lss\", \"category\": \"BASE-SEGOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ss\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"lss\", \"category\": \"BASE-SEGOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 48}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ss\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"lss\", \"category\": \"BASE-SEGOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 80}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ss\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"{load} mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"{load} mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"{load} mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"{load} mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"{load} mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"{load} mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"{load} mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"es\", \"cs\", \"ss\", \"ds\", \"fs\", \"gs\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"es\", \"cs\", \"ss\", \"ds\", \"fs\", \"gs\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"es\", \"cs\", \"ss\", \"ds\", \"fs\", \"gs\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"es\", \"cs\", \"ss\", \"ds\", \"fs\", \"gs\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"es\", \"ss\", \"ds\", \"fs\", \"gs\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"es\", \"ss\", \"ds\", \"fs\", \"gs\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": false, \"dest\": true, \"width\": 8, \"magic\": true},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16, \"magic\": true},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": false, \"dest\": true, \"width\": 32, \"magic\": true},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": false, \"dest\": true, \"width\": 64, \"magic\": true},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16, \"magic\": true}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": false, \"width\": 32, \"magic\": true}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": false, \"width\": 64, \"magic\": true}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"movsd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"movsw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movsx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movzx\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"cr0\", \"cr2\", \"cr3\", \"cr4\", \"cr8\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"cr0\", \"cr2\", \"cr3\", \"cr4\", \"cr8\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"dr0\", \"dr1\", \"dr2\", \"dr3\", \"dr4\", \"dr5\", \"dr6\", \"dr7\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mov\", \"category\": \"BASE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"dr0\", \"dr1\", \"dr2\", \"dr3\", \"dr4\", \"dr5\", \"dr6\", \"dr7\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"mul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"mul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"mul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"mul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"dx\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"mul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"mul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rdx\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"mul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"dx\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"mul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"edx\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"mul\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rdx\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"undef\", \"undef\", \"undef\", \"undef\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock neg\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"nop\", \"category\": \"BASE-NOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"nop\", \"category\": \"BASE-WIDENOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"nop\", \"category\": \"BASE-WIDENOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"nop\", \"category\": \"BASE-WIDENOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"nop\", \"category\": \"BASE-WIDENOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"nop\", \"category\": \"BASE-WIDENOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"nop\", \"category\": \"BASE-WIDENOP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lock not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lock not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lock not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"lock not\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock or\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"pop\", \"category\": \"BASE-POP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ]\n  },\n  {\"name\": \"pop\", \"category\": \"BASE-POP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ]\n  },\n  {\"name\": \"pop\", \"category\": \"BASE-POP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ]\n  },\n  {\"name\": \"pop\", \"category\": \"BASE-POP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ]\n  },\n  {\"name\": \"popw\", \"category\": \"BASE-POP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"fs\"], \"src\": false, \"dest\": true, \"width\": 16, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ]\n  },\n  {\"name\": \"pop\", \"category\": \"BASE-POP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"fs\"], \"src\": false, \"dest\": true, \"width\": 16, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ]\n  },\n  {\"name\": \"popw\", \"category\": \"BASE-POP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"gs\"], \"src\": false, \"dest\": true, \"width\": 16, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ]\n  },\n  {\"name\": \"pop\", \"category\": \"BASE-POP\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"gs\"], \"src\": false, \"dest\": true, \"width\": 16, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ]\n  },\n  {\"name\": \"popfw\", \"category\": \"BASE-POP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"w\", \"w\", \"w\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"push\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"push\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"push\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"push\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"pushw\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"push\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"pushw\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"pushw\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"push\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"push\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"pushw\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"fs\"], \"src\": true, \"dest\": false, \"width\": 16, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"push\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"fs\"], \"src\": true, \"dest\": false, \"width\": 16, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"pushw\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"gs\"], \"src\": true, \"dest\": false, \"width\": 16, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 16}\n    ]\n  },\n  {\"name\": \"push\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"gs\"], \"src\": true, \"dest\": false, \"width\": 16, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 64}\n    ]\n  },\n  {\"name\": \"pushfw\", \"category\": \"BASE-PUSH\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"r\", \"r\", \"r\", \"r\", \"r\", \"r\", \"r\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcl\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rcr\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r/w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe cmpsb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe cmpsd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe cmpsw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe scasb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe scasd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe scasw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne cmpsb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne cmpsd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne cmpsw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne scasb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne scasd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne scasw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"cw\", \"cw\", \"cw\", \"r/cw\", \"cw\", \"\", \"\", \"r\", \"cw\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe lodsb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne lodsb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe lodsd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne lodsd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe lodsw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne lodsw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe movsb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne movsb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe movsd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne movsd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe movsw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne movsw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [\"rsi\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe stosb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne stosb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe stosd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne stosd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repe stosw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"repne stosw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"rcx\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"retfw\", \"category\": \"BASE-RET\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ]\n  },\n  {\"name\": \"retf\", \"category\": \"BASE-RET\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ]\n  },\n  {\"name\": \"retfq\", \"category\": \"BASE-RET\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 128}\n    ]\n  },\n  {\"name\": \"retfw\", \"category\": \"BASE-RET\", \"is_control_flow\": true,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ]\n  },\n  {\"name\": \"retf\", \"category\": \"BASE-RET\", \"is_control_flow\": true,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ]\n  },\n  {\"name\": \"retfq\", \"category\": \"BASE-RET\", \"is_control_flow\": true,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 128}\n    ]\n  },\n  {\"name\": \"ret\", \"category\": \"BASE-RET\", \"is_control_flow\": true,\n    \"operands\": [\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ]\n  },\n  {\"name\": \"ret\", \"category\": \"BASE-RET\", \"is_control_flow\": true,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rsp\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [\"rsp\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rol\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ror\", \"category\": \"BASE-ROTATE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"rsm\", \"category\": \"BASE-SYSRET\", \"is_control_flow\": true,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rip\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"w\", \"w\", \"w\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sahf\", \"category\": \"BASE-FLAGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sar\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setb\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setb\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setb\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setbe\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setbe\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setbe\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setl\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setl\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setl\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setle\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setle\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setle\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnb\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnb\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnb\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnbe\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnbe\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnbe\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"r\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnl\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnl\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnl\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnle\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnle\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnle\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"r\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setno\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setno\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setno\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnp\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnp\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnp\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setns\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setns\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setns\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnz\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnz\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setnz\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"seto\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"seto\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"seto\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setp\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setp\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setp\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"r\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sets\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sets\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sets\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setz\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setz\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"setz\", \"category\": \"BASE-SETCC\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": false, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shl\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shld\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shr\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [\"1\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"shrd\", \"category\": \"BASE-SHIFT\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8, \"is_signed\": false}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"undef\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"stc\", \"category\": \"BASE-FLAGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"\", \"\", \"\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"std\", \"category\": \"BASE-FLAGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"w\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sti\", \"category\": \"BASE-FLAGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"w\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"stosb\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"stosd\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"stosw\", \"category\": \"BASE-STRINGOP\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"MEM\", \"values\": [\"rdi\"], \"src\": false, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16},\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"\", \"\", \"\", \"\", \"r\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"r8b\", \"r9b\", \"r10b\", \"r11b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"spl\", \"bpl\", \"sil\", \"dil\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"{load} sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": true, \"width\": 8, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": true, \"width\": 16, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": true, \"width\": 32, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": true, \"width\": 64, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock sub\", \"category\": \"BASE-BINARY\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"test\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"test\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"test\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"test\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\"], \"src\": true, \"dest\": false, \"width\": 8, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"test\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ax\"], \"src\": true, \"dest\": false, \"width\": 16, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"test\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"eax\"], \"src\": true, \"dest\": false, \"width\": 32, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"test\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\"], \"src\": true, \"dest\": false, \"width\": 64, \"magic\": true},\n      {\"type_\": \"IMM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"ud2\", \"category\": \"BASE-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"xadd\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"xadd\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"xadd\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\", \"spl\", \"bpl\", \"sil\", \"dil\", \"r8b\", \"r9b\", \"r10b\", \"r11b\", \"r12b\", \"r13b\", \"r14b\", \"r15b\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"xadd\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"xadd\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"al\", \"cl\", \"dl\", \"bl\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"xadd\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8},\n      {\"type_\": \"REG\", \"values\": [\"ah\", \"ch\", \"dh\", \"bh\"], \"src\": true, \"dest\": true, \"width\": 8}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"xadd\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 16},\n      {\"type_\": \"REG\", \"values\": [\"ax\", \"cx\", \"dx\", \"bx\", \"sp\", \"bp\", \"si\", \"di\", \"r8w\", \"r9w\", \"r10w\", \"r11w\", \"r12w\", \"r13w\", \"r14w\", \"r15w\"], \"src\": true, \"dest\": true, \"width\": 16}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"xadd\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 32},\n      {\"type_\": \"REG\", \"values\": [\"eax\", \"ecx\", \"edx\", \"ebx\", \"esp\", \"ebp\", \"esi\", \"edi\", \"r8d\", \"r9d\", \"r10d\", \"r11d\", \"r12d\", \"r13d\", \"r14d\", \"r15d\"], \"src\": true, \"dest\": true, \"width\": 32}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"xadd\", \"category\": \"BASE-SEMAPHORE\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"w\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lock xor\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"xor\", \"category\": \"BASE-LOGICAL\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"w\", \"w\", \"undef\", \"w\", \"w\", \"\", \"\", \"\", \"w\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"lfence\", \"category\": \"SSE2-MISC\", \"is_control_flow\": false,\n    \"operands\": [\n\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"cmovz\", \"category\": \"BASE-CMOV\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"cmovnz\", \"category\": \"BASE-CMOV\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": false, \"dest\": true, \"width\": 64},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": [\n      {\"type_\": \"FLAGS\", \"values\": [\"\", \"\", \"\", \"r\", \"\", \"\", \"\", \"\", \"\"], \"src\": false, \"dest\": false, \"width\": 0}\n    ]\n  },\n  {\"name\": \"movaps\", \"category\": \"SSE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"xtype\": \"f32\", \"values\": [\"xmm0\", \"xmm1\", \"xmm2\", \"xmm3\", \"xmm4\", \"xmm5\", \"xmm6\", \"xmm7\", \"xmm8\", \"xmm9\", \"xmm10\", \"xmm11\", \"xmm12\", \"xmm13\", \"xmm14\", \"xmm15\"], \"src\": false, \"dest\": true, \"width\": 128},\n      {\"type_\": \"MEM\", \"values\": [], \"src\": true, \"dest\": false, \"width\": 128}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movaps\", \"category\": \"SSE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"xtype\": \"f32\", \"values\": [\"xmm0\", \"xmm1\", \"xmm2\", \"xmm3\", \"xmm4\", \"xmm5\", \"xmm6\", \"xmm7\", \"xmm8\", \"xmm9\", \"xmm10\", \"xmm11\", \"xmm12\", \"xmm13\", \"xmm14\", \"xmm15\"], \"src\": false, \"dest\": true, \"width\": 128},\n      {\"type_\": \"REG\", \"xtype\": \"f32\", \"values\": [\"xmm0\", \"xmm1\", \"xmm2\", \"xmm3\", \"xmm4\", \"xmm5\", \"xmm6\", \"xmm7\", \"xmm8\", \"xmm9\", \"xmm10\", \"xmm11\", \"xmm12\", \"xmm13\", \"xmm14\", \"xmm15\"], \"src\": true, \"dest\": false, \"width\": 128}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movaps\", \"category\": \"SSE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"MEM\", \"values\": [], \"src\": false, \"dest\": true, \"width\": 128},\n      {\"type_\": \"REG\", \"xtype\": \"f32\", \"values\": [\"xmm0\", \"xmm1\", \"xmm2\", \"xmm3\", \"xmm4\", \"xmm5\", \"xmm6\", \"xmm7\", \"xmm8\", \"xmm9\", \"xmm10\", \"xmm11\", \"xmm12\", \"xmm13\", \"xmm14\", \"xmm15\"], \"src\": true, \"dest\": false, \"width\": 128}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movaps\", \"category\": \"SSE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"xtype\": \"f32\", \"values\": [\"xmm0\", \"xmm1\", \"xmm2\", \"xmm3\", \"xmm4\", \"xmm5\", \"xmm6\", \"xmm7\", \"xmm8\", \"xmm9\", \"xmm10\", \"xmm11\", \"xmm12\", \"xmm13\", \"xmm14\", \"xmm15\"], \"src\": false, \"dest\": true, \"width\": 128},\n      {\"type_\": \"REG\", \"xtype\": \"f32\", \"values\": [\"xmm0\", \"xmm1\", \"xmm2\", \"xmm3\", \"xmm4\", \"xmm5\", \"xmm6\", \"xmm7\", \"xmm8\", \"xmm9\", \"xmm10\", \"xmm11\", \"xmm12\", \"xmm13\", \"xmm14\", \"xmm15\"], \"src\": true, \"dest\": false, \"width\": 128}\n    ],\n    \"implicit_operands\": []\n  },\n  {\"name\": \"movhps\", \"category\": \"SSE-DATAXFER\", \"is_control_flow\": false,\n    \"operands\": [\n      {\"type_\": \"REG\", \"xtype\": \"f32\", \"values\": [\"xmm0\", \"xmm1\", \"xmm2\", \"xmm3\", \"xmm4\", \"xmm5\", \"xmm6\", \"xmm7\", \"xmm8\", \"xmm9\", \"xmm10\", \"xmm11\", \"xmm12\", \"xmm13\", \"xmm14\", \"xmm15\"], \"src\": false, \"dest\": true, \"width\": 128},\n      {\"type_\": \"REG\", \"values\": [\"rax\", \"rcx\", \"rdx\", \"rbx\", \"rsp\", \"rbp\", \"rsi\", \"rdi\", \"r8\", \"r9\", \"r10\", \"r11\", \"r12\", \"r13\", \"r14\", \"r15\"], \"src\": true, \"dest\": false, \"width\": 64}\n    ],\n    \"implicit_operands\": []\n  }\n]\n"
  },
  {
    "path": "tests/x86_tests/model_common.py",
    "content": "\"\"\"\nFile: Collection of helper classes for x86 model tests.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nfrom __future__ import annotations\nfrom typing import List, Generator, Literal\nfrom dataclasses import dataclass\n\nimport os\nimport tempfile\nfrom pathlib import Path\n\nfrom rvzr.tc_components.test_case_code import TestCaseProgram\nfrom rvzr.tc_components.test_case_data import InputData\nfrom rvzr.isa_spec import InstructionSet\nfrom rvzr.elf_parser import ELFParser\nfrom rvzr.arch.x86.target_desc import X86TargetDesc\nfrom rvzr.arch.x86.asm_parser import X86AsmParser\nfrom rvzr.arch.x86.generator import X86Generator\nfrom rvzr.config import CONF\n\ntest_path = Path(__file__).resolve()\ntest_dir = test_path.parent\n\nASM_HEADER = \"\"\"\n.intel_syntax noprefix\n.section .data.main\n\"\"\"\n\n# Base addresses for calculating expected contract traces\nPC0 = 0x8  # Initial program counter offset for Unicorn backend\nDATA_BASE = 0x1000000  # Base address for data section in test environment\nCODE_BASE = 0x8000  # Base address for code section in test environment\nMAIN_OFFSET = 0x1000  # Offset for main actor code section\nFAULTY_OFFSET = 0x2000  # Offset for faulty actor code section\n\n# Default values for memory and registers in test inputs\nMEM_DEFAULT_VALUE = 1\nREG_DEFAULT_VALUE = 2\nMEM_FAULTY_DEFAULT_VALUE = 3\nRSP_DEFAULT_VALUE = FAULTY_OFFSET - 8\n\n# Register indices for x86-64 (used in test input array indexing)\nRAX, RBX, RCX, RDX, RSI, RDI, FLAGS, RSP = 0, 1, 2, 3, 4, 5, 6, 7\nNUM_TEST_GPRS = 7  # Number of GPRs initialized in default test inputs\n\n# SIMD register indices\nXMM0, XMM1 = 0, 1\n\nBackend = Literal[\"dr\", \"uc\"]\n\n\n@dataclass\nclass Inst:\n    \"\"\"Instruction with its size and memory address.\"\"\"\n    text: str\n    size: int\n    mem_address: int\n    mem_value: int\n    pc_offset: int = 0\n\n\nclass InstList:\n    \"\"\"List of instructions with their memory addresses.\"\"\"\n    instructions: List[Inst]\n\n    def __init__(self, instructions: List[Inst], backend: Backend):\n        self.backend = backend\n        self.start_offset = PC0 if backend == \"uc\" else 0\n\n        # Wrap instructions with backend-specific macros\n        wrapped = self._wrap_instructions_for_backend(instructions)\n        self.set_offsets(wrapped)\n        self.instructions = wrapped\n\n    def _wrap_instructions_for_backend(self, instructions: List[Inst]) -> List[Inst]:\n        if self.backend == \"dr\":\n            return self._wrap_instructions_for_dr(instructions)\n        # uc\n        return self._wrap_instructions_for_uc(instructions)\n\n    def _wrap_instructions_for_dr(self, instructions: List[Inst]) -> List[Inst]:\n        \"\"\"\n        DynamoRIO test cases are wrapped in measurement macros plus a NOP at test_case_exit.\n        \"\"\"\n        wrapped = []\n        wrapped.append(Inst(\".macro.measurement_start:\", 8, 0, 0))\n        wrapped.extend(instructions)\n        wrapped.append(Inst(\".macro.measurement_end:\", 8, 0, 0))\n        wrapped.append(Inst(\".test_case_exit:nop\", 1, 0, 0))\n        return wrapped\n\n    def _wrap_instructions_for_uc(self, instructions: List[Inst]) -> List[Inst]:\n        \"\"\"\n        Unicorn test cases have measurement_end macro inserted automatically.\n        \"\"\"\n        wrapped = []\n        wrapped.extend(instructions)\n        wrapped.append(Inst(\".macro.measurement_end:\", 8, 0, 0))\n        wrapped.append(Inst(\".test_case_exit:nop\", 1, 0, 0))\n        return wrapped\n\n    def __iter__(self) -> Generator[Inst, None, None]:\n        yield from self.instructions\n\n    def __getitem__(self, index: int) -> Inst:\n        return self.instructions[index]\n\n    def set_offsets(self, instructions: List[Inst]) -> None:\n        \"\"\" Set the pc_offset for each instruction in a list \"\"\"\n        pc = self.start_offset\n        for inst in instructions:\n            inst.pc_offset = pc\n            pc += inst.size\n\n    def to_test_case(self) -> TestCaseProgram:\n        \"\"\"Load a test case from the assembly string.\n\n        :return: Parsed TestCaseProgram object\n        \"\"\"\n        min_x86_path = test_dir / \"min_x86.json\"\n\n        asm_str = ASM_HEADER\n        asm_str += \"\\n\".join([x.text for x in self.instructions])\n\n        instruction_set = InstructionSet(min_x86_path.absolute().as_posix())\n        target_desc = X86TargetDesc()\n        elf_parser = ELFParser(target_desc)\n        asm_parser = X86AsmParser(instruction_set, target_desc)\n        generator = X86Generator(CONF.program_generator_seed, instruction_set, target_desc,\n                                 asm_parser, elf_parser)\n\n        # Create temp file and parse\n        with tempfile.NamedTemporaryFile(\n                mode='w', delete=False, suffix='.asm', encoding='utf-8') as f:\n            f.write(asm_str)\n            temp_path = f.name\n\n        try:\n            tc: TestCaseProgram = asm_parser.parse_file(temp_path, generator, elf_parser)\n        finally:\n            os.unlink(temp_path)\n\n        return tc\n\n    def get_expected_observations(self, execution_order: List[int], observe_pc: bool,\n                                  observe_mem: bool, observe_val: bool) -> List[int]:\n        \"\"\"Get expected observations for executed instructions.\n\n        :param execution_order: List of instruction indices in execution order\n        :param observe_pc: Whether to observe program counter values\n        :param observe_mem: Whether to observe memory addresses\n        :param observe_val: Whether to observe memory values\n        :return: List of expected observation values\n        \"\"\"\n        adjusted_order = self._adjust_execution_order_for_backend(execution_order)\n        return self._collect_observations(adjusted_order, observe_pc, observe_mem, observe_val)\n\n    def _adjust_execution_order_for_backend(self, execution_order: List[int]) -> List[int]:\n        last_org_id = len(self.instructions) - 3\n        last_actual_id = len(self.instructions) - 1\n\n        if self.backend == \"dr\":\n            return self._adjust_order_for_dr(execution_order, last_org_id, last_actual_id)\n        else:  # uc\n            return self._adjust_order_for_uc(execution_order, last_org_id, last_actual_id)\n\n    def _adjust_order_for_dr(self, execution_order: List[int], last_org_id: int,\n                             last_actual_id: int) -> List[int]:\n        \"\"\"Adjust execution order for DynamoRIO backend.\n        DynamoRIO includes measurement_start at the beginning, and appends\n        measurement_end + NOP at the end.\n        \"\"\"\n        updated_order = [0]  # measurement_start\n        for idx in execution_order:\n            updated_order.append(idx + 1)\n            if idx + 1 == last_org_id:\n                updated_order.append(last_actual_id - 1)  # measurement_end\n                updated_order.append(last_actual_id)  # NOP at test_case_exit\n        return updated_order\n\n    def _adjust_order_for_uc(self, execution_order: List[int], last_org_id: int,\n                             last_actual_id: int) -> List[int]:\n        \"\"\"Adjust execution order for Unicorn backend.\n        Unicorn appends measurement_end at the end. The NOP is not observed.\n        \"\"\"\n        updated_order = []\n        for idx in execution_order:\n            updated_order.append(idx)\n            if idx == last_org_id:\n                updated_order.append(last_actual_id - 1)  # measurement_end\n                # NOP is not observed on UC\n        return updated_order\n\n    def _collect_observations(self, execution_order: List[int], observe_pc: bool, observe_mem: bool,\n                              observe_val: bool) -> List[int]:\n        \"\"\"Collect observations based on execution order and observation flags.\n        \"\"\"\n        observations = []\n        for exec_id in execution_order:\n            inst = self.instructions[exec_id]\n            if inst.size == 0:  # not an actual instruction\n                continue\n            if observe_pc:\n                observations.append(inst.pc_offset)\n            if observe_mem and inst.mem_address != 0:\n                observations.append(inst.mem_address)\n            if observe_val and inst.mem_value != 0:\n                observations.append(inst.mem_value)\n        return observations\n\n\nclass InputBuilder:\n    \"\"\"Helper class to create InputData for x86 tests.\"\"\"\n\n    def get_default_input(self) -> InputData:\n        \"\"\"Create default InputData for x86 tests.\n\n        :return: InputData with default values for memory and registers\n        \"\"\"\n        input_ = InputData()\n        input_[0]['main'][0] = MEM_DEFAULT_VALUE\n        input_[0]['main'][1] = MEM_DEFAULT_VALUE\n        input_[0]['faulty'][0] = MEM_FAULTY_DEFAULT_VALUE\n        input_[0]['faulty'][1] = MEM_FAULTY_DEFAULT_VALUE\n        for i in range(NUM_TEST_GPRS):\n            input_[0]['gpr'][i] = REG_DEFAULT_VALUE\n        return input_\n\n    def get_input_with_zeroed_gprs(self, *gpr_indices: int) -> InputData:\n        \"\"\"Create InputData with specified GPRs set to 0 for taint tracking.\n\n        :param gpr_indices: Register indices to initialize to 0\n        :return: InputData with specified registers set to 0\n        \"\"\"\n        input_ = InputData()\n        for gpr_idx in gpr_indices:\n            input_[0]['gpr'][gpr_idx] = 0\n        return input_\n\n    def get_input_with_zeroed_memory(self, **memory_regions: int) -> InputData:\n        \"\"\"Create InputData with specified memory regions set to 0 for taint tracking.\n\n        :param memory_regions: Keyword arguments where key is region name (e.g., 'main', 'faulty')\n                              and value is the index within that region\n        :return: InputData with specified memory regions set to 0\n        \"\"\"\n        input_ = InputData()\n        for region, idx in memory_regions.items():\n            input_[0][region][idx] = 0\n        return input_\n"
  },
  {
    "path": "tests/x86_tests/unit_dr_decoder.py",
    "content": "\"\"\"\nFile: Collection of unit tests for DynamoRIO backend adaptor.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# pylint: disable=too-many-arguments\n# pylint: disable=too-few-public-methods\n# pylint: disable=too-many-public-methods\n# pylint: disable=protected-access\n# pylint: disable=missing-function-docstring\n\nimport unittest\nimport struct\nimport os\n# from unittest.mock import MagicMock\nfrom tempfile import NamedTemporaryFile\nfrom typing import Any, List, Dict\n\nfrom rvzr.model_dynamorio.trace_decoder import TraceDecoder, TraceEntryType\nfrom rvzr.model_dynamorio.trace_decoder import DebugTraceEntryType\n\n# ------------------------------------------------------------------------------\n# Leakage trace representation\n# ------------------------------------------------------------------------------\n# Content of the test trace\nTEST_TRACE: List[Dict[str, Any]] = [\n    {\n        \"addr\": 0x0,\n        \"size\": 8,\n        \"type\": TraceEntryType.ENTRY_PC.value\n    },\n    {\n        \"addr\": 0xdeadbeef,\n        \"size\": 4,\n        \"type\": TraceEntryType.ENTRY_READ.value\n    },\n    {\n        \"addr\": 0xcafecafe,\n        \"size\": 8,\n        \"type\": TraceEntryType.ENTRY_WRITE.value\n    },\n    {\n        \"addr\": 11,\n        \"size\": 0,\n        \"type\": TraceEntryType.ENTRY_EXCEPTION.value\n    },\n    {\n        \"addr\": 0x0,\n        \"size\": 0x0,\n        \"type\": TraceEntryType.ENTRY_EOT.value\n    },\n]\n# Format string to parse a trace entry\nTRACE_FMT = \"<QIBxxx\"\n\n# ------------------------------------------------------------------------------\n# Debug trace representation\n# ------------------------------------------------------------------------------\n# Contents of the debug test trace\nTEST_DBG_TRACE: List[Dict[str, Any]] = [\n    {\n        \"type\": DebugTraceEntryType.ENTRY_REG_DUMP.value,\n        \"spec\": 0,\n        \"xax\": 0xaaaaaaaa,\n        \"xbx\": 0xbbbbbbbb,\n        \"xcx\": 0xcccccccc,\n        \"xdx\": 0xdddddddd,\n        \"xsi\": 0xeeeeeeee,\n        \"xdi\": 0xffffffff,\n        \"pc\": 0xdeadbeef,\n    },\n    {\n        \"type\": DebugTraceEntryType.ENTRY_LOC.value,\n        \"spec\": 0,\n        \"offset\": 0xABCD,\n        \"module_name\": \"/usr/lib/test.so\" + (\"\\x00\" * 32),\n    },\n    {\n        \"type\": DebugTraceEntryType.ENTRY_READ.value,\n        \"spec\": 0,\n        \"address\": 0xcafecafe,\n        \"value\": 0xabababab,\n        \"size\": 0xf,\n    },\n    {\n        \"type\": DebugTraceEntryType.ENTRY_WRITE.value,\n        \"spec\": 0,\n        \"address\": 0xcdcdcdcd,\n        \"value\": 0xcafecafe,\n        \"size\": 8,\n    },\n    {\n        \"type\": DebugTraceEntryType.ENTRY_CHECKPOINT.value,\n        \"spec\": 1,\n        \"rollback_pc\": 0xdeadbeef,\n        \"cur_window_size\": 1,\n        \"cur_store_log_size\": 1,\n    },\n    {\n        \"type\": DebugTraceEntryType.ENTRY_ROLLBACK_STORE.value,\n        \"spec\": 1,\n        \"addr\": 0xcdcdcdcd,\n        \"val\": 0xcafecafe,\n        \"size\": 0xf,\n        \"nesting_level\": 1,\n    },\n    {\n        \"type\": DebugTraceEntryType.ENTRY_ROLLBACK.value,\n        \"spec\": 1,\n        \"nesting\": 1,\n        \"rollback_pc\": 0xdeadbeef,\n    },\n    {\n        \"type\": DebugTraceEntryType.ENTRY_EXCEPTION.value,\n        \"spec\": 0,\n        \"signal\": 11,\n        \"address\": 0xcdcdcdcd,\n    },\n    {\n        \"type\": DebugTraceEntryType.ENTRY_EOT.value,\n        \"spec\": 0,\n    },\n]\n# Shared fields for debug entries\nDBG_TRACE_PREFIX = '<BB' + ('x' * 6)\n# Fields specific to each entry type\nDBG_TRACE_FMT = {\n    DebugTraceEntryType.ENTRY_EOT: DBG_TRACE_PREFIX,\n    DebugTraceEntryType.ENTRY_REG_DUMP: DBG_TRACE_PREFIX + ('Q' * 7),\n    DebugTraceEntryType.ENTRY_READ: DBG_TRACE_PREFIX + 'QQQ',\n    DebugTraceEntryType.ENTRY_WRITE: DBG_TRACE_PREFIX + 'QQQ',\n    DebugTraceEntryType.ENTRY_LOC: DBG_TRACE_PREFIX + 'Q' + ('c' * 48),\n    DebugTraceEntryType.ENTRY_EXCEPTION: DBG_TRACE_PREFIX + 'ixxxxQ',\n    DebugTraceEntryType.ENTRY_CHECKPOINT: DBG_TRACE_PREFIX + 'QQI',\n    DebugTraceEntryType.ENTRY_ROLLBACK: DBG_TRACE_PREFIX + 'IxxxxQ',\n    DebugTraceEntryType.ENTRY_ROLLBACK_STORE: DBG_TRACE_PREFIX + 'QQIxxxxQ',\n}\n\n# ------------------------------------------------------------------------------\n# Testsuite\n# ------------------------------------------------------------------------------\n\n\nclass DRTraceDecodeTest(unittest.TestCase):\n    \"\"\"\n    Suite of tests for the DR trace decoder\n    \"\"\"\n\n    # --------------------------------------------------------------------------\n    # Helpers\n    # --------------------------------------------------------------------------\n    def _find_entry_of_type(self, t: TraceEntryType) -> dict[str, Any]:\n        for e in TEST_TRACE:\n            if e[\"type\"] == t.value:\n                return e\n\n        raise ValueError(f\"No entry for type {t}\")\n\n    def _encode_from_dict(self, entry: dict[str, Any]) -> bytes:\n        return struct.pack(TRACE_FMT, entry[\"addr\"], entry[\"size\"], entry[\"type\"])\n\n    def _check_trace_equivalence(self, expected: dict[str, Any], decoded: Any) -> None:\n        self.assertEqual(expected[\"addr\"], decoded.addr)\n        self.assertEqual(expected[\"size\"], decoded.size)\n        self.assertEqual(expected[\"type\"], TraceEntryType(decoded.type).value)\n\n    # --------------------------------------------------------------------------\n    # Test cases\n    # --------------------------------------------------------------------------\n    def test_trace_decoding(self) -> None:\n        decoder = TraceDecoder()\n\n        # Encode the special marker\n        packed_trace = struct.pack(\"c\", \"T\".encode('utf-8'))\n        packed_trace += b'\\x00' * 7  # Padding to ensure the marker is 8 bytes long\n\n        # Encode all entries\n        for test_entry in TEST_TRACE:\n            packed_trace += self._encode_from_dict(test_entry)\n\n        with NamedTemporaryFile(\"wb\", delete=False) as f:\n            # Write encoded entries to file\n            f.write(packed_trace)\n            f.close()\n            # Decode the file\n            parsed_traces = decoder.decode_trace_file(f.name)\n            self.assertEqual(len(parsed_traces), 1)\n            # Check decoded entries\n            for idx, decoded in enumerate(parsed_traces[0]):\n                self._check_trace_equivalence(TEST_TRACE[idx], decoded)\n\n    def test_is_corrupted(self) -> None:\n        decoder = TraceDecoder()\n\n        # Encode the special marker\n        marker = struct.pack(\"c\", \"T\".encode('utf-8'))\n        marker += b'\\x00' * 7  # Padding to ensure the marker is 8 bytes long\n        pc = self._encode_from_dict(self._find_entry_of_type(TraceEntryType.ENTRY_PC))\n        xcpt = self._encode_from_dict(self._find_entry_of_type(TraceEntryType.ENTRY_EXCEPTION))\n        eot = self._encode_from_dict(self._find_entry_of_type(TraceEntryType.ENTRY_EOT))\n\n        # Only EOT is valid at the end of the trace\n        traces = [(pc, True), (pc + xcpt, True), (pc + eot, False), (pc + xcpt + eot, False)]\n\n        for t in traces:\n            with NamedTemporaryFile(\"wb\", delete=False) as f:\n                # Write encoded entries to file\n                f.write(marker + t[0])\n\n            # Decode the file\n            if t[1]:\n                with self.assertRaises(ValueError):\n                    decoder.decode_trace_file(f.name)\n            else:\n                parsed_traces = decoder.decode_trace_file(f.name)\n                self.assertEqual(len(parsed_traces), 1)\n\n            os.remove(f.name)\n\n\nclass DRDebugTraceDecodeTest(unittest.TestCase):\n    \"\"\"\n    Suite of tests for the DR trace decoder for debug traces\n    \"\"\"\n\n    # --------------------------------------------------------------------------\n    # Helpers\n    # --------------------------------------------------------------------------\n    def _find_entry_of_type(self, t: DebugTraceEntryType) -> dict[str, Any]:\n        for e in TEST_DBG_TRACE:\n            if e[\"type\"] == t.value:\n                return e\n\n        raise ValueError(f\"No debug entry for type {t}\")\n\n    def _encode_from_dict(self, entry: dict[str, Any]) -> bytes:\n        # Get format string depending on the entry type\n        fmt = DBG_TRACE_FMT[DebugTraceEntryType(entry[\"type\"])]\n        # Add padding if needed\n        if struct.calcsize(fmt) < 64:\n            fmt += 'x' * (64 - struct.calcsize(fmt))\n\n        # Get the values to encode\n        vals = list(entry.values())\n        to_pack = []\n        for v in vals:\n            if isinstance(v, str):\n                # Encode strings as char arrays\n                to_pack.extend([x.encode('utf-8') for x in list(v)])\n            else:\n                # Otherwise just use the value in the map\n                to_pack.append(v)\n\n        return struct.pack(fmt, *to_pack)\n\n    def _check_dbg_trace_equivalence(self, expected: dict[str, Any], decoded: Any) -> None:\n        type_ = DebugTraceEntryType(decoded.type)\n\n        if type_ == DebugTraceEntryType.ENTRY_REG_DUMP:\n            self.assertEqual(expected[\"type\"], type_.value)\n            self.assertEqual(expected[\"spec\"], decoded.nesting_level)\n            self.assertEqual(expected[\"xax\"], decoded.regs.xax)\n            self.assertEqual(expected[\"xbx\"], decoded.regs.xbx)\n            self.assertEqual(expected[\"xcx\"], decoded.regs.xcx)\n            self.assertEqual(expected[\"xdx\"], decoded.regs.xdx)\n            self.assertEqual(expected[\"xsi\"], decoded.regs.xsi)\n            self.assertEqual(expected[\"xdi\"], decoded.regs.xdi)\n            self.assertEqual(expected[\"pc\"], decoded.regs.pc)\n        elif type_ == DebugTraceEntryType.ENTRY_LOC:\n            self.assertEqual(expected[\"type\"], type_.value)\n            self.assertEqual(expected[\"spec\"], decoded.nesting_level)\n            self.assertEqual(expected[\"offset\"], decoded.loc.offset)\n            self.assertEqual(expected[\"module_name\"],\n                             (b''.join(decoded.loc.module_name)).decode('utf-8'))\n        elif type_ == DebugTraceEntryType.ENTRY_READ:\n            self.assertEqual(expected[\"type\"], type_.value)\n            self.assertEqual(expected[\"spec\"], decoded.nesting_level)\n            self.assertEqual(expected[\"address\"], decoded.mem.address)\n            self.assertEqual(expected[\"value\"], decoded.mem.value)\n            self.assertEqual(expected[\"size\"], decoded.mem.size)\n        elif type_ == DebugTraceEntryType.ENTRY_WRITE:\n            self.assertEqual(expected[\"type\"], type_.value)\n            self.assertEqual(expected[\"spec\"], decoded.nesting_level)\n            self.assertEqual(expected[\"address\"], decoded.mem.address)\n            self.assertEqual(expected[\"value\"], decoded.mem.value)\n            self.assertEqual(expected[\"size\"], decoded.mem.size)\n        elif type_ == DebugTraceEntryType.ENTRY_CHECKPOINT:\n            self.assertEqual(expected[\"type\"], type_.value)\n            self.assertEqual(expected[\"spec\"], decoded.nesting_level)\n            self.assertEqual(expected[\"rollback_pc\"], decoded.checkpoint.rollback_pc)\n            self.assertEqual(expected[\"cur_window_size\"], decoded.checkpoint.cur_window_size)\n            self.assertEqual(expected[\"cur_store_log_size\"], decoded.checkpoint.cur_store_log_size)\n        elif type_ == DebugTraceEntryType.ENTRY_ROLLBACK_STORE:\n            self.assertEqual(expected[\"type\"], type_.value)\n            self.assertEqual(expected[\"spec\"], decoded.nesting_level)\n            self.assertEqual(expected[\"addr\"], decoded.rollback_store.addr)\n            self.assertEqual(expected[\"val\"], decoded.rollback_store.val)\n            self.assertEqual(expected[\"size\"], decoded.rollback_store.size)\n            self.assertEqual(expected[\"nesting_level\"], decoded.rollback_store.nesting_level)\n        elif type_ == DebugTraceEntryType.ENTRY_ROLLBACK:\n            self.assertEqual(expected[\"type\"], type_.value)\n            self.assertEqual(expected[\"spec\"], decoded.nesting_level)\n            self.assertEqual(expected[\"nesting\"], decoded.rollback.nesting)\n            self.assertEqual(expected[\"rollback_pc\"], decoded.rollback.rollback_pc)\n        elif type_ == DebugTraceEntryType.ENTRY_EXCEPTION:\n            self.assertEqual(expected[\"type\"], type_.value)\n            self.assertEqual(expected[\"spec\"], decoded.nesting_level)\n            self.assertEqual(expected[\"signal\"], decoded.xcpt.signal)\n            self.assertEqual(expected[\"address\"], decoded.xcpt.address)\n        elif type_ == DebugTraceEntryType.ENTRY_EOT:\n            self.assertEqual(expected[\"type\"], type_.value)\n            self.assertEqual(expected[\"spec\"], decoded.nesting_level)\n        else:\n            raise ValueError(\"Unknown debug trace type\")\n\n    # --------------------------------------------------------------------------\n    # Test cases\n    # --------------------------------------------------------------------------\n    def test_debug_entry_decoding(self) -> None:\n        decoder = TraceDecoder()\n\n        for original in TEST_DBG_TRACE:\n            # Encode entry to a bytes array\n            encoded = self._encode_from_dict(original)\n            # Decode it as an object\n            decoded = decoder._decode_debug_trace_entry(encoded)\n            # Test the decoded output\n            self._check_dbg_trace_equivalence(original, decoded)\n\n    def test_debug_trace_decoding(self) -> None:\n        decoder = TraceDecoder()\n\n        # Encode the special marker\n        packed_trace = struct.pack(\"c\", \"D\".encode('utf-8'))\n        packed_trace += b'\\x00' * 7  # Padding to ensure the marker is 8 bytes long\n\n        # Encode all entries\n        for test_entry in TEST_DBG_TRACE:\n            packed_trace += self._encode_from_dict(test_entry)\n\n        with NamedTemporaryFile(\"wb\", delete=False) as f:\n            # Write encoded entries to a file\n            f.write(packed_trace)\n            f.close()\n            # Decode the file\n            parsed_dbg_traces = decoder.decode_debug_trace_file(f.name)\n            self.assertEqual(len(parsed_dbg_traces), 1)\n            # Check decoded entries\n            for idx, decoded in enumerate(parsed_dbg_traces[0]):\n                self._check_dbg_trace_equivalence(TEST_DBG_TRACE[idx], decoded)\n\n    def test_is_corrupted(self) -> None:\n        decoder = TraceDecoder()\n\n        # Encode the special marker\n        marker = struct.pack(\"c\", \"D\".encode('utf-8'))\n        marker += b'\\x00' * 7  # Padding to ensure the marker is 8 bytes long\n        pc = self._encode_from_dict(self._find_entry_of_type(DebugTraceEntryType.ENTRY_REG_DUMP))\n        xcpt = self._encode_from_dict(self._find_entry_of_type(DebugTraceEntryType.ENTRY_EXCEPTION))\n        eot = self._encode_from_dict(self._find_entry_of_type(DebugTraceEntryType.ENTRY_EOT))\n\n        # Only EOT is valid at the end of the trace\n        traces = [(pc, True), (pc + xcpt, True), (pc + eot, False), (pc + xcpt + eot, False)]\n\n        for t in traces:\n            with NamedTemporaryFile(\"wb\", delete=False) as f:\n                # Write encoded entries to file\n                f.write(marker + t[0])\n\n            # Decode the file\n            if t[1]:\n                with self.assertRaises(ValueError):\n                    decoder.decode_debug_trace_file(f.name)\n            else:\n                parsed_traces = decoder.decode_debug_trace_file(f.name)\n                self.assertEqual(len(parsed_traces), 1)\n\n            os.remove(f.name)\n"
  },
  {
    "path": "tests/x86_tests/unit_fuzzer.py",
    "content": "\"\"\"\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# pylint: disable=missing-function-docstring\n# pylint: disable=missing-class-docstring\n# pylint: disable=protected-access\n\nimport os\nimport unittest\nimport tempfile\nfrom pathlib import Path\n\nfrom rvzr.factory import get_asm_parser, get_program_generator\nfrom rvzr.elf_parser import ELFParser\nfrom rvzr.isa_spec import InstructionSet\nfrom rvzr.config import CONF\nfrom rvzr.logs import update_logging_after_config_change\nfrom rvzr.arch.x86.target_desc import X86TargetDesc\nfrom rvzr.arch.x86.fuzzer import _create_fenced_test_case\n\nCONF.instruction_set = \"x86-64\"\ntest_path = Path(__file__).resolve()\ntest_dir = test_path.parent\n\n\n# ==================================================================================================\n# Tests\n# ==================================================================================================\nclass X86FuzzerTest(unittest.TestCase):\n\n    @classmethod\n    def setUpClass(cls) -> None:\n        CONF.logging_modes = []\n        update_logging_after_config_change()\n\n    def test__create_fenced_test_case(self) -> None:\n        # Test that the function _create_fenced_test_case adds fences to\n        # the assembly file in a correct way\n\n        instruction_set = InstructionSet((test_dir / \"min_x86.json\").absolute().as_posix())\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        asm_parser = get_asm_parser(instruction_set)\n        elf_parser = ELFParser(X86TargetDesc())\n\n        # original_asm = \"test.asm\"\n        # fenced_asm = \"fenced_test.asm\"\n        with tempfile.NamedTemporaryFile(delete=False) as original:\n            original_asm = original.name\n        with tempfile.NamedTemporaryFile(delete=False) as fenced:\n            fenced_asm = fenced.name\n\n        with open(original_asm, 'w') as f:\n            f.write(\"\"\"\n            .intel_syntax noprefix\n            .section .data.main\n            .function_1:\n            .macro.measurement_start:\n\n            # This is a comment\n            .byte 0x90\n\n            jne .l1\n            loopne .l1\n            .l1:\n            adc rax, rbx\n            cmp rbx, rcx\n\n            .test_case_exit:\n            \"\"\")\n\n        _ = _create_fenced_test_case(original_asm, fenced_asm, asm_parser, generator, elf_parser)\n        fenced_lines = []\n        with open(fenced_asm, 'r') as f:\n            for line in f:\n                fenced_lines.append(line.strip())\n\n        # clean up\n        os.unlink(original_asm)\n        os.unlink(fenced_asm)\n\n        # Check that the fences are placed in expected places\n        self.assertIn(\"lfence\", fenced_lines[8])\n        self.assertIn(\"lfence\", fenced_lines[13])\n        self.assertIn(\"lfence\", fenced_lines[15])\n        self.assertIn(\"lfence\", fenced_lines[17])\n\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "tests/x86_tests/unit_generators.py",
    "content": "\"\"\"\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# pylint: disable=missing-function-docstring\n# pylint: disable=missing-class-docstring\n\nimport unittest\nimport tempfile\nimport subprocess\nimport os\nfrom pathlib import Path\nfrom copy import deepcopy\n\nfrom rvzr.arch.x86.generator import X86Generator, _X86Printer, _X86PatchUndefinedFlagsPass\nfrom rvzr.arch.x86.target_desc import X86TargetDesc\nfrom rvzr.elf_parser import ELFParser\nfrom rvzr.factory import get_program_generator, get_asm_parser\nfrom rvzr.isa_spec import InstructionSet\nfrom rvzr.tc_components.actor import ActorMode\nfrom rvzr.tc_components.test_case_code import TestCaseProgram, Function, BasicBlock\nfrom rvzr.tc_components.test_case_binary import SymbolTableEntry\nfrom rvzr.code_generator import assemble\nfrom rvzr.config import CONF\nfrom rvzr.logs import update_logging_after_config_change\n\nCONF.instruction_set = \"x86-64\"\ntest_path = Path(__file__).resolve()\ntest_dir = test_path.parent\n\nASM_OPCODE = \"\"\"\n.intel_syntax noprefix\n.section .data.main\n.byte 0x90\n.test_case_exit:\n\"\"\"\n\n_ALL_CATEGORIES = [\n    \"3DNOW_PREFETCH-PREFETCH\", \"ADOX_ADCX-ADOX_ADCX\", \"BASE-BINARY\", \"BASE-BITBYTE\", \"BASE-CMOV\",\n    \"BASE-COND_BR\", \"BASE-CONVERT\", \"BASE-DATAXFER\", \"BASE-FLAGOP\", \"BASE-LOGICAL\", \"BASE-MISC\",\n    \"BASE-NOP\", \"BASE-POP\", \"BASE-PUSH\", \"BASE-ROTATE\", \"BASE-SEMAPHORE\", \"BASE-SETCC\",\n    \"BASE-SHIFT\", \"BASE-WIDENOP\", \"LONGMODE-CONVERT\", \"LONGMODE-DATAXFER\", \"LONGMODE-POP\",\n    \"LONGMODE-PUSH\", \"LONGMODE-SEMAPHORE\", \"MMX-MMX\", \"MMX-LOGICAL\", \"MMX-DATAXFER\", \"SSE2-MMX\",\n    \"SSE3-MMX\", \"SSSE3-MMX\", \"SSE-CONVERT\", \"SSE-DATAXFER\", \"SSE-MISC\", \"SSE-PREFETCH\", \"SSE-SSE\",\n    \"SSE2-CONVERT\", \"SSE2-DATAXFER\", \"SSE2-LOGICAL\", \"SSE2-MISC\", \"SSE2-SSE\", \"SSE3-DATAXFER\",\n    \"SSE3-SSE\", \"SSSE3-SSE\", \"SSE4-LOGICAL\", \"SSE4-SSE\", \"AVX-AVX\", \"AVX-BROADCAST\", \"AVX-DATAXFER\",\n    \"AVX-LOGICAL\", \"AVX-STTNI\", \"AVX2-AVX2\", \"AVX2-BROADCAST\", \"AVX2-DATAXFER\", \"AVX2-LOGICAL\",\n    \"AES-AES\", \"AVXAES-AES\", \"BMI1-BMI1\", \"BMI2-BMI2\", \"MOVBE-DATAXFER\", \"LZCNT-LZCNT\",\n    \"PCLMULQDQ-PCLMULQDQ\", \"VAES-VAES\", \"3DNOW-3DNOW\", \"VPCLMULQDQ-VPCLMULQDQ\", \"SHA-SHA\",\n    \"SSE4a-BITBYTE\", \"FMA-VFMA\", \"MOVDIR-MOVDIR\", \"GFNI-GFNI\", \"FMA4-FMA4\", \"AVX_VNNI-VEX\",\n    \"3DNOW-MMX\", \"LONGMODE-RET\", \"BASE-RET\", \"BASE-CALL\", \"BASE-UNCOND_BR\", \"SSE-LOGICAL_FP\",\n    \"AVX-LOGICAL_FP\", \"SSE2-LOGICAL_FP\", \"AVX2GATHER-AVX2GATHER\", \"BASE-STRINGOP\",\n    \"LONGMODE-STRINGOP\", \"CLDEMOTE-CLDEMOTE\", \"CLFLUSHOPT-CLFLUSHOPT\", \"CLFSH-MISC\", \"CLWB-CLWB\",\n    \"CLZERO-CLZERO\", \"PREFETCHWT1-PREFETCHWT1\", \"SERIALIZE-SERIALIZE\", \"BASE-SYSCALL\",\n    \"BASE-SYSRET\", \"BASE-SEGOP\", \"BASE-INTERRUPT\", \"LONGMODE-SYSRET\", \"PKU-PKU\", \"PCONFIG-PCONFIG\",\n    \"BASE-IO\", \"BASE-IOSTRINGOP\", \"LONGMODE-SYSCALL\", \"RDPRU-RDPRU\", \"RDPID-RDPID\", \"SMAP-SMAP\",\n    \"UINTR-UINTR, MCOMMIT-MISC\", \"PTWRITE-PTWRITE\", \"TBM-TBM\", \"AVX512EVEX-LOGICAL\",\n    \"AVX512EVEX-GFNI\", \"AVX512EVEX-EXPAND\", \"AVX512EVEX-DATAXFER\", \"AVX512EVEX-VFMA\",\n    \"AVX512EVEX-LOGICAL_FP\", \"AVX512EVEX-VBMI2\", \"AVX512EVEX-IFMA\", \"AVX512EVEX-FP16\",\n    \"AVX512EVEX-BROADCAST\", \"AVX512EVEX-COMPRESS\", \"AVX512EVEX-AVX512_VBMI\", \"AVX512EVEX-CONFLICT\",\n    \"AVX512EVEX-VAES\", \"AVX512EVEX-VPCLMULQDQ\", \"AVX512EVEX-AVX512\", \"AVX512EVEX-BLEND\",\n    \"AVX512EVEX-CONVERT\", \"AVX512EVEX-AVX512_4FMAPS\", \"RDRAND-RDRAND\", \"RDSEED-RDSEED\"\n]\n\n\nclass X86GeneratorTest(unittest.TestCase):\n\n    @classmethod\n    def setUpClass(cls) -> None:\n        CONF.logging_modes = []\n        update_logging_after_config_change()\n\n    @staticmethod\n    def load_tc(asm_str: str) -> TestCaseProgram:\n\n        instruction_set = InstructionSet((test_dir / \"min_x86.json\").absolute().as_posix())\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        asm_parser = get_asm_parser(instruction_set)\n        elf_parser = ELFParser(X86TargetDesc())\n\n        asm_file = tempfile.NamedTemporaryFile(delete=False)\n        with open(asm_file.name, \"w\") as f:\n            f.write(asm_str)\n        tc: TestCaseProgram = asm_parser.parse_file(asm_file.name, generator, elf_parser)\n        asm_file.close()\n        os.unlink(asm_file.name)\n        return tc\n\n    def test_x86_configuration(self) -> None:\n        CONF.generator = \"random\"\n        instruction_set = InstructionSet((test_dir / \"min_x86.json\").absolute().as_posix(),\n                                         CONF.instruction_categories)\n        gen = get_program_generator(CONF.program_generator_seed, instruction_set)\n        self.assertEqual(gen.__class__, X86Generator)\n\n    def _test_all_instructions(self, instruction_set: InstructionSet) -> None:\n        # pylint: disable=protected-access\n        # Note: This function tests internals of the generator, which is why we\n        # have to disable the protected-access warning.\n\n        asm_file = tempfile.NamedTemporaryFile(\"w\", delete=False)\n        obj_file = tempfile.NamedTemporaryFile(\"w\", delete=False)\n\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        function_generator = generator._function_generator\n        tc = TestCaseProgram(asm_file.name)\n        tc.assign_obj(obj_file.name)\n\n        func = function_generator.generate_empty(\".function_0\", tc.find_section(name=\"main\"))\n        printer = _X86Printer(X86TargetDesc())\n        all_instructions = ['.intel_syntax noprefix\\n']\n\n        # try generating instruction strings\n        for bb in func:\n            for instruction_spec in instruction_set.non_control_flow_specs:\n                # fill up with random operand, following the spec\n                inst = generator.generate_instruction(instruction_spec)\n                bb.insert_after(bb.get_last(), inst)\n\n            for instr in bb:\n                instr_str = printer._instruction_to_str(instr)\n                self.assertTrue(instr_str, f'Instruction {instr} was not generated.')\n                all_instructions.append(instr_str + \"\\n\")\n\n        for i in all_instructions:\n            asm_file.write(i)\n\n        # check if the generated instructions are valid\n        assembly_failed = False\n        try:\n            assemble(tc)\n        except subprocess.CalledProcessError:\n            assembly_failed = True\n        else:\n            obj_file.close()\n            os.unlink(obj_file.name)\n        asm_file.close()\n        os.unlink(asm_file.name)\n\n        if assembly_failed:\n            self.fail(\"Generated invalid instruction(s)\")\n\n    def test_x86_all_instructions_reduced(self) -> None:\n        instruction_set = InstructionSet((test_dir / \"min_x86.json\").absolute().as_posix(),\n                                         _ALL_CATEGORIES)\n        self._test_all_instructions(instruction_set)\n\n    def test_x86_all_instructions_full(self) -> None:\n        if not (test_dir / \"../../base.json\").exists():\n            self.skipTest(\"base.json not available; skipping test.\")\n\n        instruction_set = InstructionSet((test_dir / \"../../base.json\").absolute().as_posix(),\n                                         _ALL_CATEGORIES)\n        self._test_all_instructions(instruction_set)\n\n    def test_x86_asm_parsing_basic(self) -> None:\n        instruction_set = InstructionSet((test_dir / \"min_x86.json\").absolute().as_posix())\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        asm_parser = get_asm_parser(instruction_set)\n        elf_parser = ELFParser(X86TargetDesc())\n\n        asm_name = (test_dir / \"asm/asm_basic.asm\").absolute().as_posix()\n        tc: TestCaseProgram = asm_parser.parse_file(asm_name, generator, elf_parser)\n        section = tc[0]\n        functions = list(section)\n\n        self.assertEqual(len(functions), 2)\n\n        main = functions[0]\n        self.assertEqual(main.name, \".function_0\")\n\n        self.assertEqual(len(main), 3)\n\n        bb0 = main[1]\n        bb1 = main[2]\n        exit_ = main.get_exit_bb()\n\n        self.assertEqual(bb0.successors[0], bb1)\n        self.assertEqual(bb1.successors[0], exit_)\n\n        self.assertEqual(functions[1].name, \".function_end\")\n\n    def test_x86_asm_parsing_opcode(self) -> None:\n\n        tc = self.load_tc(ASM_OPCODE)\n        functions = list(tc[0])\n\n        main_iter = iter(functions[0])\n        bb0 = next(main_iter)\n        insts = list(bb0)\n        self.assertEqual(insts[0].name, \"macro\")\n        self.assertEqual(insts[1].name, \"opcode\")\n\n    def test_x86_asm_parsing_section(self) -> None:\n        prev_actors = deepcopy(CONF.get_actors_conf())\n        CONF.get_actors_conf()[\"guest_1\"] = deepcopy(CONF._actor_default)\n        CONF.get_actors_conf()[\"guest_1\"][\"name\"] = \"guest_1\"\n        CONF.get_actors_conf()[\"guest_1\"][\"mode\"] = \"guest\"\n        CONF.get_actors_conf()[\"guest_1\"][\"privilege_level\"] = \"kernel\"\n\n        instruction_set = InstructionSet((test_dir / \"min_x86.json\").absolute().as_posix())\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        asm_parser = get_asm_parser(instruction_set)\n        elf_parser = ELFParser(X86TargetDesc())\n        name = (test_dir / \"asm/asm_multiactor.asm\").absolute().as_posix()\n        tc: TestCaseProgram = asm_parser.parse_file(name, generator, elf_parser)\n\n        self.assertEqual(tc.n_actors(), 2)\n        self.assertEqual(tc.find_actor(name=\"main\").mode, ActorMode.HOST)\n        self.assertEqual(tc.find_actor(name=\"main\").get_id(), 0)\n        self.assertEqual(tc.find_actor(name=\"guest_1\").mode, ActorMode.GUEST)\n        self.assertEqual(tc.find_actor(name=\"guest_1\").get_id(), 1)\n\n        self.assertEqual(len(tc), 2)\n\n        sec1 = tc[0]\n        self.assertEqual(len(sec1), 3)\n        self.assertEqual(sec1.owner.get_id(), 0)\n        self.assertTrue(sec1.owner.is_main)\n\n        f1 = sec1[0]\n        self.assertEqual(f1.name, \".function_0\")\n        self.assertEqual(len(f1[0]), 3)\n\n        f2 = sec1[1]\n        self.assertEqual(f2.name, \".function_2\")\n        self.assertEqual(len(f2[0]), 1)\n\n        sec2 = tc[1]\n        self.assertEqual(len(sec2), 1)\n        self.assertEqual(sec2.owner.get_id(), 1)\n        self.assertFalse(sec2.owner.is_main)\n\n        f1 = sec2[0]\n        self.assertEqual(f1.name, \".function_1\")\n        self.assertEqual(len(f1[0]), 1)\n\n        CONF._actors = prev_actors\n\n    def test_x86_asm_parsing_symbols(self) -> None:\n        prev_actors = deepcopy(CONF.get_actors_conf())\n        CONF.get_actors_conf()[\"guest_1\"] = deepcopy(CONF._actor_default)\n        CONF.get_actors_conf()[\"guest_1\"][\"name\"] = \"guest_1\"\n        CONF.get_actors_conf()[\"guest_1\"][\"mode\"] = \"guest\"\n        CONF.get_actors_conf()[\"guest_1\"][\"privilege_level\"] = \"kernel\"\n\n        instruction_set = InstructionSet((test_dir / \"min_x86.json\").absolute().as_posix())\n\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        asm_parser = get_asm_parser(instruction_set)\n        elf_parser = ELFParser(X86TargetDesc())\n        name = (test_dir / \"asm/asm_symbol.asm\").absolute().as_posix()\n        tc: TestCaseProgram = asm_parser.parse_file(name, generator, elf_parser)\n        obj = tc.get_obj()\n        symbol_table = obj.symbol_table()\n\n        self.assertEqual(symbol_table[0], SymbolTableEntry(0, 0, 0, 0))  # function_0\n        self.assertEqual(symbol_table[1], SymbolTableEntry(0, 0, 1, 0))\n        self.assertEqual(symbol_table[2], SymbolTableEntry(0, 9, 2, 0))\n        self.assertEqual(symbol_table[3], SymbolTableEntry(0, 20, 0, 1))  # function_1\n        self.assertEqual(symbol_table[4], SymbolTableEntry(1, 0, 0, 2))  # function_2\n\n        CONF._actors = prev_actors\n\n    def test_x86_undef_flag_patch(self) -> None:\n        instruction_set = InstructionSet((test_dir / \"min_x86.json\").absolute().as_posix(),\n                                         CONF.instruction_categories + [\"BASE-FLAGOP\"])\n        undef_instr_spec = list(filter(lambda x: x.name == 'bsf', instruction_set.instructions))[0]\n        read_instr_spec = list(filter(lambda x: x.name == 'lahf', instruction_set.instructions))[0]\n\n        generator = get_program_generator(CONF.program_generator_seed, instruction_set)\n        undef_instr = generator.generate_instruction(undef_instr_spec)\n        read_instr = generator.generate_instruction(read_instr_spec)\n\n        test_case = TestCaseProgram(\"\")\n        sec = test_case[0]\n        func = Function(\".function_0\", sec)\n        sec.append(func)\n        bb = BasicBlock(\".bb0\", func)\n        func.append(bb)\n        bb.insert_after(bb.get_last(), undef_instr)\n        bb.insert_after(bb.get_last(), read_instr)\n\n        _X86PatchUndefinedFlagsPass(instruction_set, generator).run_on_test_case(test_case)\n        self.assertEqual(len(bb), 3)\n"
  },
  {
    "path": "tests/x86_tests/unit_isa_loader.py",
    "content": "\"\"\"\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\nimport unittest\nfrom pathlib import Path\n\nfrom rvzr.isa_spec import InstructionSet\nfrom rvzr.config import CONF\n\ntest_path = Path(__file__).resolve()\ntest_dir = test_path.parent\nCONF.instruction_set = \"x86-64\"\n\n\nclass x86ISALoaderTest(unittest.TestCase):\n\n    def test_instruction_filtering(self) -> None:\n        instruction_set = InstructionSet((test_dir / \"min_x86.json\").absolute().as_posix(),\n                                         [\"BASE-BINARY\"])\n        inst_names = [i.name for i in instruction_set.instructions]\n        self.assertNotIn(\"HLT\", inst_names)\n"
  },
  {
    "path": "tests/x86_tests/unit_model.py",
    "content": "\"\"\"\nFile: Collection of unit tests for x86 model backends.\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# pylint: disable=too-many-arguments\n# pylint: disable=too-few-public-methods\n# pylint: disable=too-many-public-methods\n# pylint: disable=protected-access\n# pylint: disable=missing-function-docstring\n\nimport unittest\nfrom typing import Callable, List, Optional, Tuple, Union, Any, Dict\nfrom copy import deepcopy\nfrom pathlib import Path\nfrom functools import wraps\n\nfrom rvzr.model_dynamorio.model import DynamoRIOModel\nfrom rvzr.model_unicorn.model import X86UnicornModel\n\nfrom rvzr.tc_components.test_case_data import InputData\nfrom rvzr.traces import CTrace\nfrom rvzr.factory import get_model\nfrom rvzr.config import CONF, ConfigException, Conf\nfrom rvzr.logs import update_logging_after_config_change\n\nfrom .model_common import Inst, InstList, InputBuilder, \\\n    MAIN_OFFSET, FAULTY_OFFSET, MEM_DEFAULT_VALUE, \\\n    REG_DEFAULT_VALUE, MEM_FAULTY_DEFAULT_VALUE, RSP_DEFAULT_VALUE, CODE_BASE, DATA_BASE, Backend, \\\n    RAX, RBX, RCX, RDX, RSI, RDI\n\nModelType = Union[DynamoRIOModel, X86UnicornModel]\n\nTEST_PATH = Path(__file__).resolve()\nTEST_DIR = TEST_PATH.parent\n\nPF_MASK = 0xfffffffffffffffe\n\n# Test values\nTEST_MEM_VALUE_A = 42\nTEST_MEM_VALUE_B = 0x42\nPOISON_VALUE = 0xDEADBEEF\n\n\ndef skip_for_backend(backend: Backend, reason: str = \"not supported\") -> Callable[[Any], Any]:\n    \"\"\"Decorator to skip tests for specific backends.\n\n    :param backend: Backend to skip ('dr' or 'uc')\n    :param reason: Reason for skipping (default: \"not supported\")\n\n    Usage:\n        @skip_for_backend(\"dr\")\n        def test_something(self):\n            ...\n    \"\"\"\n\n    def decorator(test_func: Callable[[Any], Any]) -> Callable[[Any], Any]:\n\n        @wraps(test_func)\n        def wrapper(self: '_SharedX86Model') -> Any:\n            if self._backend == backend:\n                raise unittest.SkipTest(reason)\n            return test_func(self)\n\n        return wrapper\n\n    return decorator\n\n\nclass _SharedX86Model(unittest.TestCase):\n    \"\"\"Base class with common test infrastructure for x86 model backends.\"\"\"\n\n    _prev_obs_clause: Optional[str] = None\n    _prev_exec_clause: Optional[List[str]] = None\n    _prev_backend: Optional[str] = None\n    _prev_conf: Optional[Conf] = None\n    _backend: Backend\n    _backend_long: str\n\n    # Exclude this parent class from test discovery\n    @classmethod\n    def setUpClass(cls) -> None:\n        if cls is _SharedX86Model:\n            raise unittest.SkipTest(\"Skipping base class\")\n\n    @classmethod\n    def _configure_class(cls,\n                         backend_long: str,\n                         additional_config: Optional[Dict[str, Any]] = None) -> None:\n        \"\"\"Configure test class with backend-specific settings.\n\n        :param backend_long: Full backend name ('dynamorio' or 'unicorn')\n        :param additional_config: Optional dict of additional CONF attributes to set\n        \"\"\"\n        cls._prev_conf = deepcopy(CONF)\n        CONF.model_backend = backend_long\n        CONF._no_generation = True\n        CONF.logging_modes = []\n\n        # Apply additional backend-specific configuration\n        if additional_config:\n            for attr, value in additional_config.items():\n                setattr(CONF, attr, value)\n\n        update_logging_after_config_change()\n\n    @classmethod\n    def _teardown_class(cls) -> None:\n        \"\"\"Restore configuration to pre-test state.\"\"\"\n        if cls._prev_conf is not None:\n            for attr, value in cls._prev_conf.__dict__.items():\n                setattr(CONF, attr, value)\n\n    def __init__(self, methodName: str = \"runTest\") -> None:\n        super().__init__(methodName)\n        self._input_builder = InputBuilder()\n\n    def setUp(self) -> None:\n        \"\"\"Save configuration state before each test to prevent leakage between tests.\"\"\"\n        self._save_conf()\n\n    def tearDown(self) -> None:\n        \"\"\"Restore configuration state after each test.\"\"\"\n        self._restore_conf()\n\n    @staticmethod\n    def _get_default_ct_trace() -> List[int]:\n        \"\"\"Get default CT trace (empty for base).\"\"\"\n        trace: List[int] = []\n        return trace\n\n    def _save_conf(self) -> None:\n        self._prev_obs_clause = CONF.contract_observation_clause\n        self._prev_exec_clause = CONF.contract_execution_clause\n        self._prev_backend = CONF.model_backend\n\n    def _restore_conf(self) -> None:\n        assert self._prev_obs_clause is not None and \\\n               self._prev_exec_clause is not None and \\\n               self._prev_backend is not None\n        CONF.contract_observation_clause = self._prev_obs_clause\n        CONF.contract_execution_clause = self._prev_exec_clause\n        CONF.model_backend = self._prev_backend\n\n    def _get_model(self,\n                   obs_clause: str,\n                   exec_clause: List[str],\n                   data_bases: Tuple[int, int],\n                   enable_mismatch_check: bool = False) -> ModelType:\n        raise NotImplementedError()\n\n    def _get_trace(self,\n                   test_case: InstList,\n                   input_data: List[InputData],\n                   obs_clause: str = \"ct\",\n                   exec_clause: Optional[List[str]] = None,\n                   data_bases: Tuple[int, int] = (DATA_BASE, CODE_BASE),\n                   nesting: int = 1,\n                   enable_mismatch_check: bool = False,\n                   pte_mask: int = 0) -> List[CTrace]:\n        if exec_clause is None:\n            exec_clause = [\"seq\"]\n\n        model = self._get_model(obs_clause, exec_clause, data_bases, enable_mismatch_check)\n        tc = test_case.to_test_case()\n        if pte_mask != 0:\n            tc.find_actor(name=\"main\").data_properties &= pte_mask  # type: ignore\n        model.load_test_case(tc)\n        ctraces = model.trace_test_case(input_data, nesting=nesting)\n        return ctraces\n\n    def test_no_trace(self) -> None:\n        # Test that tracing with no inputs returns an empty list\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        input_data: List[InputData] = []\n        ctraces = self._get_trace(\n            test_case=test_case,\n            input_data=input_data,\n        )\n        self.assertEqual(len(ctraces), 0)\n\n    def test_mismatch_check_mode(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = InputData()\n        input_[0]['gpr'][RAX] = 1\n        input_[0]['gpr'][RBX] = 2\n        input_[0]['gpr'][RCX] = 3\n        input_[0]['gpr'][RDX] = 4\n        input_[0]['gpr'][RSI] = 5\n        input_[0]['gpr'][RDI] = 6\n\n        ctraces = self._get_trace(\n            test_case=test_case,\n            input_data=[input_],\n            enable_mismatch_check=True,\n        )\n\n        reg_values = ctraces[0].get_untyped()\n        self.assertEqual(len(reg_values), 6)\n        self.assertEqual(reg_values[0], 0)\n        self.assertEqual(reg_values[1], 2)\n        self.assertEqual(reg_values[2], 3)\n        self.assertEqual(reg_values[3], 4)\n        self.assertEqual(reg_values[4], 5)\n        self.assertEqual(reg_values[5], 6)\n\n    def test_mismatch_check_mode_2(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"mov qword ptr [r14], 42\", 3, MAIN_OFFSET + 0, TEST_MEM_VALUE_A),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, TEST_MEM_VALUE_A),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n\n        ctraces = self._get_trace(\n            test_case=test_case,\n            input_data=[input_],\n            enable_mismatch_check=True,\n        )\n\n        reg_values = ctraces[0].get_untyped()\n        self.assertEqual(len(reg_values), 6)\n        self.assertEqual(reg_values[0], test_case[1].mem_value)\n        self.assertEqual(reg_values[1], REG_DEFAULT_VALUE)\n        self.assertEqual(reg_values[2], REG_DEFAULT_VALUE)\n        self.assertEqual(reg_values[3], REG_DEFAULT_VALUE)\n        self.assertEqual(reg_values[4], REG_DEFAULT_VALUE)\n        self.assertEqual(reg_values[5], REG_DEFAULT_VALUE)\n\n    @skip_for_backend(\"dr\")\n    def test_l1d_seq(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            test_case=test_case,\n            input_data=[input_],\n            obs_clause=\"l1d\",\n        )\n\n        expected_trace = test_case.get_expected_observations([0], False, True, False)\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n        self.assertEqual(str(ctraces[0]), \"^\" + \".\" * 63)\n\n    def test_ct_seq(self) -> None:\n        # Test that the tracing functions create RDBF and RCBF files\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"jz .l1\", 2, 0, 0),\n                Inst(\".l0:\", 0, 0, 0),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),\n                Inst(\".l1:\", 0, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            test_case=test_case,\n            input_data=[input_],\n        )\n        expected_trace = test_case.get_expected_observations([0, 1, 4], True, True, False)\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    def test_checkpoint_rollback_registers(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"jz .l1\", 2, 0, 0),\n                Inst(\".l0:\", 0, 0, 0),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),\n                Inst(\".l1:\", 0, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = InputData()\n        ctraces = self._get_trace(\n            test_case=test_case,\n            input_data=[input_],\n            exec_clause=[\"cond\"],\n            enable_mismatch_check=True)\n        reg_values = ctraces[0].get_untyped()\n        self.assertEqual(len(reg_values), 6)\n        self.assertEqual(reg_values[0], 0)\n\n    def test_checkpoint_rollback_memory(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"jz .l1\", 2, 0, 0),\n                Inst(\".l0:\", 0, 0, 0),\n                Inst(\"mov qword ptr [r14], 1\", 7, MAIN_OFFSET + 0, 1),\n                Inst(\".l1:\", 0, 0, 0),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),\n            ],\n            backend=self._backend,\n        )\n        input_ = InputData()\n        input_[0]['main'][0] = TEST_MEM_VALUE_B\n        ctraces = self._get_trace(\n            test_case=test_case,\n            input_data=[input_],\n            exec_clause=[\"cond\"],\n            enable_mismatch_check=True)\n        reg_values = ctraces[0].get_untyped()\n        self.assertEqual(len(reg_values), 6)\n        self.assertEqual(reg_values[0], TEST_MEM_VALUE_B)\n\n    def test_checkpoint_rollback_nested(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),  # 8\n                Inst(\"jz .l2\", 2, 0, 0),  # 11 0xb\n                Inst(\".l0:\", 0, 0, 0),  # 13\n                Inst(\"mov qword ptr [r14], 1\", 7, MAIN_OFFSET + 0, 1),  # 13  0xd\n                Inst(\"jz .l2\", 2, 0, 0),  # 20 0x14\n                Inst(\".l1:\", 0, 0, 0),  # 22 0x16\n                Inst(\"mov qword ptr [r14], 2\", 7, MAIN_OFFSET + 0, 1),  # 22 0x16\n                Inst(\"mov rbx, 1\", 7, 0, 0),  # 29  0x1d\n                Inst(\".l2:\", 0, 0, 0),  # 36  0x24\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),  # 36\n            ],\n            backend=self._backend,\n        )\n        input_ = InputData()\n        input_[0]['main'][0] = TEST_MEM_VALUE_B\n        input_[0]['gpr'][RBX] = 0x1\n        ctraces = self._get_trace(\n            test_case=test_case,\n            input_data=[input_],\n            exec_clause=[\"cond\"],\n            nesting=2,\n            enable_mismatch_check=True)\n\n        reg_values = ctraces[0].get_untyped()\n        self.assertEqual(len(reg_values), 6)\n        self.assertEqual(reg_values[0], TEST_MEM_VALUE_B)\n        self.assertEqual(reg_values[1], 1)\n\n    def test_ct_cond(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),  # 8\n                Inst(\"jz .l2\", 2, 0, 0),  # 11 0xb\n                Inst(\".l0:\", 0, 0, 0),  # 13\n                Inst(\"mov qword ptr [r14], 1\", 7, MAIN_OFFSET + 0, 1),  # 13  0xd\n                Inst(\"jz .l2\", 2, 0, 0),  # 20 0x14\n                Inst(\".l1:\", 0, 0, 0),  # 22 0x16\n                Inst(\"mov qword ptr [r14], 2\", 7, MAIN_OFFSET + 0, 1),  # 22 0x16\n                Inst(\"mov rbx, 1\", 7, 0, 0),  # 29  0x1d\n                Inst(\".l2:\", 0, 0, 0),  # 36  0x24\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),  # 36\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            test_case=test_case,\n            input_data=[input_],\n            exec_clause=[\"cond\"],\n        )\n        self.assertEqual(len(ctraces), 1)\n\n        expected_trace = test_case.get_expected_observations(\n            [\n                0,\n                1,  # first misprediction\n                3,\n                4,  # no misprediction on the second branch (nesting = 1)\n                9,  # first rollback\n                9,  # exit\n            ],\n            True,\n            True,\n            False)\n\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    def test_ct_cond_double(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),  # 8\n                Inst(\"jz .l2\", 2, 0, 0),  # 11 0xb\n                Inst(\".l0:\", 0, 0, 0),  # 13\n                Inst(\"mov qword ptr [r14], 1\", 7, MAIN_OFFSET + 0, 1),  # 13  0xd\n                Inst(\"jz .l2\", 2, 0, 0),  # 20 0x14\n                Inst(\".l1:\", 0, 0, 0),  # 22 0x16\n                Inst(\"mov qword ptr [r14], 2\", 7, MAIN_OFFSET + 0, 1),  # 22 0x16\n                Inst(\"mov rbx, 1\", 7, 0, 0),  # 29  0x1d\n                Inst(\".l2:\", 0, 0, 0),  # 36  0x24\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),  # 36\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            test_case=test_case,\n            input_data=[input_],\n            exec_clause=[\"cond\"],\n            nesting=2,\n        )\n        self.assertEqual(len(ctraces), 1)\n\n        expected_trace = test_case.get_expected_observations(\n            [\n                0,\n                1,  # first misprediction\n                3,\n                4,  # second misprediction\n                6,\n                7,\n                9,  # first rollback\n                9,  # second rollback\n                9,  # exit\n            ],\n            True,\n            True,\n            False)\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    def test_rollback_on_fence(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"jz .l1\", 2, 0, 0),\n                Inst(\".l0:\", 0, 0, 0),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),\n                Inst(\"lfence\", 2, 0, 0),\n                Inst(\"mov rax, qword ptr [r14 + 2]\", 5, MAIN_OFFSET + 2, 2),\n                Inst(\".l1:\", 0, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            exec_clause=[\"cond\"],\n            test_case=test_case,\n            input_data=[input_],\n        )\n        self.assertEqual(len(ctraces), 1)\n\n        expected_trace = test_case.get_expected_observations([0, 1, 3, 4, 6], True, True, False)\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    @skip_for_backend(\"dr\")\n    def test_ct_bpas(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"mov qword ptr [r14], 42\", 7, MAIN_OFFSET + 0, TEST_MEM_VALUE_A),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, TEST_MEM_VALUE_A),\n                Inst(\"mov rax, qword ptr [r14 + rax]\", 4, MAIN_OFFSET + TEST_MEM_VALUE_A, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            exec_clause=[\"bpas\"],\n            test_case=test_case,\n            input_data=[input_],\n        )\n\n        expected_trace: List[int] = []\n        expected_trace.append(test_case[0].pc_offset)\n        expected_trace.append(test_case[0].mem_address)\n\n        # speculative\n        expected_trace.append(test_case[1].pc_offset)\n        expected_trace.append(test_case[1].mem_address)\n        rax = MEM_DEFAULT_VALUE\n        expected_trace.append(test_case[2].pc_offset)\n        expected_trace.append(MAIN_OFFSET + rax)\n        expected_trace.append(test_case[3].pc_offset)\n\n        # after rollback\n        expected_trace.append(test_case[1].pc_offset)\n        expected_trace.append(test_case[1].mem_address)\n        expected_trace.append(test_case[2].pc_offset)\n        expected_trace.append(test_case[2].mem_address)\n        expected_trace.append(test_case[3].pc_offset)\n\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    @skip_for_backend(\"dr\")\n    def test_fault_handling(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"mov rax, qword ptr [r14 + 0x1000]\", 7, FAULTY_OFFSET, 0),\n                Inst(\"mov rax, qword ptr [r14 + rax]\", 4, MAIN_OFFSET + 3, 0),\n                Inst(\"mov rbx, qword ptr [r14 + rbx]\", 4, MAIN_OFFSET + REG_DEFAULT_VALUE, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            test_case=test_case, input_data=[input_], nesting=1, pte_mask=PF_MASK)\n\n        # Fault at instruction 0: PC, mem_addr, and RSP (fault handler stack)\n        expected_trace = test_case.get_expected_observations([0], True, True, False)\n        expected_trace.append(RSP_DEFAULT_VALUE)  # Stack pointer from fault handler\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    @skip_for_backend(\"dr\")\n    def test_ct_deh(self) -> None:\n        # Test X86UnicornDEH with CTTracer (Delayed Exception Handling)\n        test_case = InstList(\n            [\n                Inst(\"mov rax, qword ptr [r14 + 0x1000]\", 7, FAULTY_OFFSET, 0),\n                Inst(\"mov rax, qword ptr [r14 + rax]\", 4, MAIN_OFFSET + 3, 0),\n                Inst(\"mov rbx, qword ptr [r14 + rbx]\", 4, MAIN_OFFSET + REG_DEFAULT_VALUE, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            exec_clause=[\"delayed-exception-handling\"],\n            test_case=test_case,\n            input_data=[input_],\n            pte_mask=PF_MASK)\n\n        # DEH: fault at 0, continue speculatively to 2, then handle fault\n        expected_trace: List[int] = []\n        expected_trace.append(test_case[0].pc_offset)\n        expected_trace.append(test_case[0].mem_address)\n        expected_trace.append(RSP_DEFAULT_VALUE)\n        expected_trace.append(test_case[1].pc_offset)\n        expected_trace.append(test_case[2].pc_offset)\n        expected_trace.append(test_case[2].mem_address)\n        expected_trace.append(test_case[3].pc_offset)\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    @skip_for_backend(\"dr\")\n    def test_ct_nullinj_assist(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"mov rax, qword ptr [r14 + 0x1000]\", 7, FAULTY_OFFSET, 0),\n                Inst(\"mov rax, qword ptr [r14 + rax]\", 4, MAIN_OFFSET + 3, 0),\n                Inst(\"mov rbx, qword ptr [r14 + rbx]\", 4, MAIN_OFFSET + REG_DEFAULT_VALUE, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            exec_clause=[\"nullinj-assist\"],\n            test_case=test_case,\n            input_data=[input_],\n            pte_mask=PF_MASK)\n\n        # Complex trace: fault, re-execute with null injection, speculate, rollback, re-execute\n        # Manual construction needed due to complex execution flow\n        expected_trace: List[int] = []\n        # fault\n        expected_trace.append(test_case[0].pc_offset)\n        expected_trace.append(test_case[0].mem_address)\n        expected_trace.append(RSP_DEFAULT_VALUE)\n        # re-execute with changed permissions and inject zero into rax\n        expected_trace.append(test_case[0].pc_offset)\n        expected_trace.append(test_case[0].mem_address)\n        rax = 0\n        # execute with speculative rax\n        expected_trace.append(test_case[1].pc_offset)  # traced twice due to a quirk in Unicorn\n        expected_trace.append(test_case[1].pc_offset)\n        expected_trace.append(MAIN_OFFSET + rax)\n        expected_trace.append(test_case[2].pc_offset)\n        expected_trace.append(test_case[2].mem_address)\n        expected_trace.append(test_case[3].pc_offset)  # measurement_end\n        # rollback and re-execute without a fault\n        expected_trace.append(test_case[0].pc_offset)\n        expected_trace.append(test_case[0].mem_address)\n        expected_trace.append(test_case[1].pc_offset)\n        expected_trace.append(test_case[1].mem_address)\n        expected_trace.append(test_case[2].pc_offset)\n        expected_trace.append(test_case[2].mem_address)\n        expected_trace.append(test_case[3].pc_offset)  # measurement_end\n\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    @skip_for_backend(\"dr\")\n    def test_ct_nullinj_term(self) -> None:\n        # Test X86UnicornNull with CTTracer (null injection with termination)\n        test_case = InstList(\n            [\n                Inst(\"mov rax, qword ptr [r14 + 0x1000]\", 7, FAULTY_OFFSET, 0),\n                Inst(\"mov rax, qword ptr [r14 + rax]\", 4, MAIN_OFFSET + 3, 0),\n                Inst(\"mov rbx, qword ptr [r14 + rbx]\", 4, MAIN_OFFSET + REG_DEFAULT_VALUE, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            exec_clause=[\"nullinj-fault\"],\n            test_case=test_case,\n            input_data=[input_],\n            pte_mask=PF_MASK)\n\n        # Complex trace: fault, re-execute with null injection, speculate (no rollback)\n        expected_trace: List[int] = []\n        # fault\n        expected_trace.append(test_case[0].pc_offset)\n        expected_trace.append(test_case[0].mem_address)\n        expected_trace.append(RSP_DEFAULT_VALUE)\n        # re-execute with changed permissions and inject zero into rax\n        expected_trace.append(test_case[0].pc_offset)\n        expected_trace.append(test_case[0].mem_address)\n        rax = 0\n        # execute with speculative rax (terminates without rollback)\n        expected_trace.append(test_case[1].pc_offset)  # traced twice due to a quirk in Unicorn\n        expected_trace.append(test_case[1].pc_offset)\n        expected_trace.append(MAIN_OFFSET + rax)\n        expected_trace.append(test_case[2].pc_offset)\n        expected_trace.append(test_case[2].mem_address)\n        expected_trace.append(test_case[3].pc_offset)  # end nop\n\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    @skip_for_backend(\"dr\")\n    def test_ct_meltdown(self) -> None:\n        # Test X86Meltdown with CTTracer (Meltdown vulnerability)\n        test_case = InstList(\n            [\n                Inst(\"mov rax, qword ptr [r14 + 0x1000]\", 7, FAULTY_OFFSET, 0),\n                Inst(\"mov rax, qword ptr [r14 + rax]\", 4, MAIN_OFFSET + 3, 0),\n                Inst(\"mov rbx, qword ptr [r14 + rbx]\", 4, MAIN_OFFSET + REG_DEFAULT_VALUE, 0),\n            ],\n            backend=\"uc\",\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            exec_clause=[\"meltdown\"], test_case=test_case, input_data=[input_], pte_mask=PF_MASK)\n\n        # Meltdown: fault, re-execute with leaked value, speculate\n        expected_trace: List[int] = []\n        # fault\n        expected_trace.append(test_case[0].pc_offset)\n        expected_trace.append(test_case[0].mem_address)\n        expected_trace.append(RSP_DEFAULT_VALUE)\n        # re-execute with changed permissions and inject leaked value into rax\n        expected_trace.append(test_case[0].pc_offset)\n        expected_trace.append(test_case[0].mem_address)\n        rax = MEM_FAULTY_DEFAULT_VALUE\n        # execute with speculative rax containing leaked data\n        expected_trace.append(test_case[1].pc_offset)\n        expected_trace.append(MAIN_OFFSET + rax)\n        expected_trace.append(test_case[2].pc_offset)\n        expected_trace.append(test_case[2].mem_address)\n        expected_trace.append(test_case[3].pc_offset)  # measurement_end\n\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    @skip_for_backend(\"dr\")\n    def test_arch_seq(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"jz .l1\", 2, 0, 0),\n                Inst(\".l0:\", 0, 0, 0),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),\n                Inst(\".l1:\", 0, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(obs_clause=\"arch\", test_case=test_case, input_data=[input_])\n        self.assertEqual(len(ctraces), 1)\n\n        # ArchTracer observes PC, memory addresses, and values\n        # Plus initial register state (7 registers)\n        reg_state = [REG_DEFAULT_VALUE] * 7\n        trace_observations = test_case.get_expected_observations([0, 1, 4], True, True, True)\n        expected_trace = reg_state + trace_observations\n\n        self.assertEqual(ctraces[0].get_untyped(), expected_trace)\n\n    @skip_for_backend(\"uc\")\n    def test_ind(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"lea rax,qword ptr [rip+.l0]\", 7, 0, 0),\n                Inst(\"call rax\", 2, 0, 0),\n                Inst(\".end:\", 0, 0, 0),\n                Inst(\"jmp .l2\", 2, 0, 0),\n                Inst(\".l0:\", 0, 0, 0),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),\n                Inst(\"ret\", 1, 0, 0),\n                Inst(\".l2:\", 0, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(obs_clause=\"ind\", test_case=test_case, input_data=[input_])\n        self.assertEqual(len(ctraces), 1)\n\n        expected_trace: List[int] = []\n        # Call (src and dest)\n        expected_trace.append(test_case[3].pc_offset)\n        expected_trace.append(test_case[6].pc_offset)\n        # Ret (src and dest)\n        expected_trace.append(test_case[8].pc_offset)\n        expected_trace.append(test_case[4].pc_offset)\n\n        # Node: last two rets are inserted by the instrumentation: ignore them\n        self.assertEqual(ctraces[0].get_untyped()[:-2], expected_trace)\n\n    @skip_for_backend(\"uc\")\n    def test_ind_spec(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"jz .end\", 2, 0, 0),\n                Inst(\".l0:\", 0, 0, 0),\n                Inst(\"lea rax,qword ptr [rip+.l3]\", 7, 0, 0),\n                Inst(\"call rax\", 2, 0, 0),\n                Inst(\".l1:\", 0, 0, 0),\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"mov rax, qword ptr [rax]\", 3, MAIN_OFFSET + 0, 1),\n                Inst(\"call rax\", 2, 0, 0),\n                Inst(\".l2:\", 0, 0, 0),\n                Inst(\"jmp .end\", 2, 0, 0),\n                Inst(\".l3:\", 0, 0, 0),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),\n                Inst(\"ret\", 1, 0, 0),\n                Inst(\".end:\", 0, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n        ctraces = self._get_trace(\n            obs_clause=\"ind\", exec_clause=[\"cond\"], test_case=test_case, input_data=[input_])\n        self.assertEqual(len(ctraces), 1)\n\n        expected_trace: List[int] = []\n\n        # Call (src and dest)\n        expected_trace.append(test_case[5].pc_offset)\n        expected_trace.append(test_case[13].pc_offset)\n        # Ret (src and dest)\n        expected_trace.append(test_case[14].pc_offset)\n        expected_trace.append(test_case[6].pc_offset)\n\n        # Node: last two rets are inserted by the instrumentation: ignore them\n        self.assertEqual(ctraces[0].get_untyped()[:-2], expected_trace)\n\n    @skip_for_backend(\"uc\")\n    def test_ind_poison(self) -> None:\n        test_case = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"jz .end\", 2, 0, 0),\n                Inst(\".l0:\", 0, 0, 0),\n                Inst(\"lea rax,qword ptr [rip+.l3]\", 7, 0, 0),\n                Inst(\"call rax\", 2, 0, 0),\n                Inst(\".l1:\", 0, 0, 0),\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"mov rax, qword ptr [rax]\", 3, MAIN_OFFSET + 0, 1),\n                Inst(\"call rax\", 2, 0, 0),\n                Inst(\".l2:\", 0, 0, 0),\n                Inst(\"jmp .end\", 2, 0, 0),\n                Inst(\".l3:\", 0, 0, 0),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),\n                Inst(\"ret\", 1, 0, 0),\n                Inst(\".end:\", 0, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        input_ = self._input_builder.get_default_input()\n\n        model = self._get_model(\"ind\", [\"cond\"], (DATA_BASE, CODE_BASE))\n        model.poison_value = POISON_VALUE  # type: ignore\n\n        tc = test_case.to_test_case()\n        model.load_test_case(tc)\n\n        ctraces = model.trace_test_case([input_], 1)\n        code_base_addr = model.layout.code_start()\n        self.assertEqual(len(ctraces), 1)\n\n        expected_trace: List[int] = []\n\n        # Call (src and dest)\n        expected_trace.append(test_case[5].pc_offset)\n        expected_trace.append(test_case[13].pc_offset)\n        # Ret (src and dest)\n        expected_trace.append(test_case[14].pc_offset)\n        expected_trace.append(test_case[6].pc_offset)\n        # Second call: this is reachable only if speculation doesn't get rolled-back on the\n        # previous faulty load.\n        expected_trace.append(test_case[9].pc_offset)\n        # The call should be trying to jump to the poison value\n        expected_trace.append(model.poison_value - code_base_addr)  # type: ignore\n\n        # Node: last two rets are inserted by the instrumentation: ignore them\n        self.assertEqual(ctraces[0].get_untyped()[:-2], expected_trace)\n\n\nclass X86DRModelTest(_SharedX86Model):\n    \"\"\"Unit tests for the x86 DynamoRIO backend adaptor.\"\"\"\n\n    def __init__(self, methodName: str) -> None:\n        super().__init__(methodName)\n        self._backend = \"dr\"\n        self._backend_long = \"dynamorio\"\n\n    @classmethod\n    def setUpClass(cls) -> None:\n        cls._configure_class(\"dynamorio\")\n\n    @classmethod\n    def tearDownClass(cls) -> None:\n        cls._teardown_class()\n\n    def setUp(self) -> None:\n        self._skip_if_not_installed()\n        super().setUp()\n\n    def _skip_if_not_installed(self) -> None:\n        try:\n            DynamoRIOModel._check_if_installed()\n        except FileNotFoundError:\n            self.skipTest(\"DynamoRIO is not installed\")\n\n    def _get_model(self,\n                   obs_clause: str,\n                   exec_clause: List[str],\n                   data_bases: Tuple[int, int],\n                   enable_mismatch_check: bool = False) -> DynamoRIOModel:\n        CONF.contract_observation_clause = obs_clause\n        CONF.contract_execution_clause = exec_clause\n        CONF.model_backend = \"dynamorio\"\n        model = get_model(data_bases, enable_mismatch_check_mode=enable_mismatch_check)\n        assert isinstance(model, DynamoRIOModel)\n        return model\n\n    def test_clause_configuration(self) -> None:\n        # Create a model instance\n        model = self._get_model(\"ct\", [\"seq\"], (DATA_BASE, CODE_BASE))\n        self.assertEqual(model._obs_clause_name, \"ct\")\n        self.assertEqual(model._exec_clause_name, \"seq\")\n\n        # Set new clauses (invalid)\n        with self.assertRaises(ConfigException) as e:\n            _ = self._get_model(\"invalid\", [\"seq\"], (DATA_BASE, CODE_BASE))\n        self.assertIn(\"unsupported observation clause\", str(e.exception))\n        self.assertIn(\"- ct\", str(e.exception))\n\n        with self.assertRaises(ConfigException) as e:\n            _ = self._get_model(\"ct\", [\"invalid\"], (DATA_BASE, CODE_BASE))\n        self.assertIn(\"unsupported execution clause\", str(e.exception))\n        self.assertIn(\"- seq\", str(e.exception))\n\n        # Set new clauses (invalid, alt interface)\n        model = DynamoRIOModel((DATA_BASE, CODE_BASE))\n        with self.assertRaises(ValueError):\n            model.configure_clauses(\"invalid\", \"seq\")\n        with self.assertRaises(ValueError):\n            model.configure_clauses(\"ct\", \"invalid\")\n\n        self._restore_conf()\n\n    def test_load_test_case(self) -> None:\n        model = self._get_model(\"ct\", [\"seq\"], (DATA_BASE, CODE_BASE))\n        inst = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        tc = inst.to_test_case()\n        model.load_test_case(tc)\n\n        # Check that a temporary file was created and that it contains the test case\n        self.assertIsNotNone(model._files.rcbf)\n        assert model._files.rcbf is not None\n        with open(model._files.rcbf, \"rb\") as f:\n            rcbf_data = f.read()\n        self.assertNotEqual(len(rcbf_data), 0)\n\n        self._restore_conf()\n\n    def test_tc_dispatch(self) -> None:\n        # Test that the tracing functions create RDBF and RCBF files\n\n        model = self._get_model(\"ct\", [\"seq\"], (DATA_BASE, CODE_BASE))\n        inst = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        tc = inst.to_test_case()\n        model.load_test_case(tc)\n        _ = model.trace_test_case([InputData()], 1)\n\n        # RDBF\n        self.assertIsNotNone(model._files.rdbf)\n        assert model._files.rdbf is not None\n        with open(model._files.rdbf, \"rb\") as f:\n            rdbf_data = f.read()\n        self.assertNotEqual(len(rdbf_data), 0)\n        # FIXME: the next two statements should be a part of test_case_data tests\n        self.assertEqual(rdbf_data[0], 1)  # number of actors\n        self.assertEqual(rdbf_data[8], 1)  # number of inputs\n\n        # RCBF\n        self.assertIsNotNone(model._files.rcbf)\n        assert model._files.rcbf is not None\n        with open(model._files.rcbf, \"rb\") as f:\n            rcbf_data = f.read()\n        self.assertEqual(rcbf_data[0], 1)  # number of actors\n\n        # Check that loading another test case with a different input overwrites the files\n        inst = InstList(\n            [\n                Inst(\"xor rax, rax\", 3, 0, 0),\n                Inst(\"jz .l1\", 2, 0, 0),\n                Inst(\".l0:\", 0, 0, 0),\n                Inst(\"mov rax, qword ptr [r14]\", 3, MAIN_OFFSET + 0, 1),\n                Inst(\".l1:\", 0, 0, 0),\n            ],\n            backend=self._backend,\n        )\n        tc = inst.to_test_case()\n        input_ = InputData()\n        input_[0][\"main\"][0] = TEST_MEM_VALUE_A\n        model.load_test_case(tc)\n        _ = model.trace_test_case([input_], 1)\n\n        with open(model._files.rcbf, \"rb\") as f:\n            rcbf_data2 = f.read()\n        self.assertNotEqual(len(rcbf_data2), 0)\n        self.assertNotEqual(rcbf_data, rcbf_data2)\n\n        with open(model._files.rdbf, \"rb\") as f:\n            rdbf_data2 = f.read()\n        self.assertNotEqual(len(rdbf_data2), 0)\n        self.assertNotEqual(rdbf_data, rdbf_data2)\n\n        self._restore_conf()\n\n\nclass UnicornModelTest(_SharedX86Model):  # pylint: disable=too-many-public-methods\n    \"\"\"Unit tests for the x86 Unicorn backend adaptor.\"\"\"\n\n    def __init__(self, methodName: str) -> None:\n        super().__init__(methodName)\n        self._backend = \"uc\"\n        self._backend_long = \"unicorn\"\n\n    @classmethod\n    def setUpClass(cls) -> None:\n        cls._configure_class(\"unicorn\", {\n            'instruction_set': 'x86-64',\n            'data_generator_seed': 10,\n        })\n\n    @classmethod\n    def tearDownClass(cls) -> None:\n        cls._teardown_class()\n\n    def _get_model(self,\n                   obs_clause: str,\n                   exec_clause: List[str],\n                   data_bases: Tuple[int, int],\n                   enable_mismatch_check: bool = False) -> X86UnicornModel:\n        CONF.contract_observation_clause = obs_clause\n        CONF.contract_execution_clause = exec_clause\n        CONF.model_backend = \"unicorn\"\n        model = get_model(data_bases, enable_mismatch_check_mode=enable_mismatch_check)\n        assert isinstance(model, X86UnicornModel)\n        return model\n\n\nif __name__ == '__main__':\n    unittest.main()\n"
  },
  {
    "path": "tests/x86_tests/unit_taint_tracker.py",
    "content": "\"\"\"\nFile: collection of tests for the taint tracking logic in all model backends\n\nCopyright (C) Microsoft Corporation\nSPDX-License-Identifier: MIT\n\"\"\"\n# pylint: disable=missing-function-docstring\n# pylint: disable=missing-class-docstring\n# pylint: disable=too-many-public-methods\n\nimport unittest\nfrom abc import ABC\nfrom typing import List, Tuple, Union\nfrom copy import deepcopy\n\nfrom rvzr.model_unicorn.model import X86UnicornModel\nfrom rvzr.model_dynamorio.model import DynamoRIOModel\nfrom rvzr.tc_components.test_case_data import InputData, InputTaint\nfrom rvzr.traces import CTrace\nfrom rvzr.factory import get_model\nfrom rvzr.config import CONF, Conf\nfrom rvzr.logs import update_logging_after_config_change\n\nfrom .model_common import Inst, InstList, DATA_BASE, CODE_BASE, Backend, \\\n    RAX, RBX, RCX, FLAGS, XMM0, InputBuilder\n\n\n# ==================================================================================================\n# Tests\n# ==================================================================================================\nclass _SharedTaintTrackerTest(ABC, unittest.TestCase):\n    \"\"\"Abstract base class for X86 taint tracking tests.\n\n    Subclasses must define:\n        _backend: Backend type (\"dr\" or \"uc\")\n        _model_backend_name: Model backend name (\"dynamorio\" or \"unicorn\")\n    \"\"\"\n\n    _prev_conf: Conf\n    _backend: Backend\n    _model_backend_name: str\n\n    # Exclude this parent class from test discovery\n    @classmethod\n    def setUpClass(cls) -> None:\n        if cls is _SharedTaintTrackerTest:\n            raise unittest.SkipTest(\"Skipping base class\")\n        # Validate that subclass defines required attributes\n        if not hasattr(cls, '_backend') or not hasattr(cls, '_model_backend_name'):\n            raise TypeError(\n                f\"{cls.__name__} must define class attributes '_backend' and '_model_backend_name'\")\n        # Save and configure settings for taint tracking tests\n        cls._prev_conf = deepcopy(CONF)\n        CONF.instruction_set = \"x86-64\"\n        CONF.model_backend = cls._model_backend_name\n        CONF._no_generation = True  # type: ignore\n        CONF.logging_modes = []\n        update_logging_after_config_change()\n\n    @classmethod\n    def tearDownClass(cls) -> None:\n        # Restore configuration\n        for attr, value in cls._prev_conf.__dict__.items():\n            setattr(CONF, attr, value)\n\n    def __init__(self, methodName: str) -> None:\n        super().__init__(methodName)\n        self._input_builder = InputBuilder()\n\n    def _get_model(self) -> Union[DynamoRIOModel, X86UnicornModel]:\n        \"\"\"Create a model configured for taint tracking.\"\"\"\n        CONF.contract_observation_clause = \"ct\"\n        CONF.contract_execution_clause = [\"seq\"]\n        CONF.model_backend = self._model_backend_name\n        model = get_model((DATA_BASE, CODE_BASE), enable_mismatch_check_mode=False)\n        assert isinstance(model, (DynamoRIOModel, X86UnicornModel))\n        return model\n\n    def _trace_with_taints(self, test_case: InstList,\n                           inputs: List[InputData]) -> Tuple[List[CTrace], List[InputTaint]]:\n        \"\"\"Helper to load test case and trace with taints.\"\"\"\n        model = self._get_model()\n        tc = test_case.to_test_case()\n        model.load_test_case(tc)\n        ctraces, taints = model.trace_test_case_with_taints(inputs, nesting=1)\n        return ctraces, taints\n\n    def _run_taint_test(self, instructions: List[Inst], input_: InputData) -> InputTaint:\n        \"\"\"Run a taint test and return the taint result for the first input.\n\n        :param instructions: List of instructions to execute\n        :param input_: Input data to use for the test\n        :return: Taint information for the first input\n        \"\"\"\n        test_case = InstList(instructions, backend=self._backend)\n        _, taints = self._trace_with_taints(test_case, [input_])\n        return taints[0]\n\n    def test_basic_taint(self) -> None:\n        instructions = [\n            Inst(\"mov rax, qword ptr [r14 + rax]\", 3, 0, 0),\n        ]\n        input_ = self._input_builder.get_input_with_zeroed_gprs(RAX)\n        taint = self._run_taint_test(instructions, input_)\n        self.assertTrue(taint[0]['gpr'][RAX])\n\n    def test_reg_to_reg(self) -> None:\n        instructions = [\n            Inst(\"add rax, rbx\", 0, 0, 0),\n            Inst(\"mov rax, qword ptr [r14 + rax]\", 0, 0, 0),\n        ]\n        input_ = self._input_builder.get_input_with_zeroed_gprs(RAX, RBX)\n        taint = self._run_taint_test(instructions, input_)\n        self.assertTrue(taint[0]['gpr'][RAX])\n        self.assertTrue(taint[0]['gpr'][RBX])\n\n    def test_mem_to_reg(self) -> None:\n        instructions = [\n            Inst(\"mov rbx, qword ptr [r14 + rbx]\", 0, 0, 0),  # main[0] -> RBX\n            Inst(\"mov rax, rbx\", 0, 0, 0),  # RBX -> RAX\n            Inst(\"and rax, 0b1\", 0, 0, 0),\n            Inst(\"mov rax, qword ptr [r14 + rax]\", 0, 0, 0),  # RAX tainted\n        ]\n        input_ = self._input_builder.get_input_with_zeroed_gprs(RAX, RBX)\n        taint = self._run_taint_test(instructions, input_)\n        self.assertFalse(taint[0]['gpr'][RAX])\n        self.assertTrue(taint[0]['gpr'][RBX])\n        self.assertTrue(taint[0]['main'][0])\n\n    def test_load_to_store(self) -> None:\n        instructions = [\n            Inst(\"mov qword ptr [r14], rax\", 0, 0, 0),  # RAX -> main[0]\n            Inst(\"mov rbx, qword ptr [r14]\", 0, 0, 0),  # main[0] -> RBX\n            Inst(\"mov rax, qword ptr [r14 + rbx]\", 0, 0, 0),  # RBX tainted\n        ]\n        input_ = self._input_builder.get_input_with_zeroed_gprs(RAX, RBX)\n        taint = self._run_taint_test(instructions, input_)\n        self.assertTrue(taint[0]['gpr'][RAX])\n        self.assertFalse(taint[0]['gpr'][RBX])\n\n    def test_unaligned_memory_access_taints_both_qwords(self) -> None:\n        # Memory accesses spanning 8-byte boundaries must taint both qwords\n        instructions = [\n            Inst(\"mov rax, qword ptr [r14 + 0x4]\", 0, 0, 0),  # main[0:1] -> RAX\n            Inst(\"mov rax, qword ptr [r14 + rax]\", 0, 0, 0),  # RAX tainted\n        ]\n        input_ = self._input_builder.get_input_with_zeroed_memory(main=0)\n        input_[0]['main'][1] = 0\n        taint = self._run_taint_test(instructions, input_)\n        self.assertTrue(taint[0]['main'][0])\n        self.assertTrue(taint[0]['main'][1])\n\n    def test_simd_register_dependencies_are_tracked(self) -> None:\n        # Taint tracking should work for SIMD (XMM) registers\n        instructions = [\n            Inst(\"movaps xmm0, xmm1\", 0, 0, 0),  # XMM1 -> XMM0\n            Inst(\"movaps xmmword ptr [r14], xmm0\", 0, 0, 0),  # XMM0 -> main[0]\n            Inst(\"mov rax, qword ptr [r14]\", 0, 0, 0),  # main[0] -> RAX\n            Inst(\"and rax, 0b1\", 0, 0, 0),\n            Inst(\"mov rax, qword ptr [r14 + rax]\", 0, 0, 0),  # RAX tainted\n        ]\n        input_ = InputData()\n        taint = self._run_taint_test(instructions, input_)\n        self.assertTrue(taint[0]['simd'][XMM0])\n\n    def test_32bit_writes_preserve_64bit_dependencies(self) -> None:\n        # Writing to 32-bit registers (eax) should preserve dependencies from 64-bit (rax)\n        instructions = [\n            Inst(\"mov rax, rbx\", 0, 0, 0),  # RBX -> RAX\n            Inst(\"mov eax, ecx\", 0, 0, 0),  # ECX -> EAX (RAX must remain dependent on RBX)\n            Inst(\"mov rax, qword ptr [r14 + rax]\", 0, 0, 0),  # RAX tainted\n        ]\n        input_ = self._input_builder.get_input_with_zeroed_gprs(RAX, RBX, RCX)\n        taint = self._run_taint_test(instructions, input_)\n        self.assertFalse(taint[0]['gpr'][RAX])\n        self.assertTrue(taint[0]['gpr'][RBX])\n        self.assertTrue(taint[0]['gpr'][RCX])\n\n    def test_lea_address_computation_propagates_taint(self) -> None:\n        # LEA computes addresses; operands used in address calculation should be tainted\n        instructions = [\n            Inst(\"lea rax, qword ptr [rbx]\", 0, 0, 0),  # RBX -> RAX\n            Inst(\"mov rax, qword ptr [r14 + rax]\", 0, 0, 0),  # RAX tainted\n        ]\n        input_ = self._input_builder.get_input_with_zeroed_gprs(RAX, RBX)\n        taint = self._run_taint_test(instructions, input_)\n        self.assertFalse(taint[0]['gpr'][RAX])\n        self.assertTrue(taint[0]['gpr'][RBX])\n\n    def test_control_flow_dependency_taints_condition(self) -> None:\n        # Data used in conditional branches creates control-flow dependencies\n        instructions = [\n            Inst(\"mov rax, qword ptr [r14 + 0x0]\", 0, 0, 0),  # main[0] -> RAX\n            Inst(\"cmp rax, 0\", 0, 0, 0),  # RAX -> flags\n            Inst(\"je .label\", 0, 0, 0),  # Conditional branch on flags\n            Inst(\".label:\", 0, 0, 0),\n        ]\n        input_ = self._input_builder.get_input_with_zeroed_gprs(RAX)\n        taint = self._run_taint_test(instructions, input_)\n        self.assertTrue(taint[0]['gpr'][FLAGS])\n        self.assertTrue(taint[0]['main'][0])\n        self.assertFalse(taint[0]['gpr'][RAX])\n\n\nclass X86DRTaintTrackerTest(_SharedTaintTrackerTest):\n    \"\"\"Unit tests for the x86 DynamoRIO backend adaptor.\"\"\"\n\n    _backend: Backend = \"dr\"\n    _model_backend_name: str = \"dynamorio\"\n\n    def _skip_if_not_installed(self) -> None:\n        try:\n            DynamoRIOModel._check_if_installed()  # type: ignore\n        except FileNotFoundError:\n            self.skipTest(\"DynamoRIO is not installed\")\n\n    def setUp(self) -> None:\n        self._skip_if_not_installed()\n\n\nclass UnicornTaintTrackerTest(_SharedTaintTrackerTest):  # pylint: disable=too-many-public-methods\n    \"\"\"Unit tests for the x86 Unicorn backend adaptor.\"\"\"\n\n    _backend: Backend = \"uc\"\n    _model_backend_name: str = \"unicorn\"\n\n\nif __name__ == '__main__':\n    unittest.main()\n"
  }
]