Rebase on upstream main and update Bun profile with missing APIs#8
Rebase on upstream main and update Bun profile with missing APIs#8robobun wants to merge 16 commits intooven-sh:mainfrom
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>
- Add Bun.connect, Bun.listen, Bun.sliceAnsi, Bun.readableStreamToFormData - Add Bun.cron (.remove, .parse), Bun.plugin, Bun.gc - Add Bun.argv, Bun.env, Bun.main, Bun.cwd, Bun.stdin/stdout/stderr - Add Bun.markdown.react method - Add Listener, UDPSocket, BuildArtifact object groups - Fix JSONL methods (parse + parseChunk, not stringify) - Fix Bun.dns to include resolve/prefetch/getCacheStats/reverse - Fix Bun.udpSocket return type
4710684 to
e62eb32
Compare
WalkthroughThis PR consolidates instance/static/private class member operation variants into unified opcodes accepting an Changes
Possibly related PRs
🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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.
Merges upstream
googleprojectzero/fuzzillimain (was 14 commits behind, now current) and updates the Bun profile with missing APIs.After merging this PR, the fork will be only ahead of upstream (0 behind) —
upstream/mainis an ancestor of this branch via the merge commit, so GitHub won't show "N behind".Important: merge this PR (don't squash), so the merge commit's upstream parent link is preserved. Squashing would lose it and GitHub would show "behind" again.
New APIs added to BunProfile
Functions:
Bun.connect— TCP/TLS client connectionsBun.listen— TCP/TLS server listenersBun.sliceAnsi— ANSI-aware string slicingBun.readableStreamToFormData— stream-to-FormData conversionBun.cron/Bun.cron.remove/Bun.cron.parse— cron schedulingBun.plugin— plugin registrationBun.gc— garbage collection triggerProperties:
Bun.argv,Bun.env,Bun.main,Bun.cwdBun.stdin,Bun.stdout,Bun.stderrObject groups:
Listener— returned byBun.listen()UDPSocket— returned byBun.udpSocket()BuildArtifact— returned byBun.build()Fixes
Bun.JSONL: methods areparse+parseChunk(was incorrectlystringify)Bun.markdown: addedreactmethod (was missing, only hadhtml/render)Bun.dns: expanded from justlookupto includeresolve,prefetch,getCacheStats,reverseBun.udpSocket: now returns typedBunUDPSocketinstead ofjsAnythingVerification
Swift 6.1 build passes.
upstream/mainis a merge ancestor — post-merge shows 0 behind.