From e6a8205c6e7e917c96f6c3c70a88c934718b05eb Mon Sep 17 00:00:00 2001 From: Peter Hoddie Date: Mon, 15 Apr 2024 05:21:19 -0700 Subject: [PATCH] XS code generators, object groups, etc (#431) Update XS Profile --- Sources/FuzzilliCli/Profiles/XSProfile.swift | 399 ++++++++++++++++++- 1 file changed, 393 insertions(+), 6 deletions(-) diff --git a/Sources/FuzzilliCli/Profiles/XSProfile.swift b/Sources/FuzzilliCli/Profiles/XSProfile.swift index cab43046b..44ddab3ef 100644 --- a/Sources/FuzzilliCli/Profiles/XSProfile.swift +++ b/Sources/FuzzilliCli/Profiles/XSProfile.swift @@ -1,4 +1,4 @@ -// Copyright 2019-2022 Google LLC +// Copyright 2019-2024 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -14,12 +14,379 @@ import Fuzzilli +fileprivate let StressXSGC = CodeGenerator("StressXSGC", inputs: .required(.function())) { b, f in + let arguments = b.randomArguments(forCalling: f) + + let index = b.loadInt(1) + let end = b.loadInt(128) + let gc = b.loadBuiltin("gc") + b.callFunction(gc, withArgs: [index]) + b.buildWhileLoop({b.compare(index, with: end, using: .lessThan)}) { + b.callFunction(f, withArgs: arguments) + b.unary(.PostInc, index) + let result = b.callFunction(gc, withArgs: [index]) + b.buildIf(result) { + b.loopBreak() + } + } +} + +fileprivate let StressXSMemoryFail = CodeGenerator("StressXSMemoryFail", inputs: .required(.function())) { b, f in + let arguments = b.randomArguments(forCalling: f) + + let index = b.loadInt(1) + let max = b.loadInt(1000000) + let memoryFail = b.loadBuiltin("memoryFail") + b.callFunction(memoryFail, withArgs: [max]) // count how many allocations this function makes + b.callFunction(f, withArgs: arguments) + var end = b.callFunction(memoryFail, withArgs: [index]) + end = b.binary(max, end, with: .Sub) + b.buildWhileLoop({b.compare(index, with: end, using: .lessThan)}) { + b.callFunction(f, withArgs: arguments) + b.unary(.PostInc, index) + b.callFunction(memoryFail, withArgs: [index]) + } +} + +fileprivate let HardenGenerator = CodeGenerator("HardenGenerator", inputs: .required(.object())) { b, obj in + let harden = b.loadBuiltin("harden") + + if probability(0.05) { + let lockdown = b.loadBuiltin("lockdown") + b.callFunction(lockdown) + } + b.callFunction(harden, withArgs: [obj]) +} + +fileprivate let ModuleSourceGenerator = RecursiveCodeGenerator("ModuleSourceGenerator") { b in + let moduleSourceConstructor = b.loadBuiltin("ModuleSource") + + let code = b.buildCodeString() { + b.buildRecursive() + } + + b.construct(moduleSourceConstructor, withArgs: [code]) +} + +fileprivate let CompartmentGenerator = RecursiveCodeGenerator("CompartmentGenerator") { b in + let compartmentConstructor = b.loadBuiltin("Compartment") + + var endowments = [String: Variable]() // may be used as endowments argument or globalLexicals + var moduleMap = [String: Variable]() + var options = [String: Variable]() + + for _ in 0.. 'X'", + "(arg0, arg1, arg2, arg3, arg4) => arg0 + arg1 + arg2 + arg3 + arg4", + "() => 42" + ] + + let lastIndices = [ + "undefined", "-1", "0", + "1", "2", "3", + "4", "5", "6", + "7", "8", "9", + "50", "4294967296", "2147483647", + "2147483648", "NaN", "Not a Number" + ] + + let f = b.buildPlainFunction(with: .parameters(n: 0)) { _ in + let (pattern, flags) = b.randomRegExpPatternAndFlags() + let regExpVar = b.loadRegExp(pattern, flags) + + let lastIndex = chooseUniform(from: lastIndices) + let lastIndexString = b.loadString(lastIndex) + + b.setProperty("lastIndex", of: regExpVar, to: lastIndexString) + + let subjectVar: Variable + + if probability(0.1) { + subjectVar = b.loadString(twoByteSubjectString) + } else { + subjectVar = b.loadString(b.randomString()) + } + + let resultVar = b.loadNull() + + b.buildTryCatchFinally(tryBody: { + let symbol = b.loadBuiltin("Symbol") + withEqualProbability({ + let res = b.callMethod("exec", on: regExpVar, withArgs: [subjectVar]) + b.reassign(resultVar, to: res) + }, { + let prop = b.getProperty("match", of: symbol) + let res = b.callComputedMethod(prop, on: regExpVar, withArgs: [subjectVar]) + b.reassign(resultVar, to: res) + }, { + let prop = b.getProperty("replace", of: symbol) + let replacement = withEqualProbability({ + b.loadString(b.randomString()) + }, { + b.loadString(chooseUniform(from: replacementCandidates)) + }) + let res = b.callComputedMethod(prop, on: regExpVar, withArgs: [subjectVar, replacement]) + b.reassign(resultVar, to: res) + }, { + let prop = b.getProperty("search", of: symbol) + let res = b.callComputedMethod(prop, on: regExpVar, withArgs: [subjectVar]) + b.reassign(resultVar, to: res) + }, { + let prop = b.getProperty("split", of: symbol) + let randomSplitLimit = withEqualProbability({ + "undefined" + }, { + "'not a number'" + }, { + String(b.randomInt()) + }) + let limit = b.loadString(randomSplitLimit) + let res = b.callComputedMethod(symbol, on: regExpVar, withArgs: [subjectVar, limit]) + b.reassign(resultVar, to: res) + }, { + let res = b.callMethod("test", on: regExpVar, withArgs: [subjectVar]) + b.reassign(resultVar, to: res) + }) + }, catchBody: { _ in + }) + + b.build(n: 7) + + b.doReturn(resultVar) + } + + b.callFunction(f) + + b.build(n: 15) +} + +public extension ILType { + /// Type of a JavaScript Compartment object. + static let jsCompartment = ILType.object(ofGroup: "Compartment", withProperties: ["globalThis"], withMethods: ["evaluate", "import", "importNow" /* , "module" */]) + + static let jsCompartmentConstructor = ILType.constructor([.opt(.object()), .opt(.object()), .opt(.object())] => .jsCompartment) + .object(ofGroup: "CompartmentConstructor", withProperties: ["prototype"], withMethods: []) + + static let jsModuleSource = ILType.object(ofGroup: "ModuleSource", withProperties: ["bindings", "needsImport", "needsImportMeta"]) + + static let jsModuleSourceConstructor = ILType.constructor([.opt(.string)] => .jsModuleSource) + .object(ofGroup: "ModuleSourceConstructor", withProperties: ["prototype"], withMethods: []) +} + +/// Object group modelling JavaScript compartments. +let jsCompartments = ObjectGroup( + name: "Compartment", + instanceType: .jsCompartment, + properties: [ + "globalThis" : .object() + ], + methods: [ //@@ import/importNow can accept more than strings + "import" : [.string] => .jsPromise, + "importNow" : [.string] => .anything, + // "module" : [.opt(.string)] => .object(), (currently unavailable) + "evaluate" : [.string] => .anything, + ] +) + +let jsCompartmentConstructor = ObjectGroup( + name: "CompartmentConstructor", + instanceType: .jsCompartmentConstructor, + properties: [ + "prototype" : .object() + ], + methods: [:] +) + +/// Object group modelling JavaScript ModuleSources. +let jsModuleSources = ObjectGroup( + name: "ModuleSource", + instanceType: .jsModuleSource, + properties: [ + "bindings" : .object(), + "needsImport" : .object(), + "needsImportMeta" : .object(), + ], + methods: [:] +) + +let jsModuleSourceConstructor = ObjectGroup( + name: "ModuleSourceConstructor", + instanceType: .jsModuleSourceConstructor, + properties: [ + "prototype" : .object() + ], + methods: [:] +) + let xsProfile = Profile( processArgs: { randomize in ["-f"] }, - processEnv: ["UBSAN_OPTIONS":"handle_segv=0"], + processEnv: ["UBSAN_OPTIONS":"handle_segv=0:symbolize=1:print_stacktrace=1:silence_unsigned_overflow=1", + "ASAN_OPTIONS": "handle_segv=0:abort_on_error=1:symbolize=1", + "MSAN_OPTIONS": "handle_segv=0:abort_on_error=1:symbolize=1", + "MSAN_SYMBOLIZER_PATH": "/usr/bin/llvm-symbolizer"], maxExecsBeforeRespawn: 1000, @@ -44,9 +411,21 @@ let xsProfile = Profile( ("fuzzilli('FUZZILLI_CRASH', 2)", .shouldCrash), ], - additionalCodeGenerators: [], + additionalCodeGenerators: [ + (StressXSMemoryFail, 5), + (StressXSGC, 5), + (HardenGenerator, 5), + (CompartmentGenerator, 5), + (CompartmentEvaluateGenerator, 5), + (UnicodeStringGenerator, 2), + (ModuleSourceGenerator, 3), + (HexGenerator, 2), + (Base64Generator, 2), + ], - additionalProgramTemplates: WeightedList([]), + additionalProgramTemplates: WeightedList([ + (RegExpFuzzer, 1), + ]), disabledCodeGenerators: [], @@ -54,11 +433,19 @@ let xsProfile = Profile( additionalBuiltins: [ "gc" : .function([] => .undefined), + "memoryFail" : .function([.number] => .number), "print" : .function([.string] => .undefined), - "placeholder" : .function([] => .undefined), + + // hardened javascript + "Compartment" : .function([.opt(.object()), .opt(.object()), .opt(.object())] => .jsCompartmentConstructor), + "ModuleSource" : .function([.opt(.string)] => .jsModuleSourceConstructor), + "harden" : .function([.object()] => .object()), + "lockdown" : .function([] => .undefined) , + "petrify" : .function([.anything] => .anything), + "mutabilities" : .function([.object()] => .object()) ], - additionalObjectGroups: [], + additionalObjectGroups: [jsCompartments, jsCompartmentConstructor, jsModuleSources, jsModuleSourceConstructor], optionalPostProcessor: nil )