Skip to content

Propagate parse errors out of instruction and expression lists - #2824

Open
Nishuuzz wants to merge 1 commit into
WebAssembly:mainfrom
Nishuuzz:fix/propagate-expr-parse-errors
Open

Propagate parse errors out of instruction and expression lists#2824
Nishuuzz wants to merge 1 commit into
WebAssembly:mainfrom
Nishuuzz:fix/propagate-expr-parse-errors

Conversation

@Nishuuzz

Copy link
Copy Markdown
Contributor

wat2wasm, wast2json and wat-desugar read freed memory on malformed text input — #2820. The parser holds raw TypeVector* pointers to block signatures in resolve_type_vectors_ and only dereferences them once the whole module has parsed, so a block destroyed on an error path in between leaves a dangling entry behind.

The diagnosis is @zherczeg's, from the issue thread: ParseModuleField was returning Ok for a field that had in fact failed. Instrumenting it shows exactly that — the field records 8 errors and still succeeds:

[DBG] ParseExprList: ParseExpr failed, synchronising and returning Ok anyway
[DBG] field -> Ok, errors 0 -> 8

ParseInstrList and ParseExprList synchronise past a bad sub-expression and then return Result::Ok unconditionally, so the failure never reaches ParseModuleField and the rollback added in #2805 never runs.

So this just passes the error back. Both loops still recover and carry on, so every error is still reported — they only remember that something was dropped. ParseModuleField then fails as it should, and #2805's rollback discards the stale resolutions.

The test churn

20 expected outputs get shorter, which is most of the diff: 470 lines removed against 43 added.

Previously, having reported the real error, the parser would carry on and emit a second redundant one — nearly always unexpected token ), expected EOF pointing at the end of the module. That no longer appears:

 out/test/parse/expr/bad-load-align.txt:3:25: error: unexpected token align=foo, expected ).
 (module (func (i32.load align=foo (i32.const 0))))
                         ^^^^^^^^^
-out/test/parse/expr/bad-load-align.txt:3:50: error: unexpected token ), expected EOF.
-(module (func (i32.load align=foo (i32.const 0))))
-                                                 ^

I checked the direction of every one of those diffs: they only ever remove lines. No test gains an error and no valid module starts being rejected. Still, it does touch spec test expectations, so shout if you would rather it were done differently.

Test

test/parse/bad-block-ref-result-discarded.txt is the reproducer from the issue. It is deliberately malformed, and it only aborts under a sanitizer, so realistically it is the asan CI jobs that would catch a regression rather than a normal run.

Full suite is green locally against a debug ASAN/UBSAN build with all submodules checked out: 2495 tests, 0 failures.

Fixes #2820.

ParseInstrList and ParseExprList synchronise past a bad sub-expression
and then return Result::Ok unconditionally, so a field that failed to
parse looks to its caller like it succeeded. ParseModuleField therefore
never fails, the rollback added in WebAssembly#2805 never runs, and the deferred
reference-type resolutions registered by a discarded block are left
pointing at freed memory.

Pass the error back instead. Both loops still recover and carry on, so
every error is still reported; they only remember that something was
dropped.

This also stops a second, redundant error being reported after the real
one, which is why the expected output of a number of tests gets shorter.

Diagnosed by @zherczeg on the issue.

Fixes WebAssembly#2820.
@zherczeg

Copy link
Copy Markdown
Collaborator

I support this change. @sbc100 what do you think? Do you know what was the reason of the original concept?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Heap use-after-free in WastParser::ResolveTargetTypeVector on malformed text input

2 participants