Skip to content
Please note that GitHub no longer supports Internet Explorer.

We recommend upgrading to the latest Microsoft Edge, Google Chrome, or Firefox.

Learn more
Permalink
Browse files

A new type system for Fuzzilli

This commit consists of three different parts:

1. A new type system (see FuzzIL/TypeSystem.swift). The goal of this type system
is to be as simple as possible while still being able to express all (relevant)
operations that can be performed on a JS value. As such, the type system e.g.
has to be able to express that a value can be called as a function or
constructed with the 'new' keyword, or that it is an object with a set of
properties and methods. It also has to express that something is e.g. both an
object with methods and properties and a function that can be called. Finally,
it has to be able to express that a variable can be one of several types (e.g.
because the variable is written to again, or because different properties are
assigned in conditionally executing branches).
The type system is based on union types, with a notion of "merged" types to
express that a value is two or more types at the same time.

2. A static model of the JS environment (see Core/JavaScriptEnvironment.swift).
This model stores the types of all (relevant) entities that are available in a
default JS environment: builtin objects, constructors, functions etc. as well as
property types and method signatures.

3. An abstract intepreter that can perform lightweight interpretation of FuzzIL
code to compute static type information. For that purpose it interacts with the
environment to obtain type information for various builtins.

This change should allow code generators to generate better code as they now
have more information about e.g. available methods or properties at hand. On the
other hand, the new features can be disabled by setting
Configuration.useAbstractInterpretation to false and thus turning off most of
the new logic of the abstract interpreter. This will leave most variable types
at .unknown, thus "emulating" the old behaviour.
  • Loading branch information
Samuel Groß
Samuel Groß committed Jun 28, 2019
1 parent 1448b67 commit 01da2627d5f91ad24517ee66c1321d3bddd193f8
Showing with 3,964 additions and 319 deletions.
  1. +15 −2 Sources/Fuzzilli/Configuration.swift
  2. +54 −58 Sources/Fuzzilli/Core/CodeGenerators.swift
  3. +74 −0 Sources/Fuzzilli/Core/Environment.swift
  4. +832 −32 Sources/Fuzzilli/Core/JavaScriptEnvironment.swift
  5. +83 −19 Sources/Fuzzilli/Core/ProgramBuilder.swift
  6. +358 −0 Sources/Fuzzilli/FuzzIL/AbstractInterpreter.swift
  7. +0 −82 Sources/Fuzzilli/FuzzIL/Analyzer.swift
  8. +5 −3 Sources/Fuzzilli/FuzzIL/Instruction.swift
  9. +9 −6 Sources/Fuzzilli/FuzzIL/Operations.swift
  10. +6 −2 Sources/Fuzzilli/FuzzIL/Program.swift
  11. +845 −24 Sources/Fuzzilli/FuzzIL/TypeSystem.swift
  12. +3 −3 Sources/Fuzzilli/Fuzzer.swift
  13. +14 −4 Sources/Fuzzilli/Lifting/JavaScriptLifter.swift
  14. +17 −1 Sources/Fuzzilli/Lifting/Lifter.swift
  15. +13 −6 Sources/Fuzzilli/Lifting/ScriptWriter.swift
  16. +1 −1 Sources/Fuzzilli/Modules/NetworkSync.swift
  17. +5 −6 Sources/Fuzzilli/Modules/Storage.swift
  18. +1 −1 Sources/Fuzzilli/Mutators/InputMutator.swift
  19. +1 −1 Sources/Fuzzilli/Mutators/InsertionMutator.swift
  20. +3 −3 Sources/Fuzzilli/Mutators/JITStressMutator.swift
  21. +7 −7 Sources/Fuzzilli/Mutators/OperationMutator.swift
  22. +34 −7 Sources/Fuzzilli/Util/VariableMap.swift
  23. +11 −7 Sources/FuzzilliCli/Profiles/JSCProfile.swift
  24. +1 −3 Sources/FuzzilliCli/Profiles/Profile.swift
  25. +8 −5 Sources/FuzzilliCli/Profiles/SpidermonkeyProfile.swift
  26. +5 −5 Sources/FuzzilliCli/Profiles/V8Profile.swift
  27. +7 −15 Sources/FuzzilliCli/Settings.swift
  28. +13 −0 Sources/FuzzilliCli/TerminalUI.swift
  29. +12 −3 Sources/FuzzilliCli/main.swift
  30. +1 −1 Tests/FuzzilliTests/InliningTest.swift
  31. +417 −0 Tests/FuzzilliTests/InterpreterTest.swift
  32. +81 −6 Tests/FuzzilliTests/MockFuzzer.swift
  33. +2 −1 Tests/FuzzilliTests/ProgramSerializationTest.swift
  34. +937 −0 Tests/FuzzilliTests/TypeSystemTest.swift
  35. +17 −1 Tests/FuzzilliTests/VariableMapTest.swift
  36. +72 −4 Tests/FuzzilliTests/XCTestManifests.swift
@@ -48,7 +48,18 @@ public struct Configuration {
/// Dropout can provide a way to make multiple instances less "similar" to each
/// other as it forces them to (re)discover edges in a different way.
public let dropoutRate: Double


/// Abstractly interpret the generated FuzzIL programs to compute static type information.
/// This is used by code generators to produce valid code as much as possible. However,
/// it is a performance overhead and is also imprecise as the execution semantics of FuzzIL
/// and the target language are not strictly the same.
/// As an example, FuzzIL does not have the concept of JS prototypes, so operations on prototype
/// objects aren't correctly handled.
/// This configuration option makes it possible to disable the abstract interpretation. In that
/// case, all variables will have the .unknown type and code generators will fall back to
/// picking random variables as inputs.
public let useAbstractInterpretation: Bool

public init(timeout: UInt32 = 250,
skipStartupTests: Bool = false,
speedTestMode: Bool = false,
@@ -57,7 +68,8 @@ public struct Configuration {
isMaster: Bool = false,
isWorker: Bool = false,
minimizationLimit: UInt = 0,
dropoutRate: Double = 0.01) {
dropoutRate: Double = 0.01,
useAbstractInterpretation: Bool = true) {
self.timeout = timeout
self.speedTestMode = speedTestMode
self.logLevel = logLevel
@@ -66,5 +78,6 @@ public struct Configuration {
self.isWorker = isWorker
self.dropoutRate = dropoutRate
self.minimizationLimit = minimizationLimit
self.useAbstractInterpretation = useAbstractInterpretation
}
}
@@ -46,7 +46,7 @@ public func NullValueGenerator(_ b: ProgramBuilder) {
public func ObjectLiteralGenerator(_ b: ProgramBuilder) {
var initialProperties = [String: Variable]()
for _ in 0..<Int.random(in: 0..<10) {
initialProperties[b.genPropertyName()] = b.randVar()
initialProperties[b.genPropertyNameForWrite()] = b.randVar()
}
b.createObject(with: initialProperties)
}
@@ -64,7 +64,7 @@ public func ObjectLiteralWithSpreadGenerator(_ b: ProgramBuilder) {
var spreads = [Variable]()
for _ in 0..<Int.random(in: 0..<10) {
withProbability(0.5, do: {
initialProperties[b.genPropertyName()] = b.randVar()
initialProperties[b.genPropertyNameForWrite()] = b.randVar()
}, else: {
spreads.append(b.randVar())
})
@@ -92,55 +92,58 @@ public func FunctionDefinitionGenerator(_ b: ProgramBuilder) {
// For functions, we always generate one random instruction and one return instruction as function body.
// This ensures that generating one random instruction does not accidentially generate multiple instructions
// (which increases the likelyhood of runtime exceptions), but also generates somewhat useful functions.
b.defineFunction(numParameters: Int.random(in: 2...5), isJSStrictMode: probability(0.2), hasRestParam: probability(0.2)) { _ in
b.defineFunction(withSignature: FunctionSignature(withParameterCount: Int.random(in: 2...5), hasRestParam: probability(0.1)), isJSStrictMode: probability(0.2)) { _ in
b.generate()
b.doReturn(value: b.randVar())
}
}

public func PropertyRetrievalGenerator(_ b: ProgramBuilder) {
let object = b.randVar(ofType: .MaybeObject)
let propertyName = b.genPropertyName()

let object = b.randVar(ofType: .object())
let propertyName = b.type(of: object).randomProperty() ?? b.genPropertyNameForRead()
b.loadProperty(propertyName, of: object)
}

public func PropertyAssignmentGenerator(_ b: ProgramBuilder) {
let object = b.randVar(ofType: .MaybeObject)

let propertyName = b.genPropertyName()
let object = b.randVar(ofType: .object())
let propertyName: String
// Either change an existing property or define a new one
if probability(0.5) {
propertyName = b.type(of: object).randomProperty() ?? b.genPropertyNameForWrite()
} else {
propertyName = b.genPropertyNameForWrite()
}
let value = b.randVar()
b.storeProperty(value, as: propertyName, on: object)
}

public func PropertyRemovalGenerator(_ b: ProgramBuilder) {
let object = b.randVar(ofType: .MaybeObject)
let propertyName = b.genPropertyName()

let object = b.randVar(ofType: .object())
let propertyName = b.type(of: object).randomProperty() ?? b.genPropertyNameForWrite()
b.deleteProperty(propertyName, of: object)
}

public func ElementRetrievalGenerator(_ b: ProgramBuilder) {
let array = b.randVar(ofType: .MaybeObject)
let array = b.randVar(ofType: .object())
let index = b.genIndex()
b.loadElement(index, of: array)
}

public func ElementAssignmentGenerator(_ b: ProgramBuilder) {
let array = b.randVar(ofType: .MaybeObject)
let array = b.randVar(ofType: .object())
let index = b.genIndex()
let value = b.randVar()
b.storeElement(value, at: index, of: array)
}

public func ElementRemovalGenerator(_ b: ProgramBuilder) {
let array = b.randVar(ofType: .MaybeObject)
let array = b.randVar(ofType: .object())
let index = b.genIndex()
b.deleteElement(index, of: array)
}

public func ComputedPropertyAssignmentGenerator(_ b: ProgramBuilder) {
let object = b.randVar(ofType: .MaybeObject)
let object = b.randVar(ofType: .object())

let propertyName = b.randVar()
let value = b.randVar()
@@ -182,45 +185,38 @@ public func InGenerator(_ b: ProgramBuilder) {
b.doIn(lhs, rhs)
}

public func generateCallArguments(_ b: ProgramBuilder, n: Int) -> [Variable] {
var arguments = [Variable]()
for _ in 0..<n {
arguments.append(b.randVar())
}
return arguments
}

public func MethodCallGenerator(_ b: ProgramBuilder) {
let object = b.randVar(ofType: .MaybeObject)
let methodName = b.genMethodName()
let arguments = generateCallArguments(b, n: Int.random(in: 2...5))
let object = b.randVar(ofType: .object())
let methodName = b.type(of: object).randomMethod() ?? b.genMethodName()
let arguments = b.generateCallArguments(forMethod: methodName, on: object)

b.callMethod(methodName, on: object, withArgs: arguments)
}

public func FunctionCallGenerator(_ b: ProgramBuilder) {
let function = b.randVar(ofType: .Function)
let arguments = generateCallArguments(b, n: Int.random(in: 2...5))
let function = b.randVar(ofType: .function())
let arguments = b.generateCallArguments(for: function)

b.callFunction(function, withArgs: arguments)
}

public func FunctionReturnGenerator(_ b: ProgramBuilder) {
if b.isInFunction {
b.doReturn(value: b.randVar())
}
}

public func ConstructorCallGenerator(_ b: ProgramBuilder) {
// Want to also call builtin constructors here
let constructor = b.randVar(ofType: .MaybeFunction)
let arguments = generateCallArguments(b, n: Int.random(in: 2...5))
let constructor = b.randVar(ofType: .constructor())
let arguments = b.generateCallArguments(for: constructor)

b.construct(constructor, withArgs: arguments)
}

public func FunctionCallWithSpreadGenerator(_ b: ProgramBuilder) {
let function = b.randVar(ofType: .Function)
let arguments = generateCallArguments(b, n: Int.random(in: 1...5))
let function = b.randVar(ofType: .function())
// Since we are spreading, the signature doesn't actually help, so ignore it completely
let arguments = b.generateCallArguments(for: FunctionSignature.forUnknownFunction)

// Pick some random arguments to spread.
let spreads = arguments.map({ _ in Bool.random() })
@@ -244,7 +240,7 @@ public func PhiGenerator(_ b: ProgramBuilder) {
}

public func ReassignmentGenerator(_ b: ProgramBuilder) {
if let output = b.randPhi() {
if let output = b.randVar(ofGuaranteedType: .phi(of: .anything)) {
let input = b.randVar()
b.copy(input, to: output)
}
@@ -262,7 +258,7 @@ public func IfStatementGenerator(_ b: ProgramBuilder) {
b.run(chooseUniform(from: [ComparisonGenerator, ComparisonGenerator, TypeTestGenerator, InstanceOfGenerator]))
}

let cond = b.randVar(ofType: .Boolean)
let cond = b.randVar(ofType: .boolean)
let phi = b.phi(b.randVar())
b.beginIf(cond) {
b.generate()
@@ -307,14 +303,14 @@ public func ForLoopGenerator(_ b: ProgramBuilder) {
}

public func ForInLoopGenerator(_ b: ProgramBuilder) {
let obj = b.randVar(ofType: .MaybeObject)
let obj = b.randVar(ofType: .object())
b.forInLoop(obj) { _ in
b.generate()
}
}

public func ForOfLoopGenerator(_ b: ProgramBuilder) {
let obj = b.randVar(ofType: .MaybeObject)
let obj = b.randVar(ofType: .object())
b.forOfLoop(obj) { _ in
b.generate()
}
@@ -379,48 +375,48 @@ public func WellKnownPropertyLoadGenerator(_ b: ProgramBuilder) {
let Symbol = b.loadBuiltin("Symbol")
let name = chooseUniform(from: ["isConcatSpreadable", "iterator", "match", "replace", "search", "species", "split", "toPrimitive", "toStringTag", "unscopables"])
let pname = b.loadProperty(name, of: Symbol)
let obj = b.randVar(ofType: .MaybeObject)
let obj = b.randVar(ofType: .object())
b.loadComputedProperty(pname, of: obj)
}

public func WellKnownPropertyStoreGenerator(_ b: ProgramBuilder) {
let Symbol = b.loadBuiltin("Symbol")
let name = chooseUniform(from: ["isConcatSpreadable", "iterator", "match", "replace", "search", "species", "split", "toPrimitive", "toStringTag", "unscopables"])
let pname = b.loadProperty(name, of: Symbol)
let obj = b.randVar(ofType: .MaybeObject)
let obj = b.randVar(ofType: .object())
let val = b.randVar()
b.storeComputedProperty(val, as: pname, on: obj)
}

public func PrototypeAccessGenerator(_ b: ProgramBuilder) {
let obj = b.randVar(ofType: .MaybeObject)
let obj = b.randVar(ofType: .object())
b.loadProperty("__proto__", of: obj)
}

public func PrototypeOverwriteGenerator(_ b: ProgramBuilder) {
let obj = b.randVar(ofType: .MaybeObject)
let proto = b.randVar(ofType: .MaybeObject)
let obj = b.randVar(ofType: .object())
let proto = b.randVar(ofType: .object())
b.storeProperty(proto, as: "__proto__", on: obj)
}

public func CallbackPropertyGenerator(_ b: ProgramBuilder) {
let obj = b.randVar(ofType: .MaybeObject)
let callback = b.randVar(ofType: .Function)
let obj = b.randVar(ofType: .object())
let callback = b.randVar(ofType: .function())
let propertyName = chooseUniform(from: ["valueOf", "toString"])
b.storeProperty(callback, as: propertyName, on: obj)
}

public func PropertyAccessorGenerator(_ b: ProgramBuilder) {
let receiver = b.randVar(ofType: .MaybeObject)
let propertyName = probability(0.5) ? b.loadString(b.genPropertyName()) : b.loadInt(b.genIndex())
let receiver = b.randVar(ofType: .object())
let propertyName = probability(0.5) ? b.loadString(b.genPropertyNameForWrite()) : b.loadInt(b.genIndex())

var initialProperties = [String: Variable]()
withEqualProbability({
initialProperties = ["get": b.randVar(ofType: .Function)]
initialProperties = ["get": b.randVar(ofType: .function())]
}, {
initialProperties = ["set": b.randVar(ofType: .Function)]
initialProperties = ["set": b.randVar(ofType: .function())]
}, {
initialProperties = ["get": b.randVar(ofType: .Function), "set": b.randVar(ofType: .Function)]
initialProperties = ["get": b.randVar(ofType: .function()), "set": b.randVar(ofType: .function())]
})
let descriptor = b.createObject(with: initialProperties)

@@ -437,7 +433,7 @@ public func ProxyGenerator(_ b: ProgramBuilder) {
for _ in 0..<Int.random(in: 0..<candidates.count) {
let hook = chooseUniform(from: candidates)
candidates.remove(hook)
handlerProperties[hook] = b.randVar(ofType: .Function)
handlerProperties[hook] = b.randVar(ofType: .function())
}
let handler = b.createObject(with: handlerProperties)

@@ -448,7 +444,7 @@ public func ProxyGenerator(_ b: ProgramBuilder) {

// Tries to change the length property of some object
public func LengthChangeGenerator(_ b: ProgramBuilder) {
let target = b.randVar(ofType: .MaybeObject)
let target = b.randVar(ofType: .object())
let newLength: Variable
if probability(0.5) {
newLength = b.loadInt(Int.random(in: 0..<3))
@@ -460,20 +456,20 @@ public func LengthChangeGenerator(_ b: ProgramBuilder) {

// Tries to change the element kind of an array
public func ElementKindChangeGenerator(_ b: ProgramBuilder) {
let target = b.randVar(ofType: .MaybeObject)
let target = b.randVar(ofType: .object())
let value = b.randVar()
b.storeElement(value, at: Int.random(in: 0..<3), of: target)
}

// Generates a JavaScript 'with' statement
public func WithStatementGenerator(_ b: ProgramBuilder) {
let obj = b.randVar(ofType: .MaybeObject)
let obj = b.randVar(ofType: .object())
b.with(obj) {
withProbability(0.5, do: { () -> Void in
b.loadFromScope(id: b.genPropertyName())
b.loadFromScope(id: b.genPropertyNameForRead())
}, else: { () -> Void in
let value = b.randVar()
b.storeToScope(value, as: b.genPropertyName())
b.storeToScope(value, as: b.genPropertyNameForWrite())
})
b.generate()
}
@@ -483,13 +479,13 @@ public func LoadFromScopeGenerator(_ b: ProgramBuilder) {
guard b.isInWithStatement else {
return
}
b.loadFromScope(id: b.genPropertyName())
b.loadFromScope(id: b.genPropertyNameForRead())
}

public func StoreToScopeGenerator(_ b: ProgramBuilder) {
guard b.isInWithStatement else {
return
}
let value = b.randVar()
b.storeToScope(value, as: b.genPropertyName())
b.storeToScope(value, as: b.genPropertyNameForWrite())
}

0 comments on commit 01da262

Please sign in to comment.
You can’t perform that action at this time.