📄 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.*;
/**
* 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 2005/12/16 14:11:30 andos 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.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -