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.classfile;
20  
21  import java.io.DataInput;
22  import java.io.DataInputStream;
23  import java.io.DataOutputStream;
24  import java.io.IOException;
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  import org.apache.bcel.Const;
29  import org.apache.bcel.util.Args;
30  
31  /**
32   * Abstract super class for <em>Attribute</em> objects. Currently the <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>, <em>Exceptiontable</em>,
33   * <em>LineNumberTable</em>, <em>LocalVariableTable</em>, <em>InnerClasses</em> and <em>Synthetic</em> attributes are supported. The <em>Unknown</em> attribute
34   * stands for non-standard-attributes.
35   *
36   * <pre>
37   * attribute_info {
38   *   u2 attribute_name_index;
39   *   u4 attribute_length;
40   *   u1 info[attribute_length];
41   * }
42   * </pre>
43   * <p>
44   * The default maximum number of attribute nesting levels in {@link #readAttribute(DataInput, ConstantPool)} is {@code 64} before throwing a
45   * {@link ClassFormatException}. This is configurable through the system property {@code org.apache.bcel.classfile.Attribute.maxNestingDepth}. Attributes may
46   * legitimately nest (for example, a <em>Code</em> attribute carries its own attribute table, and <em>Record</em> components carry theirs), but a malicious
47   * class file can nest such attributes deeply enough to overflow the parser's stack.
48   * </p>
49   *
50   * @see ConstantValue
51   * @see SourceFile
52   * @see Code
53   * @see Unknown
54   * @see ExceptionTable
55   * @see LineNumberTable
56   * @see LocalVariableTable
57   * @see InnerClasses
58   * @see Synthetic
59   * @see Deprecated
60   * @see Signature
61   */
62  public abstract class Attribute implements Cloneable, Node {
63  
64      private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off
65  
66      /**
67       * Maximum number of attribute nesting levels {@link #readAttribute(DataInput, ConstantPool)} accepts before throwing a {@link ClassFormatException},
68       * configurable through the system property {@code org.apache.bcel.classfile.Attribute.maxNestingDepth}. Attributes may legitimately nest (for example, a
69       * <em>Code</em> attribute carries its own attribute table, and <em>Record</em> components carry theirs), but a malicious class file can nest such
70       * attributes deeply enough to overflow the parser's stack.
71       */
72      private static final int MAX_NESTING_DEPTH = Integer.getInteger(Attribute.class.getCanonicalName() + ".maxNestingDepth", 64).intValue();
73  
74      /**
75       * The per-thread attribute nesting depth of {@link #readAttribute(DataInput, ConstantPool)}.
76       */
77      private static final ThreadLocal<Integer> NESTING_DEPTH = ThreadLocal.withInitial(() -> Integer.valueOf(0));
78  
79      private static final Map<String, Object> READERS = new HashMap<>();
80  
81      /**
82       * Empty array.
83       *
84       * @since 6.6.0
85       */
86      public static final Attribute[] EMPTY_ARRAY = {};
87  
88      /**
89       * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
90       * standard attributes such as "LineNumberTable", because those are handled internally.
91       *
92       * @param name The name of the attribute as stored in the class file.
93       * @param attributeReader The reader object.
94       * @deprecated (6.0) Use {@link #addAttributeReader(String, UnknownAttributeReader)} instead.
95       */
96      @java.lang.Deprecated
97      public static void addAttributeReader(final String name, final AttributeReader attributeReader) {
98          READERS.put(name, attributeReader);
99      }
100 
101     /**
102      * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the
103      * standard attributes such as "LineNumberTable", because those are handled internally.
104      *
105      * @param name The name of the attribute as stored in the class file.
106      * @param unknownAttributeReader The reader object.
107      */
108     public static void addAttributeReader(final String name, final UnknownAttributeReader unknownAttributeReader) {
109         READERS.put(name, unknownAttributeReader);
110     }
111 
112     /**
113      * Prints a message to stderr if debug mode is enabled.
114      *
115      * @param msg The message to print.
116      */
117     protected static void println(final String msg) {
118         if (debug) {
119             System.err.println(msg);
120         }
121     }
122 
123     /**
124      * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
125      * is called by the Field and Method constructor methods.
126      *
127      * @see Field
128      * @see Method
129      * @param dataInput Input stream.
130      * @param constantPool Array of constants.
131      * @return Attribute.
132      * @throws IOException Thrown if an I/O error occurs.
133      * @since 6.0
134      */
135     public static Attribute readAttribute(final DataInput dataInput, final ConstantPool constantPool) throws IOException {
136         // Track the nesting depth to guard against malicious class files that nest attributes (for example, a Code attribute inside a Code attribute, or
137         // mutually recursive Record component attributes) deeply enough to overflow the parser's stack (CWE-674).
138         final int depth = NESTING_DEPTH.get().intValue() + 1;
139         if (depth > MAX_NESTING_DEPTH) {
140             throw new ClassFormatException("Attributes are nested more than " + MAX_NESTING_DEPTH + " levels deep; if this is a valid class file, raise the"
141                     + " limit with the system property " + Attribute.class.getCanonicalName() + ".maxNestingDepth.");
142         }
143         NESTING_DEPTH.set(Integer.valueOf(depth));
144         try {
145             return readAttribute0(dataInput, constantPool);
146         } finally {
147             if (depth == 1) {
148                 NESTING_DEPTH.remove();
149             } else {
150                 NESTING_DEPTH.set(Integer.valueOf(depth - 1));
151             }
152         }
153     }
154 
155     /**
156      * Class method reads one attribute from the input data stream. This method must not be accessible from the outside. It
157      * is called by the Field and Method constructor methods.
158      *
159      * @see Field
160      * @see Method
161      * @param dataInputStream Input stream.
162      * @param constantPool Array of constants.
163      * @return Attribute.
164      * @throws IOException Thrown if an I/O error occurs.
165      */
166     public static Attribute readAttribute(final DataInputStream dataInputStream, final ConstantPool constantPool) throws IOException {
167         return readAttribute((DataInput) dataInputStream, constantPool);
168     }
169 
170     /**
171      * Reads one attribute without tracking the nesting depth; only to be called by {@link #readAttribute(DataInput, ConstantPool)}.
172      */
173     private static Attribute readAttribute0(final DataInput dataInput, final ConstantPool constantPool) throws IOException {
174         byte tag = Const.ATTR_UNKNOWN; // Unknown attribute
175         // Get class name from constant pool via 'name_index' indirection
176         final int nameIndex = dataInput.readUnsignedShort();
177         final String name = constantPool.getConstantUtf8(nameIndex).getBytes();
178 
179         // Length of data in bytes
180         final int length = dataInput.readInt();
181 
182         // Compare strings to find known attribute
183         for (byte i = 0; i < Const.KNOWN_ATTRIBUTES; i++) {
184             if (name.equals(Const.getAttributeName(i))) {
185                 tag = i; // found!
186                 break;
187             }
188         }
189 
190         // Call proper constructor, depending on 'tag'
191         switch (tag) {
192         case Const.ATTR_UNKNOWN:
193             final Object r = READERS.get(name);
194             if (r instanceof UnknownAttributeReader) {
195                 return ((UnknownAttributeReader) r).createAttribute(nameIndex, length, dataInput, constantPool);
196             }
197             return new Unknown(nameIndex, length, dataInput, constantPool);
198         case Const.ATTR_CONSTANT_VALUE:
199             return new ConstantValue(nameIndex, length, dataInput, constantPool);
200         case Const.ATTR_SOURCE_FILE:
201             return new SourceFile(nameIndex, length, dataInput, constantPool);
202         case Const.ATTR_CODE:
203             return new Code(nameIndex, length, dataInput, constantPool);
204         case Const.ATTR_EXCEPTIONS:
205             return new ExceptionTable(nameIndex, length, dataInput, constantPool);
206         case Const.ATTR_LINE_NUMBER_TABLE:
207             return new LineNumberTable(nameIndex, length, dataInput, constantPool);
208         case Const.ATTR_LOCAL_VARIABLE_TABLE:
209             return new LocalVariableTable(nameIndex, length, dataInput, constantPool);
210         case Const.ATTR_INNER_CLASSES:
211             return new InnerClasses(nameIndex, length, dataInput, constantPool);
212         case Const.ATTR_SYNTHETIC:
213             return new Synthetic(nameIndex, length, dataInput, constantPool);
214         case Const.ATTR_DEPRECATED:
215             return new Deprecated(nameIndex, length, dataInput, constantPool);
216         case Const.ATTR_PMG:
217             return new PMGClass(nameIndex, length, dataInput, constantPool);
218         case Const.ATTR_SIGNATURE:
219             return new Signature(nameIndex, length, dataInput, constantPool);
220         case Const.ATTR_STACK_MAP:
221             // old style stack map: unneeded for JDK5 and below;
222             // illegal(?) for JDK6 and above. So just delete with a warning.
223             println("Warning: Obsolete StackMap attribute ignored.");
224             return new Unknown(nameIndex, length, dataInput, constantPool);
225         case Const.ATTR_RUNTIME_VISIBLE_ANNOTATIONS:
226             return new RuntimeVisibleAnnotations(nameIndex, length, dataInput, constantPool);
227         case Const.ATTR_RUNTIME_INVISIBLE_ANNOTATIONS:
228             return new RuntimeInvisibleAnnotations(nameIndex, length, dataInput, constantPool);
229         case Const.ATTR_RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS:
230             return new RuntimeVisibleParameterAnnotations(nameIndex, length, dataInput, constantPool);
231         case Const.ATTR_RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS:
232             return new RuntimeInvisibleParameterAnnotations(nameIndex, length, dataInput, constantPool);
233         case Const.ATTR_ANNOTATION_DEFAULT:
234             return new AnnotationDefault(nameIndex, length, dataInput, constantPool);
235         case Const.ATTR_LOCAL_VARIABLE_TYPE_TABLE:
236             return new LocalVariableTypeTable(nameIndex, length, dataInput, constantPool);
237         case Const.ATTR_ENCLOSING_METHOD:
238             return new EnclosingMethod(nameIndex, length, dataInput, constantPool);
239         case Const.ATTR_STACK_MAP_TABLE:
240             // read new style stack map: StackMapTable. The rest of the code
241             // calls this a StackMap for historical reasons.
242             return new StackMap(nameIndex, length, dataInput, constantPool);
243         case Const.ATTR_BOOTSTRAP_METHODS:
244             return new BootstrapMethods(nameIndex, length, dataInput, constantPool);
245         case Const.ATTR_METHOD_PARAMETERS:
246             return new MethodParameters(nameIndex, length, dataInput, constantPool);
247         case Const.ATTR_MODULE:
248             return new Module(nameIndex, length, dataInput, constantPool);
249         case Const.ATTR_MODULE_PACKAGES:
250             return new ModulePackages(nameIndex, length, dataInput, constantPool);
251         case Const.ATTR_MODULE_MAIN_CLASS:
252             return new ModuleMainClass(nameIndex, length, dataInput, constantPool);
253         case Const.ATTR_NEST_HOST:
254             return new NestHost(nameIndex, length, dataInput, constantPool);
255         case Const.ATTR_NEST_MEMBERS:
256             return new NestMembers(nameIndex, length, dataInput, constantPool);
257         case Const.ATTR_RECORD:
258             return new Record(nameIndex, length, dataInput, constantPool);
259         case Const.ATTR_PERMITTED_SUBCLASSES:
260             return new PermittedSubclasses(nameIndex, length, dataInput, constantPool);
261         default:
262             // Never reached
263             throw new IllegalStateException("Unrecognized attribute type tag parsed: " + tag);
264         }
265     }
266 
267     /**
268      * Remove attribute reader
269      *
270      * @param name The name of the attribute as stored in the class file.
271      */
272     public static void removeAttributeReader(final String name) {
273         READERS.remove(name);
274     }
275 
276     /**
277      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter.
278      */
279     @java.lang.Deprecated
280     protected int name_index; // Points to attribute name in constant pool TODO make private (has getter & setter)
281 
282     /**
283      * @deprecated (since 6.0) (since 6.0) will be made private; do not access directly, use getter/setter.
284      */
285     @java.lang.Deprecated
286     protected int length; // Content length of attribute field TODO make private (has getter & setter).
287 
288     /**
289      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter.
290      */
291     @java.lang.Deprecated
292     protected byte tag; // Tag to distinguish subclasses TODO make private & final; supposed to be immutable.
293 
294     /**
295      * @deprecated (since 6.0) will be made private; do not access directly, use getter/setter.
296      */
297     @java.lang.Deprecated
298     protected ConstantPool constant_pool; // TODO make private (has getter & setter).
299 
300     /**
301      * Constructs an instance.
302      *
303      * <pre>
304      * attribute_info {
305      *   u2 attribute_name_index;
306      *   u4 attribute_length;
307      *   u1 info[attribute_length];
308      * }
309      * </pre>
310      *
311      * @param tag tag.
312      * @param nameIndex u2 name index.
313      * @param length u4 length.
314      * @param constantPool constant pool.
315      */
316     protected Attribute(final byte tag, final int nameIndex, final int length, final ConstantPool constantPool) {
317         this.tag = tag;
318         this.name_index = Args.requireU2(nameIndex, 0, constantPool.getLength(), getClass().getSimpleName() + " name index");
319         this.length = Args.requireU4(length, getClass().getSimpleName() + " attribute length");
320         this.constant_pool = constantPool;
321     }
322 
323     /**
324      * Called by objects that are traversing the nodes of the tree implicitly defined by the contents of a Java class.
325      * I.e., the hierarchy of methods, fields, attributes, etc. spawns a tree of objects.
326      *
327      * @param v Visitor object.
328      */
329     @Override
330     public abstract void accept(Visitor v);
331 
332     /**
333      * Use copy() if you want to have a deep copy(), that is, with all references copied correctly.
334      *
335      * @return shallow copy of this attribute.
336      */
337     @Override
338     public Object clone() {
339         Attribute attr = null;
340         try {
341             attr = (Attribute) super.clone();
342         } catch (final CloneNotSupportedException e) {
343             throw new UnsupportedOperationException("Clone Not Supported", e); // never happens
344         }
345         return attr;
346     }
347 
348     /**
349      * Creates a deep copy of this attribute.
350      *
351      * @param constantPool constant pool to save.
352      * @return deep copy of this attribute.
353      */
354     public abstract Attribute copy(ConstantPool constantPool);
355 
356     /**
357      * Dumps attribute to file stream in binary format.
358      *
359      * @param file Output file stream.
360      * @throws IOException Thrown if an I/O error occurs.
361      */
362     public void dump(final DataOutputStream file) throws IOException {
363         file.writeShort(name_index);
364         file.writeInt(length);
365     }
366 
367     /**
368      * Gets the constant pool used by this object.
369      *
370      * @return Constant pool used by this object.
371      * @see ConstantPool
372      */
373     public final ConstantPool getConstantPool() {
374         return constant_pool;
375     }
376 
377     /**
378      * Gets the length of attribute field in bytes.
379      *
380      * @return Length of attribute field in bytes.
381      */
382     public final int getLength() {
383         return length;
384     }
385 
386     /**
387      * Gets the name of attribute.
388      *
389      * @return Name of attribute.
390      * @since 6.0
391      */
392     public String getName() {
393         return constant_pool.getConstantUtf8(name_index).getBytes();
394     }
395 
396     /**
397      * Gets the name index in constant pool of attribute name.
398      *
399      * @return Name index in constant pool of attribute name.
400      */
401     public final int getNameIndex() {
402         return name_index;
403     }
404 
405     /**
406      * Gets the tag of attribute, that is, its type.
407      *
408      * @return Tag of attribute, that is, its type. Value may not be altered, thus there is no setTag() method.
409      */
410     public final byte getTag() {
411         return tag;
412     }
413 
414     /**
415      * Sets the constant pool to be used for this object.
416      *
417      * @param constantPool Constant pool to be used for this object.
418      * @see ConstantPool
419      */
420     public final void setConstantPool(final ConstantPool constantPool) {
421         this.constant_pool = constantPool;
422     }
423 
424     /**
425      * Sets the length in bytes.
426      *
427      * @param length length in bytes.
428      */
429     public final void setLength(final int length) {
430         this.length = length;
431     }
432 
433     /**
434      * Sets the name index of attribute.
435      *
436      * @param nameIndex of attribute.
437      */
438     public final void setNameIndex(final int nameIndex) {
439         this.name_index = nameIndex;
440     }
441 
442     /**
443      * @return attribute name.
444      */
445     @Override
446     public String toString() {
447         return Const.getAttributeName(tag);
448     }
449 }