📄 pass3averifier.java
字号:
package org.apache.bcel.verifier.statics;/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2001 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" and * "Apache BCEL" must not be used to endorse or promote products * derived from this software without prior written permission. For * written permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache", * "Apache BCEL", nor may "Apache" appear in their name, without * prior written permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. */import org.apache.bcel.*;import org.apache.bcel.generic.*;import org.apache.bcel.classfile.*;import org.apache.bcel.verifier.*;import org.apache.bcel.verifier.exc.*;import java.util.ArrayList;import java.util.HashMap;/** * This PassVerifier verifies a class file according to * pass 3, static part as described in The Java Virtual * Machine Specification, 2nd edition. * More detailed information is to be found at the do_verify() * method's documentation. * * @version $Id: Pass3aVerifier.java,v 1.1.1.1 2001/10/29 20:00:37 jvanzyl Exp $ * @author <A HREF="http://www.inf.fu-berlin.de/~ehaase"/>Enver Haase</A> * @see #do_verify() */public final class Pass3aVerifier extends PassVerifier{ /** The Verifier that created this. */ private Verifier myOwner; /** * The method number to verify. * This is the index in the array returned * by JavaClass.getMethods(). */ private int method_no; /** The one and only InstructionList object used by an instance of this class. It's here for performance reasons by do_verify() and its callees. */ InstructionList instructionList; /** The one and only Code object used by an instance of this class. It's here for performance reasons by do_verify() and its callees. */ Code code; /** Should only be instantiated by a Verifier. */ public Pass3aVerifier(Verifier owner, int method_no){ myOwner = owner; this.method_no = method_no; } /** * Pass 3a is the verification of static constraints of * JVM code (such as legal targets of branch instructions). * This is the part of pass 3 where you do not need data * flow analysis. * JustIce also delays the checks for a correct exception * table of a Code attribute and correct line number entries * in a LineNumberTable attribute of a Code attribute (which * conceptually belong to pass 2) to this pass. Also, most * of the check for valid local variable entries in a * LocalVariableTable attribute of a Code attribute is * delayed until this pass. * All these checks need access to the code array of the * Code attribute. * * @throws InvalidMethodException if the method to verify does not exist. */ public VerificationResult do_verify(){ if (myOwner.doPass2().equals(VerificationResult.VR_OK)){ // Okay, class file was loaded correctly by Pass 1 // and satisfies static constraints of Pass 2. JavaClass jc = Repository.lookupClass(myOwner.getClassName()); Method[] methods = jc.getMethods(); if (method_no >= methods.length){ throw new InvalidMethodException("METHOD DOES NOT EXIST!"); } Method method = methods[method_no]; code = method.getCode(); // No Code? Nothing to verify! if ( method.isAbstract() || method.isNative() ){ // IF mg HAS NO CODE (static constraint of Pass 2) return VerificationResult.VR_OK; } // TODO: // We want a very sophisticated code examination here with good explanations // on where to look for an illegal instruction or such. // Only after that we should try to build an InstructionList and throw an // AssertionViolatedException if after our examination InstructionList building // still fails. // That examination should be implemented in a byte-oriented way, i.e. look for // an instruction, make sure its validity, count its length, find the next // instruction and so on. try{ instructionList = new InstructionList(method.getCode().getCode()); } catch(RuntimeException re){ return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Bad bytecode in the code array of the Code attribute of method '"+method+"'."); } instructionList.setPositions(true); // Start verification. VerificationResult vr = VerificationResult.VR_OK; //default try{ delayedPass2Checks(); } catch(ClassConstraintException cce){ vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage()); return vr; } try{ pass3StaticInstructionChecks(); pass3StaticInstructionOperandsChecks(); } catch(StaticCodeConstraintException scce){ vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, scce.getMessage()); } return vr; } else{ //did not pass Pass 2. return VerificationResult.VR_NOTYET; } } /** * These are the checks that could be done in pass 2 but are delayed to pass 3 * for performance reasons. Also, these checks need access to the code array * of the Code attribute of a Method so it's okay to perform them here. * Also see the description of the do_verify() method. * * @throws ClassConstraintException if the verification fails. * @see #do_verify() */ private void delayedPass2Checks(){ int[] instructionPositions = instructionList.getInstructionPositions(); int codeLength = code.getCode().length; ///////////////////// // LineNumberTable // ///////////////////// LineNumberTable lnt = code.getLineNumberTable(); if (lnt != null){ LineNumber[] lineNumbers = lnt.getLineNumberTable(); IntList offsets = new IntList(); lineNumber_loop: for (int i=0; i < lineNumbers.length; i++){ // may appear in any order. for (int j=0; j < instructionPositions.length; j++){ // TODO: Make this a binary search! The instructionPositions array is naturally ordered! int offset = lineNumbers[i].getStartPC(); if (instructionPositions[j] == offset){ if (offsets.contains(offset)){ addMessage("LineNumberTable attribute '"+code.getLineNumberTable()+"' refers to the same code offset ('"+offset+"') more than once which is violating the semantics [but is sometimes produced by IBM's 'jikes' compiler]."); } else{ offsets.add(offset); } continue lineNumber_loop; } } throw new ClassConstraintException("Code attribute '"+code+"' has a LineNumberTable attribute '"+code.getLineNumberTable()+"' referring to a code offset ('"+lineNumbers[i].getStartPC()+"') that does not exist."); } } /////////////////////////// // LocalVariableTable(s) // /////////////////////////// /* We cannot use code.getLocalVariableTable() because there could be more than only one. This is a bug in BCEL. */ Attribute[] atts = code.getAttributes(); for (int a=0; a<atts.length; a++){ if (atts[a] instanceof LocalVariableTable){ LocalVariableTable lvt = (LocalVariableTable) atts[a]; if (lvt != null){ LocalVariable[] localVariables = lvt.getLocalVariableTable(); for (int i=0; i<localVariables.length; i++){ int startpc = localVariables[i].getStartPC(); int length = localVariables[i].getLength(); if (!contains(instructionPositions, startpc)){ throw new ClassConstraintException("Code attribute '"+code+"' has a LocalVariableTable attribute '"+code.getLocalVariableTable()+"' referring to a code offset ('"+startpc+"') that does not exist."); } if ( (!contains(instructionPositions, startpc+length)) && (startpc+length != codeLength) ){ throw new ClassConstraintException("Code attribute '"+code+"' has a LocalVariableTable attribute '"+code.getLocalVariableTable()+"' referring to a code offset start_pc+length ('"+(startpc+length)+"') that does not exist."); } } } } } //////////////////// // ExceptionTable // //////////////////// // In BCEL's "classfile" API, the startPC/endPC-notation is // inclusive/exclusive as in the Java Virtual Machine Specification. // WARNING: This is not true for BCEL's "generic" API. CodeException[] exceptionTable = code.getExceptionTable(); for (int i=0; i<exceptionTable.length; i++){ int startpc = exceptionTable[i].getStartPC(); int endpc = exceptionTable[i].getEndPC(); int handlerpc = exceptionTable[i].getHandlerPC(); if (startpc >= endpc){ throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has its start_pc ('"+startpc+"') not smaller than its end_pc ('"+endpc+"')."); } if (!contains(instructionPositions, startpc)){ throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has a non-existant bytecode offset as its start_pc ('"+startpc+"')."); } if ( (!contains(instructionPositions, endpc)) && (endpc != codeLength)){ throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has a non-existant bytecode offset as its end_pc ('"+startpc+"') [that is also not equal to code_length ('"+codeLength+"')]."); } if (!contains(instructionPositions, handlerpc)){ throw new ClassConstraintException("Code attribute '"+code+"' has an exception_table entry '"+exceptionTable[i]+"' that has a non-existant bytecode offset as its handler_pc ('"+handlerpc+"')."); } } } /** * These are the checks if constraints are satisfied which are described in the * Java Virtual Machine Specification, Second Edition as Static Constraints on * the instructions of Java Virtual Machine Code (chapter 4.8.1). * * @throws StaticCodeConstraintException if the verification fails. */ private void pass3StaticInstructionChecks(){ // Code array must not be empty: // Enforced in pass 2 (also stated in the static constraints of the Code // array in vmspec2), together with pass 1 (reading code_length bytes and // interpreting them as code[]). So this must not be checked again here. if (! (code.getCode().length < 65536)){// contradicts vmspec2 page 152 ("Limitations"), but is on page 134. throw new StaticCodeInstructionConstraintException("Code array in code attribute '"+code+"' too big: must be smaller than 65536 bytes."); } // First opcode at offset 0: okay, that's clear. Nothing to do. // Only instances of the instructions documented in Section 6.4 may appear in // the code array. // For BCEL's sake, we cannot handle WIDE stuff, but hopefully BCEL does its job right :) // The last byte of the last instruction in the code array must be the byte at index // code_length-1 : See the do_verify() comments. We actually don't iterate through the // byte array, but use an InstructionList so we cannot check for this. But BCEL does // things right, so it's implicitely okay. // TODO: Check how BCEL handles (and will handle) instructions like IMPDEP1, IMPDEP2, // BREAKPOINT... that BCEL knows about but which are illegal anyway. // We currently go the safe way here. InstructionHandle ih = instructionList.getStart(); while (ih != null){ Instruction i = ih.getInstruction(); if (i instanceof IMPDEP1){ throw new StaticCodeInstructionConstraintException("IMPDEP1 must not be in the code, it is an illegal instruction for _internal_ JVM use!"); } if (i instanceof IMPDEP2){ throw new StaticCodeInstructionConstraintException("IMPDEP2 must not be in the code, it is an illegal instruction for _internal_ JVM use!"); } if (i instanceof BREAKPOINT){ throw new StaticCodeInstructionConstraintException("BREAKPOINT must not be in the code, it is an illegal instruction for _internal_ JVM use!"); } ih = ih.getNext(); } // The original verifier seems to do this check here, too. // An unreachable last instruction may also not fall through the // end of the code, which is stupid -- but with the original // verifier's subroutine semantics one cannot predict reachability. Instruction last = instructionList.getEnd().getInstruction(); if (! ((last instanceof ReturnInstruction) || (last instanceof RET) || (last instanceof GotoInstruction) || (last instanceof ATHROW) )) // JSR / JSR_W would possibly RETurn and then fall off the code! throw new StaticCodeInstructionConstraintException("Execution must not fall off the bottom of the code array. This constraint is enforced statically as some existing verifiers do - so it may be a false alarm if the last instruction is not reachable."); } /** * These are the checks for the satisfaction of constraints which are described in the * Java Virtual Machine Specification, Second Edition as Static Constraints on * the operands of instructions of Java Virtual Machine Code (chapter 4.8.1). * BCEL parses the code array to create an InstructionList and therefore has to check * some of these constraints. Additional checks are also implemented here. * * @throws StaticCodeConstraintException if the verification fails. */ private void pass3StaticInstructionOperandsChecks(){ // When building up the InstructionList, BCEL has already done all those checks // mentioned in The Java Virtual Machine Specification, Second Edition, as // "static constraints on the operands of instructions in the code array". // TODO: see the do_verify() comments. Maybe we should really work on the // byte array first to give more comprehensive messages. // TODO: Review Exception API, possibly build in some "offending instruction" thing // when we're ready to insulate the offending instruction by doing the // above thing. // TODO: Implement as much as possible here. BCEL does _not_ check everything. ConstantPoolGen cpg = new ConstantPoolGen(Repository.lookupClass(myOwner.getClassName()).getConstantPool()); InstOperandConstraintVisitor v = new InstOperandConstraintVisitor(cpg); // Checks for the things BCEL does _not_ handle itself. InstructionHandle ih = instructionList.getStart(); while (ih != null){ Instruction i = ih.getInstruction(); // An "own" constraint, due to JustIce's new definition of what "subroutine" means. if (i instanceof JsrInstruction){ InstructionHandle target = ((JsrInstruction) i).getTarget(); if (target == instructionList.getStart()){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -