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