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.classfile; 020 021import java.io.DataInput; 022import java.io.DataInputStream; 023import java.io.DataOutputStream; 024import java.io.IOException; 025import java.util.HashMap; 026import java.util.Map; 027 028import org.apache.bcel.Const; 029import org.apache.bcel.util.Args; 030 031/** 032 * Abstract super class for <em>Attribute</em> objects. Currently the <em>ConstantValue</em>, <em>SourceFile</em>, <em>Code</em>, <em>Exceptiontable</em>, 033 * <em>LineNumberTable</em>, <em>LocalVariableTable</em>, <em>InnerClasses</em> and <em>Synthetic</em> attributes are supported. The <em>Unknown</em> attribute 034 * stands for non-standard-attributes. 035 * 036 * <pre> 037 * attribute_info { 038 * u2 attribute_name_index; 039 * u4 attribute_length; 040 * u1 info[attribute_length]; 041 * } 042 * </pre> 043 * <p> 044 * The default maximum number of attribute nesting levels in {@link #readAttribute(DataInput, ConstantPool)} is {@code 64} before throwing a 045 * {@link ClassFormatException}. This is configurable through the system property {@code org.apache.bcel.classfile.Attribute.maxNestingDepth}. Attributes may 046 * legitimately nest (for example, a <em>Code</em> attribute carries its own attribute table, and <em>Record</em> components carry theirs), but a malicious 047 * class file can nest such attributes deeply enough to overflow the parser's stack. 048 * </p> 049 * 050 * @see ConstantValue 051 * @see SourceFile 052 * @see Code 053 * @see Unknown 054 * @see ExceptionTable 055 * @see LineNumberTable 056 * @see LocalVariableTable 057 * @see InnerClasses 058 * @see Synthetic 059 * @see Deprecated 060 * @see Signature 061 */ 062public abstract class Attribute implements Cloneable, Node { 063 064 private static final boolean debug = Boolean.getBoolean(Attribute.class.getCanonicalName() + ".debug"); // Debugging on/off 065 066 /** 067 * Maximum number of attribute nesting levels {@link #readAttribute(DataInput, ConstantPool)} accepts before throwing a {@link ClassFormatException}, 068 * configurable through the system property {@code org.apache.bcel.classfile.Attribute.maxNestingDepth}. Attributes may legitimately nest (for example, a 069 * <em>Code</em> attribute carries its own attribute table, and <em>Record</em> components carry theirs), but a malicious class file can nest such 070 * attributes deeply enough to overflow the parser's stack. 071 */ 072 private static final int MAX_NESTING_DEPTH = Integer.getInteger(Attribute.class.getCanonicalName() + ".maxNestingDepth", 64).intValue(); 073 074 /** 075 * The per-thread attribute nesting depth of {@link #readAttribute(DataInput, ConstantPool)}. 076 */ 077 private static final ThreadLocal<Integer> NESTING_DEPTH = ThreadLocal.withInitial(() -> Integer.valueOf(0)); 078 079 private static final Map<String, Object> READERS = new HashMap<>(); 080 081 /** 082 * Empty array. 083 * 084 * @since 6.6.0 085 */ 086 public static final Attribute[] EMPTY_ARRAY = {}; 087 088 /** 089 * Add an Attribute reader capable of parsing (user-defined) attributes named "name". You should not add readers for the 090 * standard attributes such as "LineNumberTable", because those are handled internally. 091 * 092 * @param name The name of the attribute as stored in the class file. 093 * @param attributeReader The reader object. 094 * @deprecated (6.0) Use {@link #addAttributeReader(String, UnknownAttributeReader)} instead. 095 */ 096 @java.lang.Deprecated 097 public static void addAttributeReader(final String name, final AttributeReader attributeReader) { 098 READERS.put(name, attributeReader); 099 } 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}