diff --git a/Scripts/RunFuzzilli.sh b/Scripts/RunFuzzilli.sh new file mode 100755 index 000000000..c63fdbe93 --- /dev/null +++ b/Scripts/RunFuzzilli.sh @@ -0,0 +1 @@ +swift run FuzzilliCli --profile=v8 --engine=multi --corpus=postgresql --postgres-url=postgresql://fuzzilli:password@localhost:5432/fuzzilli --logLevel=verbose --timeout=1500 --diagnostics ~/projects/ritsec/vrig/vrigatoni/v8/out/fuzzbuild/d8 diff --git a/Scripts/SetupPostgres.sh b/Scripts/SetupPostgres.sh new file mode 100755 index 000000000..746084ecb --- /dev/null +++ b/Scripts/SetupPostgres.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# Setup PostgreSQL for Fuzzilli testing +set -e + +echo "=== Fuzzilli PostgreSQL Setup ===" + +# Detect container runtime +if command -v docker &> /dev/null && command -v docker-compose &> /dev/null; then + CONTAINER_RUNTIME="docker" + echo "Using Docker" +elif command -v podman &> /dev/null; then + CONTAINER_RUNTIME="podman" + echo "Using Podman" +else + echo "Error: Neither docker-compose nor podman is available" + echo "Please install docker-compose or podman to continue" + exit 1 +fi + +# Detect compose command +if command -v docker-compose &> /dev/null; then + COMPOSE_CMD="docker-compose" +elif command -v podman-compose &> /dev/null; then + COMPOSE_CMD="podman-compose" +else + echo "Error: No compose command found, do you have docker-compose or po installed?" + exit 1 +fi + +# Check if container runtime is accessible +if ! $CONTAINER_RUNTIME info &> /dev/null; then + echo "Error: $CONTAINER_RUNTIME is not accessible" + echo "Please ensure $CONTAINER_RUNTIME is running and try again" + exit 1 +fi + +echo "Starting PostgreSQL container..." +$COMPOSE_CMD up -d postgres + +echo "Waiting for PostgreSQL to be ready..." +timeout=60 +counter=0 +while ! $COMPOSE_CMD exec postgres pg_isready -U fuzzilli -d fuzzilli &> /dev/null; do + if [ $counter -ge $timeout ]; then + echo "Error: PostgreSQL failed to start within $timeout seconds" + $COMPOSE_CMD logs postgres + exit 1 + fi + echo "Waiting for PostgreSQL... ($counter/$timeout)" + sleep 2 + counter=$((counter + 2)) +done + +echo "PostgreSQL is ready!" + +# Test connection +echo "Testing database connection..." +$COMPOSE_CMD exec postgres psql -U fuzzilli -d fuzzilli -c "SELECT version();" + +echo "Checking if tables exist..." +$COMPOSE_CMD exec postgres psql -U fuzzilli -d fuzzilli -c " +SELECT table_name +FROM information_schema.tables +WHERE table_schema = 'public' +ORDER BY table_name; +" + +echo "Checking execution types..." +$COMPOSE_CMD exec postgres psql -U fuzzilli -d fuzzilli -c " +SELECT id, title, description +FROM execution_type +ORDER BY id; +" + +echo "Checking mutator types..." +$COMPOSE_CMD exec postgres psql -U fuzzilli -d fuzzilli -c " +SELECT id, name, category +FROM mutator_type +ORDER BY id; +" + +echo "Checking execution outcomes..." +$COMPOSE_CMD exec postgres psql -U fuzzilli -d fuzzilli -c " +SELECT id, outcome, description +FROM execution_outcome +ORDER BY id; +" + +echo "" +echo "=== PostgreSQL Setup Complete ===" +echo "Connection string: postgresql://fuzzilli:fuzzilli123@localhost:5433/fuzzilli" +echo "" +echo "To start pgAdmin (optional):" +echo " $COMPOSE_CMD up -d pgadmin" +echo " Open http://localhost:8080" +echo " Login: admin@fuzzilli.local / admin123" +echo "" +echo "To stop PostgreSQL:" +echo " $COMPOSE_CMD down" +echo "" +echo "To view logs:" +echo " $COMPOSE_CMD logs postgres" diff --git a/Sources/Fuzzilli/Engines/FuzzEngine.swift b/Sources/Fuzzilli/Engines/FuzzEngine.swift index 815caad30..422615cd5 100755 --- a/Sources/Fuzzilli/Engines/FuzzEngine.swift +++ b/Sources/Fuzzilli/Engines/FuzzEngine.swift @@ -42,6 +42,7 @@ public class FuzzEngine: ComponentBase { switch execution.outcome { case .crashed(let termsig): fuzzer.processCrash(program, withSignal: termsig, withStderr: execution.stderr, withStdout: execution.stdout, origin: .local, withExectime: execution.execTime) + fuzzer.runtimeWeightedMutators.adjustBatchWeight(fuzzer.runtimeWeightedMutators.getLastElements(), 1.1, 0.9) program.contributors.generatedCrashingSample() case .succeeded: @@ -57,8 +58,10 @@ public class FuzzEngine: ComponentBase { if isInteresting { program.contributors.generatedInterestingSample() + fuzzer.runtimeWeightedMutators.adjustBatchWeight(fuzzer.runtimeWeightedMutators.getLastElements(), 1.1, 0.9) } else { program.contributors.generatedValidSample() + fuzzer.runtimeWeightedMutators.adjustBatchWeight(fuzzer.runtimeWeightedMutators.getLastElements(), 0.9, 1.1) } case .failed(_): diff --git a/Sources/Fuzzilli/Engines/RuntimeHybridEngine.swift b/Sources/Fuzzilli/Engines/RuntimeHybridEngine.swift new file mode 100755 index 000000000..8180f986a --- /dev/null +++ b/Sources/Fuzzilli/Engines/RuntimeHybridEngine.swift @@ -0,0 +1,181 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + +public class RuntimeHybridEngine: FuzzEngine { + // The number of mutations to perform to a single sample per round + private let numConsecutiveMutations: Int + + // The different outcomes of one fuzzing iterations. + private enum CodeGenerationOutcome: String, CaseIterable { + case success = "Success" + case generatedCodeFailed = "Generated code failed" + case generatedCodeTimedOut = "Generated code timed out" + case generatedCodeCrashed = "Generated code crashed" + } + private var outcomeCounts = [CodeGenerationOutcome: Int]() + + // Additional statistics about the generated programs. + private var totalInstructionsGenerated = 0 + private var programsGenerated = 0 + private var percentageOfGuardedOperationsAfterCodeGeneration = MovingAverage(n: 1000) + private var percentageOfGuardedOperationsAfterCodeRefining = MovingAverage(n: 1000) + + // We use the FixupMutator to "fix" the generated programs based on runtime information (e.g. remove unneeded try-catch). + private var fixupMutator = FixupMutator(name: "HybridEngineFixupMutator") + + public init(numConsecutiveMutations: Int) { + self.numConsecutiveMutations = numConsecutiveMutations + super.init(name: "HybridEngine") + + for outcome in CodeGenerationOutcome.allCases { + outcomeCounts[outcome] = 0 + } + } + + override func initialize() { + if fuzzer.config.logLevel.isAtLeast(.verbose) { + fuzzer.timers.scheduleTask(every: 30 * Minutes) { + guard self.programsGenerated > 0 else { return } + + // TODO move into Statistics? + self.logger.verbose("Program Template Statistics:") + let nameMaxLength = self.fuzzer.programTemplates.map({ $0.name.count }).max()! + for template in self.fuzzer.programTemplates { + let name = template.name.rightPadded(toLength: nameMaxLength) + let correctnessRate = Statistics.percentageOrNa(template.correctnessRate, 7) + let interestingSamplesRate = Statistics.percentageOrNa(template.interestingSamplesRate, 7) + let timeoutRate = Statistics.percentageOrNa(template.timeoutRate, 6) + let avgInstructionsAdded = String(format: "%.2f", template.avgNumberOfInstructionsGenerated).leftPadded(toLength: 5) + let samplesGenerated = template.totalSamples + self.logger.verbose(" \(name) : Correctness rate: \(correctnessRate), Interesting sample rate: \(interestingSamplesRate), Timeout rate: \(timeoutRate), Avg. # of instructions generated: \(avgInstructionsAdded), Total # of generated samples: \(samplesGenerated)") + } + + let totalOutcomes = self.outcomeCounts.values.reduce(0, +) + self.logger.verbose("Frequencies of code generation outcomes:") + for outcome in CodeGenerationOutcome.allCases { + let count = self.outcomeCounts[outcome]! + let frequency = (Double(count) / Double(totalOutcomes)) * 100.0 + self.logger.verbose(" \(outcome.rawValue.rightPadded(toLength: 25)): \(String(format: "%.2f%%", frequency))") + } + + self.logger.verbose("Number of generated programs: \(self.programsGenerated)") + self.logger.verbose("Average programs size: \(self.totalInstructionsGenerated / self.programsGenerated)") + self.logger.verbose("Average percentage of guarded operations after code generation: \(String(format: "%.2f%", self.percentageOfGuardedOperationsAfterCodeGeneration.currentValue))%") + self.logger.verbose("Average percentage of guarded operations after code refining: \(String(format: "%.2f%", self.percentageOfGuardedOperationsAfterCodeRefining.currentValue))%") + } + } + } + + private func generateTemplateProgram(template: ProgramTemplate) -> Program { + let b = fuzzer.makeBuilder() + b.traceHeader("Generating program based on \(template.name) template") + template.generate(in: b) + let program = b.finalize() + + program.contributors.insert(template) + template.addedInstructions(program.size) + return program + } + + public override func fuzzOne(_ group: DispatchGroup) { + let template = fuzzer.programTemplates.randomElement() + + let generatedProgram = generateTemplateProgram(template: template) + + // Update basic codegen statistics. + totalInstructionsGenerated += generatedProgram.size + programsGenerated += 1 + percentageOfGuardedOperationsAfterCodeGeneration.add(computePercentageOfGuardedOperations(in: generatedProgram)) + + // We use a higher timeout for the initial execution as pure code generation should only rarely lead to infinite loops/recursion. + // On the other hand, the generated program may contain slow operations (e.g. try-catch guards) that the subsequent fixup may remove. + let outcome = execute(generatedProgram, withTimeout: fuzzer.config.timeout * 2) + switch outcome { + case .succeeded: + recordOutcome(.success) + case .failed: + return recordOutcome(.generatedCodeFailed) + case .timedOut: + return recordOutcome(.generatedCodeTimedOut) + case .crashed: + return recordOutcome(.generatedCodeCrashed) + } + + // Now perform one round of fixup to improve the generated program based on runtime information and in particular remove all try-catch guards that are not needed. + // For example, at runtime we'll know the exact type of variables, including object methods and properties, which we do not necessarily know statically during code generation. + // As such, it is much easier to select a "good" method/property to access at runtime than it is during static code generation. Further, it is trivial to determine which + // operations raise an exception at runtime, but hard to determine that statically at code generation time. So we can be overly conservative and wrap many operations in + // try-catch (i.e. "guard" them), then remove the unnecessary guards after code generation based on runtime information. This is what fixup achieves. + let refinedProgram: Program + if let result = fixupMutator.mutate(generatedProgram, for: fuzzer) { + refinedProgram = result + percentageOfGuardedOperationsAfterCodeRefining.add(computePercentageOfGuardedOperations(in: refinedProgram)) + } else { + // Fixup is expected to fail sometimes, for example if there is nothing to fix. + refinedProgram = generatedProgram + } + + // Now mutate the program a number of times. + // We do this for example because pure code generation will often not generate "weird" code (e.g. weird inputs to operations, infinite loops, very large arrays, odd-looking object/class literals, etc.), but mutators are pretty good at that. + // Further, some mutators have access to runtime information (e.g. Probe and Explore mutator) which the static code generation lacks. + var parent = refinedProgram + for _ in 0.. Double { + let numGuardedOperations = Double(program.code.filter({ $0.isGuarded }).count) + // We also count try-catch blocks as guards for the purpose of these statistics, and we count them as 3 instructions + // as they at least need the BeginTry and EndTryCatchFinally, plus either a BeginCatch or BeginFinally. + let numTryCatchBlocks = Double(program.code.filter({ $0.op is BeginTry }).count) + return ((numGuardedOperations + numTryCatchBlocks * 3) / Double(program.size)) * 100.0 + } +} diff --git a/Sources/Fuzzilli/Engines/RuntimeMutationEngine.swift b/Sources/Fuzzilli/Engines/RuntimeMutationEngine.swift new file mode 100755 index 000000000..acf55a80d --- /dev/null +++ b/Sources/Fuzzilli/Engines/RuntimeMutationEngine.swift @@ -0,0 +1,92 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import Foundation + +/// The core fuzzer responsible for generating and executing programs. +public class RuntimeMutationEngine: FuzzEngine { + // The number of consecutive mutations to apply to a sample. + private let numConsecutiveMutations: Int + + public init(numConsecutiveMutations: Int) { + self.numConsecutiveMutations = numConsecutiveMutations + super.init(name: "MutationEngine") + } + + /// Perform one round of fuzzing. + /// + /// High-level fuzzing algorithm: + /// + /// let parent = pickSampleFromCorpus() + /// repeat N times: + /// let current = mutate(parent) + /// execute(current) + /// if current produced crashed: + /// output current + /// elif current resulted in a runtime exception or a time out: + /// // do nothing + /// elif current produced new, interesting behaviour: + /// minimize and add to corpus + /// else + /// parent = current + /// + /// + /// This ensures that samples will be mutated multiple times as long + /// as the intermediate results do not cause a runtime exception. + public override func fuzzOne(_ group: DispatchGroup) { + var parent = fuzzer.corpus.randomElementForMutating() + parent = prepareForMutating(parent) + for _ in 0.. Program { + let b = fuzzer.makeBuilder() + b.buildPrefix() + b.append(program) + return b.finalize() + } +} diff --git a/Sources/Fuzzilli/Engines/RuntimeMutationWeighting.swift b/Sources/Fuzzilli/Engines/RuntimeMutationWeighting.swift new file mode 100644 index 000000000..e69de29bb diff --git a/Sources/Fuzzilli/Fuzzer.swift b/Sources/Fuzzilli/Fuzzer.swift index e2a2369f6..b8b335ddc 100755 --- a/Sources/Fuzzilli/Fuzzer.swift +++ b/Sources/Fuzzilli/Fuzzer.swift @@ -51,6 +51,9 @@ public class Fuzzer { /// The mutators used by the engine. public let mutators: WeightedList + /// The mutators used by the engine when dynamically weighted during runtime + public let runtimeWeightedMutators: RuntimeWeightedList + /// The evaluator to score generated programs. public let evaluator: ProgramEvaluator @@ -160,6 +163,7 @@ public class Fuzzer { public init( configuration: Configuration, scriptRunner: ScriptRunner, engine: FuzzEngine, mutators: WeightedList, codeGenerators: WeightedList, programTemplates: WeightedList, evaluator: ProgramEvaluator, + runtimeWeightedMutators: RuntimeWeightedList, environment: JavaScriptEnvironment, lifter: Lifter, corpus: Corpus, minimizer: Minimizer, queue: DispatchQueue? = nil ) { let uniqueId = UUID() @@ -171,6 +175,7 @@ public class Fuzzer { self.timers = Timers(queue: self.queue) self.engine = engine self.mutators = mutators + self.runtimeWeightedMutators = runtimeWeightedMutators self.codeGenerators = codeGenerators self.programTemplates = programTemplates @@ -723,9 +728,9 @@ public class Fuzzer { aspects = intersection } while !didConverge || attempt < minAttempts } - if origin == .local { - iterationOfLastInteratingSample = iterations - } + // Update the iteration counter for any interesting program found + // This is crucial for corpus generation phase to properly track progress + iterationOfLastInteratingSample = iterations // Determine whether the program needs to be minimized, then, using this helper function, dispatch the appropriate // event and insert the sample into the corpus. @@ -910,6 +915,7 @@ public class Fuzzer { // Perform the next iteration as soon as all tasks related to the current iteration are finished. fuzzGroup.notify(queue: queue) { + self.runtimeWeightedMutators.flushLastElements() self.fuzzOne() } } diff --git a/Sources/Fuzzilli/Util/MockFuzzer.swift b/Sources/Fuzzilli/Util/MockFuzzer.swift index a7bab9463..084138fe4 100755 --- a/Sources/Fuzzilli/Util/MockFuzzer.swift +++ b/Sources/Fuzzilli/Util/MockFuzzer.swift @@ -96,6 +96,13 @@ public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engi (CombineMutator(), 1), ]) + let runtimeMutators = RuntimeWeightedList([ + (CodeGenMutator(), 1), + (OperationMutator(), 1), + (InputMutator(typeAwareness: .loose), 1), + (CombineMutator(), 1) + ]) + let engine = maybeEngine ?? MutationEngine(numConsecutiveMutations: 5) // The evaluator to score produced samples. @@ -133,6 +140,7 @@ public func makeMockFuzzer(config maybeConfiguration: Configuration? = nil, engi codeGenerators: codeGenerators, programTemplates: programTemplates, evaluator: evaluator, + runtimeWeightedMutators: runtimeMutators, environment: environment, lifter: lifter, corpus: corpus, diff --git a/Sources/Fuzzilli/Util/RuntimeWeightedList.swift b/Sources/Fuzzilli/Util/RuntimeWeightedList.swift new file mode 100644 index 000000000..23bc58b3e --- /dev/null +++ b/Sources/Fuzzilli/Util/RuntimeWeightedList.swift @@ -0,0 +1,110 @@ + +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// A list where each element also has a weight, which determines how frequently it is selected by randomElement(). +/// For example, an element with weight 10 is 2x more likely to be selected by randomElement() than an element with weight 5. +public class RuntimeWeightedList: WeightedList { + private var elements = [( + elem: Element, + weight: Int, + cumulativeWeight: Int, + runtimeWeight: Float, + cumulativeRuntimeWeight: Float + )]() + private(set) var totalRuntimeWeight: Float = 0.0 + // cache of most recently selected mutators + private var lastElements: [Element] = [] + + public override init(_ values: [(Element, Int)]) { + super.init() + totalWeight = values.count + for (e, _) in values { + append(e, withWeight: 1, runtimeWeight: 1.0) + } + } + + public var description: String { + return String(format: "%.2f", totalRuntimeWeight) + } + + // When applying the factor to the elements runtimeWeight, floating point imprecision is introduced. + // This can cause floating point drift for the last element's cumulativeRuntimeWeight and the totalRuntimeWeight + // which may have an impact when choosing a weighted mutation. + public func adjustWeight(_ elem: Element, _ factor: Float) { + var hitElement = false; + var diffWeight: Float = 0.0 + for i in 0.. Bool) -> RuntimeWeightedList { + //var r: RuntimeWeightedList = RuntimeWeightedList() + //for (e, w, cw, rw, crw) in elements where isIncluded(e) { + // append(e, withWeight: w) + //} + return self + } + + public func append(_ elem: Element, withWeight weight: Int, runtimeWeight: Float) { + assert(weight > 0) + totalRuntimeWeight += runtimeWeight + elements.append((elem, weight, totalWeight, runtimeWeight, totalRuntimeWeight)) + } + + public func weightedElement() -> Element { + let k = Float.random(in: 0.0...totalRuntimeWeight - elements.last!.runtimeWeight) + //print("elements.count == \(elements.count); elements.last.cumulativeRuntimeWeight == \(elements.last!.cumulativeRuntimeWeight)") + for i in 0..= k { + lastElements.append(elements[i].elem) + return elements[i].elem + } + } + return elements.last!.elem + } + + public func getLastElements() -> [Element] { + return lastElements + } + + public func popLastElement() -> Void { + let _ = lastElements.popLast() + } + + public func flushLastElements() -> Void { + lastElements = [] + } +} diff --git a/Sources/Fuzzilli/Util/WeightedList.swift b/Sources/Fuzzilli/Util/WeightedList.swift index c32d1c790..6300d4726 100755 --- a/Sources/Fuzzilli/Util/WeightedList.swift +++ b/Sources/Fuzzilli/Util/WeightedList.swift @@ -14,9 +14,9 @@ /// A list where each element also has a weight, which determines how frequently it is selected by randomElement(). /// For example, an element with weight 10 is 2x more likely to be selected by randomElement() than an element with weight 5. -public struct WeightedList: Sequence { +public class WeightedList: Sequence { private var elements = [(elem: Element, weight: Int, cumulativeWeight: Int)]() - private(set) var totalWeight = 0 + public var totalWeight: Int = 0 public init() {} @@ -34,7 +34,7 @@ public struct WeightedList: Sequence { return count == 0 } - public mutating func append(_ elem: Element, withWeight weight: Int) { + public func append(_ elem: Element, withWeight weight: Int) { assert(weight > 0) elements.append((elem, weight, totalWeight)) totalWeight += weight diff --git a/Sources/FuzzilliCli/TerminalUI.swift b/Sources/FuzzilliCli/TerminalUI.swift index 1bb5f1c84..6c0d5663a 100755 --- a/Sources/FuzzilliCli/TerminalUI.swift +++ b/Sources/FuzzilliCli/TerminalUI.swift @@ -146,6 +146,7 @@ class TerminalUI { Execs / Second: \(String(format: "%.2f", stats.execsPerSecond)) Fuzzer Overhead: \(String(format: "%.2f", stats.fuzzerOverhead * 100))% Minimization Overhead: \(String(format: "%.2f", stats.minimizationOverhead * 100))% + Mutation Weights: \(fuzzer.runtimeWeightedMutators.description) Total Execs: \(stats.totalExecs) """) } diff --git a/Sources/FuzzilliCli/main.swift b/Sources/FuzzilliCli/main.swift index 6180ac08f..95394bf76 100755 --- a/Sources/FuzzilliCli/main.swift +++ b/Sources/FuzzilliCli/main.swift @@ -187,7 +187,7 @@ guard let logLevel = logLevelByName[logLevelName] else { configError("Invalid log level \(logLevelName)") } -let validEngines = ["mutation", "hybrid", "multi"] +let validEngines = ["mutation", "hybrid", "multi", "runtime-multi"] guard validEngines.contains(engineName) else { configError("--engine must be one of \(validEngines)") } @@ -435,6 +435,21 @@ func makeFuzzer(with configuration: Configuration) -> Fuzzer { configError("List of enabled mutators is empty. There needs to be at least one mutator available.") } + let runtimeMutators = RuntimeWeightedList([ + (ExplorationMutator(), 3), + (CodeGenMutator(), 2), + (SpliceMutator(), 2), + (ProbingMutator(), 2), + (InputMutator(typeAwareness: .loose), 2), + (InputMutator(typeAwareness: .aware), 1), + // Can be enabled for experimental use, ConcatMutator is a limited version of CombineMutator + // (ConcatMutator(), 1), + (OperationMutator(), 1), + (CombineMutator(), 1), + // Include this once it does more than just remove unneeded try-catch + // (FixupMutator()), 1), + ]) + // Engines to execute programs. let engine: FuzzEngine switch engineName { @@ -454,6 +469,20 @@ func makeFuzzer(with configuration: Configuration) -> Fuzzer { // For the same reason, we also use a relatively larger iterationsPerEngine value, so that // the MutationEngine can already find most "low-hanging fruits" in its first run. engine = MultiEngine(engines: engines, initialActive: mutationEngine, iterationsPerEngine: 10000) + case "runtime-multi": + let runtimeMutationEngine = RuntimeMutationEngine(numConsecutiveMutations: consecutiveMutations) + let runtimeHybridEngine = RuntimeHybridEngine(numConsecutiveMutations: consecutiveMutations) + let engines = WeightedList([ + (runtimeMutationEngine, 1), + (runtimeHybridEngine, 1), + ]) + // We explicitly want to start with the MutationEngine since we'll probably be finding + // lots of new samples during early fuzzing. The samples generated by the HybridEngine tend + // to be much larger than those from the MutationEngine and will therefore take much longer + // to minimize, making the fuzzer less efficient. + // For the same reason, we also use a relatively larger iterationsPerEngine value, so that + // the MutationEngine can already find most "low-hanging fruits" in its first run. + engine = MultiEngine(engines: engines, initialActive: runtimeMutationEngine, iterationsPerEngine: 10000) default: engine = MutationEngine(numConsecutiveMutations: consecutiveMutations) } @@ -548,6 +577,7 @@ func makeFuzzer(with configuration: Configuration) -> Fuzzer { codeGenerators: codeGenerators, programTemplates: programTemplates, evaluator: evaluator, + runtimeWeightedMutators: runtimeMutators, environment: environment, lifter: lifter, corpus: corpus, diff --git a/runFuzzilli.sh b/runFuzzilli.sh deleted file mode 100755 index fe7789a51..000000000 --- a/runFuzzilli.sh +++ /dev/null @@ -1 +0,0 @@ -swift run FuzzilliCli --profile=v8 --engine=multi --resume --corpus=basic --storagePath=./Corpus --logLevel=verbose --timeout=1500 --diagnostics /usr/share/vrigatoni/v8_2/v8/out/fuzzbuild/d8 diff --git a/scripts/setup-postgres.sh b/scripts/setup-postgres.sh deleted file mode 100755 index 48091bba7..000000000 --- a/scripts/setup-postgres.sh +++ /dev/null @@ -1,87 +0,0 @@ -#!/bin/bash - -# Setup PostgreSQL for Fuzzilli testing -set -e - -echo "=== Fuzzilli PostgreSQL Setup ===" - -# Check if docker-compose is available -if ! command -v docker-compose &> /dev/null; then - echo "Error: docker-compose is not installed" - echo "Please install docker-compose to continue" - exit 1 -fi - -# Check if docker is running -if ! docker info &> /dev/null; then - echo "Error: Docker is not running" - echo "Please start Docker and try again" - exit 1 -fi - -echo "Starting PostgreSQL container..." -docker-compose up -d postgres - -echo "Waiting for PostgreSQL to be ready..." -timeout=60 -counter=0 -while ! docker-compose exec postgres pg_isready -U fuzzilli -d fuzzilli &> /dev/null; do - if [ $counter -ge $timeout ]; then - echo "Error: PostgreSQL failed to start within $timeout seconds" - docker-compose logs postgres - exit 1 - fi - echo "Waiting for PostgreSQL... ($counter/$timeout)" - sleep 2 - counter=$((counter + 2)) -done - -echo "PostgreSQL is ready!" - -# Test connection -echo "Testing database connection..." -docker-compose exec postgres psql -U fuzzilli -d fuzzilli -c "SELECT version();" - -echo "Checking if tables exist..." -docker-compose exec postgres psql -U fuzzilli -d fuzzilli -c " -SELECT table_name -FROM information_schema.tables -WHERE table_schema = 'public' -ORDER BY table_name; -" - -echo "Checking execution types..." -docker-compose exec postgres psql -U fuzzilli -d fuzzilli -c " -SELECT id, title, description -FROM execution_type -ORDER BY id; -" - -echo "Checking mutator types..." -docker-compose exec postgres psql -U fuzzilli -d fuzzilli -c " -SELECT id, name, category -FROM mutator_type -ORDER BY id; -" - -echo "Checking execution outcomes..." -docker-compose exec postgres psql -U fuzzilli -d fuzzilli -c " -SELECT id, outcome, description -FROM execution_outcome -ORDER BY id; -" - -echo "" -echo "=== PostgreSQL Setup Complete ===" -echo "Connection string: postgresql://fuzzilli:fuzzilli123@localhost:5433/fuzzilli" -echo "" -echo "To start pgAdmin (optional):" -echo " docker-compose up -d pgadmin" -echo " Open http://localhost:8080" -echo " Login: admin@fuzzilli.local / admin123" -echo "" -echo "To stop PostgreSQL:" -echo " docker-compose down" -echo "" -echo "To view logs:" -echo " docker-compose logs postgres"