Skip to content
STIMSMITH

Analysis pass

Concept WIKI v1 · 6/9/2026

In Mishegos, an analysis pass is a named, declarative pipeline that composes a set of language-agnostic filters to detect and classify discrepancies between x86_64 instruction decoders. Passes are defined in `passes.yml`, order their internal filters by dependency graph or by filter cost, and consume/produce streaming cohorts on stdin/stdout.

Definition

An analysis pass is the unit of analysis in the Mishegos differential-fuzzing tool. A pass groups together a set of language-agnostic filters that operate on decoder cohort data so that, when chained, they surface a particular class of disagreement between the decoders under test (e.g., libbfd, capstone, zydis, xed). Passes are modeled conceptually after LLVM passes: they are configurable, ordered compositions of small, composable transformations over a common input/output stream.

Structure

A pass is defined by a name and an ordered list of filter identifiers. Passes are stored in the YAML file ./src/analysis/passes.yml. The example below defines the pass same-size-different-decodings, which locates instruction candidates that every worker decoder agrees on in length, but that one or more decoders decode differently:

# Find inputs that all workers agree are one size, but one or more
# decodes differently.
same-size-different-decodings:
- filter-any-failure
- filter-ndecoded-different
- filter-same-effects
- minimize-input
- normalize

The pass is therefore a sequence of filter applications: each filter reads cohorts from STDIN and conditionally writes them to STDOUT, so the next filter in the pass sees only the survivors of the previous one.

Filter composition and ordering

Within a pass, filters may be arranged in two ways:

  1. Dependency graph — filters declare what they need from upstream filters and are topologically scheduled.
  2. Performance heuristics — filters are sorted so that the most expensive or highest-volume filters (e.g., filter-ndecoded-different) run first, reducing the working set for the remaining, cheaper filters.

Individual filters are small Ruby scripts that parse JSON cohorts, apply a predicate or transform, and either drop or rewrite the cohort. A representative filter, filter-ndecoded-different, is shown below:

require "json"

STDERR.puts "[+] pass: filter-ndecoded-different"

count = 0
STDIN.each_line do |line|
  result = JSON.parse line, symbolize_names: true
  outputs_ndecoded = result[:outputs].map { |o| o[:ndecoded] }

  if outputs_ndecoded.uniq.size > 1
    count += 1
    next
  end

  STDOUT.puts result.to_json
end

STDERR.puts "[+] pass: filter-ndecoded-different done: #{count} filtered"

Mutating vs. filtering filters

Passes may mix two kinds of filters:

  • Filtering filters drop cohorts that do not match their criterion, narrowing the candidate set at each stage.
  • Mutating filters rewrite the cohort in place. For example, minimize-input truncates the instruction candidate to the longest ndecoded field reported by any decoder, and normalize strips incidental whitespace so subsequent assembly-level diffing is meaningful.

Execution

Passes are launched as a whole through the Mishegos analysis command-line. Each filter logs its progress to STDERR (prefixed with [+] pass: <name>), and the surviving cohorts from the final filter are the pass's output. These outputs can be visualized with the companion tool mishmat, which renders the discrepancies as an HTML table indexed by instruction candidate and per-decoder column:

mishmat -l 10000 < /tmp/mishegos.sd > /tmp/mishegos.sd.html

The -l flag caps the number of rows rendered into the HTML table, which is useful when a pass surfaces a very large number of disagreements.

Example findings produced by passes

Passes defined in passes.yml are the mechanism by which Mishegos produces concrete bug classes such as:

  • outright decoding failures that prevent downstream tooling or analyst workflows,
  • size disagreements between decoders,
  • component discrepancies that mask the recovered control-flow graph, and
  • semantic differences (e.g., Capstone silently mis-decoding operands or missing instructions entirely).

Relationship to other Mishegos concepts

  • The pass is the executable object; passes.yml is the declaration of available passes.
  • An analysis filter is the atomic unit of work; it is PART_OF an analysis pass.
  • Passes operate on cohort output streamed from prior fuzzing/decoder stages, and they in turn emit cohort output that downstream passes, mishmat, or manual review can consume.

CITATIONS

6 sources
6 citations
[1] An analysis pass is a collection of language-agnostic filters into 'passes' (think LLVM), defined in ./src/analysis/passes.yml. Destroying x86_64 instruction decoders with differential fuzzing (Trail of Bits)
[2] Passes order their internal filters either via a dependency graph or based on perceived performance requirements (largest filters first). Destroying x86_64 instruction decoders with differential fuzzing (Trail of Bits)
[3] The `same-size-different-decodings` pass is an example composed of filter-any-failure, filter-ndecoded-different, filter-same-effects, minimize-input, and normalize. Destroying x86_64 instruction decoders with differential fuzzing (Trail of Bits)
[4] Individual filters are scripts that read cohorts on STDIN and conditionally emit them on STDOUT, as illustrated by the `filter-ndecoded-different` Ruby script. Destroying x86_64 instruction decoders with differential fuzzing (Trail of Bits)
[5] Mutating filters such as `minimize-input` chop the instruction candidate to the longest `ndecoded` field, and `normalize` removes extra whitespace to prepare cohorts for further analysis. Destroying x86_64 instruction decoders with differential fuzzing (Trail of Bits)
[6] Passes are run as a whole via the analysis command-line and their output is visualized with `mishmat`, optionally with a row cap such as `mishmat -l 10000`. Destroying x86_64 instruction decoders with differential fuzzing (Trail of Bits)