Sync fork with upstream googleprojectzero/fuzzilli main - #8
Conversation
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>
4710684 to
e62eb32
Compare
WalkthroughThis 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. ChangesCore IR and Wasm pipeline
Code generation, lifting, and templates
Tests and fixtures
Possibly related PRs
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
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 | 🟡 MinorAdd 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 acceptsstring | 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 | 🟠 MajorValidate 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 bynumOutputs,numInnerOutputs, andnumInputs. If mutation or minimization swaps in a different signature definition,wasmJsCallcan touchinstr.outputwhen 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 | 🟠 MajorReuse field 2 for
parameterCountandoutputCountinstead of field 1 to maintain protobuf wire compatibility.Lines 11111 and 11141 decode field 1 as
Int32forBeginWasmFunctionandEndWasmFunctionrespectively, 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 inoperations.prototo 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
📒 Files selected for processing (39)
.github/workflows/swift.ymlSources/Fuzzilli/Base/ProgramBuilder.swiftSources/Fuzzilli/CodeGen/CodeGeneratorWeights.swiftSources/Fuzzilli/CodeGen/CodeGenerators.swiftSources/Fuzzilli/CodeGen/WasmCodeGenerators.swiftSources/Fuzzilli/Compiler/Compiler.swiftSources/Fuzzilli/Compiler/Parser/parser.jsSources/Fuzzilli/Environment/JavaScriptEnvironment.swiftSources/Fuzzilli/FuzzIL/Instruction.swiftSources/Fuzzilli/FuzzIL/JSTyper.swiftSources/Fuzzilli/FuzzIL/JsOperations.swiftSources/Fuzzilli/FuzzIL/Opcodes.swiftSources/Fuzzilli/FuzzIL/Semantics.swiftSources/Fuzzilli/FuzzIL/WasmOperations.swiftSources/Fuzzilli/Fuzzer.swiftSources/Fuzzilli/Lifting/FuzzILLifter.swiftSources/Fuzzilli/Lifting/JavaScriptExploreLifting.swiftSources/Fuzzilli/Lifting/JavaScriptLifter.swiftSources/Fuzzilli/Lifting/JavaScriptProbeLifting.swiftSources/Fuzzilli/Lifting/JavaScriptRuntimeAssistedMutatorLifting.swiftSources/Fuzzilli/Lifting/WasmLifter.swiftSources/Fuzzilli/Minimization/BlockReducer.swiftSources/Fuzzilli/Minimization/InliningReducer.swiftSources/Fuzzilli/Mutators/OperationMutator.swiftSources/Fuzzilli/Mutators/ProbingMutator.swiftSources/Fuzzilli/Mutators/RuntimeAssistedMutator.swiftSources/Fuzzilli/Profiles/BunProfile.swiftSources/Fuzzilli/Protobuf/ast.pb.swiftSources/Fuzzilli/Protobuf/ast.protoSources/Fuzzilli/Protobuf/operations.pb.swiftSources/Fuzzilli/Protobuf/operations.protoSources/Fuzzilli/Protobuf/program.pb.swiftSources/Fuzzilli/Protobuf/program.protoTests/FuzzilliTests/CompilerTests/computed_and_indexed_properties.jsTests/FuzzilliTests/CrashingInstrumentationMutator.swiftTests/FuzzilliTests/LifterTest.swiftTests/FuzzilliTests/MinimizerTest.swiftTests/FuzzilliTests/ProgramBuilderTest.swiftTests/FuzzilliTests/RuntimeAssistedMutatorTests.swift
💤 Files with no reviewable changes (1)
- Sources/Fuzzilli/Mutators/ProbingMutator.swift
| 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 |
There was a problem hiding this comment.
🛠️ 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.
| // 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 => []) | ||
| } |
There was a problem hiding this comment.
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).
| let parameters = b.randomParameters() | ||
| b.emit( | ||
| BeginClassPrivateInstanceMethod( | ||
| BeginClassPrivateMethod( | ||
| methodName: methodName, | ||
| parameters: parameters.parameters)) | ||
| parameters: parameters.parameters, | ||
| isStatic: false)) |
There was a problem hiding this comment.
🧹 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).
| // 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, |
There was a problem hiding this comment.
🧹 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.
| 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 | ||
| } |
There was a problem hiding this comment.
🧹 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).
| // 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) |
There was a problem hiding this comment.
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.
| // 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.
| // BunJSONL - JSONL parser | ||
| static let bunJSONL = ILType.object( | ||
| ofGroup: "BunJSONL", | ||
| withProperties: [], | ||
| withMethods: ["parse", "stringify"] | ||
| withMethods: ["parse", "parseChunk"] |
There was a problem hiding this comment.
🧩 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:
- 1: https://bun.com/docs/runtime/jsonl
- 2: https://bun.com/reference/bun/JSONL
- 3: https://github.com/oven-sh/bun/blob/main/docs/runtime/jsonl.mdx
- 4: https://bun.com/reference/bun/JSONL/parseChunk
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".
| // 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"] |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "BunProfile.swift" -type fRepository: 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 -20Repository: 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:
- 1: https://bun.com/reference/bun/BuildArtifact
- 2: https://bun.sh/reference/bun/build
- 3: https://bun.com/reference/bun/BuildArtifact/bytes
- 4: https://bun.com/reference/bun/build
- 5: https://bun.sh/reference/bun/BuildOutput
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.
| 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} |
There was a problem hiding this comment.
🧩 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
fiRepository: 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 -100Repository: 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 -80Repository: 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)"); |
There was a problem hiding this comment.
🧹 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.
| 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>
…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
e62eb32 to
c650dad
Compare
There was a problem hiding this comment.
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 winRemoved safety assertion for embedded newlines.
The previous
assert(!line.contains("\n"))guard is gone fromemit(). 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 reachingLoadString/Directivecontent), 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.countAs 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 valueRemove the unused destructuring helpers
liftArrayDestructPatternandliftObjectDestructPatternhave 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 winPreserve stdout and fuzzout in differential reports.
DiffExecution.diffcarries formatted stdout/stderr/fuzzout, butprocessDifferentialstores 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.stdoutandexecution.fuzzoutalongsideexecution.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 winEmit 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 winInclude
.blockStatementinallCasesanddescription.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 winCheck
wasmLabelType, notwasmTagType.
isWasmLabelTypecurrently 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
📒 Files selected for processing (176)
.github/workflows/swift.yml.gitignore.swift-formatPRESUBMIT.pyPackage.swiftSources/FuzzILTool/main.swiftSources/Fuzzilli/Base/ContextGraph.swiftSources/Fuzzilli/Base/Contributor.swiftSources/Fuzzilli/Base/Events.swiftSources/Fuzzilli/Base/Logging.swiftSources/Fuzzilli/Base/ProgramBuilder.swiftSources/Fuzzilli/Base/Timers.swiftSources/Fuzzilli/CodeGen/BinaryenWasmGenerator.swiftSources/Fuzzilli/CodeGen/CodeGenerator.swiftSources/Fuzzilli/CodeGen/CodeGeneratorWeights.swiftSources/Fuzzilli/CodeGen/CodeGenerators.swiftSources/Fuzzilli/CodeGen/ProgramTemplate.swiftSources/Fuzzilli/CodeGen/ProgramTemplateWeights.swiftSources/Fuzzilli/CodeGen/ProgramTemplates.swiftSources/Fuzzilli/CodeGen/WasmCodeGenerators.swiftSources/Fuzzilli/Compiler/Compiler.swiftSources/Fuzzilli/Compiler/JavaScriptParser.swiftSources/Fuzzilli/Compiler/Parser/parser.jsSources/Fuzzilli/Configuration.swiftSources/Fuzzilli/Corpus/BasicCorpus.swiftSources/Fuzzilli/Corpus/Corpus.swiftSources/Fuzzilli/Corpus/MarkovCorpus.swiftSources/Fuzzilli/DumplingDiffOracle/Oracle.swiftSources/Fuzzilli/Engines/FuzzEngine.swiftSources/Fuzzilli/Engines/GenerativeEngine.swiftSources/Fuzzilli/Engines/HybridEngine.swiftSources/Fuzzilli/Engines/MultiEngine.swiftSources/Fuzzilli/Engines/MutationEngine.swiftSources/Fuzzilli/Environment/JavaScriptEnvironment.swiftSources/Fuzzilli/Evaluation/ProgramCoverageEvaluator.swiftSources/Fuzzilli/Evaluation/ProgramEvaluator.swiftSources/Fuzzilli/Execution/Execution.swiftSources/Fuzzilli/Execution/REPRL.swiftSources/Fuzzilli/FuzzIL/Analyzer.swiftSources/Fuzzilli/FuzzIL/Blocks.swiftSources/Fuzzilli/FuzzIL/Code.swiftSources/Fuzzilli/FuzzIL/Context.swiftSources/Fuzzilli/FuzzIL/Instruction.swiftSources/Fuzzilli/FuzzIL/JSTyper.swiftSources/Fuzzilli/FuzzIL/JsOperations.swiftSources/Fuzzilli/FuzzIL/Opcodes.swiftSources/Fuzzilli/FuzzIL/Operation.swiftSources/Fuzzilli/FuzzIL/Program.swiftSources/Fuzzilli/FuzzIL/ProgramComments.swiftSources/Fuzzilli/FuzzIL/Semantics.swiftSources/Fuzzilli/FuzzIL/TypeSystem.swiftSources/Fuzzilli/FuzzIL/Variable.swiftSources/Fuzzilli/FuzzIL/WasmOperations.swiftSources/Fuzzilli/Fuzzer.swiftSources/Fuzzilli/Lifting/Expression.swiftSources/Fuzzilli/Lifting/FuzzILLifter.swiftSources/Fuzzilli/Lifting/JSExpressions.swiftSources/Fuzzilli/Lifting/JavaScriptExploreLifting.swiftSources/Fuzzilli/Lifting/JavaScriptFixupLifting.swiftSources/Fuzzilli/Lifting/JavaScriptLifter.swiftSources/Fuzzilli/Lifting/JavaScriptProbeLifting.swiftSources/Fuzzilli/Lifting/JavaScriptRuntimeAssistedMutatorLifting.swiftSources/Fuzzilli/Lifting/ScriptWriter.swiftSources/Fuzzilli/Lifting/WasmLifter.swiftSources/Fuzzilli/Minimization/BlockReducer.swiftSources/Fuzzilli/Minimization/DataFlowSimplifier.swiftSources/Fuzzilli/Minimization/DeduplicatingReducer.swiftSources/Fuzzilli/Minimization/InliningReducer.swiftSources/Fuzzilli/Minimization/InstructionSimplifier.swiftSources/Fuzzilli/Minimization/LoopSimplifier.swiftSources/Fuzzilli/Minimization/MinimizationHelper.swiftSources/Fuzzilli/Minimization/MinimizationPostProcessor.swiftSources/Fuzzilli/Minimization/Minimizer.swiftSources/Fuzzilli/Minimization/ReassignReducer.swiftSources/Fuzzilli/Minimization/VariadicInputReducer.swiftSources/Fuzzilli/Minimization/WasmTypeGroupReducer.swiftSources/Fuzzilli/Modules/NetworkSync.swiftSources/Fuzzilli/Modules/Statistics.swiftSources/Fuzzilli/Modules/Storage.swiftSources/Fuzzilli/Modules/Sync.swiftSources/Fuzzilli/Modules/ThreadSync.swiftSources/Fuzzilli/Mutators/BaseInstructionMutator.swiftSources/Fuzzilli/Mutators/BinaryenWasmMutator.swiftSources/Fuzzilli/Mutators/CodeGenMutator.swiftSources/Fuzzilli/Mutators/CombineMutator.swiftSources/Fuzzilli/Mutators/ConcatMutator.swiftSources/Fuzzilli/Mutators/ExplorationMutator.swiftSources/Fuzzilli/Mutators/FixupMutator.swiftSources/Fuzzilli/Mutators/InputMutator.swiftSources/Fuzzilli/Mutators/MutatorSettings.swiftSources/Fuzzilli/Mutators/OperationMutator.swiftSources/Fuzzilli/Mutators/ProbingMutator.swiftSources/Fuzzilli/Mutators/RuntimeAssistedMutator.swiftSources/Fuzzilli/Mutators/SpliceMutator.swiftSources/Fuzzilli/Profiles/BunProfile.swiftSources/Fuzzilli/Profiles/DuktapeProfile.swiftSources/Fuzzilli/Profiles/JSCProfile.swiftSources/Fuzzilli/Profiles/JerryscriptProfile.swiftSources/Fuzzilli/Profiles/NjsProfile.swiftSources/Fuzzilli/Profiles/Profile.swiftSources/Fuzzilli/Profiles/QjsProfile.swiftSources/Fuzzilli/Profiles/QtjsProfile.swiftSources/Fuzzilli/Profiles/Serenity.swiftSources/Fuzzilli/Profiles/SpidermonkeyProfile.swiftSources/Fuzzilli/Profiles/V8CommonProfile.swiftSources/Fuzzilli/Profiles/V8DumplingProfile.swiftSources/Fuzzilli/Profiles/V8HoleFuzzingProfile.swiftSources/Fuzzilli/Profiles/V8Profile.swiftSources/Fuzzilli/Profiles/V8SandboxProfile.swiftSources/Fuzzilli/Profiles/XSProfile.swiftSources/Fuzzilli/Protobuf/ProtoUtils.swiftSources/Fuzzilli/Protobuf/README.mdSources/Fuzzilli/Protobuf/ast.pb.swiftSources/Fuzzilli/Protobuf/ast.protoSources/Fuzzilli/Protobuf/gen_programproto.pySources/Fuzzilli/Protobuf/operations.pb.swiftSources/Fuzzilli/Protobuf/operations.protoSources/Fuzzilli/Protobuf/program.pb.swiftSources/Fuzzilli/Protobuf/program.protoSources/Fuzzilli/Protobuf/sync.pb.swiftSources/Fuzzilli/Protobuf/sync.protoSources/Fuzzilli/Util/Arguments.swiftSources/Fuzzilli/Util/BinaryenRunner.swiftSources/Fuzzilli/Util/CInterop.swiftSources/Fuzzilli/Util/JavaScriptExecutor.swiftSources/Fuzzilli/Util/Misc.swiftSources/Fuzzilli/Util/MockFuzzer.swiftSources/Fuzzilli/Util/OutputBuffer.swiftSources/Fuzzilli/Util/Random.swiftSources/Fuzzilli/Util/VariableMap.swiftSources/Fuzzilli/Util/VariableSet.swiftSources/Fuzzilli/Util/WeightedList.swiftSources/FuzzilliCli/TerminalUI.swiftSources/FuzzilliCli/main.swiftSources/FuzzilliDetectMissingBuiltins/main.swiftSources/REPRLRun/main.swiftSources/RelateTool/main.swiftTests/FuzzilliTests/AnalyzerTest.swiftTests/FuzzilliTests/CompilerTests.swiftTests/FuzzilliTests/CompilerTests/advanced_loops.jsTests/FuzzilliTests/CompilerTests/computed_and_indexed_properties.jsTests/FuzzilliTests/CompilerTests/destructuring.jsTests/FuzzilliTests/CompilerTests/function_with_default_parameters.jsTests/FuzzilliTests/CompilerTests/labels.jsTests/FuzzilliTests/CompilerTests/large_index_delete.jsTests/FuzzilliTests/CompilerTests/methods_with_default_parameters.jsTests/FuzzilliTests/CompilerTests/nested_destructuring.jsTests/FuzzilliTests/CompilerTests/super.jsTests/FuzzilliTests/ContextGraphTest.swiftTests/FuzzilliTests/CrashingInstrumentationMutator.swiftTests/FuzzilliTests/DiffOracleTests.swiftTests/FuzzilliTests/EngineTests.swiftTests/FuzzilliTests/EnvironmentTest.swiftTests/FuzzilliTests/InstructionTests.swiftTests/FuzzilliTests/JSTyperTests.swiftTests/FuzzilliTests/LabelTests.swiftTests/FuzzilliTests/Leb128Test.swiftTests/FuzzilliTests/LifterTest.swiftTests/FuzzilliTests/LiveTests.swiftTests/FuzzilliTests/MinimizerTest.swiftTests/FuzzilliTests/MutatorTests.swiftTests/FuzzilliTests/ProbingMutatorTests.swiftTests/FuzzilliTests/ProgramBuilderTest.swiftTests/FuzzilliTests/ProgramSerializationTest.swiftTests/FuzzilliTests/RingBufferTest.swiftTests/FuzzilliTests/RuntimeAssistedMutatorTests.swiftTests/FuzzilliTests/ScriptWriterTest.swiftTests/FuzzilliTests/TestUtils.swiftTests/FuzzilliTests/TypeSystemTest.swiftTests/FuzzilliTests/VariableMapTest.swiftTests/FuzzilliTests/VariableSetTest.swiftTests/FuzzilliTests/WasmAtomicsTests.swiftTests/FuzzilliTests/WasmTableTests.swiftTests/FuzzilliTests/WasmTests.swiftTools/presubmit.pyTools/transpile_tests/transpile_tests.py
💤 Files with no reviewable changes (1)
- Sources/Fuzzilli/FuzzIL/Blocks.swift
| // 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) |
There was a problem hiding this comment.
🎯 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.
| // 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.
| 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" | ||
| ) | ||
| } |
There was a problem hiding this comment.
🩺 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
| 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) } |
There was a problem hiding this comment.
🎯 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.
| 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.
| 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) |
There was a problem hiding this comment.
🎯 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.
| 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.
| 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) |
There was a problem hiding this comment.
🎯 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.
| 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 |
There was a problem hiding this comment.
🎯 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.
| 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.
| init(pattern: DestructuringPattern, numInputs: Int, numOutputs: Int) { | ||
| self.pattern = pattern | ||
| assert(numInputs == 1 + pattern.numExtraInputs) | ||
| super.init(numInputs: numInputs, numOutputs: numOutputs, attributes: [.isMutable]) | ||
| } |
There was a problem hiding this comment.
🗄️ 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.
| 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.
| // 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) |
There was a problem hiding this comment.
🗄️ 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.
| static let asyncIterable = BaseType(rawValue: 1 << 11) | ||
| static let iterable = BaseType([BaseType(rawValue: 1 << 12), .asyncIterable]) | ||
|
|
There was a problem hiding this comment.
🎯 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.
| 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.
| // 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; | ||
| } |
There was a problem hiding this comment.
🎯 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' SourcesRepository: 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/FuzzilliRepository: 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.
c650dad to
cfbc26f
Compare
Syncs the fork with upstream
googleprojectzero/fuzzillimain. 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), soupstream/mainis an ancestor of the branch tip. After merging this PR,mainshows 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/mainas a parent (same trick as the earlierRecord upstream/main as mergedcommit).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.asyncFunctionto.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'sGetJavaScriptExecutorOrSkipTesthelper (used throughout LiveTests).Profile adaptation
Upstream added a required
additionalOptionsBagsparameter to theProfileinitializer, soBunProfile.swiftnow passesadditionalOptionsBags: []. No other profile changes.Verification
swift buildpasses.swift testpasses exceptWasmAtomicsTests/testRMWOrderingandtestCmpxchgOrdering, which fail identically on pristineupstream/main(the container's JS engine is too old for the newer WASM atomics memory-ordering codegen). Not introduced by this PR.