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