[PR #1981] [MERGED] fix(wasm): inspect modules whose custom section names contain NUL #4946

Closed
opened 2026-10-31 00:57:16 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1981
Author: @viniciusdsandrade
Created: 10/11/2026
Status: ✅ Merged
Merged: 10/12/2026
Merged by: @morluto

Base: main ← Head: fix/wasm-custom-section-nul-names


📝 Commits (4)

  • 76a9c9d fix(wasm): frame custom section names at their displayed NUL
  • 4e694ff test(wasm): cover NUL-terminated custom section names
  • 20492cd test(wasm): verify a NUL-containing custom section name with real WABT
  • 941eadd refactor(wasm): share WABT's NUL-terminated name display

📊 Changes

5 files changed (+25 additions, -9 deletions)

View changed files

📝 scripts/verify-wasm-artifact.mjs (+1 -0)
📝 src/wasm/WabtOutput.test.ts (+13 -0)
📝 src/wasm/WabtOutput.ts (+6 -6)
📝 tests/fixtures/wasm/README.md (+3 -2)
📝 tests/fixtures/wasm/custom-names.wat (+2 -1)

📄 Description

Summary

inspect_wasm_artifact / inspect-wasm-artifact rejects a valid module when a custom section name contains U+0000. WABT validates the module, but wasm-objdump -h prints the name only up to the NUL, while the adapter compares that row with the complete name decoded from WAT. Compare it with the displayed prefix instead, the rule #1967 already applies to debug module names.

No existing issue covers this. It is the custom-section counterpart of the module-name display handling merged in #1967.

Problem and expected behavior

WABT 1.0.42 (ff0ef7e0) prints a custom section name in header mode with printf("\"" PRIstringview "\"\n", ...) in BinaryReaderObjdump::BeginCustomSection, and PRIstringview is %.*s, so the output stops at the first NUL. wasm2wat writes the same name as "visible\00hidden", which parseWabtWat decodes to visible\u0000hidden. parseWabtOutput then looks for "visible\u0000hidden" where objdump printed "visible", throws WABT custom-section header does not match decoded WAT., and the tool returns unreadable_output instead of Evidence.

Smallest trigger, compiled with WABT 1.0.42 wat2wasm --enable-all:

(module (@custom "visible\00hidden" "payload"))

U+0000 is valid UTF-8 and WABT reads section names through ReadStr, which only checks UTF-8, so wasm-validate --enable-all accepts the module. Expected: Evidence whose custom section description is the text objdump displayed, "visible".

Change and scope

parseWabtOutput compares each custom-section row with the decoded name up to its first NUL, as it already does for module name: preamble lines. The section description stays objdump's row text, like every other section kind; the complete name remains in the decoded WAT. Malformed rows, names that differ before the NUL, the custom-section inventory count and range checks are still rejected. No schema, contract or WAT decoding change.

Contract and boundary impact

  • Semantic owner and earliest changed stage: WABT objdump output framing in src/wasm/WabtOutput.ts.
  • CLI and MCP/tool-catalog contract: unchanged inputs and schemas; formerly rejected modules return Evidence.
  • Provider, bridge, target-format, or platform compatibility: existing WABT 1.0.42 profile.
  • Evidence, artifact, provenance, or reconstruction contract: header/detail text, decoded WAT, digests and section ranges are unchanged; a custom section description is the displayed name.
  • Process execution, authorization, cleanup, or containment impact: none.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none.

Evidence and regression coverage

  • Tests added or updated: src/wasm/WabtOutput.test.ts frames the real objdump golden with the decoded name test\0hidden (description "test") and still rejects other\0test. The real-tool fixture tests/fixtures/wasm/custom-names.wat gains visible\00hidden, and scripts/verify-wasm-artifact.mjs expects its "visible" description through CLI and MCP.
  • Base reproduction or other evidence: source-derived, from WABT 1.0.42 binary-reader-objdump.cc (BeginCustomSection), wat-writer.cc (WriteQuotedData escapes byte 0 as \00), wast-parser.cc (ParseQuotedText), binary-reader.cc (ReadStr) and utf8.cc (IsValidUtf8). The first assertion of the new unit test throws on the base revision.
  • User-visible CLI/MCP output (if applicable): before, unreadable_output with reason WABT custom-section header does not match decoded WAT.; after, Evidence with the custom section description "visible".
  • Remaining proof gaps: the real WABT lane (npm run verify:wasm:artifact -- --require-tools) is manual and was not run, and the focused tests were not run locally.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • No local commands were run: this environment has no installed dependencies or WABT tools. The evidence above is source-derived; CI runs the deterministic suite.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: none run.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no added authority. A crafted name still cannot stand in for parser delimiters: the displayed prefix must match the decoded name, and the existing row, inventory and range checks remain.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1981 **Author:** [@viniciusdsandrade](https://github.com/viniciusdsandrade) **Created:** 10/11/2026 **Status:** ✅ Merged **Merged:** 10/12/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/wasm-custom-section-nul-names` --- ### 📝 Commits (4) - [`76a9c9d`](https://github.com/morluto/rea/commit/76a9c9d21eb41780427bce8237d98e40c8b13f91) fix(wasm): frame custom section names at their displayed NUL - [`4e694ff`](https://github.com/morluto/rea/commit/4e694ff60c4c138375502cea17b204c9a062f36e) test(wasm): cover NUL-terminated custom section names - [`20492cd`](https://github.com/morluto/rea/commit/20492cd679b0cb469f93266f7edb362b1d9fc1e9) test(wasm): verify a NUL-containing custom section name with real WABT - [`941eadd`](https://github.com/morluto/rea/commit/941eadd55b87ce1fc6146542e47a1ecce80d2d70) refactor(wasm): share WABT's NUL-terminated name display ### 📊 Changes **5 files changed** (+25 additions, -9 deletions) <details> <summary>View changed files</summary> 📝 `scripts/verify-wasm-artifact.mjs` (+1 -0) 📝 `src/wasm/WabtOutput.test.ts` (+13 -0) 📝 `src/wasm/WabtOutput.ts` (+6 -6) 📝 `tests/fixtures/wasm/README.md` (+3 -2) 📝 `tests/fixtures/wasm/custom-names.wat` (+2 -1) </details> ### 📄 Description ## Summary `inspect_wasm_artifact` / `inspect-wasm-artifact` rejects a valid module when a custom section name contains U+0000. WABT validates the module, but `wasm-objdump -h` prints the name only up to the NUL, while the adapter compares that row with the complete name decoded from WAT. Compare it with the displayed prefix instead, the rule #1967 already applies to debug module names. No existing issue covers this. It is the custom-section counterpart of the module-name display handling merged in #1967. ## Problem and expected behavior WABT 1.0.42 (`ff0ef7e0`) prints a custom section name in header mode with `printf("\"" PRIstringview "\"\n", ...)` in `BinaryReaderObjdump::BeginCustomSection`, and `PRIstringview` is `%.*s`, so the output stops at the first NUL. `wasm2wat` writes the same name as `"visible\00hidden"`, which `parseWabtWat` decodes to `visible\u0000hidden`. `parseWabtOutput` then looks for `"visible\u0000hidden"` where objdump printed `"visible"`, throws `WABT custom-section header does not match decoded WAT.`, and the tool returns `unreadable_output` instead of Evidence. Smallest trigger, compiled with WABT 1.0.42 `wat2wasm --enable-all`: ```wat (module (@custom "visible\00hidden" "payload")) ``` U+0000 is valid UTF-8 and WABT reads section names through `ReadStr`, which only checks UTF-8, so `wasm-validate --enable-all` accepts the module. Expected: Evidence whose custom section description is the text objdump displayed, `"visible"`. ## Change and scope `parseWabtOutput` compares each custom-section row with the decoded name up to its first NUL, as it already does for `module name:` preamble lines. The section `description` stays objdump's row text, like every other section kind; the complete name remains in the decoded WAT. Malformed rows, names that differ before the NUL, the custom-section inventory count and range checks are still rejected. No schema, contract or WAT decoding change. ## Contract and boundary impact - Semantic owner and earliest changed stage: WABT objdump output framing in `src/wasm/WabtOutput.ts`. - CLI and MCP/tool-catalog contract: unchanged inputs and schemas; formerly rejected modules return Evidence. - Provider, bridge, target-format, or platform compatibility: existing WABT 1.0.42 profile. - Evidence, artifact, provenance, or reconstruction contract: header/detail text, decoded WAT, digests and section ranges are unchanged; a custom section description is the displayed name. - Process execution, authorization, cleanup, or containment impact: none. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none. ## Evidence and regression coverage - Tests added or updated: `src/wasm/WabtOutput.test.ts` frames the real objdump golden with the decoded name `test\0hidden` (description `"test"`) and still rejects `other\0test`. The real-tool fixture `tests/fixtures/wasm/custom-names.wat` gains `visible\00hidden`, and `scripts/verify-wasm-artifact.mjs` expects its `"visible"` description through CLI and MCP. - Base reproduction or other evidence: source-derived, from WABT 1.0.42 `binary-reader-objdump.cc` (`BeginCustomSection`), `wat-writer.cc` (`WriteQuotedData` escapes byte 0 as `\00`), `wast-parser.cc` (`ParseQuotedText`), `binary-reader.cc` (`ReadStr`) and `utf8.cc` (`IsValidUtf8`). The first assertion of the new unit test throws on the base revision. - User-visible CLI/MCP output (if applicable): before, `unreadable_output` with reason `WABT custom-section header does not match decoded WAT.`; after, Evidence with the custom section description `"visible"`. - Remaining proof gaps: the real WABT lane (`npm run verify:wasm:artifact -- --require-tools`) is manual and was not run, and the focused tests were not run locally. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - No local commands were run: this environment has no installed dependencies or WABT tools. The evidence above is source-derived; CI runs the deterministic suite. ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: none run. - Package or release metadata impact: none. - Security, privacy, process, or containment review: no added authority. A crafted name still cannot stand in for parser delimiters: the displayed prefix must match the decoded name, and the existing row, inventory and range checks remain. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 00:57:16 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#4946
No description provided.