Skip to content

Commit 4dc6de5

Browse files
LiedtkeV8-internal LUCI CQ
authored andcommitted
[sandbox] Also run the post processor in the GenerativeEngine
When the Fuzzer is in State.corpusGeneration, it will not use the configured Fuzzer.engine but a GenerativeEngine. With this change the GenearativeEngine will also run any registered FuzzingPostProcessor. Change-Id: I6a1c6d262bee25edea648e369b7baf5d2a16d09c Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8551317 Reviewed-by: Carl Smith <cffsmith@google.com> Commit-Queue: Matthias Liedtke <mliedtke@google.com> Reviewed-by: Samuel Groß <saelo@google.com>
1 parent 38f4ca1 commit 4dc6de5

4 files changed

Lines changed: 83 additions & 17 deletions

File tree

Sources/Fuzzilli/Engines/FuzzEngine.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import Foundation
1616

1717
public class FuzzEngine: ComponentBase {
18-
private var postProcessor: FuzzingPostProcessor? = nil
18+
private(set) var postProcessor: FuzzingPostProcessor? = nil
1919

2020
override init(name: String) {
2121
super.init(name: name)

Sources/Fuzzilli/Fuzzer.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,11 @@ public class Fuzzer {
178178
self.minimizer = minimizer
179179
self.logger = Logger(withLabel: "Fuzzer")
180180

181+
// Pass-through any postprocessor to the generative engine.
182+
if let postProcessor = engine.postProcessor {
183+
corpusGenerationEngine.registerPostProcessor(postProcessor)
184+
}
185+
181186
// Register this fuzzer instance with its queue so that it is possible to
182187
// obtain a reference to the Fuzzer instance when running on its queue.
183188
// This creates a reference cycle, but Fuzzer instances aren't expected
@@ -804,7 +809,7 @@ public class Fuzzer {
804809
case .none:
805810
break
806811
case .iterationsPerformed(let maxIterations):
807-
if iterations > maxIterations {
812+
if iterations >= maxIterations {
808813
return shutdown(reason: .finished)
809814
}
810815
case .timeFuzzed(let maxRuntime):
@@ -959,11 +964,13 @@ public class Fuzzer {
959964

960965
switch expectedResult {
961966
case .shouldSucceed where execution.outcome != .succeeded:
962-
logger.fatal("Testcase \"\(test)\" did not execute successfully")
967+
logger.fatal("Testcase \"\(test)\" did not execute successfully" +
968+
"\nstdout:\n\(execution.stdout)\nstderr:\n\(execution.stderr)")
963969
case .shouldCrash where !execution.outcome.isCrash():
964970
logger.fatal("Testcase \"\(test)\" did not crash")
965971
case .shouldNotCrash where execution.outcome.isCrash():
966-
logger.fatal("Testcase \"\(test)\" unexpectedly crashed")
972+
logger.fatal("Testcase \"\(test)\" unexpectedly crashed" +
973+
"\nstdout:\n\(execution.stdout)\nstderr:\n\(execution.stderr)")
967974
default:
968975
// Test passed
969976
break

Sources/Fuzzilli/Util/MockFuzzer.swift

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,7 @@ class MockEvaluator: ProgramEvaluator {
8181
}
8282

8383
/// Create a fuzzer instance usable for testing.
84-
public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engine maybeEngine: FuzzEngine? = nil, runner maybeRunner: ScriptRunner? = nil, environment maybeEnvironment: JavaScriptEnvironment? = nil, evaluator maybeEvaluator: ProgramEvaluator? = nil, corpus maybeCorpus: Corpus? = nil, codeGenerators additionalCodeGenerators : [(CodeGenerator, Int)] = []) -> Fuzzer {
85-
dispatchPrecondition(condition: .onQueue(DispatchQueue.main))
86-
84+
public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engine maybeEngine: FuzzEngine? = nil, runner maybeRunner: ScriptRunner? = nil, environment maybeEnvironment: JavaScriptEnvironment? = nil, evaluator maybeEvaluator: ProgramEvaluator? = nil, corpus maybeCorpus: Corpus? = nil, codeGenerators additionalCodeGenerators : [(CodeGenerator, Int)] = [], queue: DispatchQueue? = nil) -> Fuzzer {
8785
// The configuration of this fuzzer.
8886
let configuration = maybeConfiguration ?? Configuration(logLevel: .warning)
8987

@@ -139,19 +137,29 @@ public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engi
139137
lifter: lifter,
140138
corpus: corpus,
141139
minimizer: minimizer,
142-
queue: DispatchQueue.main)
140+
queue: queue ?? DispatchQueue.main)
143141

144-
fuzzer.registerEventListener(for: fuzzer.events.Log) { ev in
145-
print("[\(ev.label)] \(ev.message)")
146-
}
142+
let initializeFuzzer = {
143+
fuzzer.registerEventListener(for: fuzzer.events.Log) { ev in
144+
print("[\(ev.label)] \(ev.message)")
145+
}
147146

148-
fuzzer.initialize()
147+
fuzzer.initialize()
149148

150-
// Tests can also rely on the corpus not being empty
151-
let b = fuzzer.makeBuilder()
152-
b.buildPrefix()
153-
b.build(n: 50, by: .generating)
154-
corpus.add(b.finalize(), ProgramAspects(outcome: .succeeded))
149+
// Tests can also rely on the corpus not being empty
150+
let b = fuzzer.makeBuilder()
151+
b.buildPrefix()
152+
b.build(n: 50, by: .generating)
153+
corpus.add(b.finalize(), ProgramAspects(outcome: .succeeded))
154+
}
155+
// If a DispatchQueue was provided by the caller, initialize the fuzzer
156+
// there. Otherwise initialize it directly.
157+
if let queue {
158+
queue.sync {initializeFuzzer()}
159+
} else {
160+
dispatchPrecondition(condition: .onQueue(DispatchQueue.main))
161+
initializeFuzzer()
162+
}
155163

156164
return fuzzer
157165
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import XCTest
16+
@testable import Fuzzilli
17+
18+
class EngineTests: XCTestCase {
19+
func testPostProcessorOnGenerativeEngine() throws {
20+
class MockPostProcessor : FuzzingPostProcessor {
21+
var callCount = 0
22+
func process(_ program: Program, for fuzzer: Fuzzer) -> Program {
23+
callCount += 1
24+
return program
25+
}
26+
}
27+
let mockPostProcessor = MockPostProcessor()
28+
29+
let engine = MutationEngine(numConsecutiveMutations: 1)
30+
engine.registerPostProcessor(mockPostProcessor)
31+
let q = DispatchQueue(label: "fuzzerQueue")
32+
let fuzzer = makeMockFuzzer(engine: engine, queue: q)
33+
XCTAssertNotNil(fuzzer.corpusGenerationEngine.postProcessor)
34+
XCTAssertEqual(mockPostProcessor.callCount, 0)
35+
q.sync {
36+
fuzzer.start(runUntil: .iterationsPerformed(3))
37+
}
38+
// Synchronize on the queue 2 times, so that each time at least one new
39+
// fuzzOne() is executed on the DispatchQueue in that time.
40+
// This should work consistently as the DispatchQueue is not marked
41+
// with DispatchQueue.Attributes.concurrent, so each time one of the
42+
// que.sync {} is executed, a fuzzOne() was executed as well.
43+
q.sync {}
44+
q.sync {}
45+
XCTAssertEqual(mockPostProcessor.callCount, 3)
46+
// No more tasks are queued.
47+
q.sync {}
48+
XCTAssertEqual(mockPostProcessor.callCount, 3)
49+
XCTAssert(fuzzer.isStopped)
50+
}
51+
}

0 commit comments

Comments
 (0)