Skip to content

objdiff-wasm: Parsing the same file with different side params corrupts subsequent diff results #332

@macabeus

Description

@macabeus

Summary

When using objdiff-wasm, parsing the same .o file first as 'base' (for a one-sided diff) and then as 'target' (for a two-sided diff) causes the second comparison to return incorrect results.

Specifically, it reports a perfect match (0 differences) when there are actually significant differences between the two files.

Versions

  • objdiff-wasm: 3.6.1
  • Node.js: v22

Reproduction

The fixtures are MIPS big-endian ELF .o files.
The target.o has dual OBJECT+FUNC symbols for the same address.

git clone git@github.com:macabeus/objdiff-issue.git
cd objdiff-issue
npm install

# Correct behavior — reports differences:
node reproduce.mjs

# Bug — reports perfect match:
node reproduce.mjs --preparse

Without pre-parse (correct):

Skipping pre-parse step.

Comparing current.o (base) vs target.o (target)...
  Matching:    22
  Different:   20

✓  Differences correctly detected.

With pre-parse (bug):

Pre-parsing target.o as "base" (one-sided diff)...
Done.

Comparing current.o (base) vs target.o (target)...
  Matching:    40
  Different:   0

⚠  No differences reported — but the .o files contain different code!

What the repro script does

  1. Without --preparse: Parses current.o as 'base' and target.o as 'target', runs a two-sided diff, and counts matching vs. differing instruction rows for symbol func_800B3F64_1E1014. Correctly reports 22 matching / 20 different.

  2. With --preparse: First parses target.o as 'base' and runs a one-sided diff (i.e., runDiff(base, undefined, ...)). Then does the same two-sided comparison as above. This time it incorrectly reports 40 matching / 0 different.

The only difference between the two runs is the extra Object.parse() + runDiff() call on target.o before the real comparison. This suggests that WASM module state from the first parse leaks into the second.

Context

I discovered this while working on Mizuchi, a plugin-based pipeline runner.
One plugin was parsing the target .o to extract assembly (one-sided diff as 'base'), and later another plugin ran the actual two-sided comparison. The prior parse caused the comparison to always report a perfect match, masking real code differences.

I noticed later that it happens on Kappa too, when using the command Compare a symbol from two object files. It runs the pre-parsing to list the symbols before the actual diff.

output.mp4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions