import java.io.File; import java.io.IOException; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Random; import java.util.Scanner; /* a stub for your team's fuzzer */ public class Fuzzer { // Blah - Test 2 private static final String OUTPUT_FILE = "fuzz.txt"; // The percentage of outputs that will start with a full stack private static final int STACK_FULL_PERCENTAGE = 10; // The percentage of inputs that will be incorrect private static final int INPUT_ERROR_PERCENTAGE = 5; // The percentage of instructions that will be incorrect private static final int LINE_ERROR_PERCENTAGE = 2; // Limit on generation of inputs private static final int NUMBER_TO_GENERATE = 10; // Instruction number range for each input private static final int INSTRUCTION_MIN = 0; private static final int INSTRUCTION_MAX = 1000; // Maximum variable name length private static final int VAR_NAME_LENGTH_MAX = 1000; private static final int VAR_ASCII_MIN = 33; private static final int VAR_ASCII_MAX = 127; // Variable range private static final int VAR_MIN = -2147483648; private static final int VAR_MAX = 2147483647; // Maximum number of items in stack private static final int MAX_STACK_SIZE = 512; // Random number generator private static Random rand = new Random(System.currentTimeMillis()); private static ArrayList<String> vars = new ArrayList<>(); // Instruction Count Array private static int[] counts = {0,0,0,0,0,0,0,0,0,0,0,0}; // Instruction Added Probability Array private static int[] addProb = {0,0,0,0,0,0,0,0,0,0,0,0}; // Pathway Prob Map, E.g. key = [PUSH, POP, STORE] - value = 2, this value is added to base prob private static HashMap<List<Instruction>, Integer> pathwayProb = new HashMap<List<Instruction>, Integer>(); // Current stack of instructions private static ArrayList<Instruction> instructionStack = new ArrayList<Instruction>(); // Max stack of instructions before resetting stack private static final int MAX_STACK = 2; public static void main(String[] args) throws IOException { System.out.println(Instruction.getBNF()); FileOutputStream out = null; PrintWriter pw = null; int runCount = getRun(); try { out = new FileOutputStream(OUTPUT_FILE); pw = new PrintWriter(out); /* We just print one instruction. Hint: you might want to make use of the instruction grammar which is effectively encoded in Instruction.java */ // pw.print(getStaticTests()); pw.println(generateRunInputs(runCount)); }catch (Exception e){ e.printStackTrace(System.err); System.exit(1); }finally{ if (pw != null){ pw.flush(); } if (out != null){ out.close(); } } writeRun(runCount); } private static int getRun(){ int runCount = 0; try { Scanner input = new Scanner(new File("runCount.txt")); runCount = input.nextInt() + 1; } catch (Exception e){ // Carry on } return runCount; } private static void writeRun(int runCount) throws IOException { FileOutputStream out = null; PrintWriter pw = null; try { out = new FileOutputStream("runCount.txt"); pw = new PrintWriter(out); pw.print(runCount); }catch (Exception e){ e.printStackTrace(System.err); System.exit(1); }finally{ if (pw != null){ pw.flush(); } if (out != null){ out.close(); } } } private static String generateRunInputs(int runCount){ switch (runCount){ case 0: // Test with stack full return generateInput(true, INSTRUCTION_MAX, MAX_STACK_SIZE, true, false); case 1: // Test with stack full return generateInput(true, INSTRUCTION_MAX, MAX_STACK_SIZE - 1, true, false); case 3: // Run static tests and empty stack return getStaticTests() + generateInput(true, INSTRUCTION_MAX, 0, true, false); case 4: // Test with dynamic probability return generateInput(true, INSTRUCTION_MAX, 0, true, false); case 5: // Test with long var names return generateInput(true, INSTRUCTION_MAX, 0, true, true); } // Run from random stack return generateInput(true, INSTRUCTION_MAX, 0, true, false); } /* * Given an instruction, adds the instruction count to the global array * and adds probability to the other instructions */ private static void addCountProb(Instruction instruction) { int index = instruction.ordinal(); for (int i = 0; i < 12; i++) { if (i != index) { counts[i] += 1; addProb[i] += Instruction.values()[i].getProbability(); } } } /*** * Generates a list of different inputs for the program * * @return list of input strings */ private static String generateMultipleInputs(){ // Initiate variables StringBuilder result = new StringBuilder(); // For debugging purposes int generated = 0; // Loop to create list of inputs while (generated < NUMBER_TO_GENERATE) { // Determine if the line will be correct or not boolean stackFull = rand.nextInt(100) < STACK_FULL_PERCENTAGE; result.append(generateInput(false, INSTRUCTION_MAX, MAX_STACK_SIZE, false, false)); // Increment generated generated += 1; } return result.toString(); } private static void incrementStackAndCount(Instruction newInstruction) { // Instruction values Instruction allInstructions[] = Instruction.values(); // Add base prob to stack with every other instruction for (Instruction inst: allInstructions) { if (!inst.equals(newInstruction)) { List<Instruction> tempStack = new ArrayList<Instruction>(instructionStack); tempStack.add(inst); if (pathwayProb.containsKey(tempStack)) { int oldProb = pathwayProb.get(tempStack); pathwayProb.replace(tempStack, oldProb + inst.getProbability()); } else { pathwayProb.put(tempStack, inst.getProbability()); } } } // Add instruction to stack instructionStack.add(newInstruction); // Check if size is bigger than max stack, then reset stack if (instructionStack.size() >= MAX_STACK) { instructionStack.clear(); } System.out.println("stack: " + Arrays.toString(instructionStack.toArray())); for (List<Instruction> list: pathwayProb.keySet()) { String key = list.toString(); String value = pathwayProb.get(list).toString(); System.out.println("map: " + key + " " + value); } } /*** * Generates a line of input for the program * * @param correct flag, which determines if the line will be correctly formulated * @param numInstructions for the line * @return the concatenated input for the program as a string */ private static String generateInput(boolean correct, long numInstructions, int stackPreload, boolean dynamicProb, boolean longVarNames){ int stackSize = 0; int counter = 0; StringBuilder result = new StringBuilder(); for ( int i = 0 ; i < stackPreload ; i++ ) { result.append(completeInstruction(true, Instruction.PUSH, false)); stackSize++; } if (correct) { while (counter < numInstructions) { Instruction newInstr = Instruction.getRandomInstruction(stackSize, instructionStack, pathwayProb); stackSize = stackSize + newInstr.getStackChange(); result.append(completeInstruction(true, newInstr, false)); if (dynamicProb) { incrementStackAndCount(newInstr); } counter += 1; } } else { while (counter < numInstructions) { Instruction newInstr; if (rand.nextInt(100) < LINE_ERROR_PERCENTAGE){ newInstr = Instruction.getRandomInstruction(2, instructionStack, pathwayProb); result.append(completeInstruction(false, newInstr, longVarNames)); if (dynamicProb) { incrementStackAndCount(newInstr); } } else { newInstr = Instruction.getRandomInstruction(stackSize, instructionStack, pathwayProb); result.append(completeInstruction(true, newInstr, false)); if (dynamicProb) { incrementStackAndCount(newInstr); } } stackSize = stackSize + newInstr.getStackChange(); if (stackSize < 0){ stackSize = 0; } counter += 1; } } return result.toString(); } /*** * Adds parameters to an individual instruction * * @param correct flag * @param instruction type * @return string with parameter */ private static String completeInstruction(boolean correct, Instruction instruction, boolean longVarNames){ String name = ""; switch (instruction) { case PUSH: if (correct) { // If correct, generate as usual name = " " + ((Integer) randomRange(VAR_MIN, VAR_MAX)).toString(); } else { // If incorrect, increase the range to outside +- int31_t name = " " + ((Long) (randomRange(-1,1)*((long) VAR_MAX + (long) randomRange(0, VAR_MAX)))).toString(); } break; case LOAD: case REM: // If not correct, make up a name not in the list if (!correct){ name = generateName(longVarNames); } // If no variables, return empty string else if (vars.size() == 0){ return ""; } // Otherwise, get a variable name from the list else { int index = rand.nextInt(vars.size()); name = " " + vars.get(index); vars.remove(index); } break; case STORE: // If not correct, make up a name not in the list if (correct || vars.size() == 0){ // Make up a name name = generateName(longVarNames); vars.add(name); } // If no variables, return empty string else { name = vars.get(randomRange(0,vars.size()-1)); } name = " " + name; break; case SAVE: name = " " + generateName(longVarNames) + ".txt"; break; case PLUS: case SUB: case MULT: case DIV: case POP: case LIST: case PRINT: break; } return instruction.getOpcode() + name + "\n"; } /*** * Generates a random name not longer than VAR_NAME_LENGTH_MAX * Characters are bounded by ASCII indexes at VAR_ASCII_MIN and VAR_ASCII_MAX * * @return the name generated */ private static String generateName(boolean longVarName){ StringBuilder name = new StringBuilder(); int counter = 0; int length; if (longVarName){ length = VAR_NAME_LENGTH_MAX + 100; } else { // Randomise the length of the string length = rand.nextInt(VAR_NAME_LENGTH_MAX); } // Add random bounded characters to string while (counter < length){ name.append((char) randomRange(VAR_ASCII_MIN, VAR_ASCII_MAX)); counter += 1; } return name.toString(); } private static int randomRange(int min, int max){ // Remove negatives and get around int limitations if (min == VAR_MIN){ return 2* (rand.nextInt(max) + (min/2)); } else if (min < 0){ max = max - min; return rand.nextInt(max) + min; } return min + rand.nextInt(max - min); } /*** * Get the desired static boundary tests * * @return a string containing all of the boundary tests' instructions */ private static String getStaticTests() { // Note. Some operations will crash dc if performed, so they cannot be tested // Examples include *, /, -, +, pop, store operations on empty stack StringBuilder result = new StringBuilder(); // Test opreations that need variables on no variables defined result.append(Instruction.LIST.getOpcode() + "\n"); result.append(Instruction.LOAD.getOpcode() + " VAR1\n"); result.append(Instruction.REM.getOpcode() + " VAR1\n"); // Test operations that use stack on empty stack result.append(Instruction.PRINT.getOpcode() + "\n"); // Prep for next tests result.append(Instruction.PUSH.getOpcode() + " 5\n"); result.append(Instruction.STORE.getOpcode() + " VAR1\n"); result.append(Instruction.PUSH.getOpcode() + " 10\n"); result.append(Instruction.STORE.getOpcode() + " VAR2\n"); result.append(Instruction.SAVE.getOpcode() + "\n"); // Test incorrect arguements for operation result.append(Instruction.LOAD.getOpcode() + " VAR3\n"); // Test Variables stored and used corectly result.append(Instruction.LIST.getOpcode() + "\n"); result.append(Instruction.PRINT.getOpcode() + "\n"); result.append(Instruction.LOAD.getOpcode() + " VAR2\n"); result.append(Instruction.LOAD.getOpcode() + " VAR1\n"); result.append(Instruction.PRINT.getOpcode() + "\n"); result.append(Instruction.POP.getOpcode() + "\n"); result.append(Instruction.PRINT.getOpcode() + "\n"); result.append(Instruction.REM.getOpcode() + " VAR2\n"); result.append(Instruction.LIST.getOpcode() + "\n"); result.append(Instruction.LOAD.getOpcode() + " VAR2\n"); result.append(Instruction.PRINT.getOpcode() + "\n"); result.append(Instruction.STORE.getOpcode() + " VAR1\n"); result.append(Instruction.LIST.getOpcode() + "\n"); result.append(Instruction.LOAD.getOpcode() + " VAR1\n"); result.append(Instruction.STORE.getOpcode() + " VAR1\n"); result.append(Instruction.LIST.getOpcode() + "\n"); result.append(Instruction.REM.getOpcode() + " VAR1\n"); // Test Plus operation result.append(Instruction.PUSH.getOpcode() + " "+ VAR_MAX + "\n"); result.append(Instruction.PUSH.getOpcode() + " "+ VAR_MIN + "\n"); result.append(Instruction.PLUS.getOpcode() + "\n"); result.append(Instruction.POP.getOpcode() + "\n"); result.append(Instruction.PUSH.getOpcode() + " "+ VAR_MAX + "\n"); result.append(Instruction.PUSH.getOpcode() + " 1\n"); result.append(Instruction.PLUS.getOpcode() + "\n"); result.append(Instruction.POP.getOpcode() + "\n"); // Test Subtract operation result.append(Instruction.PUSH.getOpcode() + " "+ VAR_MAX + "\n"); result.append(Instruction.PUSH.getOpcode() + " "+ VAR_MAX + "\n"); result.append(Instruction.SUB.getOpcode() + "\n"); result.append(Instruction.POP.getOpcode() + "\n"); result.append(Instruction.PUSH.getOpcode() + " 1\n"); result.append(Instruction.PUSH.getOpcode() + " "+ VAR_MIN + "\n"); result.append(Instruction.SUB.getOpcode() + "\n"); result.append(Instruction.POP.getOpcode() + "\n"); // Test Divide operation result.append(Instruction.PUSH.getOpcode() + " "+ VAR_MAX + "\n"); result.append(Instruction.PUSH.getOpcode() + " "+ VAR_MAX + "\n"); result.append(Instruction.DIV.getOpcode() + "\n"); result.append(Instruction.POP.getOpcode() + "\n"); result.append(Instruction.PUSH.getOpcode() + " 1\n"); result.append(Instruction.PUSH.getOpcode() + " "+ VAR_MIN + "\n"); result.append(Instruction.DIV.getOpcode() + "\n"); result.append(Instruction.POP.getOpcode() + "\n"); // Test Multiply operation result.append(Instruction.PUSH.getOpcode() + " "+ VAR_MAX + "\n"); result.append(Instruction.PUSH.getOpcode() + " 2\n"); result.append(Instruction.MULT.getOpcode() + "\n"); result.append(Instruction.POP.getOpcode() + "\n"); result.append(Instruction.PUSH.getOpcode() + " 2\n"); result.append(Instruction.PUSH.getOpcode() + " "+ Math.floorDiv(VAR_MIN, 2) + "\n"); result.append(Instruction.MULT.getOpcode() + "\n"); result.append(Instruction.POP.getOpcode() + "\n"); return result.toString(); } }