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