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