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