Skip to content

Commit 2b9cdba

Browse files
mi-acV8-internal LUCI CQ
authored andcommitted
Work around the f.arguments problem in dumpling mode
This works around false positives in connection with code referring to `f.arguments` in differential fuzzing. We now suppress any access to the `arguments` property and instead reject such samples. This has only an effect in differential fuzzing and is a no-op otherwise. We don't really care if the receiver actually is a function, and instead over-approximate this slightly. This might cover weird other ways of transferring the arguments to another object with `o.__proto__ = f`. Bug: 490382714 Change-Id: Ia7e78a6708f4d0db4c1ba671cfd279db8f57b70e Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/9102176 Commit-Queue: Michael Achenbach <machenbach@google.com> Reviewed-by: Matthias Liedtke <mliedtke@google.com>
1 parent 36fe897 commit 2b9cdba

File tree

6 files changed

+106
-5
lines changed

6 files changed

+106
-5
lines changed

Sources/Fuzzilli/Engines/FuzzEngine.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,17 @@ public class FuzzEngine: ComponentBase {
3232
fatalError("Must be implemented by child classes")
3333
}
3434

35-
final func execute(_ program: Program, withTimeout timeout: UInt32? = nil) -> ExecutionOutcome {
36-
let program = postProcessor?.process(program, for: fuzzer) ?? program
35+
final func execute(_ rawProgram: Program, withTimeout timeout: UInt32? = nil) -> ExecutionOutcome {
36+
let program : Program
37+
do {
38+
// An optional post processor can reject a sample right away with
39+
// the postProcessRejectionError.
40+
program = try postProcessor?.process(rawProgram, for: fuzzer) ?? rawProgram
41+
} catch InternalError.postProcessRejection {
42+
return ExecutionOutcome.failed(1)
43+
} catch {
44+
fatalError("Unexpected error in post processor.")
45+
}
3746

3847
fuzzer.dispatchEvent(fuzzer.events.ProgramGenerated, data: program)
3948

Sources/Fuzzilli/Engines/FuzzingPostProcessor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ import Foundation
1616

1717
// A post-processor can be used to modify samples generated during fuzzing before executing them.
1818
public protocol FuzzingPostProcessor {
19-
func process(_ program: Program, for fuzzer: Fuzzer) -> Program
19+
func process(_ program: Program, for fuzzer: Fuzzer) throws -> Program
2020
}

Sources/Fuzzilli/Profiles/V8DumplingProfile.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,5 +154,34 @@ let v8DumplingProfile = Profile(
154154

155155
additionalEnumerations: [.gcTypeEnum, .gcExecutionEnum],
156156

157-
optionalPostProcessor: nil
157+
optionalPostProcessor: DumplingFuzzingPostProcessor()
158158
)
159+
160+
/// A post-processor for the Dumpling profile.
161+
///
162+
/// Work-around for differential fuzzing to avoid f.arguments.
163+
/// Or any access to "arguments" with a computed property. We just
164+
/// overapproximate this by checking for any string occurence of
165+
/// "arguments" and reject the sample.
166+
public struct DumplingFuzzingPostProcessor: FuzzingPostProcessor {
167+
public func process(_ program: Program, for fuzzer: Fuzzer) throws -> Program {
168+
for instr in program.code {
169+
switch instr.op.opcode {
170+
case .loadString(let op) where op.value == "arguments":
171+
throw InternalError.postProcessRejection("\"arguments\" string")
172+
case .getProperty(let op) where op.propertyName == "arguments":
173+
throw InternalError.postProcessRejection("f.arguments access")
174+
case .setProperty(let op) where op.propertyName == "arguments":
175+
throw InternalError.postProcessRejection("f.arguments assignment")
176+
case .updateProperty(let op) where op.propertyName == "arguments":
177+
throw InternalError.postProcessRejection("f.arguments update")
178+
case .deleteProperty(let op) where op.propertyName == "arguments":
179+
throw InternalError.postProcessRejection("f.arguments deletion")
180+
default:
181+
break
182+
}
183+
}
184+
185+
return program
186+
}
187+
}

Sources/Fuzzilli/Profiles/V8SandboxProfile.swift

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

1616
// A post-processor that inserts calls to the sandbox corruption functions (defined in the codeSuffix below) into the generated samples.
1717
fileprivate struct SandboxFuzzingPostProcessor: FuzzingPostProcessor {
18-
func process(_ program: Fuzzilli.Program, for fuzzer: Fuzzer) -> Fuzzilli.Program {
18+
func process(_ program: Program, for fuzzer: Fuzzer) throws -> Program {
1919
// We don't instrument every generated program since we still want the fuzzer to make progress towards
2020
// discovering more interestesting programs and adding them to the corpus. Corrupting objects in every
2121
// generated program might hamper that.

Sources/Fuzzilli/Util/Error.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,8 @@ public enum FuzzilliError: Error {
2323
case evaluatorStateImportError(String)
2424
case codeVerificationError(String)
2525
}
26+
27+
// Simple error enum for internally handled errors, not displayed to the user.
28+
public enum InternalError: Error {
29+
case postProcessRejection(String)
30+
}

Tests/FuzzilliTests/EngineTests.swift

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,62 @@ class EngineTests: XCTestCase {
4848
XCTAssertEqual(mockPostProcessor.callCount, 3)
4949
XCTAssert(fuzzer.isStopped)
5050
}
51+
52+
// Test that certain usages of "arguments" are rejected by the Dumpling
53+
// post processor.
54+
func testDumplingPostProcessor() {
55+
let fuzzer = makeMockFuzzer()
56+
let processor = DumplingFuzzingPostProcessor()
57+
58+
let rejectedCases: [(ProgramBuilder) -> ()] = [
59+
{ b in
60+
let f = b.buildPlainFunction(with: .parameters(n: 0)) { _ in }
61+
b.getProperty("arguments", of: f)
62+
},
63+
{ b in
64+
let f = b.buildPlainFunction(with: .parameters(n: 0)) { _ in }
65+
let i = b.loadInt(0)
66+
b.setProperty("arguments", of: f, to: i)
67+
},
68+
{ b in
69+
let f = b.buildPlainFunction(with: .parameters(n: 0)) { _ in }
70+
let i = b.loadInt(0)
71+
b.updateProperty("arguments", of: f, with: i, using: BinaryOperator.Add)
72+
},
73+
{ b in
74+
let f = b.buildPlainFunction(with: .parameters(n: 0)) { _ in }
75+
b.deleteProperty("arguments", of: f)
76+
},
77+
{ b in
78+
let f = b.buildPlainFunction(with: .parameters(n: 0)) { _ in }
79+
let a = b.loadString("arguments")
80+
b.getComputedProperty(a, of: f)
81+
},
82+
]
83+
84+
for (i, rejectedCase) in rejectedCases.enumerated() {
85+
let b = fuzzer.makeBuilder()
86+
rejectedCase(b)
87+
let program = b.finalize()
88+
XCTAssertThrowsError(try processor.process(program, for: fuzzer), "test case \(i)")
89+
}
90+
91+
for acceptedCase: (ProgramBuilder) -> () in [
92+
{ b in
93+
let f = b.buildPlainFunction(with: .parameters(n: 0)) { _ in }
94+
b.getProperty("not_arguments", of: f)
95+
},
96+
{ b in
97+
let f = b.buildPlainFunction(with: .parameters(n: 0)) { _ in }
98+
let a = b.loadString("not_arguments")
99+
b.getComputedProperty(a, of: f)
100+
},
101+
] {
102+
103+
let b = fuzzer.makeBuilder()
104+
acceptedCase(b)
105+
let program = b.finalize()
106+
_ = try! processor.process(program, for: fuzzer)
107+
}
108+
}
51109
}

0 commit comments

Comments
 (0)