Skip to content

Fuzz waitqueue instructions - #9012

Merged
stevenfontanella merged 2 commits into
mainfrom
fuzz-waitqueue
Sep 29, 2026
Merged

stevenfontanella merged 2 commits into
mainfrom
fuzz-waitqueue

Conversation

@stevenfontanella

@stevenfontanella stevenfontanella commented Aug 17, 2026 •

Copy link
Copy Markdown
Member

Part of #8315. Generate waitqueue.new, waitqueue.notify, and struct.wait in the fuzzer.

When !ATOMIC_WAITS, the timeout argument on all struct.waits is forced to be equal to 0 so that programs can't block. In this case it will return either 1 (not equal) or 2 (equal and timed out waiting), but never 0 (blocked and got notified).

Ran for 57k iterations with no issues. For fuzzing against V8: shared-everything is already disabled when fuzzing against V8: link. Waitqueues are mostly implemented in V8 but currently only support i32 control words and not i64 or subtypes of eqref: https://chromium-review.googlesource.com/c/v8/v8/+/8449412/2.

@stevenfontanella
stevenfontanella changed the base branch from main to waitqueue-eq August 17, 2026 23:07
Base automatically changed from waitqueue-eq to main August 21, 2026 19:22
@stevenfontanella
stevenfontanella force-pushed the fuzz-waitqueue branch 2 times, most recently from 40e088c to d51648f Compare August 26, 2026 22:33
@stevenfontanella
stevenfontanella force-pushed the fuzz-waitqueue branch 6 times, most recently from 96f5fe4 to 218c831 Compare September 22, 2026 23:29
@stevenfontanella
stevenfontanella changed the base branch from main to supertype-fix September 22, 2026 23:30
@stevenfontanella
stevenfontanella force-pushed the fuzz-waitqueue branch 3 times, most recently from f1c1052 to 25b4a0d Compare September 23, 2026 21:02
Base automatically changed from supertype-fix to main September 23, 2026 21:23
@stevenfontanella
stevenfontanella force-pushed the fuzz-waitqueue branch 2 times, most recently from 9352c74 to 84702dd Compare September 23, 2026 21:43
@stevenfontanella stevenfontanella changed the title (WIP, Gemini) Fuzz waitqueue Fuzz waitqueue instructions Sep 23, 2026
@stevenfontanella
stevenfontanella marked this pull request as ready for review September 23, 2026 22:29
@stevenfontanella
stevenfontanella requested a review from a team as a code owner September 23, 2026 22:29
@stevenfontanella
stevenfontanella requested review from aheejin and removed request for a team September 23, 2026 22:29
@aheejin

aheejin commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

It looks parts of this PR have been already landed in #9139? Are you planning to land this too? Then can you rebase this PR onto the current main?

@stevenfontanella

Copy link
Copy Markdown
Member Author

It looks parts of this PR have been already landed in #9139? Are you planning to land this too? Then can you rebase this PR onto the current main?

Thanks, I missed this. Done.

@stevenfontanella
stevenfontanella requested review from a team, aheejin and tlively and removed request for a team and aheejin September 28, 2026 17:03
@stevenfontanella

Copy link
Copy Markdown
Member Author

(Reassign since Heejin is out)

Comment thread src/tools/fuzzing/fuzzing.cpp Outdated
Comment on lines +614 to +616
if (fieldType == Type::i32 || fieldType == Type::i64 ||
Type::isSubType(
fieldType, Type(HeapTypes::eq.getBasic(Shared), Nullable))) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It might be worth pulling this condition out into a wasm-type.h helper. I assume it shows up in at least a few places in the code base.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Done, added Field::isValidControlWord. I considered putting it on Type but we need to check if the field is packed, otherwise a packed field will look valid while it should not be.

Comment thread src/wasm-builder.h
if (type.isRef() && type.getHeapType() == HeapTypes::sharedWaitqueue) {
return makeWaitqueueNew();
}
TODO_SINGLE_COMPOUND(type);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It's not clear to me that it makes sense to use waitqueue.new here. Waitqueues have (indirectly) observable identity, and it might be the case that callers of makeConstantExpression expect that not to be the case. I would follow the lead of struct and array types and just not support waitqueues in this function.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sounds good, and looks like this isn't used in the fuzzer anyway. The only call to makeConstantExpression is creating a func ref link.

Comment on lines +1912 to +1921
if (!ATOMIC_WAITS) {
for (auto* wait : FindAll<StructWait>(func->body).list) {
if (auto* c = wait->timeout->dynCast<Const>()) {
c->value = Literal(int64_t(0));
} else if (wait->timeout->type == Type::i64) {
wait->timeout = builder.makeSequence(builder.makeDrop(wait->timeout),
builder.makeConst(int64_t(0)));
}
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we don't do this, does the fuzzer easily run into lengthy hangs? I don't believe we have a similar fixup for linear memory hangs, but it's only rarely been an issue.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

memory.atomic.wait works a little differently. memory.atomic.wait is only generated if ATOMIC_WAITS is true to begin with (FWIW this looks like a hard-coded flag that's always false), then it generates a random i64 for the timeout and has no fixup.

OTOH for struct.wait we may always generate the instruction, but ATOMIC_WAITS only determines whether the timeout can be non-0. I think this is the better way to do it since we can at least exercise the non-blocking code paths. We probably don't get hangs from memory.atomic.wait often because it would have to take an existing test that contains the instruction and mutate it to make it block (either via the timeout or the control word); it's never generated from scratch by the fuzzer while struct.wait is.

I could update memory.atomic.wait in a future PR if we want more uniformity here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would be nice to make them consistent in a follow-up, thanks.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Starting this in #9170.

Comment thread src/tools/fuzzing/fuzzing.cpp Outdated
Comment thread src/tools/fuzzing/fuzzing.cpp
Comment thread src/tools/fuzzing/heap-types.cpp Outdated
@stevenfontanella

Copy link
Copy Markdown
Member Author

Ran 62k fuzzer iterations with no issues.

@stevenfontanella
stevenfontanella merged commit 6c1a3cb into main Sep 29, 2026
16 checks passed
@stevenfontanella
stevenfontanella deleted the fuzz-waitqueue branch September 29, 2026 20:22
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.

3 participants