private boolean readClass(String clazz) throws IOException { ClassReader cr = new ClassReader(new FileInputStream(clazz)); ClassNode ca = new ClassNode() { public void visitEnd() { // accept(cv); } }; cr.accept(new CheckClassAdapter(ca), true); boolean failed = false; List methods = ca.methods; for (int i = 0; i < methods.size(); ++i) { MethodNode method = (MethodNode) methods.get(i); if (method.instructions.size() > 0) { Analyzer a = new Analyzer(new SimpleVerifier()); try { a.analyze(ca.name, method); continue; } catch (Exception e) { e.printStackTrace(); } final Frame[] frames = a.getFrames(); if (!failed) { failed = true; log("verifying of class " + clazz + " failed"); } if (verbose) log(method.name + method.desc); TraceMethodVisitor cv = new TraceMethodVisitor(null) { public void visitMaxs(int maxStack, int maxLocals) { StringBuffer buffer = new StringBuffer(); for (int i = 0; i < text.size(); ++i) { String s = frames[i] == null ? "null" : frames[i].toString(); while (s.length() < maxStack + maxLocals + 1) { s += " "; } buffer.append(Integer.toString(i + 100000).substring(1)); buffer.append(" "); buffer.append(s); buffer.append(" : "); buffer.append(text.get(i)); } if (verbose) log(buffer.toString()); } }; for (int j = 0; j < method.instructions.size(); ++j) { Object insn = method.instructions.get(j); if (insn instanceof AbstractInsnNode) { ((AbstractInsnNode) insn).accept(cv); } else { cv.visitLabel((Label) insn); } } cv.visitMaxs(method.maxStack, method.maxLocals); } } return !failed; }
@Override public void checkCall( @NonNull ClassContext context, @NonNull ClassNode classNode, @NonNull MethodNode method, @NonNull MethodInsnNode call) { String owner = call.owner; String desc = call.desc; if (owner.equals(OWNER_SECURE_RANDOM)) { if (desc.startsWith(LONG_ARG)) { checkValidSetSeed(context, call); } else if (desc.startsWith("([B)")) { // $NON-NLS-1$ // setSeed(byte[]) ... // We could do some flow analysis here to see whether the byte array getting // passed in appears to be fixed. // However, people calling this constructor rather than the simpler one // with a fixed integer are probably less likely to make that mistake... right? } } else if (owner.equals(OWNER_RANDOM) && desc.startsWith(LONG_ARG)) { // Called setSeed(long) on an instanceof a Random object. Flag this if the instance // is likely a SecureRandom. // Track allocations such that we know whether the type of the call // is on a SecureRandom rather than a Random Analyzer analyzer = new Analyzer( new BasicInterpreter() { @Override public BasicValue newValue(Type type) { if (type != null && type.getDescriptor().equals(VM_SECURE_RANDOM)) { return new BasicValue(type); } return super.newValue(type); } }); try { Frame[] frames = analyzer.analyze(classNode.name, method); InsnList instructions = method.instructions; Frame frame = frames[instructions.indexOf(call)]; int stackSlot = frame.getStackSize(); for (Type type : Type.getArgumentTypes(desc)) { stackSlot -= type.getSize(); } BasicValue stackValue = (BasicValue) frame.getStack(stackSlot); Type type = stackValue.getType(); if (type != null && type.getDescriptor().equals(VM_SECURE_RANDOM)) { checkValidSetSeed(context, call); } } catch (AnalyzerException e) { context.log(e, null); } } else if (owner.equals(OWNER_RANDOM) && desc.startsWith(LONG_ARG)) { // Called setSeed(long) on an instanceof a Random object. Flag this if the instance // is likely a SecureRandom. // TODO } }
public void visitEnd() { MethodNode mn = (MethodNode) mv; Analyzer<BasicValue> a = new Analyzer<BasicValue>(new BasicVerifier()); try { a.analyze(owner, mn); } catch (AnalyzerException e) { throw new RuntimeException(e.getMessage()); } mn.accept(next); }