001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.bcel.verifier.statics; 018 019import java.util.Arrays; 020 021import org.apache.bcel.Const; 022import org.apache.bcel.Repository; 023import org.apache.bcel.classfile.Attribute; 024import org.apache.bcel.classfile.ClassFormatException; 025import org.apache.bcel.classfile.Code; 026import org.apache.bcel.classfile.CodeException; 027import org.apache.bcel.classfile.Constant; 028import org.apache.bcel.classfile.ConstantClass; 029import org.apache.bcel.classfile.ConstantDouble; 030import org.apache.bcel.classfile.ConstantFieldref; 031import org.apache.bcel.classfile.ConstantFloat; 032import org.apache.bcel.classfile.ConstantInteger; 033import org.apache.bcel.classfile.ConstantInterfaceMethodref; 034import org.apache.bcel.classfile.ConstantLong; 035import org.apache.bcel.classfile.ConstantMethodref; 036import org.apache.bcel.classfile.ConstantNameAndType; 037import org.apache.bcel.classfile.ConstantString; 038import org.apache.bcel.classfile.ConstantUtf8; 039import org.apache.bcel.classfile.Field; 040import org.apache.bcel.classfile.JavaClass; 041import org.apache.bcel.classfile.LineNumber; 042import org.apache.bcel.classfile.LineNumberTable; 043import org.apache.bcel.classfile.LocalVariableTable; 044import org.apache.bcel.classfile.Method; 045import org.apache.bcel.generic.ALOAD; 046import org.apache.bcel.generic.ANEWARRAY; 047import org.apache.bcel.generic.ASTORE; 048import org.apache.bcel.generic.ATHROW; 049import org.apache.bcel.generic.ArrayType; 050import org.apache.bcel.generic.BREAKPOINT; 051import org.apache.bcel.generic.CHECKCAST; 052import org.apache.bcel.generic.ConstantPoolGen; 053import org.apache.bcel.generic.DLOAD; 054import org.apache.bcel.generic.DSTORE; 055import org.apache.bcel.generic.FLOAD; 056import org.apache.bcel.generic.FSTORE; 057import org.apache.bcel.generic.FieldInstruction; 058import org.apache.bcel.generic.GETSTATIC; 059import org.apache.bcel.generic.GotoInstruction; 060import org.apache.bcel.generic.IINC; 061import org.apache.bcel.generic.ILOAD; 062import org.apache.bcel.generic.IMPDEP1; 063import org.apache.bcel.generic.IMPDEP2; 064import org.apache.bcel.generic.INSTANCEOF; 065import org.apache.bcel.generic.INVOKEDYNAMIC; 066import org.apache.bcel.generic.INVOKEINTERFACE; 067import org.apache.bcel.generic.INVOKESPECIAL; 068import org.apache.bcel.generic.INVOKESTATIC; 069import org.apache.bcel.generic.INVOKEVIRTUAL; 070import org.apache.bcel.generic.ISTORE; 071import org.apache.bcel.generic.Instruction; 072import org.apache.bcel.generic.InstructionHandle; 073import org.apache.bcel.generic.InstructionList; 074import org.apache.bcel.generic.InvokeInstruction; 075import org.apache.bcel.generic.JsrInstruction; 076import org.apache.bcel.generic.LDC; 077import org.apache.bcel.generic.LDC2_W; 078import org.apache.bcel.generic.LLOAD; 079import org.apache.bcel.generic.LOOKUPSWITCH; 080import org.apache.bcel.generic.LSTORE; 081import org.apache.bcel.generic.LoadClass; 082import org.apache.bcel.generic.MULTIANEWARRAY; 083import org.apache.bcel.generic.NEW; 084import org.apache.bcel.generic.NEWARRAY; 085import org.apache.bcel.generic.ObjectType; 086import org.apache.bcel.generic.PUTSTATIC; 087import org.apache.bcel.generic.RET; 088import org.apache.bcel.generic.ReferenceType; 089import org.apache.bcel.generic.ReturnInstruction; 090import org.apache.bcel.generic.TABLESWITCH; 091import org.apache.bcel.generic.Type; 092import org.apache.bcel.verifier.PassVerifier; 093import org.apache.bcel.verifier.VerificationResult; 094import org.apache.bcel.verifier.Verifier; 095import org.apache.bcel.verifier.VerifierFactory; 096import org.apache.bcel.verifier.exc.AssertionViolatedException; 097import org.apache.bcel.verifier.exc.ClassConstraintException; 098import org.apache.bcel.verifier.exc.InvalidMethodException; 099import org.apache.bcel.verifier.exc.StaticCodeConstraintException; 100import org.apache.bcel.verifier.exc.StaticCodeInstructionConstraintException; 101import org.apache.bcel.verifier.exc.StaticCodeInstructionOperandConstraintException; 102 103/** 104 * This PassVerifier verifies a class file according to pass 3, static part as described in The Java Virtual Machine 105 * Specification, 2nd edition. More detailed information is to be found at the do_verify() method's documentation. 106 * 107 * @see #do_verify() 108 */ 109public final class Pass3aVerifier extends PassVerifier { 110 111 /** 112 * This visitor class does the actual checking for the instruction operand's constraints. 113 */ 114 private class InstOperandConstraintVisitor extends org.apache.bcel.generic.EmptyVisitor { 115 /** The ConstantPoolGen instance this Visitor operates on. */ 116 private final ConstantPoolGen constantPoolGen; 117 118 /** The only Constructor. */ 119 InstOperandConstraintVisitor(final ConstantPoolGen constantPoolGen) { 120 this.constantPoolGen = constantPoolGen; 121 } 122 123 /** 124 * A utility method to always raise an exception. 125 */ 126 private void constraintViolated(final Instruction i, final String message) { 127 throw new StaticCodeInstructionOperandConstraintException("Instruction " + tostring(i) + " constraint violated: " + message); 128 } 129 130 /** 131 * Looks for the method referenced by the given invoke instruction in the given class. 132 * 133 * @param jc the class that defines the referenced method 134 * @param invoke the instruction that references the method 135 * @return the referenced method or null if not found. 136 */ 137 private Method getMethod(final JavaClass jc, final InvokeInstruction invoke) { 138 final Method[] ms = jc.getMethods(); 139 for (final Method element : ms) { 140 if (element.getName().equals(invoke.getMethodName(constantPoolGen)) 141 && Type.getReturnType(element.getSignature()).equals(invoke.getReturnType(constantPoolGen)) 142 && Arrays.equals(Type.getArgumentTypes(element.getSignature()), invoke.getArgumentTypes(constantPoolGen))) { 143 return element; 144 } 145 } 146 147 return null; 148 } 149 150 /** 151 * Looks for the method referenced by the given invoke instruction in the given class or its super classes and super 152 * interfaces. 153 * 154 * @param jc the class that defines the referenced method 155 * @param invoke the instruction that references the method 156 * @return the referenced method or null if not found. 157 */ 158 private Method getMethodRecursive(final JavaClass jc, final InvokeInstruction invoke) throws ClassNotFoundException { 159 Method m; 160 // look in the given class 161 m = getMethod(jc, invoke); 162 if (m != null) { 163 // method found in given class 164 return m; 165 } 166 // method not found, look in super classes 167 for (final JavaClass superclass : jc.getSuperClasses()) { 168 m = getMethod(superclass, invoke); 169 if (m != null) { 170 // method found in super class 171 return m; 172 } 173 } 174 // method not found, look in super interfaces 175 for (final JavaClass superclass : jc.getInterfaces()) { 176 m = getMethod(superclass, invoke); 177 if (m != null) { 178 // method found in super interface 179 return m; 180 } 181 } 182 // method not found in the hierarchy 183 return null; 184 } 185 186 private ObjectType getObjectType(final FieldInstruction o) { 187 final ReferenceType rt = o.getReferenceType(constantPoolGen); 188 if (rt instanceof ObjectType) { 189 return (ObjectType) rt; 190 } 191 constraintViolated(o, "expecting ObjectType but got " + rt); 192 return null; 193 } 194 195 // The target of each jump and branch instruction [...] must be the opcode [...] 196 // BCEL _DOES_ handle this. 197 198 // tableswitch: BCEL will do it, supposedly. 199 200 // lookupswitch: BCEL will do it, supposedly. 201 202 /** 203 * A utility method to raise an exception if the index is not a valid constant pool index. 204 */ 205 private void indexValid(final Instruction i, final int idx) { 206 if (idx < 0 || idx >= constantPoolGen.getSize()) { 207 constraintViolated(i, "Illegal constant pool index '" + idx + "'."); 208 } 209 } 210 211 /** 212 * Utility method to return the max_locals value of the method verified by the surrounding Pass3aVerifier instance. 213 */ 214 private int maxLocals() { 215 try { 216 return Repository.lookupClass(verifier.getClassName()).getMethods()[methodNo].getCode().getMaxLocals(); 217 } catch (final ClassNotFoundException e) { 218 // FIXME: maybe not the best way to handle this 219 throw new AssertionViolatedException("Missing class: " + e, e); 220 } 221 } 222 223 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 224 @Override 225 public void visitALOAD(final ALOAD o) { 226 final int idx = o.getIndex(); 227 if (idx < 0) { 228 constraintViolated(o, "Index '" + idx + "' must be non-negative."); 229 } else { 230 final int maxminus1 = maxLocals() - 1; 231 if (idx > maxminus1) { 232 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); 233 } 234 } 235 } 236 237 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 238 @Override 239 public void visitANEWARRAY(final ANEWARRAY o) { 240 indexValid(o, o.getIndex()); 241 final Constant c = constantPoolGen.getConstant(o.getIndex()); 242 if (!(c instanceof ConstantClass)) { 243 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'."); 244 } 245 final Type t = o.getType(constantPoolGen); 246 if (t instanceof ArrayType) { 247 final int dimensions = ((ArrayType) t).getDimensions(); 248 if (dimensions > Const.MAX_ARRAY_DIMENSIONS) { 249 constraintViolated(o, 250 "Not allowed to create an array with more than " + Const.MAX_ARRAY_DIMENSIONS + " dimensions;" + " actual: " + dimensions); 251 } 252 } 253 } 254 255 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 256 @Override 257 public void visitASTORE(final ASTORE o) { 258 final int idx = o.getIndex(); 259 if (idx < 0) { 260 constraintViolated(o, "Index '" + idx + "' must be non-negative."); 261 } else { 262 final int maxminus1 = maxLocals() - 1; 263 if (idx > maxminus1) { 264 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); 265 } 266 } 267 } 268 269 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 270 @Override 271 public void visitCHECKCAST(final CHECKCAST o) { 272 indexValid(o, o.getIndex()); 273 final Constant c = constantPoolGen.getConstant(o.getIndex()); 274 if (!(c instanceof ConstantClass)) { 275 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'."); 276 } 277 } 278 279 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 280 @Override 281 public void visitDLOAD(final DLOAD o) { 282 final int idx = o.getIndex(); 283 if (idx < 0) { 284 constraintViolated(o, "Index '" + idx + "' must be non-negative." 285 + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); 286 } else { 287 final int maxminus2 = maxLocals() - 2; 288 if (idx > maxminus2) { 289 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'."); 290 } 291 } 292 } 293 294 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 295 @Override 296 public void visitDSTORE(final DSTORE o) { 297 final int idx = o.getIndex(); 298 if (idx < 0) { 299 constraintViolated(o, "Index '" + idx + "' must be non-negative." 300 + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); 301 } else { 302 final int maxminus2 = maxLocals() - 2; 303 if (idx > maxminus2) { 304 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'."); 305 } 306 } 307 } 308 309 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 310 // getfield, putfield, getstatic, putstatic 311 @Override 312 public void visitFieldInstruction(final FieldInstruction o) { 313 try { 314 indexValid(o, o.getIndex()); 315 final Constant c = constantPoolGen.getConstant(o.getIndex()); 316 if (!(c instanceof ConstantFieldref)) { 317 constraintViolated(o, "Indexing a constant that's not a CONSTANT_Fieldref but a '" + tostring(c) + "'."); 318 } 319 320 final String fieldName = o.getFieldName(constantPoolGen); 321 322 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName()); 323 Field[] fields = jc.getFields(); 324 Field f = null; 325 for (final Field field : fields) { 326 if (field.getName().equals(fieldName)) { 327 final Type fType = Type.getType(field.getSignature()); 328 final Type oType = o.getType(constantPoolGen); 329 /* 330 * TODO: Check if assignment compatibility is sufficient. What does Sun do? 331 */ 332 if (fType.equals(oType)) { 333 f = field; 334 break; 335 } 336 } 337 } 338 if (f == null) { 339 final JavaClass[] superclasses = jc.getSuperClasses(); 340 outer: for (final JavaClass superclass : superclasses) { 341 fields = superclass.getFields(); 342 for (final Field field : fields) { 343 if (field.getName().equals(fieldName)) { 344 final Type fType = Type.getType(field.getSignature()); 345 final Type oType = o.getType(constantPoolGen); 346 if (fType.equals(oType)) { 347 f = field; 348 if ((f.getAccessFlags() & (Const.ACC_PUBLIC | Const.ACC_PROTECTED)) == 0) { 349 f = null; 350 } 351 break outer; 352 } 353 } 354 } 355 } 356 if (f == null) { 357 constraintViolated(o, "Referenced field '" + fieldName + "' does not exist in class '" + jc.getClassName() + "'."); 358 } 359 } else { 360 /* 361 * TODO: Check if assignment compatibility is sufficient. What does Sun do? 362 */ 363 Type.getType(f.getSignature()); 364 o.getType(constantPoolGen); 365// Type f_type = Type.getType(f.getSignature()); 366// Type o_type = o.getType(cpg); 367 368 // Argh. Sun's implementation allows us to have multiple fields of 369 // the same name but with a different signature. 370 // if (! f_type.equals(o_type)) { 371 // constraintViolated(o, 372 // "Referenced field '"+field_name+"' has type '"+f_type+"' instead of '"+o_type+"' as expected."); 373 // } 374 375 /* TODO: Check for access modifiers here. */ 376 } 377 } catch (final ClassNotFoundException e) { 378 // FIXME: maybe not the best way to handle this 379 throw new AssertionViolatedException("Missing class: " + e, e); 380 } 381 } 382 383 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 384 @Override 385 public void visitFLOAD(final FLOAD o) { 386 final int idx = o.getIndex(); 387 if (idx < 0) { 388 constraintViolated(o, "Index '" + idx + "' must be non-negative."); 389 } else { 390 final int maxminus1 = maxLocals() - 1; 391 if (idx > maxminus1) { 392 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); 393 } 394 } 395 } 396 397 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 398 @Override 399 public void visitFSTORE(final FSTORE o) { 400 final int idx = o.getIndex(); 401 if (idx < 0) { 402 constraintViolated(o, "Index '" + idx + "' must be non-negative."); 403 } else { 404 final int maxminus1 = maxLocals() - 1; 405 if (idx > maxminus1) { 406 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); 407 } 408 } 409 } 410 411 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 412 @Override 413 public void visitGETSTATIC(final GETSTATIC o) { 414 try { 415 final String fieldName = o.getFieldName(constantPoolGen); 416 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName()); 417 final Field[] fields = jc.getFields(); 418 Field f = null; 419 for (final Field field : fields) { 420 if (field.getName().equals(fieldName)) { 421 f = field; 422 break; 423 } 424 } 425 if (f == null) { 426 throw new AssertionViolatedException("Field '" + fieldName + "' not found in " + jc.getClassName()); 427 } 428 429 if (!f.isStatic()) { 430 constraintViolated(o, "Referenced field '" + f + "' is not static which it should be."); 431 } 432 } catch (final ClassNotFoundException e) { 433 // FIXME: maybe not the best way to handle this 434 throw new AssertionViolatedException("Missing class: " + e, e); 435 } 436 } 437 438 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 439 @Override 440 public void visitIINC(final IINC o) { 441 final int idx = o.getIndex(); 442 if (idx < 0) { 443 constraintViolated(o, "Index '" + idx + "' must be non-negative."); 444 } else { 445 final int maxminus1 = maxLocals() - 1; 446 if (idx > maxminus1) { 447 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); 448 } 449 } 450 } 451 452 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 453 @Override 454 public void visitILOAD(final ILOAD o) { 455 final int idx = o.getIndex(); 456 if (idx < 0) { 457 constraintViolated(o, "Index '" + idx + "' must be non-negative."); 458 } else { 459 final int maxminus1 = maxLocals() - 1; 460 if (idx > maxminus1) { 461 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); 462 } 463 } 464 } 465 466 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 467 @Override 468 public void visitINSTANCEOF(final INSTANCEOF o) { 469 indexValid(o, o.getIndex()); 470 final Constant c = constantPoolGen.getConstant(o.getIndex()); 471 if (!(c instanceof ConstantClass)) { 472 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'."); 473 } 474 } 475 476 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 477 @Override 478 public void visitINVOKEDYNAMIC(final INVOKEDYNAMIC o) { 479 throw new UnsupportedOperationException("INVOKEDYNAMIC instruction is not supported at this time"); 480 } 481 482 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 483 @Override 484 public void visitInvokeInstruction(final InvokeInstruction o) { 485 indexValid(o, o.getIndex()); 486 if (o instanceof INVOKEVIRTUAL || o instanceof INVOKESPECIAL || o instanceof INVOKESTATIC) { 487 final Constant c = constantPoolGen.getConstant(o.getIndex()); 488 if (!(c instanceof ConstantMethodref)) { 489 constraintViolated(o, "Indexing a constant that's not a CONSTANT_Methodref but a '" + tostring(c) + "'."); 490 } else { 491 // Constants are okay due to pass2. 492 final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantMethodref) c).getNameAndTypeIndex()); 493 final ConstantUtf8 cutf8 = (ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex()); 494 if (cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && !(o instanceof INVOKESPECIAL)) { 495 constraintViolated(o, "Only INVOKESPECIAL is allowed to invoke instance initialization methods."); 496 } 497 if (!cutf8.getBytes().equals(Const.CONSTRUCTOR_NAME) && cutf8.getBytes().startsWith("<")) { 498 constraintViolated(o, "No method with a name beginning with '<' other than the instance initialization methods" 499 + " may be called by the method invocation instructions."); 500 } 501 } 502 } else { 503 final Constant c = constantPoolGen.getConstant(o.getIndex()); 504 if (!(c instanceof ConstantInterfaceMethodref)) { 505 constraintViolated(o, "Indexing a constant that's not a CONSTANT_InterfaceMethodref but a '" + tostring(c) + "'."); 506 } 507 // TODO: From time to time check if BCEL allows to detect if the 508 // 'count' operand is consistent with the information in the 509 // CONSTANT_InterfaceMethodref and if the last operand is zero. 510 // By now, BCEL hides those two operands because they're superfluous. 511 512 // Invoked method must not be <init> or <clinit> 513 final ConstantNameAndType cnat = (ConstantNameAndType) constantPoolGen.getConstant(((ConstantInterfaceMethodref) c).getNameAndTypeIndex()); 514 final String name = ((ConstantUtf8) constantPoolGen.getConstant(cnat.getNameIndex())).getBytes(); 515 if (name.equals(Const.CONSTRUCTOR_NAME)) { 516 constraintViolated(o, "Method to invoke must not be '" + Const.CONSTRUCTOR_NAME + "'."); 517 } 518 if (name.equals(Const.STATIC_INITIALIZER_NAME)) { 519 constraintViolated(o, "Method to invoke must not be '" + Const.STATIC_INITIALIZER_NAME + "'."); 520 } 521 } 522 523 // The LoadClassType is the method-declaring class, so we have to check the other types. 524 525 Type t = o.getReturnType(constantPoolGen); 526 if (t instanceof ArrayType) { 527 t = ((ArrayType) t).getBasicType(); 528 } 529 if (t instanceof ObjectType) { 530 final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName()); 531 final VerificationResult vr = v.doPass2(); 532 if (vr.getStatus() != VerificationResult.VERIFIED_OK) { 533 constraintViolated(o, "Return type class/interface could not be verified successfully: '" + vr.getMessage() + "'."); 534 } 535 } 536 537 final Type[] ts = o.getArgumentTypes(constantPoolGen); 538 for (final Type element : ts) { 539 t = element; 540 if (t instanceof ArrayType) { 541 t = ((ArrayType) t).getBasicType(); 542 } 543 if (t instanceof ObjectType) { 544 final Verifier v = VerifierFactory.getVerifier(((ObjectType) t).getClassName()); 545 final VerificationResult vr = v.doPass2(); 546 if (vr.getStatus() != VerificationResult.VERIFIED_OK) { 547 constraintViolated(o, "Argument type class/interface could not be verified successfully: '" + vr.getMessage() + "'."); 548 } 549 } 550 } 551 552 } 553 554 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 555 @Override 556 public void visitINVOKEINTERFACE(final INVOKEINTERFACE o) { 557 try { 558 // INVOKEINTERFACE is a LoadClass; the Class where the referenced method is declared in, 559 // is therefore resolved/verified. 560 // INVOKEINTERFACE is an InvokeInstruction, the argument and return types are resolved/verified, 561 // too. So are the allowed method names. 562 final String className = o.getClassName(constantPoolGen); 563 final JavaClass jc = Repository.lookupClass(className); 564 final Method m = getMethodRecursive(jc, o); 565 if (m == null) { 566 constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '" 567 + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'."); 568 } 569 if (jc.isClass()) { 570 constraintViolated(o, "Referenced class '" + jc.getClassName() + "' is a class, but not an interface as expected."); 571 } 572 } catch (final ClassNotFoundException e) { 573 // FIXME: maybe not the best way to handle this 574 throw new AssertionViolatedException("Missing class: " + e, e); 575 } 576 } 577 578 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 579 @Override 580 public void visitINVOKESPECIAL(final INVOKESPECIAL o) { 581 try { 582 // INVOKESPECIAL is a LoadClass; the Class where the referenced method is declared in, 583 // is therefore resolved/verified. 584 // INVOKESPECIAL is an InvokeInstruction, the argument and return types are resolved/verified, 585 // too. So are the allowed method names. 586 final String className = o.getClassName(constantPoolGen); 587 final JavaClass jc = Repository.lookupClass(className); 588 final Method m = getMethodRecursive(jc, o); 589 if (m == null) { 590 constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '" 591 + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'."); 592 } 593 594 JavaClass current = Repository.lookupClass(verifier.getClassName()); 595 if (current.isSuper() && Repository.instanceOf(current, jc) && !current.equals(jc) 596 && !o.getMethodName(constantPoolGen).equals(Const.CONSTRUCTOR_NAME)) { 597 // Special lookup procedure for ACC_SUPER classes. 598 599 int supidx = -1; 600 601 Method meth = null; 602 while (supidx != 0) { 603 supidx = current.getSuperclassNameIndex(); 604 current = Repository.lookupClass(current.getSuperclassName()); 605 606 final Method[] meths = current.getMethods(); 607 for (final Method meth2 : meths) { 608 if (meth2.getName().equals(o.getMethodName(constantPoolGen)) 609 && Type.getReturnType(meth2.getSignature()).equals(o.getReturnType(constantPoolGen)) 610 && Arrays.equals(Type.getArgumentTypes(meth2.getSignature()), o.getArgumentTypes(constantPoolGen))) { 611 meth = meth2; 612 break; 613 } 614 } 615 if (meth != null) { 616 break; 617 } 618 } 619 if (meth == null) { 620 constraintViolated(o, "ACC_SUPER special lookup procedure not successful: method '" + o.getMethodName(constantPoolGen) 621 + "' with proper signature not declared in superclass hierarchy."); 622 } 623 } 624 625 } catch (final ClassNotFoundException e) { 626 // FIXME: maybe not the best way to handle this 627 throw new AssertionViolatedException("Missing class: " + e, e); 628 } 629 630 } 631 632 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 633 @Override 634 public void visitINVOKESTATIC(final INVOKESTATIC o) { 635 try { 636 // INVOKESTATIC is a LoadClass; the Class where the referenced method is declared in, 637 // is therefore resolved/verified. 638 // INVOKESTATIC is an InvokeInstruction, the argument and return types are resolved/verified, 639 // too. So are the allowed method names. 640 final String className = o.getClassName(constantPoolGen); 641 final JavaClass jc = Repository.lookupClass(className); 642 final Method m = getMethodRecursive(jc, o); 643 if (m == null) { 644 constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '" 645 + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'."); 646 } else if (!m.isStatic()) { // implies it's not abstract, verified in pass 2. 647 constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' has ACC_STATIC unset."); 648 } 649 650 } catch (final ClassNotFoundException e) { 651 // FIXME: maybe not the best way to handle this 652 throw new AssertionViolatedException("Missing class: " + e, e); 653 } 654 } 655 656 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 657 @Override 658 public void visitINVOKEVIRTUAL(final INVOKEVIRTUAL o) { 659 try { 660 // INVOKEVIRTUAL is a LoadClass; the Class where the referenced method is declared in, 661 // is therefore resolved/verified. 662 // INVOKEVIRTUAL is an InvokeInstruction, the argument and return types are resolved/verified, 663 // too. So are the allowed method names. 664 final String className = o.getClassName(constantPoolGen); 665 final JavaClass jc = Repository.lookupClass(className); 666 final Method m = getMethodRecursive(jc, o); 667 if (m == null) { 668 constraintViolated(o, "Referenced method '" + o.getMethodName(constantPoolGen) + "' with expected signature '" 669 + o.getSignature(constantPoolGen) + "' not found in class '" + jc.getClassName() + "'."); 670 } 671 if (!jc.isClass()) { 672 constraintViolated(o, "Referenced class '" + jc.getClassName() + "' is an interface, but not a class as expected."); 673 } 674 675 } catch (final ClassNotFoundException e) { 676 // FIXME: maybe not the best way to handle this 677 // throw new AssertionViolatedException("Missing class: " + e, e); 678 addMessage("Unable to verify INVOKEVITUAL as cannot load target class: " + e.getCause()); 679 } 680 } 681 682 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 683 @Override 684 public void visitISTORE(final ISTORE o) { 685 final int idx = o.getIndex(); 686 if (idx < 0) { 687 constraintViolated(o, "Index '" + idx + "' must be non-negative."); 688 } else { 689 final int maxminus1 = maxLocals() - 1; 690 if (idx > maxminus1) { 691 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); 692 } 693 } 694 } 695 696 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 697 // LDC and LDC_W (LDC_W is a subclass of LDC in BCEL's model) 698 @Override 699 public void visitLDC(final LDC ldc) { 700 indexValid(ldc, ldc.getIndex()); 701 final Constant c = constantPoolGen.getConstant(ldc.getIndex()); 702 if (c instanceof ConstantClass) { 703 addMessage("Operand of LDC or LDC_W is CONSTANT_Class '" + tostring(c) + "' - this is only supported in JDK 1.5 and higher."); 704 } else if (!(c instanceof ConstantInteger || c instanceof ConstantFloat || c instanceof ConstantString)) { 705 constraintViolated(ldc, 706 "Operand of LDC or LDC_W must be one of CONSTANT_Integer, CONSTANT_Float or CONSTANT_String, but is '" + tostring(c) + "'."); 707 } 708 } 709 710 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 711 // LDC2_W 712 @Override 713 public void visitLDC2_W(final LDC2_W o) { 714 indexValid(o, o.getIndex()); 715 final Constant c = constantPoolGen.getConstant(o.getIndex()); 716 if (!(c instanceof ConstantLong || c instanceof ConstantDouble)) { 717 constraintViolated(o, "Operand of LDC2_W must be CONSTANT_Long or CONSTANT_Double, but is '" + tostring(c) + "'."); 718 } 719 try { 720 indexValid(o, o.getIndex() + 1); 721 } catch (final StaticCodeInstructionOperandConstraintException e) { 722 throw new AssertionViolatedException("Does not BCEL handle that? LDC2_W operand has a problem.", e); 723 } 724 } 725 726 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 727 @Override 728 public void visitLLOAD(final LLOAD o) { 729 final int idx = o.getIndex(); 730 if (idx < 0) { 731 constraintViolated(o, "Index '" + idx + "' must be non-negative." 732 + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); 733 } else { 734 final int maxminus2 = maxLocals() - 2; 735 if (idx > maxminus2) { 736 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'."); 737 } 738 } 739 } 740 741 /////////////////////////////////////////////////////////// 742 // The Java Virtual Machine Specification, pages 134-137 // 743 /////////////////////////////////////////////////////////// 744 /** 745 * Assures the generic preconditions of a LoadClass instance. The referenced class is loaded and pass2-verified. 746 */ 747 @Override 748 public void visitLoadClass(final LoadClass loadClass) { 749 final ObjectType t = loadClass.getLoadClassType(constantPoolGen); 750 if (t != null) { // null means "no class is loaded" 751 final Verifier v = VerifierFactory.getVerifier(t.getClassName()); 752 final VerificationResult vr = v.doPass1(); 753 if (vr.getStatus() != VerificationResult.VERIFIED_OK) { 754 constraintViolated((Instruction) loadClass, 755 "Class '" + loadClass.getLoadClassType(constantPoolGen).getClassName() + "' is referenced, but cannot be loaded: '" + vr + "'."); 756 } 757 } 758 } 759 760 /* Checks if the constraints of operands of the said instruction(s) are satisfied. */ 761 // public void visitPUTFIELD(PUTFIELD o) { 762 // for performance reasons done in Pass 3b 763 // } 764 765 /* Checks if the constraints of operands of the said instruction(s) are satisfied. */ 766 // public void visitGETFIELD(GETFIELD o) { 767 // for performance reasons done in Pass 3b 768 // } 769 770 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 771 @Override 772 public void visitLOOKUPSWITCH(final LOOKUPSWITCH o) { 773 final int[] matchs = o.getMatchs(); 774 int max = Integer.MIN_VALUE; 775 for (int i = 0; i < matchs.length; i++) { 776 if (matchs[i] == max && i != 0) { 777 constraintViolated(o, "Match '" + matchs[i] + "' occurs more than once."); 778 } 779 if (matchs[i] < max) { 780 constraintViolated(o, "Lookup table must be sorted but isn't."); 781 } else { 782 max = matchs[i]; 783 } 784 } 785 } 786 787 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 788 @Override 789 public void visitLSTORE(final LSTORE o) { 790 final int idx = o.getIndex(); 791 if (idx < 0) { 792 constraintViolated(o, "Index '" + idx + "' must be non-negative." 793 + " [Constraint by JustIce as an analogon to the single-slot xLOAD/xSTORE instructions; may not happen anyway.]"); 794 } else { 795 final int maxminus2 = maxLocals() - 2; 796 if (idx > maxminus2) { 797 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-2 '" + maxminus2 + "'."); 798 } 799 } 800 } 801 802 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 803 @Override 804 public void visitMULTIANEWARRAY(final MULTIANEWARRAY o) { 805 indexValid(o, o.getIndex()); 806 final Constant c = constantPoolGen.getConstant(o.getIndex()); 807 if (!(c instanceof ConstantClass)) { 808 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'."); 809 } 810 final int dimensions2create = o.getDimensions(); 811 if (dimensions2create < 1) { 812 constraintViolated(o, "Number of dimensions to create must be greater than zero."); 813 } 814 final Type t = o.getType(constantPoolGen); 815 if (t instanceof ArrayType) { 816 final int dimensions = ((ArrayType) t).getDimensions(); 817 if (dimensions < dimensions2create) { 818 constraintViolated(o, "Not allowed to create array with more dimensions ('" + dimensions2create 819 + "') than the one referenced by the CONSTANT_Class '" + t + "'."); 820 } 821 } else { 822 constraintViolated(o, "Expecting a CONSTANT_Class referencing an array type." 823 + " [Constraint not found in The Java Virtual Machine Specification, Second Edition, 4.8.1]"); 824 } 825 } 826 827 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 828 @Override 829 public void visitNEW(final NEW o) { 830 indexValid(o, o.getIndex()); 831 final Constant c = constantPoolGen.getConstant(o.getIndex()); 832 if (!(c instanceof ConstantClass)) { 833 constraintViolated(o, "Expecting a CONSTANT_Class operand, but found a '" + tostring(c) + "'."); 834 } else { 835 final ConstantUtf8 cutf8 = (ConstantUtf8) constantPoolGen.getConstant(((ConstantClass) c).getNameIndex()); 836 final Type t = Type.getType("L" + cutf8.getBytes() + ";"); 837 if (t instanceof ArrayType) { 838 constraintViolated(o, "NEW must not be used to create an array."); 839 } 840 } 841 842 } 843 844 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 845 @Override 846 public void visitNEWARRAY(final NEWARRAY o) { 847 final byte t = o.getTypecode(); 848 if (!(t == Const.T_BOOLEAN || t == Const.T_CHAR || t == Const.T_FLOAT || t == Const.T_DOUBLE || t == Const.T_BYTE || t == Const.T_SHORT 849 || t == Const.T_INT || t == Const.T_LONG)) { 850 constraintViolated(o, "Illegal type code '" + tostring(t) + "' for 'atype' operand."); 851 } 852 } 853 854 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 855 @Override 856 public void visitPUTSTATIC(final PUTSTATIC o) { 857 try { 858 final String fieldName = o.getFieldName(constantPoolGen); 859 final JavaClass jc = Repository.lookupClass(getObjectType(o).getClassName()); 860 final Field[] fields = jc.getFields(); 861 Field f = null; 862 for (final Field field : fields) { 863 if (field.getName().equals(fieldName)) { 864 f = field; 865 break; 866 } 867 } 868 if (f == null) { 869 throw new AssertionViolatedException("Field '" + fieldName + "' not found in " + jc.getClassName()); 870 } 871 872 if (f.isFinal() && !verifier.getClassName().equals(getObjectType(o).getClassName())) { 873 constraintViolated(o, "Referenced field '" + f + "' is final and must therefore be declared in the current class '" 874 + verifier.getClassName() + "' which is not the case: it is declared in '" + o.getReferenceType(constantPoolGen) + "'."); 875 } 876 877 if (!f.isStatic()) { 878 constraintViolated(o, "Referenced field '" + f + "' is not static which it should be."); 879 } 880 881 final String methName = Repository.lookupClass(verifier.getClassName()).getMethods()[methodNo].getName(); 882 883 // If it's an interface, it can be set only in <clinit>. 884 if (!jc.isClass() && !methName.equals(Const.STATIC_INITIALIZER_NAME)) { 885 constraintViolated(o, "Interface field '" + f + "' must be set in a '" + Const.STATIC_INITIALIZER_NAME + "' method."); 886 } 887 } catch (final ClassNotFoundException e) { 888 // FIXME: maybe not the best way to handle this 889 throw new AssertionViolatedException("Missing class: " + e, e); 890 } 891 } 892 893 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 894 @Override 895 public void visitRET(final RET o) { 896 final int idx = o.getIndex(); 897 if (idx < 0) { 898 constraintViolated(o, "Index '" + idx + "' must be non-negative."); 899 } else { 900 final int maxminus1 = maxLocals() - 1; 901 if (idx > maxminus1) { 902 constraintViolated(o, "Index '" + idx + "' must not be greater than max_locals-1 '" + maxminus1 + "'."); 903 } 904 } 905 } 906 907 // WIDE stuff is BCEL-internal and cannot be checked here. 908 909 /** Checks if the constraints of operands of the said instruction(s) are satisfied. */ 910 @Override 911 public void visitTABLESWITCH(final TABLESWITCH o) { 912 // "high" must be >= "low". We cannot check this, as BCEL hides 913 // it from us. 914 } 915 } 916 917 /** A small utility method returning if a given int i is in the given int[] ints. */ 918 private static boolean contains(final int[] ints, final int i) { 919 for (final int k : ints) { 920 if (k == i) { 921 return true; 922 } 923 } 924 return false; 925 } 926 927 /** The Verifier that created this. */ 928 private final Verifier verifier; 929 930 /** 931 * The method number to verify. This is the index in the array returned by JavaClass.getMethods(). 932 */ 933 private final int methodNo; 934 935 /** 936 * The one and only InstructionList object used by an instance of this class. It's here for performance reasons by 937 * do_verify() and its callees. 938 */ 939 private InstructionList instructionList; 940 941 /** 942 * The one and only Code object used by an instance of this class. It's here for performance reasons by do_verify() and 943 * its callees. 944 */ 945 private Code code; 946 947 /** Should only be instantiated by a Verifier. */ 948 public Pass3aVerifier(final Verifier verifier, final int methodNo) { 949 this.verifier = verifier; 950 this.methodNo = methodNo; 951 } 952 953 /** 954 * These are the checks that could be done in pass 2 but are delayed to pass 3 for performance reasons. Also, these 955 * checks need access to the code array of the Code attribute of a Method so it's okay to perform them here. Also see 956 * the description of the do_verify() method. 957 * 958 * @throws ClassConstraintException if the verification fails. 959 * @see #do_verify() 960 */ 961 private void delayedPass2Checks() { 962 963 final int[] instructionPositions = instructionList.getInstructionPositions(); 964 final int codeLength = code.getCode().length; 965 966 ///////////////////// 967 // LineNumberTable // 968 ///////////////////// 969 final LineNumberTable lnt = code.getLineNumberTable(); 970 if (lnt != null) { 971 final LineNumber[] lineNumbers = lnt.getLineNumberTable(); 972 final IntList offsets = new IntList(); 973 lineNumberLoop: for (final LineNumber lineNumber : lineNumbers) { // may appear in any order. 974 for (final int instructionPosition : instructionPositions) { 975 // TODO: Make this a binary search! The instructionPositions array is naturally ordered! 976 final int offset = lineNumber.getStartPC(); 977 if (instructionPosition == offset) { 978 if (offsets.contains(offset)) { 979 addMessage("LineNumberTable attribute '" + code.getLineNumberTable() + "' refers to the same code offset ('" + offset 980 + "') more than once" + " which is violating the semantics [but is sometimes produced by IBM's 'jikes' compiler]."); 981 } else { 982 offsets.add(offset); 983 } 984 continue lineNumberLoop; 985 } 986 } 987 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has a LineNumberTable attribute '" + code.getLineNumberTable() 988 + "' referring to a code offset ('" + lineNumber.getStartPC() + "') that does not exist."); 989 } 990 } 991 992 /////////////////////////// 993 // LocalVariableTable(s) // 994 /////////////////////////// 995 /* 996 * We cannot use code.getLocalVariableTable() because there could be more than only one. This is a bug in BCEL. 997 */ 998 final Attribute[] atts = code.getAttributes(); 999 for (final Attribute att : atts) { 1000 if (att instanceof LocalVariableTable) { 1001 ((LocalVariableTable) att).forEach(localVariable -> { 1002 final int startpc = localVariable.getStartPC(); 1003 final int length = localVariable.getLength(); 1004 1005 if (!contains(instructionPositions, startpc)) { 1006 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has a LocalVariableTable attribute '" 1007 + code.getLocalVariableTable() + "' referring to a code offset ('" + startpc + "') that does not exist."); 1008 } 1009 if (!contains(instructionPositions, startpc + length) && startpc + length != codeLength) { 1010 throw new ClassConstraintException( 1011 "Code attribute '" + tostring(code) + "' has a LocalVariableTable attribute '" + code.getLocalVariableTable() 1012 + "' referring to a code offset start_pc+length ('" + (startpc + length) + "') that does not exist."); 1013 } 1014 }); 1015 } 1016 } 1017 1018 //////////////////// 1019 // ExceptionTable // 1020 //////////////////// 1021 // In BCEL's "classfile" API, the startPC/endPC-notation is 1022 // inclusive/exclusive as in the Java Virtual Machine Specification. 1023 // WARNING: This is not true for BCEL's "generic" API. 1024 final CodeException[] exceptionTable = code.getExceptionTable(); 1025 for (final CodeException element : exceptionTable) { 1026 final int startpc = element.getStartPC(); 1027 final int endpc = element.getEndPC(); 1028 final int handlerpc = element.getHandlerPC(); 1029 if (startpc >= endpc) { 1030 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element 1031 + "' that has its start_pc ('" + startpc + "') not smaller than its end_pc ('" + endpc + "')."); 1032 } 1033 if (!contains(instructionPositions, startpc)) { 1034 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element 1035 + "' that has a non-existant bytecode offset as its start_pc ('" + startpc + "')."); 1036 } 1037 if (!contains(instructionPositions, endpc) && endpc != codeLength) { 1038 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element 1039 + "' that has a non-existant bytecode offset as its end_pc ('" + startpc + "') [that is also not equal to code_length ('" + codeLength 1040 + "')]."); 1041 } 1042 if (!contains(instructionPositions, handlerpc)) { 1043 throw new ClassConstraintException("Code attribute '" + tostring(code) + "' has an exception_table entry '" + element 1044 + "' that has a non-existant bytecode offset as its handler_pc ('" + handlerpc + "')."); 1045 } 1046 } 1047 } 1048 1049 /** 1050 * Pass 3a is the verification of static constraints of JVM code (such as legal targets of branch instructions). This is 1051 * the part of pass 3 where you do not need data flow analysis. JustIce also delays the checks for a correct exception 1052 * table of a Code attribute and correct line number entries in a LineNumberTable attribute of a Code attribute (which 1053 * conceptually belong to pass 2) to this pass. Also, most of the check for valid local variable entries in a 1054 * LocalVariableTable attribute of a Code attribute is delayed until this pass. All these checks need access to the code 1055 * array of the Code attribute. 1056 * 1057 * @throws InvalidMethodException if the method to verify does not exist. 1058 */ 1059 @Override 1060 public VerificationResult do_verify() { 1061 try { 1062 if (verifier.doPass2().equals(VerificationResult.VR_OK)) { 1063 // Okay, class file was loaded correctly by Pass 1 1064 // and satisfies static constraints of Pass 2. 1065 final JavaClass jc = Repository.lookupClass(verifier.getClassName()); 1066 final Method[] methods = jc.getMethods(); 1067 if (methodNo >= methods.length) { 1068 throw new InvalidMethodException("METHOD DOES NOT EXIST!"); 1069 } 1070 final Method method = methods[methodNo]; 1071 code = method.getCode(); 1072 1073 // No Code? Nothing to verify! 1074 if (method.isAbstract() || method.isNative()) { // IF mg HAS NO CODE (static constraint of Pass 2) 1075 return VerificationResult.VR_OK; 1076 } 1077 1078 // TODO: 1079 // We want a very sophisticated code examination here with good explanations 1080 // on where to look for an illegal instruction or such. 1081 // Only after that we should try to build an InstructionList and throw an 1082 // AssertionViolatedException if after our examination InstructionList building 1083 // still fails. 1084 // That examination should be implemented in a byte-oriented way, i.e. look for 1085 // an instruction, make sure its validity, count its length, find the next 1086 // instruction and so on. 1087 try { 1088 instructionList = new InstructionList(method.getCode().getCode()); 1089 } catch (final RuntimeException re) { 1090 return new VerificationResult(VerificationResult.VERIFIED_REJECTED, 1091 "Bad bytecode in the code array of the Code attribute of method '" + tostring(method) + "'."); 1092 } 1093 1094 instructionList.setPositions(true); 1095 1096 // Start verification. 1097 VerificationResult vr = VerificationResult.VR_OK; // default 1098 try { 1099 delayedPass2Checks(); 1100 } catch (final ClassConstraintException | ClassFormatException cce) { 1101 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, cce.getMessage()); 1102 return vr; 1103 } 1104 try { 1105 pass3StaticInstructionChecks(); 1106 pass3StaticInstructionOperandsChecks(); 1107 } catch (final StaticCodeConstraintException | ClassFormatException scce) { 1108 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, scce.getMessage()); 1109 } catch (final ClassCastException cce) { 1110 vr = new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Class Cast Exception: " + cce.getMessage()); 1111 } 1112 return vr; 1113 } 1114 // did not pass Pass 2. 1115 return VerificationResult.VR_NOTYET; 1116 } catch (final ClassNotFoundException e) { 1117 // FIXME: maybe not the best way to handle this 1118 throw new AssertionViolatedException("Missing class: " + e, e); 1119 } 1120 } 1121 1122 /** Returns the method number as supplied when instantiating. */ 1123 public int getMethodNo() { 1124 return methodNo; 1125 } 1126 1127 /** 1128 * These are the checks if constraints are satisfied which are described in the Java Virtual Machine Specification, 1129 * Second Edition as Static Constraints on the instructions of Java Virtual Machine Code (chapter 4.8.1). 1130 * 1131 * @throws StaticCodeConstraintException if the verification fails. 1132 */ 1133 private void pass3StaticInstructionChecks() { 1134 1135 // Code array must not be empty: 1136 // Enforced in pass 2 (also stated in the static constraints of the Code 1137 // array in vmspec2), together with pass 1 (reading code_length bytes and 1138 // interpreting them as code[]). So this must not be checked again here. 1139 1140 if (code.getCode().length >= Const.MAX_CODE_SIZE) {// length must be LESS than the max 1141 throw new StaticCodeInstructionConstraintException( 1142 "Code array in code attribute '" + tostring(code) + "' too big: must be smaller than " + Const.MAX_CODE_SIZE + "65536 bytes."); 1143 } 1144 1145 // First opcode at offset 0: okay, that's clear. Nothing to do. 1146 1147 // Only instances of the instructions documented in Section 6.4 may appear in 1148 // the code array. 1149 1150 // For BCEL's sake, we cannot handle WIDE stuff, but hopefully BCEL does its job right :) 1151 1152 // The last byte of the last instruction in the code array must be the byte at index 1153 // code_length-1 : See the do_verify() comments. We actually don't iterate through the 1154 // byte array, but use an InstructionList so we cannot check for this. But BCEL does 1155 // things right, so it's implicitly okay. 1156 1157 // TODO: Check how BCEL handles (and will handle) instructions like IMPDEP1, IMPDEP2, 1158 // BREAKPOINT... that BCEL knows about but which are illegal anyway. 1159 // We currently go the safe way here. 1160 InstructionHandle ih = instructionList.getStart(); 1161 while (ih != null) { 1162 final Instruction i = ih.getInstruction(); 1163 if (i instanceof IMPDEP1) { 1164 throw new StaticCodeInstructionConstraintException("IMPDEP1 must not be in the code, it is an illegal instruction for _internal_ JVM use!"); 1165 } 1166 if (i instanceof IMPDEP2) { 1167 throw new StaticCodeInstructionConstraintException("IMPDEP2 must not be in the code, it is an illegal instruction for _internal_ JVM use!"); 1168 } 1169 if (i instanceof BREAKPOINT) { 1170 throw new StaticCodeInstructionConstraintException("BREAKPOINT must not be in the code, it is an illegal instruction for _internal_ JVM use!"); 1171 } 1172 ih = ih.getNext(); 1173 } 1174 1175 // The original verifier seems to do this check here, too. 1176 // An unreachable last instruction may also not fall through the 1177 // end of the code, which is stupid -- but with the original 1178 // verifier's subroutine semantics one cannot predict reachability. 1179 final Instruction last = instructionList.getEnd().getInstruction(); 1180 if (!(last instanceof ReturnInstruction || last instanceof RET || last instanceof GotoInstruction || last instanceof ATHROW)) { 1181 throw new StaticCodeInstructionConstraintException( 1182 "Execution must not fall off the bottom of the code array." + " This constraint is enforced statically as some existing verifiers do" 1183 + " - so it may be a false alarm if the last instruction is not reachable."); 1184 } 1185 } 1186 1187 /** 1188 * These are the checks for the satisfaction of constraints which are described in the Java Virtual Machine 1189 * Specification, Second Edition as Static Constraints on the operands of instructions of Java Virtual Machine Code 1190 * (chapter 4.8.1). BCEL parses the code array to create an InstructionList and therefore has to check some of these 1191 * constraints. Additional checks are also implemented here. 1192 * 1193 * @throws StaticCodeConstraintException if the verification fails. 1194 */ 1195 private void pass3StaticInstructionOperandsChecks() { 1196 try { 1197 // When building up the InstructionList, BCEL has already done all those checks 1198 // mentioned in The Java Virtual Machine Specification, Second Edition, as 1199 // "static constraints on the operands of instructions in the code array". 1200 // TODO: see the do_verify() comments. Maybe we should really work on the 1201 // byte array first to give more comprehensive messages. 1202 // TODO: Review Exception API, possibly build in some "offending instruction" thing 1203 // when we're ready to insulate the offending instruction by doing the 1204 // above thing. 1205 1206 // TODO: Implement as much as possible here. BCEL does _not_ check everything. 1207 1208 final ConstantPoolGen cpg = new ConstantPoolGen(Repository.lookupClass(verifier.getClassName()).getConstantPool()); 1209 final InstOperandConstraintVisitor v = new InstOperandConstraintVisitor(cpg); 1210 1211 // Checks for the things BCEL does _not_ handle itself. 1212 InstructionHandle ih = instructionList.getStart(); 1213 while (ih != null) { 1214 final Instruction i = ih.getInstruction(); 1215 1216 // An "own" constraint, due to JustIce's new definition of what "subroutine" means. 1217 if (i instanceof JsrInstruction) { 1218 final InstructionHandle target = ((JsrInstruction) i).getTarget(); 1219 if (target == instructionList.getStart()) { 1220 throw new StaticCodeInstructionOperandConstraintException( 1221 "Due to JustIce's clear definition of subroutines, no JSR or JSR_W may have a top-level instruction" 1222 + " (such as the very first instruction, which is targeted by instruction '" + tostring(ih) + "' as its target."); 1223 } 1224 if (!(target.getInstruction() instanceof ASTORE)) { 1225 throw new StaticCodeInstructionOperandConstraintException( 1226 "Due to JustIce's clear definition of subroutines, no JSR or JSR_W may target anything else" 1227 + " than an ASTORE instruction. Instruction '" + tostring(ih) + "' targets '" + tostring(target) + "'."); 1228 } 1229 } 1230 1231 // vmspec2, page 134-137 1232 ih.accept(v); 1233 1234 ih = ih.getNext(); 1235 } 1236 1237 } catch (final ClassNotFoundException e) { 1238 // FIXME: maybe not the best way to handle this 1239 throw new AssertionViolatedException("Missing class: " + e, e); 1240 } 1241 } 1242 1243 /** 1244 * This method is a slightly modified version of verifier.statics.StringRepresentation.toString(final Node obj) that 1245 * accepts any Object, not just a Node. 1246 * 1247 * Returns the String representation of the Object obj; this is obj.toString() if it does not throw any 1248 * RuntimeException, or else it is a string derived only from obj's class name. 1249 */ 1250 protected String tostring(final Object obj) { 1251 String ret; 1252 try { 1253 ret = obj.toString(); 1254 } 1255 1256 catch (final RuntimeException e) { 1257 // including ClassFormatException, trying to convert the "signature" of a ReturnaddressType LocalVariable 1258 // (shouldn't occur, but people do crazy things) 1259 String s = obj.getClass().getName(); 1260 s = s.substring(s.lastIndexOf(".") + 1); 1261 ret = "<<" + s + ">>"; 1262 } 1263 return ret; 1264 } 1265}