Skip to content

Commit dcbbafd

Browse files
LiedtkeV8-internal LUCI CQ
authored andcommitted
Add simple mechanism to share data between stubs in a code generator
Bug: 445356784 Change-Id: I4a8f93b9a65f0df23c028287c908d38e7f23befc Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/8707582 Reviewed-by: Michael Achenbach <machenbach@google.com> Auto-Submit: Matthias Liedtke <mliedtke@google.com> Commit-Queue: Michael Achenbach <machenbach@google.com>
1 parent 3e040df commit dcbbafd

6 files changed

Lines changed: 176 additions & 24 deletions

File tree

Sources/Fuzzilli/Base/ProgramBuilder.swift

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,38 @@ import Foundation
1919
/// This provides methods for constructing and appending random
2020
/// instances of the different kinds of operations in a program.
2121
public class ProgramBuilder {
22+
23+
/// Runtime data used by code generators to share data between different stubs inside the same
24+
/// code generator. It is strictly required that all pushed values are also popped again in the
25+
/// same code generator.
26+
public class GeneratorRuntimeData {
27+
private var data = [String : Stack<Variable>]()
28+
29+
public func push(_ key: String, _ value: Variable) {
30+
data[key, default: .init()].push(value)
31+
}
32+
33+
public func pop(_ key: String) -> Variable {
34+
assert(data[key] != nil)
35+
return data[key]!.pop()
36+
}
37+
38+
// Fetch the most recent value for this key and push it back.
39+
public func popAndPush(_ key: String) -> Variable {
40+
assert(data[key] != nil)
41+
return data[key]!.top
42+
}
43+
44+
func reset() {
45+
#if DEBUG
46+
for (key, value) in data {
47+
assert(value.isEmpty, "Stale entries for runtime data '\(key)'")
48+
}
49+
#endif
50+
data.removeAll(keepingCapacity: false)
51+
}
52+
}
53+
2254
/// The fuzzer instance for which this builder is active.
2355
public let fuzzer: Fuzzer
2456

@@ -147,6 +179,9 @@ public class ProgramBuilder {
147179
/// The remaining CodeGenerators to call as part of a building / CodeGen step, these will "clean up" the state and fix the contexts.
148180
public var scheduled: Stack<GeneratorStub> = Stack()
149181

182+
// Runtime data that can be shared between different stubs within a CodeGenerator.
183+
var runtimeData = GeneratorRuntimeData()
184+
150185
/// Stack of active switch blocks.
151186
private var activeSwitchBlocks = Stack<SwitchBlock>()
152187

@@ -214,6 +249,7 @@ public class ProgramBuilder {
214249
activeObjectLiterals.removeAll()
215250
activeClassDefinitions.removeAll()
216251
buildLog?.reset()
252+
runtimeData.reset()
217253
}
218254

219255
/// Finalizes and returns the constructed program, then resets this builder so it can be reused for building another program.
@@ -2075,7 +2111,7 @@ public class ProgramBuilder {
20752111

20762112
var numberOfGeneratedInstructions = 0
20772113

2078-
// calculate all input requirements of this CodeGenerator.
2114+
// Calculate all input requirements of this CodeGenerator.
20792115
let inputTypes = Set(generator.parts.reduce([]) { res, gen in
20802116
return res + gen.inputs.types
20812117
})

Sources/Fuzzilli/CodeGen/CodeGenerator.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,4 +381,4 @@ extension CodeGenerator: CustomStringConvertible {
381381
let names = self.parts.map { $0.name }
382382
return names.joined(separator: ",")
383383
}
384-
}
384+
}

Sources/Fuzzilli/CodeGen/WasmCodeGenerators.swift

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1277,25 +1277,34 @@ public let WasmCodeGenerators: [CodeGenerator] = [
12771277
},
12781278
]),
12791279

1280-
// TODO: think about how we can turn this into a mulit-part Generator
1281-
CodeGenerator("WasmLoopGenerator", inContext: .single(.wasmFunction)) { b in
1282-
let function = b.currentWasmModule.currentWasmFunction
1283-
let loopCtr = function.consti32(10)
1284-
1285-
function.wasmBuildLoop(with: [] => []) { label, args in
1286-
let result = function.wasmi32BinOp(
1280+
CodeGenerator(
1281+
"WasmLoopGenerator",
1282+
[
1283+
GeneratorStub(
1284+
"WasmBeginLoopGenerator",
1285+
inContext: .single(.wasmFunction),
1286+
provides: [.wasmFunction]
1287+
) { b in
1288+
let function = b.currentWasmModule.currentWasmFunction
1289+
let loopCtr = function.consti32(10)
1290+
b.runtimeData.push("loopCounter", loopCtr)
1291+
b.emit(WasmBeginLoop(with: [] => []))
1292+
// Increase loop counter.
1293+
let result = function.wasmi32BinOp(
12871294
loopCtr, function.consti32(1), binOpKind: .Sub)
1288-
function.wasmReassign(variable: loopCtr, to: result)
1289-
1290-
b.buildRecursive(n: defaultCodeGenerationAmount)
1291-
1292-
// Backedge of loop, we continue if it is not equal to zero.
1293-
let isNotZero = function.wasmi32CompareOp(
1294-
loopCtr, function.consti32(0), using: .Ne)
1295-
function.wasmBranchIf(
1296-
isNotZero, to: label, hint: b.randomWasmBranchHint())
1297-
}
1298-
},
1295+
function.wasmReassign(variable: loopCtr, to: result)
1296+
},
1297+
GeneratorStub(
1298+
"WasmEndLoopGenerator",
1299+
inContext: .single(.wasmFunction)
1300+
) { b in
1301+
let function = b.currentWasmModule.currentWasmFunction
1302+
let loopCtr = b.runtimeData.pop("loopCounter")
1303+
// Backedge of loop, we continue if it is not equal to zero.
1304+
let isNotZero = function.wasmi32CompareOp(loopCtr, function.consti32(0), using: .Ne)
1305+
b.emit(WasmEndLoop(outputTypes: []))
1306+
},
1307+
]),
12991308

13001309
CodeGenerator("WasmLoopWithSignatureGenerator", inContext: .single(.wasmFunction)) {
13011310
b in

Sources/Fuzzilli/Util/MockFuzzer.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ class MockEvaluator: ProgramEvaluator {
8282
}
8383

8484
/// Create a fuzzer instance usable for testing.
85-
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 {
85+
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, overwriteGenerators: WeightedList<CodeGenerator>? = nil) -> Fuzzer {
8686
// The configuration of this fuzzer.
8787
let configuration = maybeConfiguration ?? Configuration(logLevel: .warning)
8888

@@ -115,7 +115,7 @@ public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engi
115115
let minimizer = Minimizer()
116116

117117
// Use all builtin CodeGenerators
118-
let codeGenerators = WeightedList<CodeGenerator>(
118+
let codeGenerators = overwriteGenerators ?? WeightedList<CodeGenerator>(
119119
(CodeGenerators + WasmCodeGenerators).map {
120120
guard let weight = codeGeneratorWeights[$0.name] else {
121121
fatalError("Missing weight for CodeGenerator \($0.name) in CodeGeneratorWeights.swift")

Sources/FuzzilliCli/main.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,8 @@ if let raw_timeout = args.string(for: "--timeout") {
172172
guard let upper = UInt32(parts[1]) else {
173173
configError("The upper bound for --timeout must be an integer")
174174
}
175-
guard lower < upper else {
176-
configError("The --timeout=lower,upper boundaries must adhere to lower < upper")
175+
guard lower <= upper else {
176+
configError("The --timeout=lower,upper boundaries must adhere to lower <= upper")
177177
}
178178
timeout = Timeout.interval(lower, upper)
179179
} else {

Tests/FuzzilliTests/ProgramBuilderTest.swift

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3049,3 +3049,110 @@ class ProgramBuilderTests: XCTestCase {
30493049
}
30503050
}
30513051
}
3052+
3053+
class ProgramBuilderRuntimeDataTests: XCTestCase {
3054+
func runFuzzerWithGenerator(_ generator: CodeGenerator) -> String {
3055+
let fuzzer = makeMockFuzzer(overwriteGenerators: WeightedList([(generator, 1)]))
3056+
let b = fuzzer.makeBuilder()
3057+
3058+
let syntheticGenerator = b.assembleSyntheticGenerator(for: generator)
3059+
XCTAssertNotNil(syntheticGenerator)
3060+
let numInstructions = b.complete(generator: syntheticGenerator!, withBudget: 3)
3061+
XCTAssertGreaterThan(numInstructions, 0)
3062+
3063+
let program = b.finalize()
3064+
return fuzzer.lifter.lift(program)
3065+
}
3066+
3067+
func testNested() {
3068+
let loopGenerator = CodeGenerator("TestDoWhileLoop", [
3069+
GeneratorStub("BeginLoop") { b in
3070+
let loopVar = b.loadInt(0)
3071+
b.runtimeData.push("loopVar", loopVar)
3072+
b.emit(BeginDoWhileLoopBody())
3073+
},
3074+
GeneratorStub("EndLoop") { b in
3075+
let loopVar = b.runtimeData.pop("loopVar")
3076+
b.unary(.PreInc, loopVar)
3077+
b.emit(BeginDoWhileLoopHeader())
3078+
let cond = b.compare(loopVar, with: b.loadInt(3), using: .lessThan)
3079+
b.emit(EndDoWhileLoop(), withInputs: [cond])
3080+
}
3081+
])
3082+
XCTAssertEqual(
3083+
runFuzzerWithGenerator(loopGenerator),
3084+
"""
3085+
let v0 = 0;
3086+
do {
3087+
let v1 = 0;
3088+
do {
3089+
++v1;
3090+
} while (v1 < 3)
3091+
++v0;
3092+
} while (v0 < 3)
3093+
3094+
""")
3095+
}
3096+
3097+
func testMultiLabel() {
3098+
var counter: Int64 = 0
3099+
let defineAndAddGenerator = CodeGenerator("TestMultiLabel", [
3100+
GeneratorStub("Define") { b in
3101+
b.runtimeData.push("first", b.loadInt(counter))
3102+
counter += 1
3103+
b.runtimeData.push("second", b.loadInt(counter))
3104+
counter += 1
3105+
b.runtimeData.push("third", b.loadInt(counter))
3106+
counter += 1
3107+
},
3108+
GeneratorStub("Add") { b in
3109+
// The order in which the different lables are popped doesn't matter.
3110+
let third = b.runtimeData.pop("third")
3111+
let first = b.runtimeData.pop("first")
3112+
let second = b.runtimeData.pop("second")
3113+
b.binary(b.binary(first, second, with: .Add), third, with: .Add)
3114+
}
3115+
])
3116+
XCTAssertEqual(
3117+
runFuzzerWithGenerator(defineAndAddGenerator),
3118+
"""
3119+
(3 + 4) + 5;
3120+
(0 + 1) + 2;
3121+
3122+
""")
3123+
}
3124+
3125+
func testPassOn() {
3126+
var counter: Int64 = 10
3127+
let defineAndAddGenerator = CodeGenerator("TestPassOn", [
3128+
GeneratorStub("Define") { b in
3129+
let value = b.loadInt(counter)
3130+
b.runtimeData.push("value", value)
3131+
b.binary(value, b.loadInt(0), with: .Add)
3132+
counter += 1
3133+
},
3134+
GeneratorStub("AddOne") { b in
3135+
let value = b.runtimeData.popAndPush("value")
3136+
b.binary(value, b.loadInt(1), with: .Add)
3137+
},
3138+
GeneratorStub("SubOne") { b in
3139+
let value = b.runtimeData.pop("value")
3140+
b.binary(value, b.loadInt(1), with: .Sub)
3141+
},
3142+
])
3143+
XCTAssertEqual(
3144+
runFuzzerWithGenerator(defineAndAddGenerator),
3145+
"""
3146+
10 + 0;
3147+
11 + 0;
3148+
11 + 1;
3149+
11 - 1;
3150+
10 + 1;
3151+
12 + 0;
3152+
12 + 1;
3153+
12 - 1;
3154+
10 - 1;
3155+
3156+
""")
3157+
}
3158+
}

0 commit comments

Comments
 (0)