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 /** 039 * DON'T USE THIS EVEN PRIVATELY! USE getJavaClass() INSTEAD. 040 * 041 * @see #getJavaClass() 042 */ 043 private JavaClass javaClass; 044 045 /** 046 * The Verifier that created this. 047 */ 048 private final Verifier verifier; 049 050 /** 051 * Should only be instantiated by a Verifier. 052 * 053 * @param verifier The verifier. 054 * @see Verifier 055 */ 056 public Pass1Verifier(final Verifier verifier) { 057 this.verifier = verifier; 058 } 059 060 /** 061 * Pass-one verification basically means loading in a class file. The Java Virtual Machine Specification is not too 062 * precise about what makes the difference between passes one and two. The answer is that only pass one is performed on 063 * a class file as long as its resolution is not requested; whereas pass two and pass three are performed during the 064 * resolution process. Only four constraints to be checked are explicitly stated by The Java Virtual Machine 065 * Specification, 2nd edition: 066 * <ul> 067 * <li>The first four bytes must contain the right magic number (0xCAFEBABE).</li> 068 * <li>All recognized attributes must be of the proper length.</li> 069 * <li>The class file must not be truncated or have extra bytes at the end.</li> 070 * <li>The constant pool must not contain any superficially unrecognizable information.</li> 071 * </ul> 072 * <p> 073 * A more in-depth documentation of what pass one should do was written by <a HREF=mailto:pwfong@cs.sfu.ca>Philip W. L. 074 * Fong</a>: 075 * </p> 076 * <ul> 077 * <li>the file should not be truncated.</li> 078 * <li>the file should not have extra bytes at the end.</li> 079 * <li>all variable-length structures should be well-formatted: 080 * <ul> 081 * <li>there should only be constant_pool_count-1 many entries in the constant pool.</li> 082 * <li>all constant pool entries should have size the same as indicated by their type tag.</li> 083 * <li>there are exactly interfaces_count many entries in the interfaces array of the class file.</li> 084 * <li>there are exactly fields_count many entries in the fields array of the class file.</li> 085 * <li>there are exactly methods_count many entries in the methods array of the class file.</li> 086 * <li>there are exactly attributes_count many entries in the attributes array of the class file, fields, methods, and 087 * code attribute.</li> 088 * <li>there should be exactly attribute_length many bytes in each attribute. Inconsistency between attribute_length and 089 * the actually size of the attribute content should be uncovered. For example, in an Exceptions attribute, the actual 090 * number of exceptions as required by the number_of_exceptions field might yeild an attribute size that doesn't match 091 * the attribute_length. Such an anomaly should be detected.</li> 092 * <li>all attributes should have proper length. In particular, under certain context (for example while parsing method_info), 093 * recognizable attributes (for example "Code" attribute) should have correct format (for example attribute_length is 2).</li> 094 * </ul> 095 * <li>Also, certain constant values are checked for validity: 096 * <ul> 097 * <li>The magic number should be 0xCAFEBABE.</li> 098 * <li>The major and minor version numbers are valid.</li> 099 * <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 < index < 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}