📄 pass3averifier.java
字号:
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 implicitly 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()){
throw new StaticCodeInstructionOperandConstraintException("Due to JustIce's clear definition of subroutines, no JSR or JSR_W may have a top-level instruction (such as the very first instruction, which is targeted by instruction '"+ih+"' as its target.");
}
if (!(target.getInstruction() instanceof ASTORE)){
throw new StaticCodeInstructionOperandConstraintException("Due to JustIce's clear definition of subroutines, no JSR or JSR_W may target anything else than an ASTORE instruction. Instruction '"+ih+"' targets '"+target+"'.");
}
}
// vmspec2, page 134-137
ih.accept(v);
ih = ih.getNext();
}
}
/** A small utility method returning if a given int i is in the given int[] ints. */
private static boolean contains(int[] ints, int i){
for (int j=0; j<ints.length; j++){
if (ints[j]==i) return true;
}
return false;
}
/** Returns the method number as supplied when instantiating. */
public int getMethodNo(){
return method_no;
}
/**
* This visitor class does the actual checking for the instruction
* operand's constraints.
*/
private class InstOperandConstraintVisitor extends org.apache.bcel.generic.EmptyVisitor{
/** The ConstantPoolGen instance this Visitor operates on. */
private ConstantPoolGen cpg;
/** The only Constructor. */
InstOperandConstraintVisitor(ConstantPoolGen cpg){
this.cpg = cpg;
}
/**
* Utility method to return the max_locals value of the method verified
* by the surrounding Pass3aVerifier instance.
*/
private int max_locals(){
return Repository.lookupClass(myOwner.getClassName()).getMethods()[method_no].getCode().getMaxLocals();
}
/**
* A utility method to always raise an exeption.
*/
private void constraintViolated(Instruction i, String message) {
throw new StaticCodeInstructionOperandConstraintException("Instruction "+i+" constraint violated: "+message);
}
/**
* A utility method to raise an exception if the index is not
* a valid constant pool index.
*/
private void indexValid(Instruction i, int idx){
if (idx < 0 || idx >= cpg.getSize()){
constraintViolated(i, "Illegal constant pool index '"+idx+"'.");
}
}
///////////////////////////////////////////////////////////
// The Java Virtual Machine Specification, pages 134-137 //
///////////////////////////////////////////////////////////
/**
* Assures the generic preconditions of a LoadClass instance.
* The referenced class is loaded and pass2-verified.
*/
public void visitLoadClass(LoadClass o){
ObjectType t = o.getLoadClassType(cpg);
if (t != null){// null means "no class is loaded"
Verifier v = VerifierFactory.getVerifier(t.getClassName());
VerificationResult vr = v.doPass1();
if (vr.getStatus() != VerificationResult.VERIFIED_OK){
constraintViolated((Instruction) o, "Class '"+o.getLoadClassType(cpg).getClassName()+"' is referenced, but cannot be loaded: '"+vr+"'.");
}
}
}
// The target of each jump and branch instruction [...] must be the opcode [...]
// BCEL _DOES_ handle this.
// tableswitch: BCEL will do it, supposedly.
// lookupswitch: BCEL will do it, supposedly.
/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
// LDC and LDC_W (LDC_W is a subclass of LDC in BCEL's model)
public void visitLDC(LDC o){
indexValid(o, o.getIndex());
Constant c = cpg.getConstant(o.getIndex());
if (! ( (c instanceof ConstantInteger) ||
(c instanceof ConstantFloat) ||
(c instanceof ConstantString) ) ){
constraintViolated(o, "Operand of LDC or LDC_W must be one of CONSTANT_Integer, CONSTANT_Float or CONSTANT_String, but is '"+c+"'.");
}
}
/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
// LDC2_W
public void visitLDC2_W(LDC2_W o){
indexValid(o, o.getIndex());
Constant c = cpg.getConstant(o.getIndex());
if (! ( (c instanceof ConstantLong) ||
(c instanceof ConstantDouble) ) ){
constraintViolated(o, "Operand of LDC2_W must be CONSTANT_Long or CONSTANT_Double, but is '"+c+"'.");
}
try{
indexValid(o, o.getIndex()+1);
}
catch(StaticCodeInstructionOperandConstraintException e){
throw new AssertionViolatedException("OOPS: Does not BCEL handle that? LDC2_W operand has a problem.");
}
}
/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
//getfield, putfield, getstatic, putstatic
public void visitFieldInstruction(FieldInstruction o){
indexValid(o, o.getIndex());
Constant c = cpg.getConstant(o.getIndex());
if (! (c instanceof ConstantFieldref)){
constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '"+c+"'.");
}
String field_name = o.getFieldName(cpg);
JavaClass jc = Repository.lookupClass(o.getClassType(cpg).getClassName());
Field[] fields = jc.getFields();
Field f = null;
for (int i=0; i<fields.length; i++){
if (fields[i].getName().equals(field_name)){
f = fields[i];
break;
}
}
if (f == null){
/* TODO: also look up if the field is inherited! */
constraintViolated(o, "Referenced field '"+field_name+"' does not exist in class '"+jc.getClassName()+"'.");
}
else{
/* TODO: Check if assignment compatibility is sufficient.
What does Sun do? */
Type f_type = Type.getType(f.getSignature());
Type o_type = o.getType(cpg);
/* TODO: Is there a way to make BCEL tell us if a field
has a void method's signature, i.e. "()I" instead of "I"? */
if (! f_type.equals(o_type)){
constraintViolated(o, "Referenced field '"+field_name+"' has type '"+f_type+"' instead of '"+o_type+"' as expected.");
}
/* TODO: Check for access modifiers here. */
}
}
/** Checks if the constraints of operands of the said instruction(s) are satisfied. */
public void visitInvokeInstruction(InvokeInstruction o){
indexValid(o, o.getIndex());
if ( (o instanceof INVOKEVIRTUAL) ||
(o instanceof INVOKESPECIAL) ||
(o instanceof INVOKESTATIC) ){
Constant c = cpg.getConstant(o.getIndex());
if (! (c instanceof ConstantMethodref)){
constraintViolated(o, "Indexing a constant that's not a CONSTANT_Methodref but a '"+c+"'.");
}
else{
// Constants are okay due to pass2.
ConstantNameAndType cnat = (ConstantNameAndType) (cpg.getConstant(((ConstantMethodref) c).getNameAndTypeIndex()));
ConstantUtf8 cutf8 = (ConstantUtf8) (cpg.getConstant(cnat.getNameIndex()));
if (cutf8.getBytes().equals(Constants.CONSTRUCTOR_NAME) && (!(o instanceof INVOKESPECIAL)) ){
constraintViolated(o, "Only INVOKESPECIAL is allowed to invoke instance initialization methods.");
}
if ( (! (cutf8.getBytes().equals(Constants.CONSTRUCTOR_NAME)) ) && (cutf8.getBytes().startsWith("<")) ){
constraintViolated(o, "No method with a name beginning with '<' other than the instance initialization methods may be called by the method invocation instructions.");
}
}
}
else{ //if (o instanceof INVOKEINTERFACE){
Constant c = cpg.getConstant(o.getIndex());
if (! (c instanceof ConstantInterfaceMethodref)){
constraintViolated(o, "Indexing a constant that's not a CONSTANT_InterfaceMethodref but a '"+c+"'.");
}
// TODO: From time to time check if BCEL allows to detect if the
// 'count' operand is consistent with the information in the
// CONSTANT_InterfaceMethodref and if the last operand is zero.
// By now, BCEL hides those two operands because they're superfluous.
// Invoked method must not be <init> or <clinit>
ConstantNameAndType cnat = (ConstantNameAndType) (cpg.getConstant(((ConstantInterfaceMethodref)c).getNameAndTypeIndex()));
String name = ((ConstantUtf8) (cpg.getConstant(cnat.getNameIndex()))).getBytes();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -