Skip to content

Sync fork with upstream googleprojectzero/fuzzilli main - #8

Merged
alii merged 224 commits into
oven-sh:mainfrom
robobun:farm/6d2cda71/rebase-and-update-profile
Jul 2, 2026
Merged

Sync fork with upstream googleprojectzero/fuzzilli main#8
alii merged 224 commits into
oven-sh:mainfrom
robobun:farm/6d2cda71/rebase-and-update-profile

Conversation

@robobun

@robobun robobun commented Apr 2, 2026

Copy link
Copy Markdown

Syncs the fork with upstream googleprojectzero/fuzzilli main. No new Bun API coverage here (that is a separate PR); this is purely the upstream sync plus the minimal adaptations needed to keep the Bun profile compiling.

Ancestry / merge guidance

This branch merges upstream/main (it is a real parent of the merge commit), so upstream/main is an ancestor of the branch tip. After merging this PR, main shows 0 behind upstream.

Merge this PR with a merge commit (not squash). A squash collapses the merge and drops the upstream parent link, which brings back the "N behind" banner. If you must squash, follow up with a graft merge commit recording upstream/main as a parent (same trick as the earlier Record upstream/main as merged commit).

Conflicts resolved

Three conflicts, all from the fork's Bun-support changes vs upstream refactors:

  • Sources/Fuzzilli/FuzzIL/Code.swift — kept the fork's top-level await allowance; upstream renamed the context member, so updated .asyncFunction to .async.
  • Sources/Fuzzilli/Compiler/Compiler.swift — kept the fork's top-level await compilation (no async-context requirement).
  • Tests/FuzzilliTests/EnvironmentTest.swift — kept the fork's GetJavaScriptExecutorOrSkipTest helper (used throughout LiveTests).

Profile adaptation

Upstream added a required additionalOptionsBags parameter to the Profile initializer, so BunProfile.swift now passes additionalOptionsBags: []. No other profile changes.

Verification

swift build passes. swift test passes except WasmAtomicsTests/testRMWOrdering and testCmpxchgOrdering, which fail identically on pristine upstream/main (the container's JS engine is too old for the newer WASM atomics memory-ordering codegen). Not introduced by this PR.

Liedtke and others added 26 commits March 18, 2026 10:53
While this changes the IL to emit wasm-gc signatures for the functions,
it doesn't yet actually allow using wasm-gc types in them.
A few places (WasmDefineTable and WasmCallIndirect /
WasmReturnCallIndirect) still need to be adapted to allow wasm-gc types
before we can actually allow indexed wasm-gc types in function
signatures.

Bug: 445356784
Change-Id: I5715f584cfa5ee664f957a28e28bf80b6f3cdd9e
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9115296
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Manos Koukoutos <manoskouk@google.com>
Change-Id: I4e2111aca7b7619584bffe9d008c60f55da18999
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9122916
Auto-Submit: Michael Achenbach <machenbach@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
This simplifies and reduces a lot of code and prepares adding support
for more kinds of class members without exploding the number of
instructions due to the additional factor 2 for static and instance
members.

Concretely this merges instructions for all members (properties,
elements and methods) that have a static and non-static (instance)
variant. The static bit is represented by a variable in the
instruction.

This was also tested locally with and without this change, both with
large number for class-related code generators. Both versions
resulted in similar correctness stats without any crashes.

Bug: 446634535
Change-Id: I57b3261e202dffeb57704d0040b2a8d02b50a9e6
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9094176
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Michael Achenbach <machenbach@google.com>
The TableType will need to be adapted for tracking wasm-gc
signatures. I just couldn't find a good reason why we'd need to store
the TableType on Table.get and Table.set?

Bug: 445356784
Change-Id: Ia115d287b27cc18f52a48ddce25b897f1a19b293
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9123736
Reviewed-by: Manos Koukoutos <manoskouk@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Change-Id: I99bc88a3cefdd5d4cbaf645b10e1cdcd66138a52
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9123977
Commit-Queue: Manos Koukoutos <manoskouk@google.com>
Reviewed-by: Manos Koukoutos <manoskouk@google.com>
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
The WasmThrowRefGenerator requires an exnref as an input. Without having
a generator that produces it, it isn't very likely that there is an
exnref available in the current program, so the generator cannot be run
in most cases.

Registering a generator producing that exnref (if a tag is available)
helps significantly.

Change-Id: Idbd9337f5a7339d58fe1f76e264569907f7081ce
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9123976
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Manos Koukoutos <manoskouk@google.com>
Commit-Queue: Manos Koukoutos <manoskouk@google.com>
The first attempt of fixing this was
commit 89691a1,
however this means we might end up not typing the inner outputs (the
tag's "elements" available inside the catch) which breaks the typer's
assumptions that everything gets typed.
Typing it with some dummy value can also lead to issues downstream (e.g.
by the next instruction taking now an input that isn't of the needed
type any more), so instead we solve this issue by always also adding a
signature as an input. As the signature is defined in Wasm, input
replacement can only happen with strict type checks, so it is safe to
rely on this.

It's a bit annoying for the WasmBeginCatch to take an extra input for
this specific problem, however, WasmBeginCatch is anyways related to the
"legacy" exception handling which isn't a properly spec'ed Wasm feature
but a "browsers have been shipping this without a finished spec" kind of
thing.

Bug: 448860865
Change-Id: I06638ccbb5ed0c9dbb7355ac198b7ace25f521b8
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9129497
Reviewed-by: Michael Achenbach <machenbach@google.com>
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
The issue was introduced with
commit 7fb8254

While I was running the fuzzer for multiple hours, the fuzzer is more
persmissive in not crashing on invalid programs send over the wire, so
this wasn't detected.

Change-Id: I34f04902915539cb688c5c6eb6825d28a123ccb0
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9130176
Reviewed-by: Michael Achenbach <machenbach@google.com>
Commit-Queue: Michael Achenbach <machenbach@google.com>
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Olivier Flückiger <olivf@google.com>
Change-Id: I6a3f252f20742dac630864ab4b07e493dbde46ec
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9133476
Commit-Queue: Michael Achenbach <machenbach@google.com>
Reviewed-by: Michael Achenbach <machenbach@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
Similar to method names, this supports all allowed ways to
define properties. Approximated valid identifiers will be used
as is, everything else will be quoted, except positive integers.

Since such a property can leak into the type information of an
object or class, also all property accesses are adapted now, similar
to method calls.

This also refactors the import of object fields and methods, unifying
the same property-key logic used in class definitions.

Computed getters and setters for object literals and classes are still
a TODO.

This also lifts some restrictions from runtime assisted mutators,
which previously only allowed simple identifiers as property names.

Bug: 446634535
Change-Id: I35a65c0073fee9bac238205557958e80c60e1186
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9111376
Commit-Queue: Michael Achenbach <machenbach@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Bug: 495679730
Change-Id: I45c1af939f3e1a81fc1c3a2649652e25c644cc82
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9137477
Reviewed-by: Darius Mercadier <dmercadier@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
When a program instrumented by RuntimeAssistedMutator crashes, we avoid calling
processCrash() on it immediately. This is because the boilerplate code added
during instrumentation makes such crashes difficult to minimize
(see, e.g., https://g-issues.chromium.org/issues/488963988?pli=1&authuser=0).

Instead, we follow this procedure:
1. Always log the crash of the instrumented program.
2. Check if the process()'d version of the instrumented program also crashes.
3. If yes, we call processCrash() on that program instead, as its more straightforward to minimize.

Bug: 488963988
Change-Id: Iffefc9435f4ef31a3fbf798d374a04f9f1fc115a
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9129498
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Leon Bettscheider <bettscheider@google.com>
We need at least "warning" level to see logs from worker threads.

Change-Id: I4ec5d9d89f5697cf5710a250888e054789716f78
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9151416
Commit-Queue: Leon Bettscheider <bettscheider@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Michael Achenbach <machenbach@google.com>
Fixed: 496097209
Change-Id: Icb0f88bcf619791fa3a45af7f0f2cd73428d37df
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9141456
Auto-Submit: Michael Achenbach <machenbach@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
This was removed in https://crrev.com/c/7708297

Bug: 42202693
Change-Id: Ia3b8b197b28b62ba6eb73aeda498ae748fb093ee
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9155336
Auto-Submit: Michael Achenbach <machenbach@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Michael Achenbach <machenbach@google.com>
This enables computed getters and setters for classes and adds the
remaining bits to support computed getters and setters for object
expressions. Also fully supports and tests getters and setters with
non-identifier names for classes.

Bug: 446634535
Change-Id: Ib18477c237674b8b9c911f36ba3e53daed136fb8
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9100459
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Michael Achenbach <machenbach@google.com>
This CL transpiles crashing instrumented FuzzIL programs to JS
(to insert the JS boilerplate code required for, e.g., explore()),
and then transpiles this JS code back to FuzzIL and reports that
program.

This change will allow to apply Fuzzilli's own program minimization
technique also on the boilerplate code. We hope that this will
result in smaller reported programs.

Bug: 488963988
Change-Id: I1e5ee51409c4c5e26db80e96d4f61060e12f0bd1
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9155916
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Bug: 430616180
Change-Id: I168f494c2a1c0510fb6524495da4adb10ccd00cc
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9155816
Reviewed-by: Michael Achenbach <machenbach@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
To align better with the current formatting. No other reason.
Also add a few swift-ignore-format annotations for weights.

Bug: 430616180
Change-Id: I07b5dae4938578a49ec393faaf18959e2867e58f
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9155817
Reviewed-by: Michael Achenbach <machenbach@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
The implicit import mechanism in Wasm needs a rework sooner or later.
For now, let's make it more robust in this case.

Fixed: 498266575
Change-Id: I17cc0021cbe945d5db16029c451e3ea6bfb55ff1
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9159837
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Michael Achenbach <machenbach@google.com>
Reviewed-by: Michael Achenbach <machenbach@google.com>
Change-Id: I9e7e47344d5c9fb3e56545be210788e1d8e492ef
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9155596
Commit-Queue: Marja Hölttä <marja@chromium.org>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
RAB/GSAB was shipped a while ago, we don't need the boosted weights any more

Change-Id: Ic7da221bc2d47b1966906566d1e0ca4616251153
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9160936
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Change-Id: If51387c564c5c4245d23122be751ce46682b0fee
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9159756
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
Mostly just to match the naming of `newValue` and the subtyping idea of
the forUseAs where we try to find something that is a subtype of the
current variable's type, not the other way around.

Change-Id: I3144025a0e1850892936087fdf275d318943f963
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9159757
Reviewed-by: Marja Hölttä <marja@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
Bug: 430616180
Change-Id: I12f41e8c87913481f05f3bf350acdb88ac08c163
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9155818
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Michael Achenbach <machenbach@google.com>
Bug: 430616180
Change-Id: I6784d3f4232e11b272cbb7a345713edff05c7426
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9156216
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Michael Achenbach <machenbach@google.com>
@robobun
robobun force-pushed the farm/6d2cda71/rebase-and-update-profile branch from 4710684 to e62eb32 Compare April 2, 2026 20:28
@coderabbitai

coderabbitai Bot commented Apr 2, 2026

Copy link
Copy Markdown

Walkthrough

This PR unifies class-member and loop opcodes, updates Wasm function/table/reference shapes, changes parser/compiler/lifter handling for keys and destructuring, adjusts fuzzing/runtime flow for bundle-aware execution, and adds related tests and repository tooling updates.

Changes

Core IR and Wasm pipeline

Layer / File(s) Summary
Schema and opcode shape
Sources/Fuzzilli/Protobuf/*, Sources/Fuzzilli/FuzzIL/*, Sources/Fuzzilli/FuzzIL/WasmOperations.swift
Class-member messages and opcodes move to unified forms with isStatic, Wasm function and table payloads switch to count-based or input-driven shapes, and new loop, bundle/module, and Wasm reference opcodes are added.
Type and semantic updates
Sources/Fuzzilli/FuzzIL/Analyzer.swift, Context.swift, Code.swift, Semantics.swift, TypeSystem.swift
Bundle-aware contexts and validation are introduced, and type/semantic checks are updated for unified class-member opcodes, new Wasm labels, destructuring, and updated signature/type-extension handling.

Code generation, lifting, and templates

Layer / File(s) Summary
Generator tables and templates
Sources/Fuzzilli/CodeGen/*, Sources/Fuzzilli/FuzzIL/WasmOperations.swift
Generator weights, Wasm code generators, and program templates add new class, loop, bundle, and Wasm cases, and Wasm templates now build functions and tables from signature defs and bundle-aware modules.
Parser and compiler changes
Sources/Fuzzilli/Compiler/*, Sources/Fuzzilli/Lifting/*
Parser and compiler now preserve unified property keys, destructuring patterns, and loop metadata, while the lifters render the new class, object, Wasm, and destructuring forms.
Runtime and engine flow
Sources/Fuzzilli/Fuzzer.swift, Sources/Fuzzilli/Engines/*, Sources/Fuzzilli/Evaluation/*, Sources/Fuzzilli/Execution/*, Sources/Fuzzilli/Mutators/*
Fuzzer execution, crash reporting, corpus import, and engine entry points are adjusted for bundle-aware operation and the new parameterless fuzzing flow.
Repository and build tooling
.github/workflows/swift.yml, .gitignore, .swift-format, PRESUBMIT.py, Package.swift, Sources/FuzzILTool/main.swift
Workflow, ignore rules, formatting, presubmit, package wiring, and CLI formatting are updated alongside the code changes.

Tests and fixtures

Layer / File(s) Summary
Coverage and fixture updates
Tests/FuzzilliTests/*, Tests/FuzzilliTests/computed_and_indexed_properties.js
Tests add coverage for weird property keys, Wasm lifting, crash reporting, and generator scheduling, and update Wasm expectation values and fixture behavior to match the new instruction shapes.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title matches the main change: syncing the fork with upstream main.
Description check ✅ Passed The description is directly related to the changeset and explains the upstream sync plus Bun profile adjustments.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
Sources/Fuzzilli/Profiles/BunProfile.swift (1)

2020-2026: ⚠️ Potential issue | 🟡 Minor

Add the optional boundary parameter for Bun.readableStreamToFormData.

The Bun runtime supports Bun.readableStreamToFormData(stream, multipartBoundaryExcludingDashes?) for parsing multipart form data with a custom boundary. The second parameter is optional and accepts string | Uint8Array | ArrayBufferView, but the current signature only models the single-argument path.

Consider using .opt(.jsAnything) instead of .opt(.string) to fully represent the accepted parameter types:

Suggested fix
-        "Bun.readableStreamToFormData"    : .function([.jsAnything] => .jsPromise),
+        "Bun.readableStreamToFormData"    : .function([.jsAnything, .opt(.jsAnything)] => .jsPromise),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/Profiles/BunProfile.swift` around lines 2020 - 2026, The
signature for Bun.readableStreamToFormData currently models only a
single-argument path; update its type to accept the optional boundary parameter
which can be string | Uint8Array | ArrayBufferView by replacing the
second-argument type with an optional jsAnything (e.g. change the entry for
"Bun.readableStreamToFormData" to a function taking [.jsAnything,
.opt(.jsAnything)] => .jsPromise or use .opt(.jsAnything) instead of
.opt(.string)) so the profile reflects the runtime's accepted types.
Sources/Fuzzilli/FuzzIL/JSTyper.swift (1)

806-822: ⚠️ Potential issue | 🟠 Major

Validate the referenced signature before using it to drive op arity.

These branches now trust instr.input(0) to define parameter/output counts, but the instruction shape is still fixed by numOutputs, numInnerOutputs, and numInputs. If mutation or minimization swaps in a different signature definition, wasmJsCall can touch instr.output when the op has no outputs, and the begin/end function paths can leave the function shape inconsistent with what the lifter expects. Please compare the signature counts against the op counts before using them and reject or fall back on mismatches.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/FuzzIL/JSTyper.swift` around lines 806 - 822, Validate the
wasm signature's parameter/output counts against the instruction's expected
shape before using it in wasmJsCall, beginWasmFunction, and endWasmFunction: for
wasmJsCall check that signature.outputTypes.count matches instr.numOutputs (or
is zero when instr has no outputs) before calling setType(of: instr.output, ...)
and before using instr.input(1)'s signature in addWasmFunction; for
beginWasmFunction ensure signature.parameter count matches instr.numInputs (and
output count matches instr.numInnerOutputs/numOutputs as appropriate) before
calling wasmTypeBeginBlock; for endWasmFunction only call setType(of:
instr.output, ...) and addWasmFunction when the signature counts match,
otherwise fall back to a safe default (e.g. use
Signature.forUnknownFunction/skip setting types) and/or log/reject the
inconsistent signature so mutation/minimization swaps don't corrupt op arity;
reference wasmFunctionSignatureDefSignature, instr.input(0), instr.output,
wasmTypeBeginBlock, setType, dynamicObjectGroupManager.addWasmFunction,
ProgramBuilder.convertWasmSignatureToJsSignature, and defUseAnalyzer.definition
when making these checks and fallbacks.
Sources/Fuzzilli/Protobuf/operations.pb.swift (1)

4001-4018: ⚠️ Potential issue | 🟠 Major

Reuse field 2 for parameterCount and outputCount instead of field 1 to maintain protobuf wire compatibility.

Lines 11111 and 11141 decode field 1 as Int32 for BeginWasmFunction and EndWasmFunction respectively, but field 1 previously held repeated message payloads (parameterTypes / outputTypes). This is a wire-incompatible protobuf change; older serialized corpora and mixed-version workers will fail to deserialize these messages without an explicit migration. Use fresh field numbers (e.g., field 2) for the new count fields in operations.proto to maintain backward compatibility.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/Protobuf/operations.pb.swift` around lines 4001 - 4018, The
new Int32 fields parameterCount (in Fuzzilli_Protobuf_BeginWasmFunction) and
outputCount (in Fuzzilli_Protobuf_EndWasmFunction) are using field number 1
which collides with the original repeated message fields
(parameterTypes/outputTypes) and breaks wire compatibility; update the proto and
generated Swift handling to use a fresh field number (e.g., field 2) instead:
change the field numbers in operations.proto for these count fields to 2, then
regenerate the Swift protobuf sources (or, if editing generated code directly,
change the tag handling for parameterCount and outputCount to the field-2 tag
and update encode/decode paths and default values accordingly) so older
serialized data remains compatible.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Sources/Fuzzilli/Base/ProgramBuilder.swift`:
- Around line 4099-4105: The prepass that builds tagSignatures force-unwraps
b.type(of: clause.tag).wasmTagType (in the tagSignatures construction that calls
b.wasmDefineAdHocSignatureType) before the per-clause validation (reportErrorIf)
runs, which causes a trap on non-tag inputs; change the prepass to avoid
force-unwrapping — instead check for a valid wasmTagType (or skip/produce a
placeholder) and only call b.wasmDefineAdHocSignatureType when wasmTagType is
present, leaving the existing reportErrorIf checks inside the catchClauses loop
to emit diagnostics for invalid tags (apply the same pattern to the other
similar helper at the other site).
- Around line 3504-3509: The initializer public init(forBuilder b:
ProgramBuilder, signatureDef: Variable) currently only asserts .wasmTypeDef();
tighten the API boundary by asserting (or preconditioning) that signatureDef is
specifically a wasm function-signature definition (e.g., check b.type(of:
signatureDef).Is(.wasmFunctionSignatureDef()) or that b.type(of:
signatureDef).wasmFunctionSignatureDefSignature is non-nil) so the later use of
wasmFunctionSignatureDefSignature is safe; apply the same stronger validation to
the other overloads/constructors and callers mentioned (the BeginWasmFunction
overloads and the code paths around the other occurrences) and, where possible,
keep BeginWasmFunction parameter types or signatures constrained to
function-signature defs instead of the more generic wasm type def to prevent
accepting arrays/structs.

In `@Sources/Fuzzilli/CodeGen/CodeGenerators.swift`:
- Around line 1260-1265: The private-class-method generators currently call
b.randomParameters() and immediately emit BeginClassPrivateMethod (symbols:
b.randomParameters, BeginClassPrivateMethod) without seeding parameter types;
before emitting BeginClassPrivateMethod call setParameterTypesForNextSubroutine
on the builder with the parameter type information returned by
b.randomParameters() so the private-method body receives the same
typed-parameter context as public class-method generators—apply the same change
to both occurrences (around the blocks that use b.randomParameters and
BeginClassPrivateMethod, including the second occurrence near the other similar
block).

In `@Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift`:
- Around line 327-330: The test matrix for GC generator scheduling is missing an
explicit case for the new weighted generator WasmCreateExnRefGenerator; add a
scheduling assertion that verifies WasmCreateExnRefGenerator is scheduled (with
expected weight/priority) similar to the existing WasmThrowRefGenerator entry so
regressions are caught—locate the GC scheduling tests that reference
"WasmThrowRefGenerator" and add a corresponding assertion or test row for
"WasmCreateExnRefGenerator" that checks it appears in the schedule and honors
its weight.

In `@Sources/Fuzzilli/FuzzIL/Instruction.swift`:
- Around line 616-679: The change alters the on-disk protobuf shape for several
class-member (e.g., the Instruction.swift cases classAddProperty,
classAddElement, classAddComputedProperty, classAddPrivateProperty,
beginClassMethod, beginClassComputedMethod, beginClassGetter/Setter, etc.) and
Wasm instruction encodings, so update the persisted-format handling by bumping
the repository's serialized format version (the serialized-format/version
constant used by the Instruction/protobuf serializer) or implement explicit
migration logic in the encoder/decoder path (where instructions are converted
to/from Fuzzilli_Protobuf_* messages) to accept both old and new shapes, and add
a short upgrade note describing the incompatibility; apply the same treatment to
the other changed ranges you mentioned (1261–1264, 1331–1336, 1866–1897,
2329–2332, 2365–2368).

In `@Sources/Fuzzilli/FuzzIL/JSTyper.swift`:
- Around line 1355-1378: Replace the forced casts that follow debug-only asserts
in the .endClassMethod, .endClassGetter and .endClassSetter cases with safe
guarded downcasts: after the existing assert(...) keep it but change the `let
beginOp = begin.op as! BeginClassMethod/BeginClassGetter/BeginClassSetter` to a
guarded conditional downcast (`guard let beginOp = begin.op as?
BeginClassMethod` / `BeginClassGetter` / `BeginClassSetter` else { return } or
otherwise exit the case), then proceed to call
dynamicObjectGroupManager.update... using the safe beginOp; this preserves the
assert in debug builds but avoids crashes in optimized builds when begin.op is
malformed.

In `@Sources/Fuzzilli/Lifting/JavaScriptLifter.swift`:
- Around line 537-542: The class method emitter in the beginClassMethod case
currently sets METHOD = quoteIdentifierIfNeeded(op.methodName) and emits
"\(staticStr)\(METHOD)(\(PARAMS)) {", which when op.methodName == "constructor"
and op.isStatic == false produces a real constructor; change the logic to detect
the special case (op.methodName == "constructor" && !op.isStatic) and instead
set METHOD to a bracketed quoted form (e.g. "[\"constructor\"]") or a computed
property string so the emitted line becomes non-constructor regular method
syntax; update the code surrounding case .beginClassMethod, METHOD, and the
w.emit call to use this alternate METHOD only for that condition.

In `@Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift`:
- Line 126: Replace the use of execution.fuzzout with the cached oldFuzzout when
calling process(...) to match the caching strategy used elsewhere; specifically
update the call in RuntimeAssistedMutator where process(...) is invoked (the
tuple assignment to (mutatedProgram, outcome)) to pass oldFuzzout so it remains
consistent with the cached value invalidation logic used around execute(),
processInstrumentedProgram, and the later reference at line 150.
- Around line 139-141: After calling logger.warning(...) and
fuzzer.processCrash(instrumentedProgram, withSignal: signal, withStderr:
oldStderr, withStdout: stdout, origin: .local, withExectime: execution.execTime)
you must return immediately to avoid falling through and re-processing the
program; add a return statement right after the fuzzer.processCrash(...) call in
the same scope (the function that later calls process()) so the function exits
once the instrumented crash has been reported.

In `@Sources/Fuzzilli/Profiles/BunProfile.swift`:
- Around line 132-136: The BunJSONL ILType currently models both methods as
string->jsAnything; update the bunJSONL ILType (symbol bunJSONL) so parse
accepts a union input type (string | TypedArray | DataView | ArrayBufferLike)
and returns an array of JS values, and parseChunk accepts the same union input
plus optional numeric start and end parameters and returns a structured object
with properties { values: Array, read: Number, done: Boolean, error: Any|null }
so the profile can exercise zero-copy/partial-buffer paths and validate the
incremental parser result shape for methods "parse" and "parseChunk".
- Around line 246-250: The BunBuildArtifact IL type (bunBuildArtifact) is
missing the documented bytes() method and the sourcemap property is incorrectly
typed; update the ILType.object for bunBuildArtifact to add "bytes" to the
withMethods array while keeping "json", and change the "sourcemap" property to
be nullable and reference a BuildArtifact (i.e., make its type an optional
BuildArtifact/ILType.object reference rather than .jsAnything) so it correctly
models Promise<Uint8Array> for bytes() and the sourcemap relationship.

In `@Sources/Fuzzilli/Protobuf/ast.pb.swift`:
- Around line 1479-1486: The generated change replaces individual key fields
with an embedded PropertyKey message for
Compiler_Protobuf_ObjectProperty/ObjectMethod/ObjectGetter/ObjectSetter which
may break wire-format compatibility; inspect ast.proto and confirm the field
numbers and types for the affected messages and the PropertyKey message, then
either restore the original scalar/oneof fields and their original tag numbers
or add the new embedded field under a new tag while keeping the old tags (mark
them deprecated) so older binaries can still parse existing payloads, implement
a version/migration field if you intend to change semantics, and finally
regenerate Sources/Fuzzilli/Protobuf/ast.pb.swift (ensuring
Compiler_Protobuf_ObjectProperty, Compiler_Protobuf_ObjectMethod,
Compiler_Protobuf_ObjectGetter, Compiler_Protobuf_ObjectSetter and PropertyKey
definitions align with the stable tags).

In `@Tests/FuzzilliTests/CrashingInstrumentationMutator.swift`:
- Line 33: The statement calling b.eval contains a trailing semicolon which is
unnecessary in Swift; update the expression b.eval("fuzzilli('FUZZILLI_CRASH',
0)"); by removing the final semicolon so it becomes
b.eval("fuzzilli('FUZZILLI_CRASH', 0)") to match Swift style and avoid
extraneous punctuation.

---

Outside diff comments:
In `@Sources/Fuzzilli/FuzzIL/JSTyper.swift`:
- Around line 806-822: Validate the wasm signature's parameter/output counts
against the instruction's expected shape before using it in wasmJsCall,
beginWasmFunction, and endWasmFunction: for wasmJsCall check that
signature.outputTypes.count matches instr.numOutputs (or is zero when instr has
no outputs) before calling setType(of: instr.output, ...) and before using
instr.input(1)'s signature in addWasmFunction; for beginWasmFunction ensure
signature.parameter count matches instr.numInputs (and output count matches
instr.numInnerOutputs/numOutputs as appropriate) before calling
wasmTypeBeginBlock; for endWasmFunction only call setType(of: instr.output, ...)
and addWasmFunction when the signature counts match, otherwise fall back to a
safe default (e.g. use Signature.forUnknownFunction/skip setting types) and/or
log/reject the inconsistent signature so mutation/minimization swaps don't
corrupt op arity; reference wasmFunctionSignatureDefSignature, instr.input(0),
instr.output, wasmTypeBeginBlock, setType,
dynamicObjectGroupManager.addWasmFunction,
ProgramBuilder.convertWasmSignatureToJsSignature, and defUseAnalyzer.definition
when making these checks and fallbacks.

In `@Sources/Fuzzilli/Profiles/BunProfile.swift`:
- Around line 2020-2026: The signature for Bun.readableStreamToFormData
currently models only a single-argument path; update its type to accept the
optional boundary parameter which can be string | Uint8Array | ArrayBufferView
by replacing the second-argument type with an optional jsAnything (e.g. change
the entry for "Bun.readableStreamToFormData" to a function taking [.jsAnything,
.opt(.jsAnything)] => .jsPromise or use .opt(.jsAnything) instead of
.opt(.string)) so the profile reflects the runtime's accepted types.

In `@Sources/Fuzzilli/Protobuf/operations.pb.swift`:
- Around line 4001-4018: The new Int32 fields parameterCount (in
Fuzzilli_Protobuf_BeginWasmFunction) and outputCount (in
Fuzzilli_Protobuf_EndWasmFunction) are using field number 1 which collides with
the original repeated message fields (parameterTypes/outputTypes) and breaks
wire compatibility; update the proto and generated Swift handling to use a fresh
field number (e.g., field 2) instead: change the field numbers in
operations.proto for these count fields to 2, then regenerate the Swift protobuf
sources (or, if editing generated code directly, change the tag handling for
parameterCount and outputCount to the field-2 tag and update encode/decode paths
and default values accordingly) so older serialized data remains compatible.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9b1d7e48-a066-4ec5-98e7-e1864aca627b

📥 Commits

Reviewing files that changed from the base of the PR and between 56cb233 and e62eb32.

📒 Files selected for processing (39)
  • .github/workflows/swift.yml
  • Sources/Fuzzilli/Base/ProgramBuilder.swift
  • Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift
  • Sources/Fuzzilli/CodeGen/CodeGenerators.swift
  • Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift
  • Sources/Fuzzilli/Compiler/Compiler.swift
  • Sources/Fuzzilli/Compiler/Parser/parser.js
  • Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift
  • Sources/Fuzzilli/FuzzIL/Instruction.swift
  • Sources/Fuzzilli/FuzzIL/JSTyper.swift
  • Sources/Fuzzilli/FuzzIL/JsOperations.swift
  • Sources/Fuzzilli/FuzzIL/Opcodes.swift
  • Sources/Fuzzilli/FuzzIL/Semantics.swift
  • Sources/Fuzzilli/FuzzIL/WasmOperations.swift
  • Sources/Fuzzilli/Fuzzer.swift
  • Sources/Fuzzilli/Lifting/FuzzILLifter.swift
  • Sources/Fuzzilli/Lifting/JavaScriptExploreLifting.swift
  • Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
  • Sources/Fuzzilli/Lifting/JavaScriptProbeLifting.swift
  • Sources/Fuzzilli/Lifting/JavaScriptRuntimeAssistedMutatorLifting.swift
  • Sources/Fuzzilli/Lifting/WasmLifter.swift
  • Sources/Fuzzilli/Minimization/BlockReducer.swift
  • Sources/Fuzzilli/Minimization/InliningReducer.swift
  • Sources/Fuzzilli/Mutators/OperationMutator.swift
  • Sources/Fuzzilli/Mutators/ProbingMutator.swift
  • Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift
  • Sources/Fuzzilli/Profiles/BunProfile.swift
  • Sources/Fuzzilli/Protobuf/ast.pb.swift
  • Sources/Fuzzilli/Protobuf/ast.proto
  • Sources/Fuzzilli/Protobuf/operations.pb.swift
  • Sources/Fuzzilli/Protobuf/operations.proto
  • Sources/Fuzzilli/Protobuf/program.pb.swift
  • Sources/Fuzzilli/Protobuf/program.proto
  • Tests/FuzzilliTests/CompilerTests/computed_and_indexed_properties.js
  • Tests/FuzzilliTests/CrashingInstrumentationMutator.swift
  • Tests/FuzzilliTests/LifterTest.swift
  • Tests/FuzzilliTests/MinimizerTest.swift
  • Tests/FuzzilliTests/ProgramBuilderTest.swift
  • Tests/FuzzilliTests/RuntimeAssistedMutatorTests.swift
💤 Files with no reviewable changes (1)
  • Sources/Fuzzilli/Mutators/ProbingMutator.swift

Comment on lines +3504 to +3509
public init(forBuilder b: ProgramBuilder, signatureDef: Variable) {
assert(b.type(of: signatureDef).Is(.wasmTypeDef()))
self.b = b
self.signature = signature
self.signature = b.type(of: signatureDef).wasmFunctionSignatureDefSignature
self.jsSignature = convertWasmSignatureToJsSignature(signature)
self.signatureDef = signatureDef

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Validate signatureDef as a function signature, not just any wasm type def.

Line 3505 only checks .wasmTypeDef(), while Line 3507 and Line 4536 immediately assume a function-signature definition. The new public overload can therefore accept an array/struct type definition and only fail later in the builder path. Tighten this invariant at the API boundary, and keep the BeginWasmFunction input typed as well.

Also applies to: 4530-4539, 5069-5070

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/Base/ProgramBuilder.swift` around lines 3504 - 3509, The
initializer public init(forBuilder b: ProgramBuilder, signatureDef: Variable)
currently only asserts .wasmTypeDef(); tighten the API boundary by asserting (or
preconditioning) that signatureDef is specifically a wasm function-signature
definition (e.g., check b.type(of: signatureDef).Is(.wasmFunctionSignatureDef())
or that b.type(of: signatureDef).wasmFunctionSignatureDefSignature is non-nil)
so the later use of wasmFunctionSignatureDefSignature is safe; apply the same
stronger validation to the other overloads/constructors and callers mentioned
(the BeginWasmFunction overloads and the code paths around the other
occurrences) and, where possible, keep BeginWasmFunction parameter types or
signatures constrained to function-signature defs instead of the more generic
wasm type def to prevent accepting arrays/structs.

Comment on lines +4099 to +4105
// Define tag signatures before the try block so they don't interfere with the try/catch
// bodies and their scoping.
// TODO(mliedtke): We should reuse the signature of the tag once tags use a wasm-gc
// signature.
let tagSignatures = catchClauses.map {
b.wasmDefineAdHocSignatureType(signature: b.type(of: $0.tag).wasmTagType!.parameters => [])
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't force-unwrap wasmTagType before the existing validation.

The new tagSignatures prepass evaluates b.type(of: clause.tag).wasmTagType! before the later reportErrorIf inside the catch loop. A non-tag input now traps here instead of producing the intended diagnostic.

Suggested fix for both helpers
-            let tagSignatures = catchClauses.map {
-                b.wasmDefineAdHocSignatureType(signature: b.type(of: $0.tag).wasmTagType!.parameters => [])
-            }
+            let tagSignatures = catchClauses.map { clause in
+                let tagType = b.type(of: clause.tag)
+                b.reportErrorIf(!tagType.isWasmTagType,
+                    "Expected tag misses the WasmTagType extension for variable \(clause.tag), typed \(tagType).")
+                return b.wasmDefineAdHocSignatureType(signature: tagType.wasmTagType!.parameters => [])
+            }

Also applies to: 4142-4148

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/Base/ProgramBuilder.swift` around lines 4099 - 4105, The
prepass that builds tagSignatures force-unwraps b.type(of:
clause.tag).wasmTagType (in the tagSignatures construction that calls
b.wasmDefineAdHocSignatureType) before the per-clause validation (reportErrorIf)
runs, which causes a trap on non-tag inputs; change the prepass to avoid
force-unwrapping — instead check for a valid wasmTagType (or skip/produce a
placeholder) and only call b.wasmDefineAdHocSignatureType when wasmTagType is
present, leaving the existing reportErrorIf checks inside the catchClauses loop
to emit diagnostics for invalid tags (apply the same pattern to the other
similar helper at the other site).

Comment on lines 1260 to +1265
let parameters = b.randomParameters()
b.emit(
BeginClassPrivateInstanceMethod(
BeginClassPrivateMethod(
methodName: methodName,
parameters: parameters.parameters))
parameters: parameters.parameters,
isStatic: false))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Seed private-method parameter types before opening the subroutine.

These two generators pick randomized parameters but still skip setParameterTypesForNextSubroutine, unlike the public class-method generators above. That means private-method bodies lose the same typed parameter information the other method bodies get.

♻️ Proposed fix
                 let methodName = b.generateString(b.randomCustomPrivateMethodName,
                     notIn: b.currentClassDefinition.privateFields)
                 let parameters = b.randomParameters()
+                b.setParameterTypesForNextSubroutine(parameters.parameterTypes)
                 b.emit(
                     BeginClassPrivateMethod(
                         methodName: methodName,
                         parameters: parameters.parameters,
                         isStatic: false))
@@
                 let methodName = b.generateString(b.randomCustomPrivateMethodName,
                     notIn: b.currentClassDefinition.privateFields)
                 let parameters = b.randomParameters()
+                b.setParameterTypesForNextSubroutine(parameters.parameterTypes)
                 b.emit(
                     BeginClassPrivateMethod(
                         methodName: methodName,
                         parameters: parameters.parameters,
                         isStatic: true))

Also applies to: 1298-1303

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/CodeGen/CodeGenerators.swift` around lines 1260 - 1265, The
private-class-method generators currently call b.randomParameters() and
immediately emit BeginClassPrivateMethod (symbols: b.randomParameters,
BeginClassPrivateMethod) without seeding parameter types; before emitting
BeginClassPrivateMethod call setParameterTypesForNextSubroutine on the builder
with the parameter type information returned by b.randomParameters() so the
private-method body receives the same typed-parameter context as public
class-method generators—apply the same change to both occurrences (around the
blocks that use b.randomParameters and BeginClassPrivateMethod, including the
second occurrence near the other similar block).

Comment on lines +327 to 330
// This generator is mostly just there, so that the WasmThrowRefGenerator
// can create an exnref on demand (if a wasm tag is present).
"WasmCreateExnRefGenerator": 1,
"WasmThrowRefGenerator": 6,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Add an explicit scheduling test for WasmCreateExnRefGenerator.

A new weighted generator is introduced here, but there’s no direct scheduling assertion for it in the GC scheduling test matrix yet. Adding one would make regressions easier to catch.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift` around lines 327 - 330,
The test matrix for GC generator scheduling is missing an explicit case for the
new weighted generator WasmCreateExnRefGenerator; add a scheduling assertion
that verifies WasmCreateExnRefGenerator is scheduled (with expected
weight/priority) similar to the existing WasmThrowRefGenerator entry so
regressions are caught—locate the GC scheduling tests that reference
"WasmThrowRefGenerator" and add a corresponding assertion or test row for
"WasmCreateExnRefGenerator" that checks it appears in the schedule and honors
its weight.

Comment on lines +616 to 679
case .classAddProperty(let op):
$0.classAddProperty = Fuzzilli_Protobuf_ClassAddProperty.with {
$0.propertyName = op.propertyName
$0.hasValue_p = op.hasValue
$0.isStatic = op.isStatic
}
case .classAddInstanceElement(let op):
$0.classAddInstanceElement = Fuzzilli_Protobuf_ClassAddInstanceElement.with {
case .classAddElement(let op):
$0.classAddElement = Fuzzilli_Protobuf_ClassAddElement.with {
$0.index = op.index
$0.hasValue_p = op.hasValue
$0.isStatic = op.isStatic
}
case .classAddInstanceComputedProperty(let op):
$0.classAddInstanceComputedProperty = Fuzzilli_Protobuf_ClassAddInstanceComputedProperty.with { $0.hasValue_p = op.hasValue }
case .beginClassInstanceMethod(let op):
$0.beginClassInstanceMethod = Fuzzilli_Protobuf_BeginClassInstanceMethod.with {
$0.methodName = op.methodName
$0.parameters = convertParameters(op.parameters)
case .classAddComputedProperty(let op):
$0.classAddComputedProperty = Fuzzilli_Protobuf_ClassAddComputedProperty.with {
$0.hasValue_p = op.hasValue
$0.isStatic = op.isStatic
}
case .endClassInstanceMethod:
$0.endClassInstanceMethod = Fuzzilli_Protobuf_EndClassInstanceMethod()
case .beginClassInstanceComputedMethod(let op):
$0.beginClassInstanceComputedMethod = Fuzzilli_Protobuf_BeginClassInstanceComputedMethod.with {
case .endClassMethod:
$0.endClassMethod = Fuzzilli_Protobuf_EndClassMethod()
case .beginClassComputedMethod(let op):
$0.beginClassComputedMethod = Fuzzilli_Protobuf_BeginClassComputedMethod.with {
$0.parameters = convertParameters(op.parameters)
$0.isStatic = op.isStatic
}
case .endClassInstanceComputedMethod:
$0.endClassInstanceComputedMethod = Fuzzilli_Protobuf_EndClassInstanceComputedMethod()
case .beginClassInstanceGetter(let op):
$0.beginClassInstanceGetter = Fuzzilli_Protobuf_BeginClassInstanceGetter.with { $0.propertyName = op.propertyName }
case .endClassInstanceGetter:
$0.endClassInstanceGetter = Fuzzilli_Protobuf_EndClassInstanceGetter()
case .beginClassInstanceSetter(let op):
$0.beginClassInstanceSetter = Fuzzilli_Protobuf_BeginClassInstanceSetter.with { $0.propertyName = op.propertyName }
case .endClassInstanceSetter:
$0.endClassInstanceSetter = Fuzzilli_Protobuf_EndClassInstanceSetter()
case .classAddStaticProperty(let op):
$0.classAddStaticProperty = Fuzzilli_Protobuf_ClassAddStaticProperty.with {
case .endClassComputedMethod:
$0.endClassComputedMethod = Fuzzilli_Protobuf_EndClassComputedMethod()
case .beginClassGetter(let op):
$0.beginClassGetter = Fuzzilli_Protobuf_BeginClassGetter.with {
$0.propertyName = op.propertyName
$0.hasValue_p = op.hasValue
$0.isStatic = op.isStatic
}
case .classAddStaticElement(let op):
$0.classAddStaticElement = Fuzzilli_Protobuf_ClassAddStaticElement.with {
$0.index = op.index
$0.hasValue_p = op.hasValue
}
case .classAddStaticComputedProperty(let op):
$0.classAddStaticComputedProperty = Fuzzilli_Protobuf_ClassAddStaticComputedProperty.with { $0.hasValue_p = op.hasValue }
case .beginClassStaticInitializer:
$0.beginClassStaticInitializer = Fuzzilli_Protobuf_BeginClassStaticInitializer()
case .endClassStaticInitializer:
$0.endClassStaticInitializer = Fuzzilli_Protobuf_EndClassStaticInitializer()
case .beginClassStaticMethod(let op):
$0.beginClassStaticMethod = Fuzzilli_Protobuf_BeginClassStaticMethod.with {
case .beginClassPrivateMethod(let op):
$0.beginClassPrivateMethod = Fuzzilli_Protobuf_BeginClassPrivateMethod.with {
$0.methodName = op.methodName
$0.parameters = convertParameters(op.parameters)
$0.isStatic = op.isStatic
}
case .endClassStaticMethod:
$0.endClassStaticMethod = Fuzzilli_Protobuf_EndClassStaticMethod()
case .beginClassStaticComputedMethod(let op):
$0.beginClassStaticComputedMethod = Fuzzilli_Protobuf_BeginClassStaticComputedMethod.with {
case .endClassPrivateMethod:
$0.endClassPrivateMethod = Fuzzilli_Protobuf_EndClassPrivateMethod()
case .beginClassMethod(let op):
$0.beginClassMethod = Fuzzilli_Protobuf_BeginClassMethod.with {
$0.methodName = op.methodName
$0.parameters = convertParameters(op.parameters)
$0.isStatic = op.isStatic
}
case .endClassStaticComputedMethod:
$0.endClassStaticComputedMethod = Fuzzilli_Protobuf_EndClassStaticComputedMethod()
case .beginClassStaticGetter(let op):
$0.beginClassStaticGetter = Fuzzilli_Protobuf_BeginClassStaticGetter.with { $0.propertyName = op.propertyName }
case .endClassStaticGetter:
$0.endClassStaticGetter = Fuzzilli_Protobuf_EndClassStaticGetter()
case .beginClassStaticSetter(let op):
$0.beginClassStaticSetter = Fuzzilli_Protobuf_BeginClassStaticSetter.with { $0.propertyName = op.propertyName }
case .endClassStaticSetter:
$0.endClassStaticSetter = Fuzzilli_Protobuf_EndClassStaticSetter()
case .classAddPrivateInstanceProperty(let op):
$0.classAddPrivateInstanceProperty = Fuzzilli_Protobuf_ClassAddPrivateInstanceProperty.with {
case .endClassGetter:
$0.endClassGetter = Fuzzilli_Protobuf_EndClassGetter()
case .beginClassSetter(let op):
$0.beginClassSetter = Fuzzilli_Protobuf_BeginClassSetter.with {
$0.propertyName = op.propertyName
$0.hasValue_p = op.hasValue
}
case .beginClassPrivateInstanceMethod(let op):
$0.beginClassPrivateInstanceMethod = Fuzzilli_Protobuf_BeginClassPrivateInstanceMethod.with {
$0.methodName = op.methodName
$0.parameters = convertParameters(op.parameters)
$0.isStatic = op.isStatic
}
case .endClassPrivateInstanceMethod:
$0.endClassPrivateInstanceMethod = Fuzzilli_Protobuf_EndClassPrivateInstanceMethod()
case .classAddPrivateStaticProperty(let op):
$0.classAddPrivateStaticProperty = Fuzzilli_Protobuf_ClassAddPrivateStaticProperty.with {
case .endClassSetter:
$0.endClassSetter = Fuzzilli_Protobuf_EndClassSetter()
case .beginClassStaticInitializer:
$0.beginClassStaticInitializer = Fuzzilli_Protobuf_BeginClassStaticInitializer()
case .endClassStaticInitializer:
$0.endClassStaticInitializer = Fuzzilli_Protobuf_EndClassStaticInitializer()
case .classAddPrivateProperty(let op):
$0.classAddPrivateProperty = Fuzzilli_Protobuf_ClassAddPrivateProperty.with {
$0.propertyName = op.propertyName
$0.hasValue_p = op.hasValue
$0.isStatic = op.isStatic
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Please make the serialized-format change explicit.

These encoder/decoder updates change the persisted shape of class-member and Wasm instructions. If stored corpora or serialized programs are expected to survive upgrade, this needs a format/version bump or an explicit reset/migration note in the upgrade path.

Also applies to: 1261-1264, 1331-1336, 1866-1897, 2329-2332, 2365-2368

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/FuzzIL/Instruction.swift` around lines 616 - 679, The change
alters the on-disk protobuf shape for several class-member (e.g., the
Instruction.swift cases classAddProperty, classAddElement,
classAddComputedProperty, classAddPrivateProperty, beginClassMethod,
beginClassComputedMethod, beginClassGetter/Setter, etc.) and Wasm instruction
encodings, so update the persisted-format handling by bumping the repository's
serialized format version (the serialized-format/version constant used by the
Instruction/protobuf serializer) or implement explicit migration logic in the
encoder/decoder path (where instructions are converted to/from
Fuzzilli_Protobuf_* messages) to accept both old and new shapes, and add a short
upgrade note describing the incompatibility; apply the same treatment to the
other changed ranges you mentioned (1261–1264, 1331–1336, 1866–1897, 2329–2332,
2365–2368).

Comment on lines +139 to +141
// If we reach here, the process()'d program did not crash, so we need to report the instrumented program.
logger.warning("Mutated program did not crash, reporting original crash of the instrumented program")
fuzzer.processCrash(instrumentedProgram, withSignal: signal, withStderr: oldStderr, withStdout: stdout, origin: .local, withExectime: execution.execTime)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Missing return after reporting instrumented program crash.

After reporting the crash at line 141, the code falls through to line 150, which calls process() again and may return a "successful" mutation. This results in inconsistent behavior: a crash is reported AND a mutated program is returned.

🐛 Proposed fix: Add return statement
             // If we reach here, the process()'d program did not crash, so we need to report the instrumented program.
             logger.warning("Mutated program did not crash, reporting original crash of the instrumented program")
             fuzzer.processCrash(instrumentedProgram, withSignal: signal, withStderr: oldStderr, withStdout: stdout, origin: .local, withExectime: execution.execTime)
+            return failure(.instrumentedProgramCrashed)
         case .succeeded:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// If we reach here, the process()'d program did not crash, so we need to report the instrumented program.
logger.warning("Mutated program did not crash, reporting original crash of the instrumented program")
fuzzer.processCrash(instrumentedProgram, withSignal: signal, withStderr: oldStderr, withStdout: stdout, origin: .local, withExectime: execution.execTime)
// If we reach here, the process()'d program did not crash, so we need to report the instrumented program.
logger.warning("Mutated program did not crash, reporting original crash of the instrumented program")
fuzzer.processCrash(instrumentedProgram, withSignal: signal, withStderr: oldStderr, withStdout: stdout, origin: .local, withExectime: execution.execTime)
return failure(.instrumentedProgramCrashed)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift` around lines 139 -
141, After calling logger.warning(...) and
fuzzer.processCrash(instrumentedProgram, withSignal: signal, withStderr:
oldStderr, withStdout: stdout, origin: .local, withExectime: execution.execTime)
you must return immediately to avoid falling through and re-processing the
program; add a return statement right after the fuzzer.processCrash(...) call in
the same scope (the function that later calls process()) so the function exits
once the instrumented crash has been reported.

Comment on lines +132 to +136
// BunJSONL - JSONL parser
static let bunJSONL = ILType.object(
ofGroup: "BunJSONL",
withProperties: [],
withMethods: ["parse", "stringify"]
withMethods: ["parse", "parseChunk"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n Sources/Fuzzilli/Profiles/BunProfile.swift | sed -n '130,140p'

Repository: oven-sh/fuzzilli

Length of output: 416


🏁 Script executed:

cat -n Sources/Fuzzilli/Profiles/BunProfile.swift | sed -n '760,780p'

Repository: oven-sh/fuzzilli

Length of output: 657


🌐 Web query:

Bun JSONL API parse parseChunk official documentation

💡 Result:

Bun provides official support for parsing JSONL (newline-delimited JSON) via the Bun.JSONL API, including parse for complete inputs and parseChunk for streaming scenarios. ## Bun.JSONL.parse(input) Parses a complete JSONL string, ArrayBufferLike, TypedArray, or DataView into an array of JavaScript values. Throws SyntaxError if no values parse successfully. Example: const results = Bun.JSONL.parse('{"name":"Alice"}\n{"name":"Bob"}\n'); // [{ name: "Alice" }, { name: "Bob" }] ## Bun.JSONL.parseChunk(input, start?, end?) For streaming, parses as many complete values as possible from the chunk. Returns ParseChunkResult { values: any[], read: number, done: boolean, error: SyntaxError | null }. Does not throw; errors are in the result. 'read' indicates consumed bytes/characters for resuming. Example: const chunk = '{"id":1}\n{"id":2}\n{"id":3'; const result = Bun.JSONL.parseChunk(chunk); console.log(result.values); // [{ id: 1 }, { id: 2 }] console.log(result.read); // 17 console.log(result.done); // false console.log(result.error); // null Streaming example: let buffer = ""; async function processStream(stream) { for await (const chunk of stream) { buffer += chunk; const result = Bun.JSONL.parseChunk(buffer); if (result.error) throw result.error; for (const value of result.values) handle(value); buffer = buffer.slice(result.read); } // Final parse remaining buffer } Official docs: https://bun.sh/docs/runtime/jsonl and https://bun.sh/reference/bun/JSONL. Parser in C++ using JavaScriptCore for speed, supports ASCII typed arrays without copying.

Citations:


Model Bun.JSONL with its real streaming contract.

Lines 770 and 771 currently reduce both APIs to string -> jsAnything, but Bun's official API accepts string | TypedArray | DataView | ArrayBufferLike; parse() returns an array of values, and parseChunk() also takes optional start and end offsets and returns a structured { values, read, done, error } result. As written, the profile misses zero-copy/partial-buffer paths and cannot exercise the incremental parser surface or result structure validation.

Suggested shape update
 public let bunJSONLGroup = ObjectGroup(
     name: "BunJSONL",
     instanceType: .bunJSONL,
     properties: [:],
     methods: [
-        "parse": [.string] => .jsAnything,
-        "parseChunk": [.string] => .jsAnything,
+        "parse": [.jsAnything] => .jsArray,
+        "parseChunk": [.jsAnything, .opt(.integer), .opt(.integer)] => .object(),
     ]
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/Profiles/BunProfile.swift` around lines 132 - 136, The
BunJSONL ILType currently models both methods as string->jsAnything; update the
bunJSONL ILType (symbol bunJSONL) so parse accepts a union input type (string |
TypedArray | DataView | ArrayBufferLike) and returns an array of JS values, and
parseChunk accepts the same union input plus optional numeric start and end
parameters and returns a structured object with properties { values: Array,
read: Number, done: Boolean, error: Any|null } so the profile can exercise
zero-copy/partial-buffer paths and validate the incremental parser result shape
for methods "parse" and "parseChunk".

Comment on lines +246 to +250
// BunBuildArtifact - result of Bun.build()
static let bunBuildArtifact = ILType.object(
ofGroup: "BunBuildArtifact",
withProperties: ["path", "size", "hash", "sourcemap", "loader", "type", "kind"],
withMethods: ["text", "json", "arrayBuffer", "slice", "stream"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "BunProfile.swift" -type f

Repository: oven-sh/fuzzilli

Length of output: 104


🏁 Script executed:

cat -n ./Sources/Fuzzilli/Profiles/BunProfile.swift | sed -n '240,260p'

Repository: oven-sh/fuzzilli

Length of output: 1793


🏁 Script executed:

cat -n ./Sources/Fuzzilli/Profiles/BunProfile.swift | sed -n '1100,1125p'

Repository: oven-sh/fuzzilli

Length of output: 968


🏁 Script executed:

grep -n "bunBlob" ./Sources/Fuzzilli/Profiles/BunProfile.swift | head -20

Repository: oven-sh/fuzzilli

Length of output: 464


🏁 Script executed:

cat -n ./Sources/Fuzzilli/Profiles/BunProfile.swift | sed -n '485,495p'

Repository: oven-sh/fuzzilli

Length of output: 552


🏁 Script executed:

cat -n ./Sources/Fuzzilli/Profiles/BunProfile.swift | sed -n '732,750p'

Repository: oven-sh/fuzzilli

Length of output: 695


🌐 Web query:

Bun.build() BuildArtifact documentation API methods bytes

💡 Result:

The Bun.BuildArtifact interface represents files generated by Bun.build. It is returned in the BuildOutput.outputs array from Bun.build(config). Key properties: - hash: null | string - kind: 'entry-point' | 'chunk' | 'asset' | 'sourcemap' | 'bytecode' - loader: Loader - path: string - readonly size: number - sourcemap: null | BuildArtifact - readonly type: string API methods (Blob-like): - arrayBuffer: Promise - bytes: Promise - Returns the contents as Uint8Array (equivalent to new Uint8Array(await arrayBuffer)) - formData: Promise - json: Promise - Decodes UTF-8 then parses JSON - slice(start?: number, end?: number, contentType?: string): Blob - stream: ReadableStream - text: Promise Example usage: const result = await Bun.build({ entrypoints: ['./src/index.ts'], outdir: './dist' }); if (result.success) { for (const artifact of result.outputs) { console.log(${artifact.path}: ${artifact.size} bytes (${artifact.kind})); const bytes = await artifact.bytes; // Access bytes } } The bytes method provides direct access to the artifact's binary contents as a Uint8Array, useful for in-memory processing or writing to custom locations.

Citations:


Add missing bytes() method; don't remove json().

Bun documents BuildArtifact as supporting both json() and bytes() methods. The current implementation correctly includes json() but omits bytes(), preventing access to the documented Promise<Uint8Array> API. Additionally, the sourcemap property (line 1109) is typed as .jsAnything but should be nullable and represent a BuildArtifact reference per documentation.

Suggested method fix
     methods: [
         "text":        [] => .jsPromise,
         "json":        [] => .jsPromise,
+        "bytes":       [] => .jsPromise,
         "arrayBuffer": [] => .jsPromise,
         "slice":       [.opt(.integer), .opt(.integer)] => .bunBlob,
         "stream":      [] => .object(),
     ]

Also applies to: 1102-1121

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/Profiles/BunProfile.swift` around lines 246 - 250, The
BunBuildArtifact IL type (bunBuildArtifact) is missing the documented bytes()
method and the sourcemap property is incorrectly typed; update the ILType.object
for bunBuildArtifact to add "bytes" to the withMethods array while keeping
"json", and change the "sourcemap" property to be nullable and reference a
BuildArtifact (i.e., make its type an optional BuildArtifact/ILType.object
reference rather than .jsAnything) so it correctly models Promise<Uint8Array>
for bytes() and the sourcemap relationship.

Comment on lines +1479 to +1486
public var key: Compiler_Protobuf_PropertyKey {
get {_key ?? Compiler_Protobuf_PropertyKey()}
set {_key = newValue}
}
/// Returns true if `key` has been explicitly set.
public var hasKey: Bool {self._key != nil}
/// Clears the value of `key`. Subsequent reads from it will return its default value.
public mutating func clearKey() {self._key = nil}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

print_message_from_file() {
  local file="$1"
  local msg="$2"
  awk -v msg="$msg" '
    $0 ~ "^[[:space:]]*message[[:space:]]+" msg "[[:space:]]*\\{" {printing=1}
    printing {
      print
      opens += gsub(/\{/, "{")
      closes += gsub(/\}/, "}")
      if (opens > 0 && opens == closes) exit
    }
  ' "$file"
}

proto_file="$(fd 'ast\.proto$' | head -n1 || true)"
if [ -z "${proto_file:-}" ]; then
  echo "ast.proto not found"
  exit 0
fi

echo "=== Current ast.proto definitions ==="
for msg in ObjectProperty ObjectMethod ObjectGetter ObjectSetter; do
  echo "-- $msg"
  print_message_from_file "$proto_file" "$msg"
  echo
done

echo "=== Previous ast.proto revision (if available) ==="
prev_commit="$(git log --format='%H' --skip=1 -n 1 -- "$proto_file" || true)"
if [ -n "${prev_commit:-}" ]; then
  tmp="$(mktemp)"
  git show "${prev_commit}:${proto_file}" > "$tmp" || true
  for msg in ObjectProperty ObjectMethod ObjectGetter ObjectSetter; do
    echo "-- $msg"
    print_message_from_file "$tmp" "$msg"
    echo
  done
  rm -f "$tmp"
else
  echo "No previous ast.proto revision found in local git history."
fi

echo "=== AST protobuf serialization/deserialization sites ==="
rg -n -C2 'Compiler_Protobuf_AST|Compiler_Protobuf_Object(Property|Method|Getter|Setter)|serialized(Data|Bytes)|init\(serialized(Data|Bytes):|merge\(serialized' .

Repository: oven-sh/fuzzilli

Length of output: 28059


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the proto file
proto_file="$(fd 'ast\.proto$' | head -n1 || true)"
if [ -z "${proto_file:-}" ]; then
  echo "ast.proto not found"
  exit 0
fi

echo "=== Full git log for ast.proto ==="
git log --oneline -- "$proto_file" | head -20

echo -e "\n=== Checking if there are changes in the current staged/working state ==="
git diff --stat -- "$proto_file" || echo "No diff"
git diff --cached --stat -- "$proto_file" || echo "No cached diff"

echo -e "\n=== Last commit that touched ast.proto ==="
last_commit="$(git log -n 1 --format='%H' -- "$proto_file")"
if [ -n "$last_commit" ]; then
  echo "Commit: $last_commit"
  echo "=== Diff in that commit ==="
  git show "$last_commit:$proto_file" | head -100
fi

Repository: oven-sh/fuzzilli

Length of output: 2528


🏁 Script executed:

#!/bin/bash
set -euo pipefail

proto_file="$(fd 'ast\.proto$' | head -n1 || true)"

# Check the parent commit
current_commit="e62eb32680afb62e87fb052a559237524b063f1c"
parent_commit="$(git rev-parse "${current_commit}^" 2>/dev/null || echo '')"

if [ -z "$parent_commit" ]; then
  echo "No parent commit found"
  exit 0
fi

echo "=== Comparing ObjectProperty in parent vs current ==="
echo "Parent commit: $parent_commit"
git show "${parent_commit}:${proto_file}" 2>/dev/null | grep -A 5 "^message ObjectProperty" || echo "ObjectProperty not found in parent"

echo -e "\n=== Comparing ObjectMethod in parent vs current ==="
git show "${parent_commit}:${proto_file}" 2>/dev/null | grep -A 8 "^message ObjectMethod" || echo "ObjectMethod not found in parent"

echo -e "\n=== Comparing ObjectGetter in parent vs current ==="
git show "${parent_commit}:${proto_file}" 2>/dev/null | grep -A 5 "^message ObjectGetter" || echo "ObjectGetter not found in parent"

echo -e "\n=== Comparing ObjectSetter in parent vs current ==="
git show "${parent_commit}:${proto_file}" 2>/dev/null | grep -A 6 "^message ObjectSetter" || echo "ObjectSetter not found in parent"

echo -e "\n=== Full diff for these messages between parent and current ==="
git diff "${parent_commit}..${current_commit}" -- "$proto_file" | grep -A 10 -B 2 "ObjectProperty\|ObjectMethod\|ObjectGetter\|ObjectSetter" | head -100

Repository: oven-sh/fuzzilli

Length of output: 651


🏁 Script executed:

#!/bin/bash
set -euo pipefail

proto_file="$(fd 'ast\.proto$' | head -n1 || true)"

echo "=== Full git log for ast.proto (if exists) ==="
git log --all --pretty=format:"%H %s" -- "$proto_file" 2>/dev/null | head -20

echo -e "\n=== Search for commit introducing PropertyKey ==="
git log -p --all -- "$proto_file" 2>/dev/null | grep -B 5 "message PropertyKey" | head -20

echo -e "\n=== Search for commit introducing ObjectProperty ==="
git log -p --all -- "$proto_file" 2>/dev/null | grep -B 5 "message ObjectProperty" | head -20

echo -e "\n=== Check current HEAD ==="
git log --oneline -n 5

echo -e "\n=== Check if there are uncommitted changes to generated swift file ==="
git status Sources/Fuzzilli/Protobuf/ast.pb.swift || true
</parameter>

Repository: oven-sh/fuzzilli

Length of output: 2169


🏁 Script executed:

#!/bin/bash
set -euo pipefail

proto_file="$(fd 'ast\.proto$' | head -n1 || true)"

echo "=== Commits that mention 'object' or 'property' in their subjects ==="
git log --all --pretty=format:"%H %s" -- "$proto_file" | grep -i "computed methods\|object literal" | head -10

echo -e "\n=== Check commit 91b69ee (computed methods in object literals) ==="
git show 91b69ee:Sources/Fuzzilli/Protobuf/ast.proto 2>/dev/null | grep -A 6 "message ObjectProperty" || echo "Not found"

echo -e "\n=== Check commit 2dc79c4 (class support) ==="
git show 2dc79c4:Sources/Fuzzilli/Protobuf/ast.proto 2>/dev/null | grep -A 6 "message ObjectProperty" || echo "Not found"

echo -e "\n=== Check a few commits back from HEAD ==="
for commit in adfa084a c88625162 2c145337; do
  echo "--- Commit $commit ---"
  git show ${commit}:Sources/Fuzzilli/Protobuf/ast.proto 2>/dev/null | grep -A 6 "message ObjectProperty" || echo "Message not found"
  echo
done

echo -e "\n=== Show the actual diff that introduced/modified ObjectProperty ==="
git log -p --all -- "$proto_file" 2>/dev/null | grep -B 10 -A 10 "message ObjectProperty" | head -80

Repository: oven-sh/fuzzilli

Length of output: 146


Ensure protobuf wire-format stability for persisted AST data.

The generated changes in Sources/Fuzzilli/Protobuf/ast.pb.swift for Compiler_Protobuf_ObjectProperty, Compiler_Protobuf_ObjectMethod, Compiler_Protobuf_ObjectGetter, and Compiler_Protobuf_ObjectSetter (lines 1479–1502, 1510–1537, 1545–1560, 1568–1593, and corresponding message implementations at 5305–5474) depend on field layout and tag assignments in the underlying proto schema. AST protobufs are actively serialized to disk and exchanged across processes throughout the codebase (Storage, ProtoUtils, Sync, tests). Any changes to field tags or message structure in Sources/Fuzzilli/Protobuf/ast.proto must maintain backward compatibility with persisted data, or a versioning and migration strategy must be introduced. If these messages changed from individual key fields to an embedded PropertyKey message, the wire format is incompatible with older payloads. Verify in ast.proto whether field tags and types are stable, or regenerate this file after implementing versioned schema changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Sources/Fuzzilli/Protobuf/ast.pb.swift` around lines 1479 - 1486, The
generated change replaces individual key fields with an embedded PropertyKey
message for
Compiler_Protobuf_ObjectProperty/ObjectMethod/ObjectGetter/ObjectSetter which
may break wire-format compatibility; inspect ast.proto and confirm the field
numbers and types for the affected messages and the PropertyKey message, then
either restore the original scalar/oneof fields and their original tag numbers
or add the new embedded field under a new tag while keeping the old tags (mark
them deprecated) so older binaries can still parse existing payloads, implement
a version/migration field if you intend to change semantics, and finally
regenerate Sources/Fuzzilli/Protobuf/ast.pb.swift (ensuring
Compiler_Protobuf_ObjectProperty, Compiler_Protobuf_ObjectMethod,
Compiler_Protobuf_ObjectGetter, Compiler_Protobuf_ObjectSetter and PropertyKey
definitions align with the stable tags).


override func instrument(_ program: Program, for fuzzer: Fuzzer) -> Program? {
let b = fuzzer.makeBuilder()
b.eval("fuzzilli('FUZZILLI_CRASH', 0)");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Remove trailing semicolon.

Swift does not require semicolons at the end of statements.

♻️ Suggested fix
-        b.eval("fuzzilli('FUZZILLI_CRASH', 0)");
+        b.eval("fuzzilli('FUZZILLI_CRASH', 0)")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
b.eval("fuzzilli('FUZZILLI_CRASH', 0)");
b.eval("fuzzilli('FUZZILLI_CRASH', 0)")
🧰 Tools
🪛 SwiftLint (0.63.2)

[Warning] 33-33: Lines should not have trailing semicolons

(trailing_semicolon)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Tests/FuzzilliTests/CrashingInstrumentationMutator.swift` at line 33, The
statement calling b.eval contains a trailing semicolon which is unnecessary in
Swift; update the expression b.eval("fuzzilli('FUZZILLI_CRASH', 0)"); by
removing the final semicolon so it becomes b.eval("fuzzilli('FUZZILLI_CRASH',
0)") to match Swift style and avoid extraneous punctuation.

This CL adds support for default parameters in generated functions.

Methods with default parameters, and JS to FuzzIL compilation
code will be added later.

Change-Id: I5b3583a8656c72a4068c497677bd6f18c98badb8
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9176497
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Leon Bettscheider <bettscheider@google.com>
mi-ac and others added 23 commits June 25, 2026 07:13
…noise

Optimize the reporting of poorly performing code generators and program templates by aggregating their stats across all fuzzer nodes and centralizing the output on the main thread.

This makes log messages more actionable and easier to read and navigate in cloud logging.

Key changes:
- Moved the generator/template stats from individual worker threads to the root node.
- Merged validSamples and interestingSamples into correctSamples.
- Print compact, table-based logs.
- Reduced log frequency to every 5th statistics update and on termination.

TAG=agy

Bug: 465497343
Change-Id: I146e8b08057ee1ed84cf64c8f308b6be7b63bbf3
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9432454
Reviewed-by: Leon Bettscheider <bettscheider@chromium.org>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Michael Achenbach <machenbach@google.com>
The index passed into the `translateInput` is unused, as the code
hard-coded to use 0.

Bug: 527887612, 524213342
Change-Id: I7e8604ae8cc814ab588614dff70a77d2db8fe732
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9471895
Commit-Queue: Tigran Bantikyan <bantikyan@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Auto-Submit: Tigran Bantikyan <bantikyan@google.com>
This CL adds `isFinal` to WasmTypeDescription and subclasses
WasmArrayTypeDescription, WasmStructTypeDescription,
WasmSignatureTypeDescription.

In code generators, we only use non-final super types.
All generated types are final with a probability of 25%.

Bug: 517707090
Change-Id: I47df3b1b8f06f5b133f3a349f0c1c7aeebdc975f
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9464195
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Leon Bettscheider <bettscheider@chromium.org>
and make them parallelizable.

- AnalyzerTest.swift
- ContextGraphTest.swift
- DiffOracleTests.swift
- EngineTests.swift

Bug: 522635668
Change-Id: Icb3e6503cd84d2fffeb9952c866e6f7a40c618f1
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9471917
Reviewed-by: Leon Bettscheider <bettscheider@chromium.org>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Bug: 515363087
Change-Id: Ib12bedcdabb2681a10afcce1bb26dae5a89c56f1
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9453876
Commit-Queue: Raphaël Hérouart <rherouart@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Bug: 522635668
Change-Id: I42ffbdb0fbf0f25a621a14466a3c4c9d815089fb
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9475596
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Leon Bettscheider <bettscheider@chromium.org>
Bug: 498924945
TAG=agy
Change-Id: Idf0bab6bef6811f82abd8bfc5586e4d699b90d4a
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9376411
Commit-Queue: Rezvan Mahdavi Hezaveh <rezvan@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
This reverts commit a799a23.

Reason for revert: Suspected to crash with:
Fatal error: Code generators must contain at least one generator to be used in the prefix

Original change's description:
> [code generators] Introduce explicit "useInPrefix"
>
> Before this CL, we chose which CodeGenerators to use in the prefix based
> on IsValueGenerator property (= all stubs in the generator require no
> inputs and produce something).
>
> However, we want to have CodeGenerators which satisfy that
> property, but which are not used in the prefix.
>
> This CL solves this problem by adding an explicit way to mark which code
> generators should be used in the prefix.
>
> CONV=fe946adb-0cf0-4d0b-a600-a45a70ecddad
> TAG=agy
> Bug: 526979176
> Change-Id: Ib5dd42efbaf082e7196992c00aa867a23c4609b0
> Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9457934
> Commit-Queue: Marja Hölttä <marja@google.com>
> Reviewed-by: Matthias Liedtke <mliedtke@google.com>

Bug: 526979176
Change-Id: I1acc51b237c4ece57cf9ef7b4637a6bd16c6ebf6
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9482515
Bot-Commit: rubber-stamper@appspot.gserviceaccount.com <rubber-stamper@appspot.gserviceaccount.com>
Commit-Queue: Michael Achenbach <machenbach@google.com>
Reviewed-by: Marja Hölttä <marja@google.com>
Previous version: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9457934

Previous description:

Before this CL, we chose which CodeGenerators to use in the prefix based
on IsValueGenerator property (= all stubs in the generator require no
inputs and produce something).

However, we want to have CodeGenerators which satisfy that
property, but which are not used in the prefix.

This CL solves this problem by adding an explicit way to mark which code
generators should be used in the prefix.

Fix: Update CodeGenerators in various files to pass useInPrefix: true as needed.

CONV=fe946adb-0cf0-4d0b-a600-a45a70ecddad
TAG=agy

Bug: 526979176
Change-Id: I20abc01ef6b28f69aa39861143c7cb9e253608fe
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9482715
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Marja Hölttä <marja@google.com>
…putMutable

Mutating the input of a pending bundle module doesn't make sense,
since it would disconnect the pending bundle module declaration
and definition.

TAG=agy
CONV=54baa3fa-0849-4897-bf71-1d1444d3dece

Change-Id: I2bfec9f49e41a1d9f73fdc9abc0b8612fe380771
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9483335
Commit-Queue: Marja Hölttä <marja@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
- EnvironmentTest.swift
- InstructionTests.swift
- Leb128Test.swift
- ProbingMutatorTests.swift
- ProgramSerializationTest.swift
- RingBufferTest.swift
- TestUtils.swift

Bug: 522635668
Change-Id: I54f26fd83eb0b393ea9f3d5ab8aabc95cc972dd2
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9482716
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Leon Bettscheider <bettscheider@chromium.org>
Otherwise workers are going to crash as they take over the generator and
mutator of the main config but miss the wasmOptPath.

Bug: 498924945
Change-Id: Ia5137e731823e9387b7cbd38a8b3328ac70158e9
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9484055
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Rezvan Mahdavi Hezaveh <rezvan@google.com>
Commit-Queue: Rezvan Mahdavi Hezaveh <rezvan@google.com>
Don't replace non-jsvariable inputs with inputs which might be of a
different type.

Additionally, mark label-related operations as isNotInputMutable.

Change-Id: Iafce817bc7781aa6f919d59e9faf8855ae6c9f82
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9483695
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Marja Hölttä <marja@google.com>
Otherwise this causes reports that this generator has too restrictive
dynamic requirements when running without --bundle, e.g.:

  Code generators with too restrictive dynamic requirements:
  Name                                   | Success | Invocations
  DynamicImportGenerator                 |   0.00% |      12487

This way, if we are in the .bundle context, we can even schedule the
generation of a module before emitting the import.

Bug: 398218423
Change-Id: I3d758298f750796cce01b143cf87ad65df9c98d7
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9484355
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Marja Hölttä <marja@google.com>
This moves the logic to construct an identical subtype
for a given supertype from code generators.

In follow-up CLs, this function will become recursive and more capable.

Bug: 517707090
Change-Id: I64cf7a6ae4f37f16d48f633670e9af9203149098
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9475595
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Leon Bettscheider <bettscheider@chromium.org>
Enables generation of non-identical subtypes for arrays.

Specifically:
- Immutable arrays w/ (elementType is nullable reference type):
  make element type in subtype non-nullable with p=50%.
- Immutable arrays w/ (elementType is reference to index type):
  find an existing subtype for the element type.

Bug: 517707090
Change-Id: I9d8fd570274b4e94042dba59be22bc425b87ed51
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9482876
Commit-Queue: Leon Bettscheider <bettscheider@chromium.org>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
This CL contains a couple of minor cleanups.

Bug: 529687716
Change-Id: Iadc25fe651f4ed9ddd79ccaa400355af75c126d9
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9489395
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Leon Bettscheider <bettscheider@chromium.org>
Bug:498924945
TAG=agy
Change-Id: I3a8d353800bf9349ea23e446e3562c49af98f3de
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9475800
Commit-Queue: Rezvan Mahdavi Hezaveh <rezvan@google.com>
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
This was removed with https://crrev.com/c/7960065

Bug: 525363217
Change-Id: Iaf4772b0f965ddd0bf823e2d7f33d2e0fa5e8642
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9489875
Commit-Queue: Marja Hölttä <marja@google.com>
Reviewed-by: Raphaël Hérouart <rherouart@google.com>
Commit-Queue: Raphaël Hérouart <rherouart@google.com>
Commit-Queue: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Marja Hölttä <marja@google.com>
Auto-Submit: Matthias Liedtke <mliedtke@google.com>
…isposables to avoid throwing.

Bug: 524562043
Change-Id: Ibbd2b13b0dff18b3a149a98d836b5f0092959ca6
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9432394
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Commit-Queue: Raphaël Hérouart <rherouart@google.com>
Bug: 524213342
Change-Id: Ia5b05c04a4068ce9d698a4ea35b6a49b2624fe2b
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9492736
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Leon Bettscheider <bettscheider@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@google.com>
This adds tests for the exploration mutator. To make them
deterministic, two helper functions for determining the runtime values
to explore are factored out for testing. Additionally, tests need
to ignore the non-deterministic seed passed to explore.

With these changes, we can now test the instrumentation and processing
of explore and compare the expected JS programs. One of the tests
should cover the logic for https://crbug.com/527887612.

Bug: 527887612, 524213342
Change-Id: If7ad6dc9dc94d7736b7bbcc4a0dd447568c226fb
Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9482875
Reviewed-by: Matthias Liedtke <mliedtke@google.com>
Reviewed-by: Leon Bettscheider <bettscheider@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@google.com>
# Conflicts:
#	Sources/Fuzzilli/Compiler/Compiler.swift
#	Sources/Fuzzilli/FuzzIL/Code.swift
#	Tests/FuzzilliTests/EnvironmentTest.swift
@robobun
robobun force-pushed the farm/6d2cda71/rebase-and-update-profile branch from e62eb32 to c650dad Compare July 2, 2026 15:15

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
Sources/Fuzzilli/Lifting/ScriptWriter.swift (1)

57-82: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Removed safety assertion for embedded newlines.

The previous assert(!line.contains("\n")) guard is gone from emit(). This function's doc comment states it "emit[s] one line of code," and its splitting logic only accounts for spaces, not newlines — if a caller ever passes a string containing a raw \n (e.g. malformed input reaching LoadString/Directive content), it will silently be written as-is with a single indentation prefix rather than being caught in debug builds or handled correctly. Restoring the assertion costs nothing and preserves this diagnostic safety net.

🛡️ Suggested fix
     mutating func emit<S: StringProtocol>(_ line: S) {
         assert(maxLineLength > currentIndention.count)
+        assert(!line.contains("\n"))
         let splitAt = maxLineLength - currentIndention.count

As per line-range change details, this assertion removal was called out as the sole functional change in this hunk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/Lifting/ScriptWriter.swift` around lines 57 - 82, The safety
guard for embedded newlines was removed from ScriptWriter.emit(_:), which breaks
the “one line of code” contract and can let raw newline input slip through
unnoticed. Restore the debug-time assertion in emit<S: StringProtocol>(_) so
callers like LoadString and Directive are still caught if they pass strings
containing “\n”, while leaving the existing space-based wrapping logic
unchanged.
Sources/Fuzzilli/Lifting/FuzzILLifter.swift (1)

1735-1767: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused destructuring helpers
liftArrayDestructPattern and liftObjectDestructPattern have no remaining call sites; the new generic destructuring path covers these cases. حذف them to trim dead code.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/Lifting/FuzzILLifter.swift` around lines 1735 - 1767, The
destructuring helper methods liftArrayDestructPattern and
liftObjectDestructPattern in FuzzILLifter are now dead code because the generic
destructuring path handles these cases. Remove both unused private helpers and
any related assertions or local logic, ensuring no remaining references depend
on them.
Sources/Fuzzilli/Fuzzer.swift (1)

897-913: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Preserve stdout and fuzzout in differential reports.

DiffExecution.diff carries formatted stdout/stderr/fuzzout, but processDifferential stores only stderr. If the differential is visible in stdout or fuzzout, the minimized program’s footer loses the key evidence.

Proposed direction
 func processDifferential(
-    _ program: Program, withStderr stderr: String,
+    _ program: Program, withStdout stdout: String, withStderr stderr: String,
+    withFuzzout fuzzout: String,
     origin: ProgramOrigin
 ) {
@@
                 let footerMessage = """
                     DIFFERENTIAL INFO
                     ==========
+                    STDOUT:
+                    \(stdout)
                     STDERR:
                     \(stderr)
+                    FUZZOUT:
+                    \(fuzzout)
                     ARGS: \(runner.processArguments.joined(separator: " "))
                     REFERENCE ARGS: \(referenceRunner!.processArguments.joined(separator: " "))
                     """

Update the call sites to pass execution.stdout and execution.fuzzout alongside execution.stderr.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/Fuzzer.swift` around lines 897 - 913, `processDifferential`
only records stderr in the footer, so differential reports can lose stdout and
fuzzout evidence. Update the `processDifferential(_:,withStderr:origin:)` flow
and its call sites from `DiffExecution.diff` to pass along `execution.stdout`
and `execution.fuzzout` in addition to `execution.stderr`, then include those
values in the footer text so minimized programs preserve all relevant output.
Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift (1)

1646-1666: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Emit the loop backedge or remove the loop-counter logic.

Line 1664 computes isNotZero, but no branch uses it, so this generator no longer creates the bounded loop described by the comments.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift` around lines 1646 - 1666,
The loop backedge logic in WasmBeginLoopGenerator/WasmEndLoopGenerator is
incomplete because the computed isNotZero value is never used. Update
WasmEndLoopGenerator to actually emit the conditional backedge using isNotZero
and the loop signature, or remove the loopCounter/loopSignature bookkeeping
entirely if the loop should no longer be bounded. Keep the behavior consistent
with the existing WasmBeginLoop and WasmEndLoop generator flow in
WasmCodeGenerators.swift.
Sources/Fuzzilli/FuzzIL/Context.swift (1)

17-35: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Include .blockStatement in allCases and description.

The new context is defined on Line 80 but omitted here, so context graph enumeration and diagnostics silently lose it.

Proposed fix
         .bundle,
+        .blockStatement,
         .moduleTopLevel,
         .workerFunction,
@@
         if self.contains(.bundle) {
             strings.append(".bundle")
         }
+        if self.contains(.blockStatement) {
+            strings.append(".blockStatement")
+        }
         if self.contains(.moduleTopLevel) {
             strings.append(".moduleTopLevel")
         }

Also applies to: 129-147

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/FuzzIL/Context.swift` around lines 17 - 35, The new Context
case is missing from the Context.allCases enumeration and from the
Context.description handling, so it is never visited or reported. Update
Context.allCases to include .blockStatement alongside the other cases, and add
the corresponding .blockStatement description branch in Context.description so
graph enumeration and diagnostics stay complete.
Sources/Fuzzilli/FuzzIL/TypeSystem.swift (1)

767-773: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Check wasmLabelType, not wasmTagType.

isWasmLabelType currently returns true for tags and false for labels, breaking any label-type checks that use this property.

Proposed fix
     public var isWasmLabelType: Bool {
-        return wasmTagType != nil
+        return wasmLabelType != nil
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/FuzzIL/TypeSystem.swift` around lines 767 - 773, The
`isWasmLabelType` property in `TypeSystem` is checking the wrong backing value
and currently returns true for tags instead of labels. Update `isWasmLabelType`
to use `wasmLabelType` (matching `wasmLabelType`/`wasmTagType` accessors) so
label-type checks correctly recognize label types and not tag types.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Sources/Fuzzilli/Base/ContextGraph.swift`:
- Around line 35-41: The shortest-path weighting in ContextGraph.Edge is using
the largest generator size instead of the cheapest concrete generator, which can
cause getShortestPath to ignore valid shorter paths. Update addGenerator(_:) so
the edge weight reflects the minimum parts.count among generators (or otherwise
the cheapest concrete generator) rather than max, and make sure
Path.randomConcretePath() selects only generators whose parts.count matches
edge.weight so the chosen concrete path stays consistent with the shortest-path
cost.

In `@Sources/Fuzzilli/CodeGen/BinaryenWasmGenerator.swift`:
- Around line 67-94: The BinaryenWasmGenerator path is crashing on unexpected
wasm-opt boundary output because runBinaryenWasmGenerator uses try! to decode
WasmBoundary and fatalError for a func export missing a signature. Update
runBinaryenWasmGenerator to fail gracefully by using do-catch or try?/guard
around JSONDecoder.decode and returning nil on malformed or incompatible JSON,
and replace the fatalError in the export.kind == "func" handling with a nil
return so one bad external-tool response only skips this generation attempt.

In `@Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift`:
- Around line 952-963: The Wasm table generators are choosing an inclusive index
range via the WasmTableGetGenerator/WasmTableSetGenerator logic, which can
produce out-of-bounds accesses and fails for empty tables. Update the index
selection to use an exclusive upper bound based on the table’s actual usable
size, and guard the empty-table case before calling wasmTableGet or
wasmTableSet. Keep the fix localized to the table access generators in
WasmCodeGenerators by using the existing tableType, function, and indexVar flow.
- Around line 750-756: The destination bounds are being computed from the source
memory instead of the selected destination memory, which can make copy-size
checks use the wrong limits. Update the logic around the `dstMemory`,
`dstMemoryTypeInfo`, and `memArg` setup to read the type information from
`dstMemory` rather than `srcMemory`, so the destination bounds are derived from
the actual destination memory when source and destination differ.
- Around line 785-793: The wasmMemoryFill generation in WasmCodeGenerators.swift
can still create an out-of-bounds one-byte write when offsetValue reaches the
end of memory. Update the nrOfBytesToUpdate calculation in the fill-generation
logic around offsetValue, offset, and memoryArgument so that it allows a
zero-length fill when offsetValue == memSize, and only adds the forced +1 when
there is remaining space to write. Keep the fix localized to the memory fill
helper that builds the wasmMemoryFill call.

In `@Sources/Fuzzilli/Compiler/Parser/parser.js`:
- Around line 417-431: The ForInStatement handling in parser.js currently
assumes the left-side declaration is a plain identifier, so destructuring can
produce an invalid SimpleVariableDeclarator with an undefined name. Update the
ForInStatement branch in visitStatement to explicitly reject non-Identifier
decl.id values before building the declaration, or otherwise extend
ForInLoop/SimpleVariableDeclarator support first if destructuring is intended.
Use the ForInStatement, visitStatement, and SimpleVariableDeclarator paths to
locate the change.

In `@Sources/Fuzzilli/Configuration.swift`:
- Around line 157-159: The Configuration initializer currently force-unwraps
storagePath when building diffConfig, which can crash if dumplingEnabled is true
and storagePath is nil. Update the Configuration init path that assigns
self.diffConfig to handle the nil case safely, either by guarding storagePath
before calling DifferentialConfig.create(for:storagePath:) or by making
diffConfig conditional on both dumplingEnabled and a non-nil storagePath.

In `@Sources/Fuzzilli/Fuzzer.swift`:
- Around line 1046-1049: The warning reason in Fuzzer.swift currently omits
disabled bundles when Wasm is enabled, so update the reason string in the logic
around config.isWasmEnabled to mention that failures can also require currently
disabled bundles; keep the wording aligned with the existing .needsBundles
failure path so the warning accurately reflects all contributing causes.
- Around line 465-469: The excluded Wasm program storage path in Fuzzer.swift
can crash because createDirectory uses try! and the Storage lookup uses as!, so
update that logic to fail safely instead of trapping. In the code handling
stored programs in Fuzzer and the storeProgram call on the Storage module, catch
directory-creation errors and verify the module cast/lookup before use; if
either step fails, return .needsWasm rather than crashing the fuzzer.

In `@Sources/Fuzzilli/FuzzIL/Code.swift`:
- Around line 256-263: The top-level await exemption in the validation logic is
too broad because the `isAllowedAwait` check in `Code.swift` skips `Await`’s
required context for any non-async context. Update this logic so the exemption
only applies when `instr.op` is `Await` and the current
`contextAnalyzer.context` represents an actual top-level JavaScript scope, not
inside sync functions, class bodies/static initializers, or Wasm contexts. Keep
the existing `requiredContext.isSubset(of:)` validation for all other cases, and
adjust the `isAllowedAwait` condition accordingly.
- Around line 521-524: The `isValidBlock(_:)` check is incorrectly allowing
`block.tail == endIndex`, which can then trap when indexing `self[block.tail]`;
update the validation in `Code.isValidBlock(_:)` to reject `endIndex` as a tail
before any subscripting, while still keeping the `head`/`tail` block-start/end
and `isMatchingEnd(for:)` checks intact.

In `@Sources/Fuzzilli/FuzzIL/JsOperations.swift`:
- Around line 1343-1355: The parameter validation in the initializer for the
subroutine/parameter metadata currently allows duplicate
defaultParameterIndices, which can misalign the input layout. Update the checks
in the init(count:hasRestParameter:defaultParameterIndices:) initializer to also
reject duplicates by ensuring the indices are unique in addition to being sorted
and in range, while preserving the existing rest-parameter validation.
- Around line 3344-3348: The Destruct initializer in JsOperations.swift only
checks numInputs against pattern.numExtraInputs, but it also needs to validate
that numOutputs matches pattern.numBindings so the instruction layout stays
consistent. Update the Destruct init(pattern:numInputs:numOutputs:)
precondition/assertion to enforce the output arity against the
DestructuringPattern, using pattern.numBindings as the reference, while keeping
the existing input validation intact.

In `@Sources/Fuzzilli/FuzzIL/TypeSystem.swift`:
- Around line 1211-1219: The TypeExtension reconstruction helpers are dropping
existing metadata when creating a new extension, which can erase previously
inferred type information. Update the rebuild paths in TypeSystem.swift,
especially the TypeExtension assembly used by the property/signature helpers and
the merge logic, so every existing field is carried forward unless intentionally
changed. Make sure symbols like TypeExtension, ILType, canMerge, and the helper
methods around the affected range preserve symbolMethods, receiver,
iterableElementType, exports, and any other extension fields instead of
resetting them.
- Around line 1557-1559: The subtype bit layout in BaseType is inverted for
iterable and asyncIterable, causing the TypeSystem relationships to be wrong.
Update the BaseType definitions for asyncIterable and iterable in TypeSystem so
that .iterable is the parent/supertype and .asyncIterable remains distinct as
intended, and verify the Is checks on these symbols now make
.asyncIterable().Is(.iterable()) true and not the reverse.

In `@Sources/Fuzzilli/Lifting/JavaScriptRuntimeAssistedMutatorLifting.swift`:
- Around line 137-141: The JavaScript lifting path is now accepting raw strings
via isShortString without preserving the previous safety filter, so quoted
literals and property names can be emitted with invalid escaping. Update
JavaScriptRuntimeAssistedMutatorLifting’s string handling to either keep the old
string safety restriction or add end-to-end escaping in the Swift lifting
helpers that serialize string values and quoted property names. Make sure the
relevant string emission code paths consistently escape quotes, backslashes,
newlines, and other control characters before generating JS.

---

Outside diff comments:
In `@Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift`:
- Around line 1646-1666: The loop backedge logic in
WasmBeginLoopGenerator/WasmEndLoopGenerator is incomplete because the computed
isNotZero value is never used. Update WasmEndLoopGenerator to actually emit the
conditional backedge using isNotZero and the loop signature, or remove the
loopCounter/loopSignature bookkeeping entirely if the loop should no longer be
bounded. Keep the behavior consistent with the existing WasmBeginLoop and
WasmEndLoop generator flow in WasmCodeGenerators.swift.

In `@Sources/Fuzzilli/Fuzzer.swift`:
- Around line 897-913: `processDifferential` only records stderr in the footer,
so differential reports can lose stdout and fuzzout evidence. Update the
`processDifferential(_:,withStderr:origin:)` flow and its call sites from
`DiffExecution.diff` to pass along `execution.stdout` and `execution.fuzzout` in
addition to `execution.stderr`, then include those values in the footer text so
minimized programs preserve all relevant output.

In `@Sources/Fuzzilli/FuzzIL/Context.swift`:
- Around line 17-35: The new Context case is missing from the Context.allCases
enumeration and from the Context.description handling, so it is never visited or
reported. Update Context.allCases to include .blockStatement alongside the other
cases, and add the corresponding .blockStatement description branch in
Context.description so graph enumeration and diagnostics stay complete.

In `@Sources/Fuzzilli/FuzzIL/TypeSystem.swift`:
- Around line 767-773: The `isWasmLabelType` property in `TypeSystem` is
checking the wrong backing value and currently returns true for tags instead of
labels. Update `isWasmLabelType` to use `wasmLabelType` (matching
`wasmLabelType`/`wasmTagType` accessors) so label-type checks correctly
recognize label types and not tag types.

In `@Sources/Fuzzilli/Lifting/FuzzILLifter.swift`:
- Around line 1735-1767: The destructuring helper methods
liftArrayDestructPattern and liftObjectDestructPattern in FuzzILLifter are now
dead code because the generic destructuring path handles these cases. Remove
both unused private helpers and any related assertions or local logic, ensuring
no remaining references depend on them.

In `@Sources/Fuzzilli/Lifting/ScriptWriter.swift`:
- Around line 57-82: The safety guard for embedded newlines was removed from
ScriptWriter.emit(_:), which breaks the “one line of code” contract and can let
raw newline input slip through unnoticed. Restore the debug-time assertion in
emit<S: StringProtocol>(_) so callers like LoadString and Directive are still
caught if they pass strings containing “\n”, while leaving the existing
space-based wrapping logic unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 8f4ea337-9ea5-4f61-b110-90e3fab7345f

📥 Commits

Reviewing files that changed from the base of the PR and between e62eb32 and c650dad.

📒 Files selected for processing (176)
  • .github/workflows/swift.yml
  • .gitignore
  • .swift-format
  • PRESUBMIT.py
  • Package.swift
  • Sources/FuzzILTool/main.swift
  • Sources/Fuzzilli/Base/ContextGraph.swift
  • Sources/Fuzzilli/Base/Contributor.swift
  • Sources/Fuzzilli/Base/Events.swift
  • Sources/Fuzzilli/Base/Logging.swift
  • Sources/Fuzzilli/Base/ProgramBuilder.swift
  • Sources/Fuzzilli/Base/Timers.swift
  • Sources/Fuzzilli/CodeGen/BinaryenWasmGenerator.swift
  • Sources/Fuzzilli/CodeGen/CodeGenerator.swift
  • Sources/Fuzzilli/CodeGen/CodeGeneratorWeights.swift
  • Sources/Fuzzilli/CodeGen/CodeGenerators.swift
  • Sources/Fuzzilli/CodeGen/ProgramTemplate.swift
  • Sources/Fuzzilli/CodeGen/ProgramTemplateWeights.swift
  • Sources/Fuzzilli/CodeGen/ProgramTemplates.swift
  • Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift
  • Sources/Fuzzilli/Compiler/Compiler.swift
  • Sources/Fuzzilli/Compiler/JavaScriptParser.swift
  • Sources/Fuzzilli/Compiler/Parser/parser.js
  • Sources/Fuzzilli/Configuration.swift
  • Sources/Fuzzilli/Corpus/BasicCorpus.swift
  • Sources/Fuzzilli/Corpus/Corpus.swift
  • Sources/Fuzzilli/Corpus/MarkovCorpus.swift
  • Sources/Fuzzilli/DumplingDiffOracle/Oracle.swift
  • Sources/Fuzzilli/Engines/FuzzEngine.swift
  • Sources/Fuzzilli/Engines/GenerativeEngine.swift
  • Sources/Fuzzilli/Engines/HybridEngine.swift
  • Sources/Fuzzilli/Engines/MultiEngine.swift
  • Sources/Fuzzilli/Engines/MutationEngine.swift
  • Sources/Fuzzilli/Environment/JavaScriptEnvironment.swift
  • Sources/Fuzzilli/Evaluation/ProgramCoverageEvaluator.swift
  • Sources/Fuzzilli/Evaluation/ProgramEvaluator.swift
  • Sources/Fuzzilli/Execution/Execution.swift
  • Sources/Fuzzilli/Execution/REPRL.swift
  • Sources/Fuzzilli/FuzzIL/Analyzer.swift
  • Sources/Fuzzilli/FuzzIL/Blocks.swift
  • Sources/Fuzzilli/FuzzIL/Code.swift
  • Sources/Fuzzilli/FuzzIL/Context.swift
  • Sources/Fuzzilli/FuzzIL/Instruction.swift
  • Sources/Fuzzilli/FuzzIL/JSTyper.swift
  • Sources/Fuzzilli/FuzzIL/JsOperations.swift
  • Sources/Fuzzilli/FuzzIL/Opcodes.swift
  • Sources/Fuzzilli/FuzzIL/Operation.swift
  • Sources/Fuzzilli/FuzzIL/Program.swift
  • Sources/Fuzzilli/FuzzIL/ProgramComments.swift
  • Sources/Fuzzilli/FuzzIL/Semantics.swift
  • Sources/Fuzzilli/FuzzIL/TypeSystem.swift
  • Sources/Fuzzilli/FuzzIL/Variable.swift
  • Sources/Fuzzilli/FuzzIL/WasmOperations.swift
  • Sources/Fuzzilli/Fuzzer.swift
  • Sources/Fuzzilli/Lifting/Expression.swift
  • Sources/Fuzzilli/Lifting/FuzzILLifter.swift
  • Sources/Fuzzilli/Lifting/JSExpressions.swift
  • Sources/Fuzzilli/Lifting/JavaScriptExploreLifting.swift
  • Sources/Fuzzilli/Lifting/JavaScriptFixupLifting.swift
  • Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
  • Sources/Fuzzilli/Lifting/JavaScriptProbeLifting.swift
  • Sources/Fuzzilli/Lifting/JavaScriptRuntimeAssistedMutatorLifting.swift
  • Sources/Fuzzilli/Lifting/ScriptWriter.swift
  • Sources/Fuzzilli/Lifting/WasmLifter.swift
  • Sources/Fuzzilli/Minimization/BlockReducer.swift
  • Sources/Fuzzilli/Minimization/DataFlowSimplifier.swift
  • Sources/Fuzzilli/Minimization/DeduplicatingReducer.swift
  • Sources/Fuzzilli/Minimization/InliningReducer.swift
  • Sources/Fuzzilli/Minimization/InstructionSimplifier.swift
  • Sources/Fuzzilli/Minimization/LoopSimplifier.swift
  • Sources/Fuzzilli/Minimization/MinimizationHelper.swift
  • Sources/Fuzzilli/Minimization/MinimizationPostProcessor.swift
  • Sources/Fuzzilli/Minimization/Minimizer.swift
  • Sources/Fuzzilli/Minimization/ReassignReducer.swift
  • Sources/Fuzzilli/Minimization/VariadicInputReducer.swift
  • Sources/Fuzzilli/Minimization/WasmTypeGroupReducer.swift
  • Sources/Fuzzilli/Modules/NetworkSync.swift
  • Sources/Fuzzilli/Modules/Statistics.swift
  • Sources/Fuzzilli/Modules/Storage.swift
  • Sources/Fuzzilli/Modules/Sync.swift
  • Sources/Fuzzilli/Modules/ThreadSync.swift
  • Sources/Fuzzilli/Mutators/BaseInstructionMutator.swift
  • Sources/Fuzzilli/Mutators/BinaryenWasmMutator.swift
  • Sources/Fuzzilli/Mutators/CodeGenMutator.swift
  • Sources/Fuzzilli/Mutators/CombineMutator.swift
  • Sources/Fuzzilli/Mutators/ConcatMutator.swift
  • Sources/Fuzzilli/Mutators/ExplorationMutator.swift
  • Sources/Fuzzilli/Mutators/FixupMutator.swift
  • Sources/Fuzzilli/Mutators/InputMutator.swift
  • Sources/Fuzzilli/Mutators/MutatorSettings.swift
  • Sources/Fuzzilli/Mutators/OperationMutator.swift
  • Sources/Fuzzilli/Mutators/ProbingMutator.swift
  • Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift
  • Sources/Fuzzilli/Mutators/SpliceMutator.swift
  • Sources/Fuzzilli/Profiles/BunProfile.swift
  • Sources/Fuzzilli/Profiles/DuktapeProfile.swift
  • Sources/Fuzzilli/Profiles/JSCProfile.swift
  • Sources/Fuzzilli/Profiles/JerryscriptProfile.swift
  • Sources/Fuzzilli/Profiles/NjsProfile.swift
  • Sources/Fuzzilli/Profiles/Profile.swift
  • Sources/Fuzzilli/Profiles/QjsProfile.swift
  • Sources/Fuzzilli/Profiles/QtjsProfile.swift
  • Sources/Fuzzilli/Profiles/Serenity.swift
  • Sources/Fuzzilli/Profiles/SpidermonkeyProfile.swift
  • Sources/Fuzzilli/Profiles/V8CommonProfile.swift
  • Sources/Fuzzilli/Profiles/V8DumplingProfile.swift
  • Sources/Fuzzilli/Profiles/V8HoleFuzzingProfile.swift
  • Sources/Fuzzilli/Profiles/V8Profile.swift
  • Sources/Fuzzilli/Profiles/V8SandboxProfile.swift
  • Sources/Fuzzilli/Profiles/XSProfile.swift
  • Sources/Fuzzilli/Protobuf/ProtoUtils.swift
  • Sources/Fuzzilli/Protobuf/README.md
  • Sources/Fuzzilli/Protobuf/ast.pb.swift
  • Sources/Fuzzilli/Protobuf/ast.proto
  • Sources/Fuzzilli/Protobuf/gen_programproto.py
  • Sources/Fuzzilli/Protobuf/operations.pb.swift
  • Sources/Fuzzilli/Protobuf/operations.proto
  • Sources/Fuzzilli/Protobuf/program.pb.swift
  • Sources/Fuzzilli/Protobuf/program.proto
  • Sources/Fuzzilli/Protobuf/sync.pb.swift
  • Sources/Fuzzilli/Protobuf/sync.proto
  • Sources/Fuzzilli/Util/Arguments.swift
  • Sources/Fuzzilli/Util/BinaryenRunner.swift
  • Sources/Fuzzilli/Util/CInterop.swift
  • Sources/Fuzzilli/Util/JavaScriptExecutor.swift
  • Sources/Fuzzilli/Util/Misc.swift
  • Sources/Fuzzilli/Util/MockFuzzer.swift
  • Sources/Fuzzilli/Util/OutputBuffer.swift
  • Sources/Fuzzilli/Util/Random.swift
  • Sources/Fuzzilli/Util/VariableMap.swift
  • Sources/Fuzzilli/Util/VariableSet.swift
  • Sources/Fuzzilli/Util/WeightedList.swift
  • Sources/FuzzilliCli/TerminalUI.swift
  • Sources/FuzzilliCli/main.swift
  • Sources/FuzzilliDetectMissingBuiltins/main.swift
  • Sources/REPRLRun/main.swift
  • Sources/RelateTool/main.swift
  • Tests/FuzzilliTests/AnalyzerTest.swift
  • Tests/FuzzilliTests/CompilerTests.swift
  • Tests/FuzzilliTests/CompilerTests/advanced_loops.js
  • Tests/FuzzilliTests/CompilerTests/computed_and_indexed_properties.js
  • Tests/FuzzilliTests/CompilerTests/destructuring.js
  • Tests/FuzzilliTests/CompilerTests/function_with_default_parameters.js
  • Tests/FuzzilliTests/CompilerTests/labels.js
  • Tests/FuzzilliTests/CompilerTests/large_index_delete.js
  • Tests/FuzzilliTests/CompilerTests/methods_with_default_parameters.js
  • Tests/FuzzilliTests/CompilerTests/nested_destructuring.js
  • Tests/FuzzilliTests/CompilerTests/super.js
  • Tests/FuzzilliTests/ContextGraphTest.swift
  • Tests/FuzzilliTests/CrashingInstrumentationMutator.swift
  • Tests/FuzzilliTests/DiffOracleTests.swift
  • Tests/FuzzilliTests/EngineTests.swift
  • Tests/FuzzilliTests/EnvironmentTest.swift
  • Tests/FuzzilliTests/InstructionTests.swift
  • Tests/FuzzilliTests/JSTyperTests.swift
  • Tests/FuzzilliTests/LabelTests.swift
  • Tests/FuzzilliTests/Leb128Test.swift
  • Tests/FuzzilliTests/LifterTest.swift
  • Tests/FuzzilliTests/LiveTests.swift
  • Tests/FuzzilliTests/MinimizerTest.swift
  • Tests/FuzzilliTests/MutatorTests.swift
  • Tests/FuzzilliTests/ProbingMutatorTests.swift
  • Tests/FuzzilliTests/ProgramBuilderTest.swift
  • Tests/FuzzilliTests/ProgramSerializationTest.swift
  • Tests/FuzzilliTests/RingBufferTest.swift
  • Tests/FuzzilliTests/RuntimeAssistedMutatorTests.swift
  • Tests/FuzzilliTests/ScriptWriterTest.swift
  • Tests/FuzzilliTests/TestUtils.swift
  • Tests/FuzzilliTests/TypeSystemTest.swift
  • Tests/FuzzilliTests/VariableMapTest.swift
  • Tests/FuzzilliTests/VariableSetTest.swift
  • Tests/FuzzilliTests/WasmAtomicsTests.swift
  • Tests/FuzzilliTests/WasmTableTests.swift
  • Tests/FuzzilliTests/WasmTests.swift
  • Tools/presubmit.py
  • Tools/transpile_tests/transpile_tests.py
💤 Files with no reviewable changes (1)
  • Sources/Fuzzilli/FuzzIL/Blocks.swift

Comment on lines +35 to +41
// How many generator stubs we at *most* need to schedule for this edge.
public var weight: Int = 0

// Adds a generator to this Edge.
public mutating func addGenerator(_ generator: CodeGenerator) {
generators.append(generator)
weight = max(weight, generator.parts.count)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use the cheapest concrete generator for shortest-path weighting.

Line 41 stores the maximum stub count, but getShortestPath minimizes this value. An edge with both a 1-part and 100-part generator is treated as cost 100 and can be skipped even though a shorter concrete path exists.

Proposed fix
 public struct GeneratorEdge {
     var generators: [CodeGenerator] = []

     // How many generator stubs we at *most* need to schedule for this edge.
-    public var weight: Int = 0
+    public var weight: Int = Int.max

     // Adds a generator to this Edge.
     public mutating func addGenerator(_ generator: CodeGenerator) {
         generators.append(generator)
-        weight = max(weight, generator.parts.count)
+        weight = min(weight, generator.parts.count)
     }
 }

Also consider making Path.randomConcretePath() choose among generators whose parts.count == edge.weight so the returned concrete path matches the shortest-path cost.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// How many generator stubs we at *most* need to schedule for this edge.
public var weight: Int = 0
// Adds a generator to this Edge.
public mutating func addGenerator(_ generator: CodeGenerator) {
generators.append(generator)
weight = max(weight, generator.parts.count)
// How many generator stubs we at *most* need to schedule for this edge.
public var weight: Int = Int.max
// Adds a generator to this Edge.
public mutating func addGenerator(_ generator: CodeGenerator) {
generators.append(generator)
weight = min(weight, generator.parts.count)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/Base/ContextGraph.swift` around lines 35 - 41, The
shortest-path weighting in ContextGraph.Edge is using the largest generator size
instead of the cheapest concrete generator, which can cause getShortestPath to
ignore valid shorter paths. Update addGenerator(_:) so the edge weight reflects
the minimum parts.count among generators (or otherwise the cheapest concrete
generator) rather than max, and make sure Path.randomConcretePath() selects only
generators whose parts.count matches edge.weight so the chosen concrete path
stays consistent with the shortest-path cost.

Comment on lines +67 to +94
public func runBinaryenWasmGenerator(b: ProgramBuilder) -> WasmModuleMetadata? {
let extraArguments = [
"--print-boundary"
]

let (wasmBytes, jsonOutput) = BinaryenRunner.runWasmOptWithTempFiles(
fuzzer: b.fuzzer,
extraArguments: extraArguments
)

// Parse JSON output and build WasmModuleMetadata dynamically
let boundary: WasmBoundary = try! JSONDecoder().decode(
WasmBoundary.self, from: Data(jsonOutput.utf8))

var functions: [WasmModuleMetadata.FunctionExport] = []
var globals: [String] = []
var tables: [String] = []
var tags: [String] = []
var memories: [String] = []

for export in boundary.exports {
switch export.kind {
case "func":
guard let type = export.type, case .signature(let sig) = type else {
fatalError(
"BinaryenWasmGenerator: Export \(export.name) of kind 'func' is missing a function signature type"
)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Crash risk from unvalidated external tool output.

try! on the JSON decode (line 78-79) and the fatalError on a missing function signature (line 90-94) will crash the entire fuzzer process if wasm-opt --print-boundary ever emits output that doesn't match the expected shape (version mismatch, new export kind, malformed signature, etc.). Since this runs inside a CodeGenerator in the main fuzzing loop, an unexpected but non-malicious variance in the external tool's output takes down the whole instance instead of just failing this one generation attempt.

Consider decoding with try?/do-catch and returning nil (skip this generator attempt) instead of crashing, consistent with how other generators are expected to fail gracefully.

🐛 Suggested fix
-    // Parse JSON output and build WasmModuleMetadata dynamically
-    let boundary: WasmBoundary = try! JSONDecoder().decode(
-        WasmBoundary.self, from: Data(jsonOutput.utf8))
+    // Parse JSON output and build WasmModuleMetadata dynamically
+    guard
+        let boundary = try? JSONDecoder().decode(
+            WasmBoundary.self, from: Data(jsonOutput.utf8))
+    else {
+        return nil
+    }

And for the missing signature case, return nil instead of fatalError so a single unexpected export doesn't crash the fuzzer.

As per static analysis hints, SwiftLint flags force_try at line 78.

🧰 Tools
🪛 SwiftLint (0.65.0)

[Error] 78-78: Force tries should be avoided

(force_try)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/CodeGen/BinaryenWasmGenerator.swift` around lines 67 - 94,
The BinaryenWasmGenerator path is crashing on unexpected wasm-opt boundary
output because runBinaryenWasmGenerator uses try! to decode WasmBoundary and
fatalError for a func export missing a signature. Update
runBinaryenWasmGenerator to fail gracefully by using do-catch or try?/guard
around JSONDecoder.decode and returning nil on malformed or incompatible JSON,
and replace the fatalError in the export.kind == "func" handling with a nil
return so one bad external-tool response only skips this generation attempt.

Source: Linters/SAST tools

Comment on lines +750 to +756
let dstMemory = b.findVariable { v in
let type = b.type(of: v)
return type.Is(.object(ofGroup: "WasmMemory"))
&& type.wasmMemoryType!.isMemory64 == srcMemoryTypeInfo.isMemory64
}!
let dstMemoryTypeInfo = b.type(of: srcMemory).wasmMemoryType!
let memArg = {v in function.memoryArgument(v, dstMemoryTypeInfo)}
let memArg = { v in function.memoryArgument(v, dstMemoryTypeInfo) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use the destination memory when computing destination bounds.

Line 755 reads srcMemory again, so copy-size bounds are calculated from the wrong memory when source and destination limits differ.

Proposed fix
-        let dstMemoryTypeInfo = b.type(of: srcMemory).wasmMemoryType!
+        let dstMemoryTypeInfo = b.type(of: dstMemory).wasmMemoryType!
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let dstMemory = b.findVariable { v in
let type = b.type(of: v)
return type.Is(.object(ofGroup: "WasmMemory"))
&& type.wasmMemoryType!.isMemory64 == srcMemoryTypeInfo.isMemory64
}!
let dstMemoryTypeInfo = b.type(of: srcMemory).wasmMemoryType!
let memArg = {v in function.memoryArgument(v, dstMemoryTypeInfo)}
let memArg = { v in function.memoryArgument(v, dstMemoryTypeInfo) }
let dstMemoryTypeInfo = b.type(of: dstMemory).wasmMemoryType!
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift` around lines 750 - 756,
The destination bounds are being computed from the source memory instead of the
selected destination memory, which can make copy-size checks use the wrong
limits. Update the logic around the `dstMemory`, `dstMemoryTypeInfo`, and
`memArg` setup to read the type information from `dstMemory` rather than
`srcMemory`, so the destination bounds are derived from the actual destination
memory when source and destination differ.

Comment on lines 785 to +793
let offsetValue = b.randomNonNegativeIndex(upTo: memSize)
let offset = function.memoryArgument(offsetValue, memoryTypeInfo)
let byteToSet = function.consti32(Int32.random(in: 0...255))
let nrOfBytesToUpdate = function.memoryArgument(Int64.random(in: 0...(memSize - offsetValue)) + 1, memoryTypeInfo)
let nrOfBytesToUpdate = function.memoryArgument(
Int64.random(in: 0...(memSize - offsetValue)) + 1, memoryTypeInfo)

function.wasmMemoryFill(memory: memory, offset: offset, byteToSet: byteToSet, nrOfBytesToUpdate: nrOfBytesToUpdate)
function.wasmMemoryFill(
memory: memory, offset: offset, byteToSet: byteToSet,
nrOfBytesToUpdate: nrOfBytesToUpdate)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Do not force an out-of-bounds one-byte fill at the memory end.

When offsetValue == memSize, Line 789 generates size 1, which writes past the end. Allow zero bytes at the end, or only force non-zero when space remains.

Proposed fix
-        let nrOfBytesToUpdate = function.memoryArgument(
-            Int64.random(in: 0...(memSize - offsetValue)) + 1, memoryTypeInfo)
+        let maxBytesToUpdate = memSize - offsetValue
+        let nrOfBytesToUpdate = function.memoryArgument(
+            maxBytesToUpdate == 0 ? 0 : Int64.random(in: 1...maxBytesToUpdate),
+            memoryTypeInfo)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let offsetValue = b.randomNonNegativeIndex(upTo: memSize)
let offset = function.memoryArgument(offsetValue, memoryTypeInfo)
let byteToSet = function.consti32(Int32.random(in: 0...255))
let nrOfBytesToUpdate = function.memoryArgument(Int64.random(in: 0...(memSize - offsetValue)) + 1, memoryTypeInfo)
let nrOfBytesToUpdate = function.memoryArgument(
Int64.random(in: 0...(memSize - offsetValue)) + 1, memoryTypeInfo)
function.wasmMemoryFill(memory: memory, offset: offset, byteToSet: byteToSet, nrOfBytesToUpdate: nrOfBytesToUpdate)
function.wasmMemoryFill(
memory: memory, offset: offset, byteToSet: byteToSet,
nrOfBytesToUpdate: nrOfBytesToUpdate)
let offsetValue = b.randomNonNegativeIndex(upTo: memSize)
let offset = function.memoryArgument(offsetValue, memoryTypeInfo)
let byteToSet = function.consti32(Int32.random(in: 0...255))
let maxBytesToUpdate = memSize - offsetValue
let nrOfBytesToUpdate = function.memoryArgument(
maxBytesToUpdate == 0 ? 0 : Int64.random(in: 1...maxBytesToUpdate),
memoryTypeInfo)
function.wasmMemoryFill(
memory: memory, offset: offset, byteToSet: byteToSet,
nrOfBytesToUpdate: nrOfBytesToUpdate)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift` around lines 785 - 793,
The wasmMemoryFill generation in WasmCodeGenerators.swift can still create an
out-of-bounds one-byte write when offsetValue reaches the end of memory. Update
the nrOfBytesToUpdate calculation in the fill-generation logic around
offsetValue, offset, and memoryArgument so that it allows a zero-length fill
when offsetValue == memSize, and only adds the forced +1 when there is remaining
space to write. Keep the fix localized to the memory fill helper that builds the
wasmMemoryFill call.

Comment on lines +952 to +963
CodeGenerator(
"WasmTableGetGenerator", inContext: .single(.wasmFunction),
inputs: .required(.object(ofGroup: "WasmTable"))
) { b, table in
let tableType = b.type(of: table).wasmTableType!
let function = b.currentWasmModule.currentWasmFunction
let index = Int.random(in: 0...tableType.limits.min)
let indexVar =
tableType.isTable64
? function.consti64(Int64(index))
: function.consti32(Int32(index))
function.wasmTableGet(tableRef: table, idx: indexVar)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use an exclusive table index range and handle empty tables.

0...tableType.limits.min can select min, which is out of bounds, and for zero-length tables it still selects index 0.

Proposed fix
         let tableType = b.type(of: table).wasmTableType!
+        guard tableType.limits.min > 0 else { return }
         let function = b.currentWasmModule.currentWasmFunction
-        let index = Int.random(in: 0...tableType.limits.min)
+        let index = Int.random(in: 0..<tableType.limits.min)
@@
         let tableType = b.type(of: table).wasmTableType!
+        guard tableType.limits.min > 0 else { return }
         let function = b.currentWasmModule.currentWasmFunction
-        let index = Int.random(in: 0...tableType.limits.min)
+        let index = Int.random(in: 0..<tableType.limits.min)

Also applies to: 966-983

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift` around lines 952 - 963,
The Wasm table generators are choosing an inclusive index range via the
WasmTableGetGenerator/WasmTableSetGenerator logic, which can produce
out-of-bounds accesses and fails for empty tables. Update the index selection to
use an exclusive upper bound based on the table’s actual usable size, and guard
the empty-table case before calling wasmTableGet or wasmTableSet. Keep the fix
localized to the table access generators in WasmCodeGenerators by using the
existing tableType, function, and indexVar flow.

Comment on lines +1343 to +1355
init(count: Int, hasRestParameter: Bool = false, defaultParameterIndices: [Int] = []) {
assert(
!hasRestParameter || !defaultParameterIndices.contains(count - 1),
"Rest parameter cannot have a default value")
assert(
defaultParameterIndices.allSatisfy({ $0 >= 0 && $0 < count }),
"Invalid default parameter index")
assert(
defaultParameterIndices == defaultParameterIndices.sorted(),
"Default parameter indices must be sorted")
self.numParameters = UInt32(count)
self.hasRestParameter = hasRestParameter
self.defaultParameterIndices = defaultParameterIndices

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject duplicate default-parameter indices.

[1, 1] is sorted and in range, but it creates two default-value inputs for the same parameter and misaligns subroutine input layout.

Proposed fix
         assert(
             defaultParameterIndices == defaultParameterIndices.sorted(),
             "Default parameter indices must be sorted")
+        assert(
+            Set(defaultParameterIndices).count == defaultParameterIndices.count,
+            "Default parameter indices must be unique")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
init(count: Int, hasRestParameter: Bool = false, defaultParameterIndices: [Int] = []) {
assert(
!hasRestParameter || !defaultParameterIndices.contains(count - 1),
"Rest parameter cannot have a default value")
assert(
defaultParameterIndices.allSatisfy({ $0 >= 0 && $0 < count }),
"Invalid default parameter index")
assert(
defaultParameterIndices == defaultParameterIndices.sorted(),
"Default parameter indices must be sorted")
self.numParameters = UInt32(count)
self.hasRestParameter = hasRestParameter
self.defaultParameterIndices = defaultParameterIndices
init(count: Int, hasRestParameter: Bool = false, defaultParameterIndices: [Int] = []) {
assert(
!hasRestParameter || !defaultParameterIndices.contains(count - 1),
"Rest parameter cannot have a default value")
assert(
defaultParameterIndices.allSatisfy({ $0 >= 0 && $0 < count }),
"Invalid default parameter index")
assert(
defaultParameterIndices == defaultParameterIndices.sorted(),
"Default parameter indices must be sorted")
assert(
Set(defaultParameterIndices).count == defaultParameterIndices.count,
"Default parameter indices must be unique")
self.numParameters = UInt32(count)
self.hasRestParameter = hasRestParameter
self.defaultParameterIndices = defaultParameterIndices
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/FuzzIL/JsOperations.swift` around lines 1343 - 1355, The
parameter validation in the initializer for the subroutine/parameter metadata
currently allows duplicate defaultParameterIndices, which can misalign the input
layout. Update the checks in the
init(count:hasRestParameter:defaultParameterIndices:) initializer to also reject
duplicates by ensuring the indices are unique in addition to being sorted and in
range, while preserving the existing rest-parameter validation.

Comment on lines +3344 to +3348
init(pattern: DestructuringPattern, numInputs: Int, numOutputs: Int) {
self.pattern = pattern
assert(numInputs == 1 + pattern.numExtraInputs)
super.init(numInputs: numInputs, numOutputs: numOutputs, attributes: [.isMutable])
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Validate destructuring output arity against the pattern.

Destruct can currently be constructed with numOutputs that does not match pattern.numBindings, leaving a statically valid instruction whose pattern/output layout disagrees.

Proposed fix
         self.pattern = pattern
         assert(numInputs == 1 + pattern.numExtraInputs)
+        assert(numOutputs == pattern.numBindings)
         super.init(numInputs: numInputs, numOutputs: numOutputs, attributes: [.isMutable])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
init(pattern: DestructuringPattern, numInputs: Int, numOutputs: Int) {
self.pattern = pattern
assert(numInputs == 1 + pattern.numExtraInputs)
super.init(numInputs: numInputs, numOutputs: numOutputs, attributes: [.isMutable])
}
init(pattern: DestructuringPattern, numInputs: Int, numOutputs: Int) {
self.pattern = pattern
assert(numInputs == 1 + pattern.numExtraInputs)
assert(numOutputs == pattern.numBindings)
super.init(numInputs: numInputs, numOutputs: numOutputs, attributes: [.isMutable])
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/FuzzIL/JsOperations.swift` around lines 3344 - 3348, The
Destruct initializer in JsOperations.swift only checks numInputs against
pattern.numExtraInputs, but it also needs to validate that numOutputs matches
pattern.numBindings so the instruction layout stays consistent. Update the
Destruct init(pattern:numInputs:numOutputs:) precondition/assertion to enforce
the output arity against the DestructuringPattern, using pattern.numBindings as
the reference, while keeping the existing input validation intact.

Comment on lines 1211 to 1219
// We just take the self.wasmExt as they have to be the same, see `canMerge`.
let ext = TypeExtension(group: group, properties: self.properties.union(other.properties), methods: self.methods.union(other.methods), signature: signature, wasmExt: wasmExt, receiver: receiver)
let ext = TypeExtension(
group: group, properties: self.properties.union(other.properties),
methods: self.methods.union(other.methods),
symbolMethods: self.symbolMethods.union(other.symbolMethods),
signature: signature, wasmExt: wasmExt, receiver: receiver,
isEnumeration: isEnumeration,
iterableElementType: iterableElementType)
return ILType(definiteType: definiteType, possibleType: possibleType, ext: ext)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve all TypeExtension fields when rebuilding extensions.

These helpers now drop fields such as symbolMethods, receiver, iterableElementType, and exports, so adding/removing a property or setting a signature can silently erase type information introduced in this PR.

Also applies to: 1243-1308

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/FuzzIL/TypeSystem.swift` around lines 1211 - 1219, The
TypeExtension reconstruction helpers are dropping existing metadata when
creating a new extension, which can erase previously inferred type information.
Update the rebuild paths in TypeSystem.swift, especially the TypeExtension
assembly used by the property/signature helpers and the merge logic, so every
existing field is carried forward unless intentionally changed. Make sure
symbols like TypeExtension, ILType, canMerge, and the helper methods around the
affected range preserve symbolMethods, receiver, iterableElementType, exports,
and any other extension fields instead of resetting them.

Comment on lines +1557 to 1559
static let asyncIterable = BaseType(rawValue: 1 << 11)
static let iterable = BaseType([BaseType(rawValue: 1 << 12), .asyncIterable])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix the iterable/asyncIterable subtype bit layout.

With the current bits, .iterable becomes the subtype of .asyncIterable, so .asyncIterable().Is(.iterable()) is false and .iterable().Is(.asyncIterable()) is true.

Proposed fix
-    static let asyncIterable = BaseType(rawValue: 1 << 11)
-    static let iterable = BaseType([BaseType(rawValue: 1 << 12), .asyncIterable])
+    static let iterable = BaseType(rawValue: 1 << 11)
+    static let asyncIterable = BaseType([BaseType(rawValue: 1 << 12), .iterable])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static let asyncIterable = BaseType(rawValue: 1 << 11)
static let iterable = BaseType([BaseType(rawValue: 1 << 12), .asyncIterable])
static let iterable = BaseType(rawValue: 1 << 11)
static let asyncIterable = BaseType([BaseType(rawValue: 1 << 12), .iterable])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/FuzzIL/TypeSystem.swift` around lines 1557 - 1559, The
subtype bit layout in BaseType is inverted for iterable and asyncIterable,
causing the TypeSystem relationships to be wrong. Update the BaseType
definitions for asyncIterable and iterable in TypeSystem so that .iterable is
the parent/supertype and .asyncIterable remains distinct as intended, and verify
the Is checks on these symbols now make .asyncIterable().Is(.iterable()) true
and not the reverse.

Comment on lines +137 to +141
// Helper function to determine if a string is short.
function isShortString(s) {
if (!isString(s)) throw "Non-string argument to isShortString: " + s;
return s.length < 50;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Locate RuntimeAssistedMutator.swift and inspect how string/special Action.Input values are decoded and re-lifted into FuzzIL/JS.
fd -a RuntimeAssistedMutator.swift
rg -n 'StringInput|SpecialInput|case .string|case .special' -A 5 $(fd -a RuntimeAssistedMutator.swift)

Repository: oven-sh/fuzzilli

Length of output: 2146


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the lifting code around the updated validator and its outputs.
LIFT="Sources/Fuzzilli/Lifting/JavaScriptRuntimeAssistedMutatorLifting.swift"
sed -n '120,170p' "$LIFT"
sed -n '280,330p' "$LIFT"
sed -n '440,490p' "$LIFT"

# Inspect the Swift-side translation path for string/special inputs.
MUT="Sources/Fuzzilli/Mutators/RuntimeAssistedMutator.swift"
sed -n '360,520p' "$MUT"

# Find the builder APIs that receive these values.
rg -n 'func loadString|func getProperty|func getComputedProperty|func setProperty|func deleteProperty|func callMethod|func callComputedMethod' Sources

Repository: oven-sh/fuzzilli

Length of output: 16609


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect how ProgramBuilder turns strings/property names into emitted code.
FILE="Sources/Fuzzilli/Base/ProgramBuilder.swift"

sed -n '2740,2815p' "$FILE"
sed -n '3260,3315p' "$FILE"
sed -n '3370,3415p' "$FILE"
sed -n '3750,3835p' "$FILE"

# Search for the literal/string escaping helpers used by code emission.
rg -n 'escape|escaped|quote|string literal|serialize.*string|JSString|StringLiteral' Sources/Fuzzilli/Base Sources/Fuzzilli

Repository: oven-sh/fuzzilli

Length of output: 15564


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the JS lifter's escaping for strings and property/method names.
FILE="Sources/Fuzzilli/Lifting/JavaScriptLifter.swift"

sed -n '330,380p' "$FILE"
sed -n '430,520p' "$FILE"
sed -n '620,690p' "$FILE"
sed -n '2125,2165p' "$FILE"

# Also inspect any helper used for identifier quoting.
rg -n 'func quoteIdentifierIfNeeded|quoteIdentifierIfNeeded\(' "$FILE"

Repository: oven-sh/fuzzilli

Length of output: 12862


Keep the old string safety filter or escape strings end-to-end isShortString now admits arbitrary characters, but the Swift lifting path still emits string values and quoted property names with incomplete escaping. Quotes, backslashes, newlines, and control chars can turn into invalid JS when these values are lifted.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Sources/Fuzzilli/Lifting/JavaScriptRuntimeAssistedMutatorLifting.swift`
around lines 137 - 141, The JavaScript lifting path is now accepting raw strings
via isShortString without preserving the previous safety filter, so quoted
literals and property names can be emitted with invalid escaping. Update
JavaScriptRuntimeAssistedMutatorLifting’s string handling to either keep the old
string safety restriction or add end-to-end escaping in the Swift lifting
helpers that serialize string values and quoted property names. Make sure the
relevant string emission code paths consistently escape quotes, backslashes,
newlines, and other control characters before generating JS.

Upstream added a required additionalOptionsBags parameter to the Profile
initializer. Pass an empty array to keep the Bun profile compiling after
the sync.
@robobun
robobun force-pushed the farm/6d2cda71/rebase-and-update-profile branch from c650dad to cfbc26f Compare July 2, 2026 15:55
@robobun robobun changed the title Rebase on upstream main and update Bun profile with missing APIs Sync fork with upstream googleprojectzero/fuzzilli main Jul 2, 2026
@alii
alii merged commit b2e8180 into oven-sh:main Jul 2, 2026
0 of 3 checks passed
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.