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