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 org.apache.bcel.Repository;
22  import org.apache.bcel.classfile.ClassFormatException;
23  import org.apache.bcel.classfile.JavaClass;
24  import org.apache.bcel.verifier.PassVerifier;
25  import org.apache.bcel.verifier.VerificationResult;
26  import org.apache.bcel.verifier.Verifier;
27  import org.apache.bcel.verifier.exc.LoadingException;
28  import org.apache.commons.lang3.ArrayUtils;
29  
30  /**
31   * This PassVerifier verifies a class file according to pass 1 as described in The Java Virtual Machine Specification,
32   * 2nd edition. More detailed information is to be found at the do_verify() method's documentation.
33   *
34   * @see #do_verify()
35   */
36  public final class Pass1Verifier extends PassVerifier {
37  
38      /**
39       * DON'T USE THIS EVEN PRIVATELY! USE getJavaClass() INSTEAD.
40       *
41       * @see #getJavaClass()
42       */
43      private JavaClass javaClass;
44  
45      /**
46       * The Verifier that created this.
47       */
48      private final Verifier verifier;
49  
50      /**
51       * Should only be instantiated by a Verifier.
52       *
53       * @param verifier The verifier.
54       * @see Verifier
55       */
56      public Pass1Verifier(final Verifier verifier) {
57          this.verifier = verifier;
58      }
59  
60      /**
61       * Pass-one verification basically means loading in a class file. The Java Virtual Machine Specification is not too
62       * precise about what makes the difference between passes one and two. The answer is that only pass one is performed on
63       * a class file as long as its resolution is not requested; whereas pass two and pass three are performed during the
64       * resolution process. Only four constraints to be checked are explicitly stated by The Java Virtual Machine
65       * Specification, 2nd edition:
66       * <ul>
67       * <li>The first four bytes must contain the right magic number (0xCAFEBABE).</li>
68       * <li>All recognized attributes must be of the proper length.</li>
69       * <li>The class file must not be truncated or have extra bytes at the end.</li>
70       * <li>The constant pool must not contain any superficially unrecognizable information.</li>
71       * </ul>
72       * <p>
73       * A more in-depth documentation of what pass one should do was written by <a HREF=mailto:pwfong@cs.sfu.ca>Philip W. L.
74       * Fong</a>:
75       * </p>
76       * <ul>
77       * <li>the file should not be truncated.</li>
78       * <li>the file should not have extra bytes at the end.</li>
79       * <li>all variable-length structures should be well-formatted:
80       * <ul>
81       * <li>there should only be constant_pool_count-1 many entries in the constant pool.</li>
82       * <li>all constant pool entries should have size the same as indicated by their type tag.</li>
83       * <li>there are exactly interfaces_count many entries in the interfaces array of the class file.</li>
84       * <li>there are exactly fields_count many entries in the fields array of the class file.</li>
85       * <li>there are exactly methods_count many entries in the methods array of the class file.</li>
86       * <li>there are exactly attributes_count many entries in the attributes array of the class file, fields, methods, and
87       * code attribute.</li>
88       * <li>there should be exactly attribute_length many bytes in each attribute. Inconsistency between attribute_length and
89       * the actually size of the attribute content should be uncovered. For example, in an Exceptions attribute, the actual
90       * number of exceptions as required by the number_of_exceptions field might yeild an attribute size that doesn't match
91       * the attribute_length. Such an anomaly should be detected.</li>
92       * <li>all attributes should have proper length. In particular, under certain context (for example while parsing method_info),
93       * recognizable attributes (for example "Code" attribute) should have correct format (for example attribute_length is 2).</li>
94       * </ul>
95       * <li>Also, certain constant values are checked for validity:
96       * <ul>
97       * <li>The magic number should be 0xCAFEBABE.</li>
98       * <li>The major and minor version numbers are valid.</li>
99       * <li>All the constant pool type tags are recognizable.</li>
100      * <li>All undocumented access flags are masked off before use. Strictly speaking, this is not really a check.</li>
101      * <li>The field this_class should point to a string that represents a legal non-array class name, and this name should
102      * be the same as the class file being loaded.</li>
103      * <li>the field super_class should point to a string that represents a legal non-array class name.</li>
104      * <li>Because some of the above checks require cross referencing the constant pool entries, guards are set up to make
105      * sure that the referenced entries are of the right type and the indices are within the legal range (0 &lt; index &lt;
106      * constant_pool_count).</li>
107      * </ul>
108      * <li>Extra checks done in pass 1:
109      * <ul>
110      * <li>the constant values of static fields should have the same type as the fields.</li>
111      * <li>the number of words in a parameter list does not exceed 255 and locals_max.</li>
112      * <li>the name and signature of fields and methods are verified to be of legal format.</li>
113      * </ul>
114      * </ul>
115      * <p>
116      * (From the Paper <a href="https://www.cs.sfu.ca/people/GradStudents/pwfong/personal/JVM/pass1/"> The Mysterious Pass
117      * One, first draft, September 2, 1997</a>.)
118      * </p>
119      * <p>
120      * However, most of this is done by parsing a class file or generating a class file into BCEL's internal data structure.
121      * <strong>Therefore, all that is really done here is look up the class file from BCEL's repository.</strong> This is also
122      * motivated by the fact that some omitted things (like the check for extra bytes at the end of the class file) are
123      * handy when actually using BCEL to repair a class file (otherwise you would not be able to load it into BCEL).
124      * </p>
125      *
126      * @see org.apache.bcel.Repository
127      * @see org.apache.bcel.Const#JVM_CLASSFILE_MAGIC
128      */
129     @Override
130     public VerificationResult do_verify() {
131         final JavaClass jc;
132         try {
133             jc = getJavaClass(); // loads in the class file if not already done.
134 
135             /* If we find more constraints to check, we should do this in an own method. */
136             // This should maybe caught by BCEL: In case of renamed .class files we get wrong
137             // JavaClass objects here.
138             // This test should be much more complicated. It needs to take the class name, remove any portion at the
139             // end that matches the file name and then see if the remainder matches anything on the class path.
140             // Dumb test for now, see if the class name ends with the file name.
141             if (jc != null && !verifier.getClassName().equals(jc.getClassName()) && !jc.getClassName().endsWith(verifier.getClassName())) {
142                 throw new LoadingException("Wrong name: the internal name of the .class file '" + jc.getClassName() + "' does not match the file's name '"
143                     + verifier.getClassName() + "'.");
144             }
145         } catch (final LoadingException | ClassFormatException e) {
146             return new VerificationResult(VerificationResult.VERIFIED_REJECTED, e.getMessage());
147         } catch (final RuntimeException e) {
148             // BCEL does not catch every possible RuntimeException; for example if
149             // a constant pool index is referenced that does not exist.
150             return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Parsing via BCEL did not succeed.  exception occurred:\n" + e.toString());
151             // Don't think we want to dump a stack trace unless we have some sort of a debug option.
152             // e.getClass().getName()+" occurred:\n"+Utility.getStackTrace(e));
153         }
154 
155         if (jc != null) {
156             return VerificationResult.VR_OK;
157         }
158         // TODO: Maybe change Repository's behavior to throw a LoadingException instead of just returning "null"
159         // if a class file cannot be found or in another way be looked up.
160         return new VerificationResult(VerificationResult.VERIFIED_REJECTED, "Repository.lookup() failed. FILE NOT FOUND?");
161     }
162 
163     /**
164      * Used to load in and return the myOwner-matching JavaClass object when needed. Avoids loading in a class file when
165      * it's not really needed!
166      */
167     private JavaClass getJavaClass() {
168         if (javaClass == null) {
169             try {
170                 javaClass = Repository.lookupClass(verifier.getClassName());
171             } catch (final ClassNotFoundException ignored) {
172                 // FIXME: currently, Pass1Verifier treats jc == null as a special
173                 // case, so we don't need to do anything here. A better solution
174                 // would be to simply throw the ClassNotFoundException
175                 // out of this method.
176             }
177         }
178         return javaClass;
179     }
180 
181     /**
182      * Currently this returns an empty array of String. One could parse the error messages of BCEL (written to
183      * {@link System#err}) when loading a class file such as detecting unknown attributes or trailing garbage at the end
184      * of a class file. However, Markus Dahm does not like the idea so this method is currently useless and therefore marked
185      * as <strong>TODO</strong>.
186      */
187     @Override
188     public String[] getMessages() {
189         return ArrayUtils.EMPTY_STRING_ARRAY;
190     }
191 
192 }