Skip to content

Print and compare prototypes in fuzzer interpreter#8457

Open
tlively wants to merge 3 commits intomainfrom
fuzzer-log-protos
Open

Print and compare prototypes in fuzzer interpreter#8457
tlively wants to merge 3 commits intomainfrom
fuzzer-log-protos

Conversation

@tlively
Copy link
Member

@tlively tlively commented Mar 11, 2026

When a struct flows out to JS, if it has a descriptor and that desriptor's first field is an externref, that field's value becomes the JS prototype of the struct. There is a class of bugs where we misoptimize something in this setup so that the JS-observable prototype changes. To catch those bugs in the fuzzer, update the fuzzer interpreter and fuzz_shell.js to print the prototypes of objects. This lets the fuzzer make sure that engines like V8 and interpreter agree on whether there is a prototype. Also update the fuzzer interpreter to compare configured prototypes when comparing two execution traces. This lets --fuzz-exec detect when optimizations have changed a prototype.

tlively added 2 commits March 11, 2026 15:18
GTO already handled keeping the prototype configuration fields on
descriptors of types that flowed out to JS via @binaryen.js.called
functions, but it did not handle propagating the information that a
type flows out to JS to that type's subtypes. It also did not consider
that types may flow out to JS via imports and exports. Fix these issues.
When a struct flows out to JS, if it has a descriptor and that desriptor's first field is an externref, that field's value becomes the JS prototype of the struct. There is a class of bugs where we misoptimize something in this setup so that the JS-observable prototype changes. To catch those bugs in the fuzzer, update the fuzzer interpreter and fuzz_shell.js to print the prototypes of objects. This lets the fuzzer make sure that engines like V8 and interpreter agree on whether there is a prototype. Also update the fuzzer interpreter to compare configured prototypes when comparing two execution traces. This lets --fuzz-exec detect when optimizations have changed a prototype.
@tlively tlively requested a review from kripken March 11, 2026 23:51
Base automatically changed from gto-jsinterop-subtypes-exports to main March 12, 2026 15:20
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this PR need rebasing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, just merged now.

}
// The environment always sees externalized references and is able to
// observe the difference between external references and externalized
// internal references. Make sure this is accounted for below by unrapping
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// internal references. Make sure this is accounted for below by unrapping
// internal references. Make sure this is accounted for below by unwrapping

std::ostream& operator<<(std::ostream& o, const WasmException& exn) {
auto exnData = exn.exn.getExnData();
return o << exnData->tag->name << " " << exnData->payload;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was only used in two places: wasm-ctor-eval and execution-results. The latter was updated to use its own printValue function to print exception payloads, so I just inlined this original method into the single remaining use site in wasm-ctor-eval.

;; CHECK-NEXT: [exception thrown: A [ref (type $array.0 (array (mut i32))) (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0[..])]]
;; CHECK-NEXT: [exception thrown: A object(null)]
(func $array (export "array") (result (ref $A))
;; Throw a very large array. We should not print all 12K items in it, as that
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment needs updating. But why is the new output better?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new output matches what will be printed by fuzz_shell.js. I figure that the less they differ, the simpler.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is simpler, yeah. We are losing something though, when the fuzzer compares its logging to itself after opts. See in fuzz_opt.py where we simplify the output for comparison reasons, e.g.

    # funcref(0) has the index of the function in it, and optimizations can
    # change that index, so ignore it
    out = re.sub(r'funcref\([\d\w$+-_:]+\)', 'funcref()', out)

I think we could do a similar thing for this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we could do something similar, but I think it's better not to. The input "fixing" is somewhat complex in the fuzzer, and bugs there can mean missing real bugs. I think it would be better to try to reduce the amount of fixing that happens in fuzz_opt.py. Also, I contend that if we ignore some difference in the fuzzer (and when comparing interpreter executions), then it's clearer if that difference doesn't show up in the textual log, either.

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.

2 participants