Skip to content

Commit 583b81c

Browse files
gahaasV8-internal LUCI CQ
authored and
V8-internal LUCI CQ
committed
[types] Add enum type
Enums (called enumerations, to avoid a conflict with the swift keyword "enum") are implemented as strings with a TypeExtension. The list of all possible values of the enum are encoded in the "properties" field of the TypeExtension. Additionally, ProgramBuilder.findOrGenerateType is extended to only pick the allowed values in case the requested type is an enumeration. Change-Id: Ic9021314b9b884daffb6f47468032051755369fd Reviewed-on: https://chrome-internal-review.googlesource.com/c/v8/fuzzilli/+/7970028 Reviewed-by: Carl Smith <[email protected]> Commit-Queue: Andreas Haas <[email protected]>
1 parent bb0b272 commit 583b81c

File tree

4 files changed

+62
-14
lines changed

4 files changed

+62
-14
lines changed

Sources/Fuzzilli/Base/ProgramBuilder.swift

+6-2
Original file line numberDiff line numberDiff line change
@@ -562,7 +562,7 @@ public class ProgramBuilder {
562562
}
563563

564564
private func generateTypeInternal(_ type: ILType) -> Variable {
565-
if probability(0.9) {
565+
if probability(0.9) && !type.isEnumeration {
566566
if let existingVariable = randomVariable(ofTypeOrSubtype: type) {
567567
return existingVariable
568568
}
@@ -581,7 +581,11 @@ public class ProgramBuilder {
581581
// TODO: Not sure how we should handle merge types, e.g. .string + .object(...).
582582
let typeGenerators: [(ILType, () -> Variable)] = [
583583
(.integer, { return self.loadInt(self.randomInt()) }),
584-
(.string, { return self.loadString(self.randomString()) }),
584+
(.string, {
585+
if type.isEnumeration {
586+
return self.loadString(type.enumValues.randomElement()!)
587+
}
588+
return self.loadString(self.randomString()) }),
585589
(.boolean, { return self.loadBool(probability(0.5)) }),
586590
(.bigint, { return self.loadBigInt(self.randomInt()) }),
587591
(.float, { return self.loadFloat(self.randomFloat()) }),

Sources/Fuzzilli/FuzzIL/TypeSystem.swift

+14
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ public struct ILType: Hashable {
151151
return ILType(definiteType: .object, ext: ext)
152152
}
153153

154+
/// Constructs an enum type, which is a string with a limited set of allowed values.
155+
public static func enumeration(ofName name: String, withValues values: [String]) -> ILType {
156+
let ext = TypeExtension(group: name, properties: Set(values), methods: Set(), signature: nil, wasmExt: nil)
157+
return ILType(definiteType: .string, ext: ext)
158+
}
159+
154160
/// An object for which it is not known what properties or methods it has, if any.
155161
public static let unknownObject: ILType = .object()
156162

@@ -369,6 +375,10 @@ public struct ILType: Hashable {
369375
return Is(.constructor()) ? ext?.signature : nil
370376
}
371377

378+
public var isEnumeration : Bool {
379+
return Is(.string) && ext != nil
380+
}
381+
372382
public var group: String? {
373383
return ext?.group
374384
}
@@ -425,6 +435,10 @@ public struct ILType: Hashable {
425435
return ext?.properties ?? Set()
426436
}
427437

438+
public var enumValues: Set<String> {
439+
return properties
440+
}
441+
428442
public var methods: Set<String> {
429443
return ext?.methods ?? Set()
430444
}

Sources/FuzzilliCli/Profiles/V8Profile.swift

+18-12
Original file line numberDiff line numberDiff line change
@@ -94,16 +94,12 @@ fileprivate let WorkerGenerator = RecursiveCodeGenerator("WorkerGenerator") { b
9494
fileprivate let GcGenerator = CodeGenerator("GcGenerator") { b in
9595
let gc = b.createNamedVariable(forBuiltin: "gc")
9696

97-
// Do minor GCs more frequently.
98-
let type = b.loadString(probability(0.25) ? "major" : "minor")
99-
// If the execution type is 'async', gc() returns a Promise, we currently
100-
// do not really handle other than typing the return of gc to .undefined |
101-
// .jsPromise. One could either chain a .then or create two wrapper
102-
// functions that are differently typed such that fuzzilli always knows
103-
// what the type of the return value is.
104-
let execution = b.loadString(probability(0.5) ? "sync" : "async")
105-
b.callFunction(gc, withArgs: [b.createObject(with: ["type": type, "execution": execution])])
106-
}
97+
// `gc()` takes a `type` parameter. If the value is 'async', gc() returns a
98+
// Promise. We currently do not really handle this other than typing the
99+
// return of gc to .undefined | .jsPromise. One could either chain a .then
100+
// or create two wrapper functions that are differently typed such that
101+
// fuzzilli always knows what the type of the return value is.
102+
b.callFunction(gc, withArgs: b.findOrGenerateArguments(forSignature: b.fuzzer.environment.type(ofBuiltin: "gc").signature!)) }
107103

108104
fileprivate let WasmStructGenerator = CodeGenerator("WasmStructGenerator") { b in
109105
b.eval("%WasmStruct()", hasOutput: true);
@@ -427,6 +423,9 @@ public extension ILType {
427423
static let jsD8FastCAPI = ILType.object(ofGroup: "D8FastCAPI", withProperties: [], withMethods: ["throw_no_fallback", "add_32bit_int"])
428424

429425
static let jsD8FastCAPIConstructor = ILType.constructor(Signature(expects: [], returns: ILType.jsD8FastCAPI))
426+
427+
static let gcTypeEnum = ILType.enumeration(ofName: "gcType", withValues: ["minor", "major"])
428+
static let gcExecutionEnum = ILType.enumeration(ofName: "gcExecution", withValues: ["async", "sync"])
430429
}
431430

432431
let jsD8 = ObjectGroup(name: "D8", instanceType: .jsD8, properties: ["test" : .jsD8Test], methods: [:])
@@ -438,6 +437,13 @@ let jsD8FastCAPI = ObjectGroup(name: "D8FastCAPI", instanceType: .jsD8FastCAPI,
438437
"add_32bit_int": Signature(expects: [Parameter.plain(ILType.integer), Parameter.plain(ILType.integer)], returns: ILType.integer)
439438
])
440439

440+
let gcOptions = ObjectGroup(
441+
name: "GCOptions",
442+
instanceType: .object(ofGroup: "GCOptions", withProperties: ["type", "execution"], withMethods: []),
443+
properties: ["type": .gcTypeEnum,
444+
"execution": .gcExecutionEnum],
445+
methods: [:])
446+
441447
let fastCallables : [(group: ILType, method: String)] = [
442448
(group: .jsD8FastCAPI, method: "throw_no_fallback"),
443449
(group: .jsD8FastCAPI, method: "add_32bit_int"),
@@ -733,12 +739,12 @@ let v8Profile = Profile(
733739
disabledMutators: [],
734740

735741
additionalBuiltins: [
736-
"gc" : .function([] => (.undefined | .jsPromise)),
742+
"gc" : .function([.opt(gcOptions.instanceType)] => (.undefined | .jsPromise)),
737743
"d8" : .jsD8,
738744
"Worker" : .constructor([.anything, .object()] => .object(withMethods: ["postMessage","getMessage"])),
739745
],
740746

741-
additionalObjectGroups: [jsD8, jsD8Test, jsD8FastCAPI],
747+
additionalObjectGroups: [jsD8, jsD8Test, jsD8FastCAPI, gcOptions],
742748

743749
optionalPostProcessor: nil
744750
)

Tests/FuzzilliTests/ProgramBuilderTest.swift

+24
Original file line numberDiff line numberDiff line change
@@ -2605,4 +2605,28 @@ class ProgramBuilderTests: XCTestCase {
26052605
XCTAssert(b.type(of: obj).Is(type3))
26062606
}
26072607

2608+
func testFindOrGenerateTypeEnum() {
2609+
let allowedValues = ["hello", "world", "foo", "bar"]
2610+
2611+
let enumType = ILType.enumeration(ofName: "myEnum", withValues: allowedValues)
2612+
let env = JavaScriptEnvironment()
2613+
let config = Configuration(logLevel: .error)
2614+
let fuzzer = makeMockFuzzer(config: config, environment: env)
2615+
let b = fuzzer.makeBuilder()
2616+
b.buildPrefix()
2617+
// Get a random variable and then change the type
2618+
2619+
let obj = b.findOrGenerateType(enumType)
2620+
XCTAssert(b.type(of: obj).Is(.string))
2621+
let program = b.finalize()
2622+
var analyzer = DefUseAnalyzer(for: program)
2623+
analyzer.analyze()
2624+
let instruction = analyzer.definition(of: obj)
2625+
switch instruction.op.opcode {
2626+
case .loadString(let value):
2627+
XCTAssert(allowedValues.contains(value.value))
2628+
default:
2629+
XCTAssert(false)
2630+
}
2631+
}
26082632
}

0 commit comments

Comments
 (0)