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