/** * Pass-one verification basically means loading in a class file. The Java Virtual Machine * Specification is not too precise about what makes the difference between passes one and two. * The answer is that only pass one is performed on a class file as long as its resolution is not * requested; whereas pass two and pass three are performed during the resolution process. Only * four constraints to be checked are explicitely stated by The Java Virtual Machine * Specification, 2nd edition: * * <UL> * <LI>The first four bytes must contain the right magic number (0xCAFEBABE). * <LI>All recognized attributes must be of the proper length. * <LI>The class file must not be truncated or have extra bytes at the end. * <LI>The constant pool must not contain any superficially unrecognizable information. * </UL> * * A more in-depth documentation of what pass one should do was written by <A * HREF=mailto:[email protected]>Philip W. L. Fong</A>: * * <UL> * <LI>the file should not be truncated. * <LI>the file should not have extra bytes at the end. * <LI>all variable-length structures should be well-formatted: * <UL> * <LI>there should only be constant_pool_count-1 many entries in the constant pool. * <LI>all constant pool entries should have size the same as indicated by their type tag. * <LI>there are exactly interfaces_count many entries in the interfaces array of the * class file. * <LI>there are exactly fields_count many entries in the fields array of the class file. * <LI>there are exactly methods_count many entries in the methods array of the class * file. * <LI>there are exactly attributes_count many entries in the attributes array of the * class file, fields, methods, and code attribute. * <LI>there should be exactly attribute_length many bytes in each attribute. * Inconsistency between attribute_length and the actually size of the attribute * content should be uncovered. For example, in an Exceptions attribute, the actual * number of exceptions as required by the number_of_exceptions field might yeild an * attribute size that doesn't match the attribute_length. Such an anomaly should be * detected. * <LI>all attributes should have proper length. In particular, under certain context * (e.g. while parsing method_info), recognizable attributes (e.g. "Code" attribute) * should have correct format (e.g. attribute_length is 2). * </UL> * <LI>Also, certain constant values are checked for validity: * <UL> * <LI>The magic number should be 0xCAFEBABE. * <LI>The major and minor version numbers are valid. * <LI>All the constant pool type tags are recognizable. * <LI>All undocumented access flags are masked off before use. Strictly speaking, this is * not really a check. * <LI>The field this_class should point to a string that represents a legal non-array * class name, and this name should be the same as the class file being loaded. * <LI>the field super_class should point to a string that represents a legal non-array * class name. * <LI>Because some of the above checks require cross referencing the constant pool * entries, guards are set up to make sure that the referenced entries are of the * right type and the indices are within the legal range (0 < index < * constant_pool_count). * </UL> * <LI>Extra checks done in pass 1: * <UL> * <LI>the constant values of static fields should have the same type as the fields. * <LI>the number of words in a parameter list does not exceed 255 and locals_max. * <LI>the name and signature of fields and methods are verified to be of legal format. * </UL> * </UL> * * (From the Paper <A HREF=http://www.cs.sfu.ca/people/GradStudents/pwfong/personal/JVM/pass1/>The * Mysterious Pass One, first draft, September 2, 1997</A>.) </BR> However, most of this is done * by parsing a class file or generating a class file into BCEL's internal data structure. * <B>Therefore, all that is really done here is look up the class file from BCEL's * repository.</B> This is also motivated by the fact that some omitted things (like the check for * extra bytes at the end of the class file) are handy when actually using BCEL to repair a class * file (otherwise you would not be able to load it into BCEL). * * @see org.aspectj.apache.bcel.Repository */ public VerificationResult do_verify() { JavaClass jc; try { jc = getJavaClass(); // loads in the class file if not already done. if (jc != null) { /* If we find more constraints to check, we should do this in an own method. */ if (!myOwner.getClassName().equals(jc.getClassName())) { // This should maybe caught by BCEL: In case of renamed .class files we get wrong // JavaClass objects here. throw new LoadingException( "Wrong name: the internal name of the .class file '" + jc.getClassName() + "' does not match the file's name '" + myOwner.getClassName() + "'."); } } } catch (LoadingException e) { return new VerificationResult(VerificationResult.VERIFIED_REJECTED, e.getMessage()); } catch (ClassFormatError e) { // BCEL sometimes is a little harsh describing exceptual situations. return new VerificationResult(VerificationResult.VERIFIED_REJECTED, e.getMessage()); } catch (RuntimeException e) { // BCEL does not catch every possible RuntimeException; e.g. if // a constant pool index is referenced that does not exist. return new VerificationResult( VerificationResult.VERIFIED_REJECTED, "Parsing via BCEL did not succeed. " + e.getClass().getName() + " occured:\n" + Utility.getStackTrace(e)); } if (jc != null) { return VerificationResult.VR_OK; } else { // TODO: Maybe change Repository's behaviour to throw a LoadingException instead of just // returning "null" // if a class file cannot be found or in another way be looked up. return new VerificationResult( VerificationResult.VERIFIED_REJECTED, "Repository.lookup() failed. FILE NOT FOUND?"); } }
/** * Pass 3b implements the data flow analysis as described in the Java Virtual Machine * Specification, Second Edition. Later versions will use LocalVariablesInfo objects to verify if * the verifier-inferred types and the class file's debug information (LocalVariables attributes) * match [TODO]. * * @see org.aspectj.apache.bcel.verifier.statics.LocalVariablesInfo * @see org.aspectj.apache.bcel.verifier.statics.Pass2Verifier#getLocalVariablesInfo(int) */ public VerificationResult do_verify() { if (!myOwner.doPass3a(method_no).equals(VerificationResult.VR_OK)) { return VerificationResult.VR_NOTYET; } // Pass 3a ran before, so it's safe to assume the JavaClass object is // in the BCEL repository. JavaClass jc = Repository.lookupClass(myOwner.getClassName()); ConstantPoolGen constantPoolGen = new ConstantPoolGen(jc.getConstantPool()); // Init Visitors InstConstraintVisitor icv = new InstConstraintVisitor(); icv.setConstantPoolGen(constantPoolGen); ExecutionVisitor ev = new ExecutionVisitor(); ev.setConstantPoolGen(constantPoolGen); Method[] methods = jc.getMethods(); // Method no "method_no" exists, we ran Pass3a before on it! try { MethodGen mg = new MethodGen(methods[method_no], myOwner.getClassName(), constantPoolGen); icv.setMethodGen(mg); ////////////// DFA BEGINS HERE //////////////// if (!(mg.isAbstract() || mg.isNative())) { // IF mg HAS CODE (See pass 2) ControlFlowGraph cfg = new ControlFlowGraph(mg); // Build the initial frame situation for this method. Frame f = new Frame(mg.getMaxLocals(), mg.getMaxStack()); if (!mg.isStatic()) { if (mg.getName().equals(Constants.CONSTRUCTOR_NAME)) { Frame._this = new UninitializedObjectType(new ObjectType(jc.getClassName())); f.getLocals().set(0, Frame._this); } else { Frame._this = null; f.getLocals().set(0, new ObjectType(jc.getClassName())); } } Type[] argtypes = mg.getArgumentTypes(); int twoslotoffset = 0; for (int j = 0; j < argtypes.length; j++) { if (argtypes[j] == Type.SHORT || argtypes[j] == Type.BYTE || argtypes[j] == Type.CHAR || argtypes[j] == Type.BOOLEAN) { argtypes[j] = Type.INT; } f.getLocals().set(twoslotoffset + j + (mg.isStatic() ? 0 : 1), argtypes[j]); if (argtypes[j].getSize() == 2) { twoslotoffset++; f.getLocals().set(twoslotoffset + j + (mg.isStatic() ? 0 : 1), Type.UNKNOWN); } } circulationPump(cfg, cfg.contextOf(mg.getInstructionList().getStart()), f, icv, ev); } } catch (VerifierConstraintViolatedException ce) { ce.extendMessage("Constraint violated in method '" + methods[method_no] + "':\n", ""); return new VerificationResult(VerificationResult.VERIFIED_REJECTED, ce.getMessage()); } catch (RuntimeException re) { // These are internal errors StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); re.printStackTrace(pw); throw new AssertionViolatedException( "Some RuntimeException occured while verify()ing class '" + jc.getClassName() + "', method '" + methods[method_no] + "'. Original RuntimeException's stack trace:\n---\n" + sw + "---\n"); } return VerificationResult.VR_OK; }
/** * Used to load in and return the myOwner-matching JavaClass object when needed. Avoids loading in * a class file when it's not really needed! */ private JavaClass getJavaClass() { if (jc == null) { jc = Repository.lookupClass(myOwner.getClassName()); } return jc; }