View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.bcel.verifier.statics;
20  
21  import java.util.Arrays;
22  import java.util.HashSet;
23  import java.util.Set;
24  
25  import org.apache.bcel.Const;
26  import org.apache.bcel.Repository;
27  import org.apache.bcel.classfile.Attribute;
28  import org.apache.bcel.classfile.ClassFormatException;
29  import org.apache.bcel.classfile.Code;
30  import org.apache.bcel.classfile.CodeException;
31  import org.apache.bcel.classfile.Constant;
32  import org.apache.bcel.classfile.ConstantCP;
33  import org.apache.bcel.classfile.ConstantClass;
34  import org.apache.bcel.classfile.ConstantDouble;
35  import org.apache.bcel.classfile.ConstantDynamic;
36  import org.apache.bcel.classfile.ConstantFieldref;
37  import org.apache.bcel.classfile.ConstantFloat;
38  import org.apache.bcel.classfile.ConstantInteger;
39  import org.apache.bcel.classfile.ConstantInterfaceMethodref;
40  import org.apache.bcel.classfile.ConstantInvokeDynamic;
41  import org.apache.bcel.classfile.ConstantLong;
42  import org.apache.bcel.classfile.ConstantMethodref;
43  import org.apache.bcel.classfile.ConstantNameAndType;
44  import org.apache.bcel.classfile.ConstantString;
45  import org.apache.bcel.classfile.ConstantUtf8;
46  import org.apache.bcel.classfile.Field;
47  import org.apache.bcel.classfile.JavaClass;
48  import org.apache.bcel.classfile.LineNumber;
49  import org.apache.bcel.classfile.LineNumberTable;
50  import org.apache.bcel.classfile.LocalVariableTable;
51  import org.apache.bcel.classfile.Method;
52  import org.apache.bcel.generic.ALOAD;
53  import org.apache.bcel.generic.ANEWARRAY;
54  import org.apache.bcel.generic.ASTORE;
55  import org.apache.bcel.generic.ATHROW;
56  import org.apache.bcel.generic.ArrayType;
57  import org.apache.bcel.generic.BREAKPOINT;
58  import org.apache.bcel.generic.CHECKCAST;
59  import org.apache.bcel.generic.ConstantPoolGen;
60  import org.apache.bcel.generic.DLOAD;
61  import org.apache.bcel.generic.DSTORE;
62  import org.apache.bcel.generic.FLOAD;
63  import org.apache.bcel.generic.FSTORE;
64  import org.apache.bcel.generic.FieldInstruction;
65  import org.apache.bcel.generic.GETSTATIC;
66  import org.apache.bcel.generic.GotoInstruction;
67  import org.apache.bcel.generic.IINC;
68  import org.apache.bcel.generic.ILOAD;
69  import org.apache.bcel.generic.IMPDEP1;
70  import org.apache.bcel.generic.IMPDEP2;
71  import org.apache.bcel.generic.INSTANCEOF;
72  import org.apache.bcel.generic.INVOKEDYNAMIC;
73  import org.apache.bcel.generic.INVOKEINTERFACE;
74  import org.apache.bcel.generic.INVOKESPECIAL;
75  import org.apache.bcel.generic.INVOKESTATIC;
76  import org.apache.bcel.generic.INVOKEVIRTUAL;
77  import org.apache.bcel.generic.ISTORE;
78  import org.apache.bcel.generic.Instruction;
79  import org.apache.bcel.generic.InstructionHandle;
80  import org.apache.bcel.generic.InstructionList;
81  import org.apache.bcel.generic.InvokeInstruction;
82  import org.apache.bcel.generic.JsrInstruction;
83  import org.apache.bcel.generic.LDC;
84  import org.apache.bcel.generic.LDC2_W;
85  import org.apache.bcel.generic.LLOAD;
86  import org.apache.bcel.generic.LOOKUPSWITCH;
87  import org.apache.bcel.generic.LSTORE;
88  import org.apache.bcel.generic.LoadClass;
89  import org.apache.bcel.generic.MULTIANEWARRAY;
90  import org.apache.bcel.generic.NEW;
91  import org.apache.bcel.generic.NEWARRAY;
92  import org.apache.bcel.generic.ObjectType;
93  import org.apache.bcel.generic.PUTSTATIC;
94  import org.apache.bcel.generic.RET;
95  import org.apache.bcel.generic.ReferenceType;
96  import org.apache.bcel.generic.ReturnInstruction;
97  import org.apache.bcel.generic.TABLESWITCH;
98  import org.apache.bcel.generic.Type;
99  import org.apache.bcel.verifier.PassVerifier;
100 import org.apache.bcel.verifier.VerificationResult;
101 import org.apache.bcel.verifier.Verifier;
102 import org.apache.bcel.verifier.VerifierFactory;
103 import org.apache.bcel.verifier.exc.AssertionViolatedException;
104 import org.apache.bcel.verifier.exc.ClassConstraintException;
105 import org.apache.bcel.verifier.exc.InvalidMethodException;
106 import org.apache.bcel.verifier.exc.StaticCodeConstraintException;
107 import org.apache.bcel.verifier.exc.StaticCodeInstructionConstraintException;
108 import 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  */
116 public 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 }