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