Skip to content

Comments

Fuzz binaryen.js.called in closed-world#8343

Merged
kripken merged 29 commits intoWebAssembly:mainfrom
kripken:fuzz.jsCalled
Feb 20, 2026
Merged

Fuzz binaryen.js.called in closed-world#8343
kripken merged 29 commits intoWebAssembly:mainfrom
kripken:fuzz.jsCalled

Conversation

@kripken
Copy link
Member

@kripken kripken commented Feb 19, 2026

Closed-world fuzzing disabled the callRef* imports until now. Those imports
get a wasm funcref and call it from JS, so they break the closed world
assumption. This PR enables callRef* imports in that mode, by only
sending them valid functions, ones marked with @binaryen.js.called.

To do this in the fuzzer,

  • Fix up closed world for each function, finding callRef* calls and
    ensuring they are given a valid funcref, not a random value.
  • Avoid calling callRef* indirectly in closed world. We have no
    good way to avoid a random funcref being sent out there, as we
    do not see those calls statically, so we can't fix them up.

@kripken kripken requested a review from tlively February 19, 2026 00:34
@kripken
Copy link
Member Author

kripken commented Feb 19, 2026

Verified by making getJSCalled return nothing, locally, which causes bugs in passes not realizing some things are marked js.called. This finds bugs with such a local change. But so far this has not found any unknown bugs.

Comment on lines +1729 to +1732
// We cannot actually use this as jsCalled if it does not have a type
// compatible with the callRef* imports. They send a funcref, so we must
// only send non-shared functions.
if (!func->type.getHeapType().isShared()) {
Copy link
Member

Choose a reason for hiding this comment

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

Why not check this before attaching the annotation?

Copy link
Member Author

Choose a reason for hiding this comment

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

It is still ok to add the annotation. And perhaps that might find bugs by itself (e.g. if a pass crashes on handling the annotation).

Comment on lines 3053 to 3054
Expression* TranslateToFuzzReader::makeCallRef(Type type) {
// look for a call target with the right type
Copy link
Member

Choose a reason for hiding this comment

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

Why aren't we just leaving this to makeRefFunc?

Copy link
Member Author

Choose a reason for hiding this comment

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

Leaving what? The old logic in makeCallRef on this line, or the new logic for avoiding bad RefFuncs?

Copy link
Member

Choose a reason for hiding this comment

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

Not related to this PR, but why are we choosing a call target in makeCallRef rather than just choosing a call target type and then calling make with that type? As it is, it seems much harder than it should be for us to generate e.g. reading the call target out of a table or out of a struct field since we're just generating a ref.func directly.

Copy link
Member Author

@kripken kripken Feb 19, 2026

Choose a reason for hiding this comment

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

rather than just choosing a call target type and then calling make with that type?

I think the current logic aims to avoid traps. If we called make with a function type, make might fail to find such a type (if no such function exists).

Copy link
Member

Choose a reason for hiding this comment

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

Could we look through the functions to find the available types, then call make with one of them? That would ensure there's always a possible function to target (assuming the generator doesn't decide to make a subtype instead, etc).

Copy link
Member Author

Choose a reason for hiding this comment

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

That is essentially what the code does right now, except that it makes a specific RefFunc rather than call make. That is less likely to trap (I'm not sure make will always find a suitable function, it might give up and emit unreachable). There is a TODO though a few lines below to generalize that,

  // TODO: half the time make a completely random item with that type.

kripken and others added 2 commits February 19, 2026 12:37
Co-authored-by: Thomas Lively <tlively123@gmail.com>
@kripken
Copy link
Member Author

kripken commented Feb 19, 2026

Found a real bug, #8345

@kripken kripken merged commit 47a72fb into WebAssembly:main Feb 20, 2026
17 checks passed
@kripken kripken deleted the fuzz.jsCalled branch February 20, 2026 17:13
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