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 }