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