Source code

    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 *  Unless required by applicable law or agreed to in writing, software
    012 *  distributed under the License is distributed on an "AS IS" BASIS,
    013 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 *  See the License for the specific language governing permissions and
    015 *  limitations under the License.
    016 */
    017package org.apache.bcel.verifier.statics;
    018
    019import org.apache.bcel.Repository;
    020import org.apache.bcel.classfile.ClassFormatException;
    021import org.apache.bcel.classfile.JavaClass;
    022import org.apache.bcel.verifier.PassVerifier;
    023import org.apache.bcel.verifier.VerificationResult;
    024import org.apache.bcel.verifier.Verifier;
    025import org.apache.bcel.verifier.exc.LoadingException;
    026import org.apache.commons.lang3.ArrayUtils;
    027
    028/**
    029 * This PassVerifier verifies a class file according to pass 1 as described in The Java Virtual Machine Specification,
    030 * 2nd edition. More detailed information is to be found at the do_verify() method's documentation.
    031 *
    032 * @see #do_verify()
    033 */
    034public final class Pass1Verifier extends PassVerifier {
    035    /**
    036     * DON'T USE THIS EVEN PRIVATELY! USE getJavaClass() INSTEAD.
    037     *
    038     * @see #getJavaClass()
    039     */
    040    private JavaClass jc;
    041
    042    /**
    043     * The Verifier that created this.
    044     */
    045    private final Verifier myOwner;
    046
    047    /**
    048     * Should only be instantiated by a Verifier.
    049     *
    050     * @see Verifier
    051     */
    052    public Pass1Verifier(final Verifier owner) {
    053        myOwner = owner;
    054    }
    055
    056    /**
    057     * Pass-one verification basically means loading in a class file. The Java Virtual Machine Specification is not too
    058     * precise about what makes the difference between passes one and two. The answer is that only pass one is performed on
    059     * a class file as long as its resolution is not requested; whereas pass two and pass three are performed during the
    060     * resolution process. Only four constraints to be checked are explicitly stated by The Java Virtual Machine
    061     * Specification, 2nd edition:
    062     * <UL>
    063     * <LI>The first four bytes must contain the right magic number (0xCAFEBABE).
    064     * <LI>All recognized attributes must be of the proper length.
    065     * <LI>The class file must not be truncated or have extra bytes at the end.
    066     * <LI>The constant pool must not contain any superficially unrecognizable information.
    067     * </UL>
    068     * A more in-depth documentation of what pass one should do was written by <A HREF=mailto:pwfong@cs.sfu.ca>Philip W. L.
    069     * Fong</A>:
    070     * <UL>
    071     * <LI>the file should not be truncated.
    072     * <LI>the file should not have extra bytes at the end.
    073     * <LI>all variable-length structures should be well-formatted:
    074     * <UL>
    075     * <LI>there should only be constant_pool_count-1 many entries in the constant pool.
    076     * <LI>all constant pool entries should have size the same as indicated by their type tag.
    077     * <LI>there are exactly interfaces_count many entries in the interfaces array of the class file.
    078     * <LI>there are exactly fields_count many entries in the fields array of the class file.
    079     * <LI>there are exactly methods_count many entries in the methods array of the class file.
    080     * <LI>there are exactly attributes_count many entries in the attributes array of the class file, fields, methods, and
    081     * code attribute.
    082     * <LI>there should be exactly attribute_length many bytes in each attribute. Inconsistency between attribute_length and
    083     * the actually size of the attribute content should be uncovered. For example, in an Exceptions attribute, the actual
    084     * number of exceptions as required by the number_of_exceptions field might yeild an attribute size that doesn't match
    085     * the attribute_length. Such an anomaly should be detected.
    086     * <LI>all attributes should have proper length. In particular, under certain context (e.g. while parsing method_info),
    087     * recognizable attributes (e.g. "Code" attribute) should have correct format (e.g. attribute_length is 2).
    088     * </UL>
    089     * <LI>Also, certain constant values are checked for validity:
    090     * <UL>
    091     * <LI>The magic number should be 0xCAFEBABE.
    092     * <LI>The major and minor version numbers are valid.
    093     * <LI>All the constant pool type tags are recognizable.
    094     * <LI>All undocumented access flags are masked off before use. Strictly speaking, this is not really a check.
    095     * <LI>The field this_class should point to a string that represents a legal non-array class name, and this name should
    096     * be the same as the class file being loaded.
    097     * <LI>the field super_class should point to a string that represents a legal non-array class name.
    098     * <LI>Because some of the above checks require cross referencing the constant pool entries, guards are set up to make
    099     * sure that the referenced entries are of the right type and the indices are within the legal range (0 &lt; index &lt;
    100     * constant_pool_count).
    101     * </UL>
    102     * <LI>Extra checks done in pass 1:
    103     * <UL>
    104     * <LI>the constant values of static fields should have the same type as the fields.
    105     * <LI>the number of words in a parameter list does not exceed 255 and locals_max.
    106     * <LI>the name and signature of fields and methods are verified to be of legal format.
    107     * </UL>
    108     * </UL>
    109     * (From the Paper <A HREF="http://www.cs.sfu.ca/people/GradStudents/pwfong/personal/JVM/pass1/"> The Mysterious Pass
    110     * One, first draft, September 2, 1997</A>.)
    111     *
    112     * <P>
    113     * However, most of this is done by parsing a class file or generating a class file into BCEL's internal data structure.
    114     * <B>Therefore, all that is really done here is look up the class file from BCEL's repository.</B> This is also
    115     * motivated by the fact that some omitted things (like the check for extra bytes at the end of the class file) are
    116     * handy when actually using BCEL to repair a class file (otherwise you would not be able to load it into BCEL).
    117     * </P>
    118     *
    119     * @see org.apache.bcel.Repository
    120     * @see org.apache.bcel.Const#JVM_CLASSFILE_MAGIC
    121     */
    122    @Override
    123    public VerificationResult do_verify() {
    124        JavaClass jc;
    125        try {
    126            jc = getJavaClass(); // loads in the class file if not already done.
    127
    128            /* If we find more constraints to check, we should do this in an own method. */
    129            // This should maybe caught by BCEL: In case of renamed .class files we get wrong
    130            // JavaClass objects here.
    131            // This test should be much more complicated. It needs to take the class name, remove any portion at the
    132            // end that matches the file name and then see if the remainder matches anything on the class path.
    133            // Dumb test for now, see if the class name ends with the file name.
    134            if (jc != null && !myOwner.getClassName().equals(jc.getClassName()) && !jc.getClassName().endsWith(myOwner.getClassName())) {
    135                throw new LoadingException("Wrong name: the internal name of the .class file '" + jc.getClassName() + "' does not match the file's name '"
    136                    + myOwner.getClassName() + "'.");
    137            }
    138        } catch (final LoadingException | ClassFormatException e) {
    139            return new VerificationResult(VerificationResult.VERIFIED_REJECTED, e.getMessage());
    140        } catch (final RuntimeException e) {
    141            // BCEL does not catch every possible RuntimeException; e.g. if
    142            // a constant pool index is referenced that does not exist.
    143            return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Parsing via BCEL did not succeed. " + " exception occurred:\n" + e.toString());
    144            // Don't think we want to dump a stack trace unless we have some sort of a debug option.
    145            // e.getClass().getName()+" occurred:\n"+Utility.getStackTrace(e));
    146        }
    147
    148        if (jc != null) {
    149            return VerificationResult.VR_OK;
    150        }
    151        // TODO: Maybe change Repository's behavior to throw a LoadingException instead of just returning "null"
    152        // if a class file cannot be found or in another way be looked up.
    153        return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Repository.lookup() failed. FILE NOT FOUND?");
    154    }
    155
    156    /**
    157     * Used to load in and return the myOwner-matching JavaClass object when needed. Avoids loading in a class file when
    158     * it's not really needed!
    159     */
    160    private JavaClass getJavaClass() {
    161        if (jc == null) {
    162            try {
    163                jc = Repository.lookupClass(myOwner.getClassName());
    164            } catch (final ClassNotFoundException ignored) {
    165                // FIXME: currently, Pass1Verifier treats jc == null as a special
    166                // case, so we don't need to do anything here. A better solution
    167                // would be to simply throw the ClassNotFoundException
    168                // out of this method.
    169            }
    170        }
    171        return jc;
    172    }
    173
    174    /**
    175     * Currently this returns an empty array of String. One could parse the error messages of BCEL (written to
    176     * java.lang.System.err) when loading a class file such as detecting unknown attributes or trailing garbage at the end
    177     * of a class file. However, Markus Dahm does not like the idea so this method is currently useless and therefore marked
    178     * as <b>TODO</b>.
    179     */
    180    @Override
    181    public String[] getMessages() {
    182        return ArrayUtils.EMPTY_STRING_ARRAY;
    183    }
    184
    185}