privatevoid addExecutorsForArchitecture(Device device, Class<? extends Executor> optimizing, Class<? extends Executor> interpreter) { // NB: Currently OptimizingBackend MUST come immediately before same arch's Interpreter. // This is because intepreter execution relies on there being an OAT file already // created to produce correct debug information. Otherwise we will see // false-positive divergences. try { if (Options.useOptimizing) {
Constructor<? extends Executor> constructor =
optimizing.getConstructor(BaseListener.class, Device.class);
executors.add(constructor.newInstance(listener, device));
} if (Options.useInterpreter) {
Constructor<? extends Executor> constructor =
interpreter.getConstructor(BaseListener.class, Device.class);
executors.add(constructor.newInstance(listener, device));
}
} catch (NoSuchMethodException e) {
Log.errorAndQuit("Executor doesn't have correct constructor.");
} catch (InstantiationException e) {
Log.errorAndQuit("Executor couldn't be instantiated.");
} catch (IllegalAccessException e) {
Log.errorAndQuit("Executor couldn't be accessed.");
} catch (IllegalArgumentException e) {
Log.errorAndQuit("Invalid arguments to instantiation of Executor.");
} catch (InvocationTargetException e) {
Log.errorAndQuit("Instantiation of Executor threw an Exception!");
}
}
protectedvoid addExecutors() {
Device device = null; if (Options.executeOnHost) {
device = new Device();
} else {
device = new Device(Options.deviceName, Options.noBootImage);
}
if (Options.useArchArm64) {
addExecutorsForArchitecture(device, Arm64OptimizingBackendExecutor.class,
Arm64InterpreterExecutor.class);
}
if (Options.useArchArm) {
addExecutorsForArchitecture(device, ArmOptimizingBackendExecutor.class,
ArmInterpreterExecutor.class);
}
if (Options.useArchX86_64) {
addExecutorsForArchitecture(device, X86_64OptimizingBackendExecutor.class,
X86_64InterpreterExecutor.class);
}
if (Options.useArchX86) {
addExecutorsForArchitecture(device, X86OptimizingBackendExecutor.class,
X86InterpreterExecutor.class);
}
// Add the first backend as the golden executor for self-divergence tests.
goldenExecutor = executors.get(0);
}
/** *CalledfromeachFuzzersubclassthatwecaninstantiate.Parsestheprogram,fuzzesit, *andthensavesit,ifmutationwassuccessful.Wecanuse--skip-mutationtobypass *themutationphase,ifwewantedtoverifythatatestprogramitselfworks.
*/ protected Program fuzz() {
String inputFile = getNextInputFilename();
Program program = loadProgram(inputFile, null); if (program == null) {
Log.errorAndQuit("Problem loading seed file.");
} // Mutate the program. if (!Options.skipMutation) {
timerMutation.start();
program.mutateTheProgram();
if (!Options.skipHostVerify && !Options.executeOnHost) {
verified = goldenExecutor.verifyOnHost(programName); if (verified) {
listener.handleSuccessfulHostVerification();
}
}
if (verified) { boolean skipAnalysis = false;
for (Executor executor : executors) {
executor.reset();
executor.prepareProgramForExecution(programName);
executor.execute(programName); if (!executor.didTargetVerify()) {
listener.handleFailedTargetVerification();
skipAnalysis = true; break;
} // Results are saved in the executors until they reset, usually at the // next iteration.
}
if (!skipAnalysis) {
listener.handleSuccessfullyFuzzedFile(programName);
analyseResults(program, programName);
}
}
/** *Checksifthedifferentoutputsweobservedalignwithdifferentarchitectures.
*/ privateboolean checkForArchitectureSplit(Map<String, List<Executor>> outputMap) { if (outputMap.size() != 2) { // Cannot have a two-way split if we don't have 2 kinds of output. returnfalse;
}
Architecture[] architectures = new Architecture[2]; int archIdx = 0;
// For each kind of output we saw, make sure they all // came from the same architecture. for (List<Executor> executorList : outputMap.values()) {
architectures[archIdx] = executorList.get(0).getArchitecture(); for (int execIdx = 1; execIdx < executorList.size(); execIdx++) { if (executorList.get(execIdx).getArchitecture() != architectures[archIdx]) { // Not every executor with this output shared the same architecture. returnfalse;
}
}
archIdx++;
}
// Now make sure that the two outputs we saw were different architectures. if (architectures[0] == architectures[1]) { returnfalse;
} returntrue;
}
privateboolean checkGoldenExecutorForSelfDivergence(String programName) { // Run golden executor multiple times, make sure it always produces // the same output, otherwise report that it is self-divergent.
// TODO: Instead, produce a list of acceptable outputs, and see if the divergent // outputs of the backends fall within this set of outputs.
String seenOutput = null; for (int i = 0; i < Options.divergenceRetry + 1; i++) {
goldenExecutor.reset();
goldenExecutor.execute(programName);
String output = goldenExecutor.getResult().getFlattenedOutput(); if (seenOutput == null) {
seenOutput = output;
} elseif (!seenOutput.equals(output)) { returntrue;
}
} returnfalse;
}
privatevoid analyseResults(Program program, String programName) { // Check timeouts. // Construct two lists of executors, those who timed out, and those who did not. // Report if we had some timeouts.
List<Executor> timedOut = new ArrayList<Executor>();
List<Executor> didNotTimeOut = new ArrayList<Executor>(); for (Executor executor : executors) { if (executor.getResult().isTimeout()) {
timedOut.add(executor);
} else {
didNotTimeOut.add(executor);
}
} if (!timedOut.isEmpty()) {
listener.handleTimeouts(timedOut, didNotTimeOut); // Do not bother reporting divergence information. return;
}
// Check divergences. // Construct a map {output1: [executor that produced output1, ...], output2: [...]} // If the map has more than one output, we had divergence, report it.
Map<String, List<Executor>> outputMap = new HashMap<String, List<Executor>>(); for (Executor executor : executors) {
String output = executor.getResult().getFlattenedOutput(); if (Options.dumpOutput) {
listener.handleDumpOutput(
executor.getResult().getFlattenedOutputWithNewlines(), executor);
} if (outputMap.containsKey(output)) {
outputMap.get(output).add(executor);
} else {
List<Executor> newList = new ArrayList<Executor>();
newList.add(executor);
outputMap.put(output, newList);
}
}
if (outputMap.size() > 1) { // Report that we had divergence.
listener.handleDivergences(outputMap);
listener.handleMutations(program.getMutations()); // If we found divergences, try running the "golden executor" // a few times in succession, to see if the output it produces is different // from run to run. If so, then we're probably executing something with either: // a) randomness // b) timing-dependent code // c) threads // So we will not consider it a "true" divergence, but still useful? if (checkGoldenExecutorForSelfDivergence(programName)) {
listener.handleSelfDivergence(); return;
} // If we found divergences, try checking if the differences // in outputs align with differences in architectures. // For example, if we have: {Output1: [ARM, ARM], Output2: [ARM64, ARM64]} if (checkForArchitectureSplit(outputMap)) {
listener.handleArchitectureSplit();
}
} else { // No problems with execution.
listener.handleSuccess(outputMap);
}
}
private Program loadProgram(String inputName, List<Mutation> mutations) {
Program program = null; try {
DexRandomAccessFile input = new DexRandomAccessFile(inputName, "r");
offsetTracker = new OffsetTracker();
input.setOffsetTracker(offsetTracker); // Read the raw DexFile
RawDexFile rawDexFile = new RawDexFile();
timerDexInput.start();
rawDexFile.read(input);
timerDexInput.stop();
input.close(); // Create the program view.
timerProgGen.start();
program = new Program(rawDexFile, mutations, listener);
timerProgGen.stop();
} catch (FileNotFoundException e) {
Log.errorAndQuit("Couldn't open a file called " + inputName);
} catch (IOException e) {
Log.errorAndQuit("IOException when trying to load a DEX test file!");
} return program;
}
try { // Write out the results of mutation.
DexRandomAccessFile output = new DexRandomAccessFile(outputName, "rw");
output.setOffsetTracker(offsetTracker); // Delete the contents of the file, in case it already existed.
output.setLength(0); // Write out the file.
timerDexOutput.start();
program.writeRawDexFile(output);
timerDexOutput.stop(); // Recalculate the header info.
timerChecksumCalc.start();
program.updateRawDexFileHeader(output);
timerChecksumCalc.stop();
output.close();
success = true;
} catch (FileNotFoundException e) {
Log.errorAndQuit("Couldn't open a file called " + outputName);
} catch (IOException e) {
Log.errorAndQuit("IOException when trying to save a DEX test file!");
} return success;
}
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.7Bemerkung:
(vorverarbeitet am 2026-06-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.