Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions src/main/java/org/apache/bcel/classfile/Attribute.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ protected static void println(final String msg) {
* @since 6.0
*/
public static Attribute readAttribute(final DataInput dataInput, final ConstantPool constantPool) throws IOException {
return readAttribute(dataInput, constantPool, false);
}

/**
* Reads an attribute using the Code encoding selected by the enclosing class file's version.
*/
static Attribute readAttribute(final DataInput dataInput, final ConstantPool constantPool, final boolean legacyCodeFormat) throws IOException {
// Track the nesting depth to guard against malicious class files that nest attributes (for example, a Code attribute inside a Code attribute, or
// mutually recursive Record component attributes) deeply enough to overflow the parser's stack (CWE-674).
final int depth = NESTING_DEPTH.get().intValue() + 1;
Expand All @@ -143,7 +150,7 @@ public static Attribute readAttribute(final DataInput dataInput, final ConstantP
}
NESTING_DEPTH.set(Integer.valueOf(depth));
try {
return readAttribute0(dataInput, constantPool);
return readAttribute0(dataInput, constantPool, legacyCodeFormat);
} finally {
if (depth == 1) {
NESTING_DEPTH.remove();
Expand All @@ -169,9 +176,9 @@ public static Attribute readAttribute(final DataInputStream dataInputStream, fin
}

/**
* Reads one attribute without tracking the nesting depth; only to be called by {@link #readAttribute(DataInput, ConstantPool)}.
* Reads one attribute without tracking the nesting depth; only to be called by {@link #readAttribute(DataInput, ConstantPool, boolean)}.
*/
private static Attribute readAttribute0(final DataInput dataInput, final ConstantPool constantPool) throws IOException {
private static Attribute readAttribute0(final DataInput dataInput, final ConstantPool constantPool, final boolean legacyCodeFormat) throws IOException {
byte tag = Const.ATTR_UNKNOWN; // Unknown attribute
// Get class name from constant pool via 'name_index' indirection
final int nameIndex = dataInput.readUnsignedShort();
Expand Down Expand Up @@ -201,7 +208,7 @@ private static Attribute readAttribute0(final DataInput dataInput, final Constan
case Const.ATTR_SOURCE_FILE:
return new SourceFile(nameIndex, length, dataInput, constantPool);
case Const.ATTR_CODE:
return new Code(nameIndex, length, dataInput, constantPool);
return new Code(nameIndex, length, dataInput, constantPool, legacyCodeFormat);
case Const.ATTR_EXCEPTIONS:
return new ExceptionTable(nameIndex, length, dataInput, constantPool);
case Const.ATTR_LINE_NUMBER_TABLE:
Expand Down Expand Up @@ -256,7 +263,7 @@ private static Attribute readAttribute0(final DataInput dataInput, final Constan
case Const.ATTR_NEST_MEMBERS:
return new NestMembers(nameIndex, length, dataInput, constantPool);
case Const.ATTR_RECORD:
return new Record(nameIndex, length, dataInput, constantPool);
return new Record(nameIndex, length, dataInput, constantPool, legacyCodeFormat);
case Const.ATTR_PERMITTED_SUBCLASSES:
return new PermittedSubclasses(nameIndex, length, dataInput, constantPool);
case Const.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS:
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/org/apache/bcel/classfile/ClassParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ static int[] readU2U2Table(final DataInput dataInput) throws IOException {
private int superclassNameIndex;
private int major; // Compiler version
private int minor; // Compiler version
private boolean legacyCodeFormat; // True if class file version is <=45.2
private int accessFlags; // Access rights of parsed class
private int[] interfaces; // Names of implemented interfaces
private ConstantPool constantPool; // collection of constants
Expand Down Expand Up @@ -192,7 +193,7 @@ private void readAttributes() throws IOException, ClassFormatException {
final int attributesCount = dataInputStream.readUnsignedShort();
attributes = new Attribute[attributesCount];
for (int i = 0; i < attributesCount; i++) {
attributes[i] = Attribute.readAttribute(dataInputStream, constantPool);
attributes[i] = Attribute.readAttribute(dataInputStream, constantPool, legacyCodeFormat);
}
}

Expand Down Expand Up @@ -237,7 +238,7 @@ private void readFields() throws IOException, ClassFormatException {
final int fieldsCount = dataInputStream.readUnsignedShort();
fields = new Field[fieldsCount];
for (int i = 0; i < fieldsCount; i++) {
fields[i] = new Field(dataInputStream, constantPool);
fields[i] = new Field(dataInputStream, constantPool, legacyCodeFormat);
}
}

Expand Down Expand Up @@ -273,7 +274,7 @@ private void readMethods() throws IOException {
final int methodsCount = dataInputStream.readUnsignedShort();
methods = new Method[methodsCount];
for (int i = 0; i < methodsCount; i++) {
methods[i] = new Method(dataInputStream, constantPool);
methods[i] = new Method(dataInputStream, constantPool, legacyCodeFormat);
}
}

Expand All @@ -286,5 +287,6 @@ private void readMethods() throws IOException {
private void readVersion() throws IOException, ClassFormatException {
minor = dataInputStream.readUnsignedShort();
major = dataInputStream.readUnsignedShort();
legacyCodeFormat = major < Const.MAJOR_1_1 || major == Const.MAJOR_1_1 && minor < Const.MINOR_1_1;
}
}
47 changes: 38 additions & 9 deletions src/main/java/org/apache/bcel/classfile/Code.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
*/
public final class Code extends Attribute {

/**
* Whether the Code header uses the smaller fields of class file versions up to 45.2.
*/
private final boolean legacyCodeFormat;
private int maxStack; // Maximum size of stack used by this method // TODO this could be made final (setter is not used)
private int maxLocals; // Number of local variables // TODO this could be made final (setter is not used)
private byte[] code; // Actual byte code
Expand All @@ -77,7 +81,7 @@ public final class Code extends Attribute {
*/
public Code(final Code code) {
this(code.getNameIndex(), code.getLength(), code.getMaxStack(), code.getMaxLocals(), code.getCode(), code.getExceptionTable(), code.getAttributes(),
code.getConstantPool());
code.getConstantPool(), code.legacyCodeFormat);
}

/**
Expand All @@ -90,10 +94,18 @@ public Code(final Code code) {
* @throws ClassFormatException Thrown if the code array read from {@code file} is greater than {@link Const#MAX_CODE_SIZE}.
*/
Code(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool) throws IOException {
this(nameIndex, length, dataInput, constantPool, false);
}

/**
* Reads a Code attribute using the header widths selected by the enclosing class file's version.
*/
Code(final int nameIndex, final int length, final DataInput dataInput, final ConstantPool constantPool, final boolean legacyCodeFormat) throws IOException {
// Initialize with some default values which will be overwritten later
this(nameIndex, length, dataInput.readUnsignedShort(), dataInput.readUnsignedShort(), (byte[]) null, (CodeException[]) null, (Attribute[]) null,
constantPool);
final int codeLength = Args.requireU4(dataInput.readInt(), 0, Const.MAX_CODE_SIZE, "Code length attribute");
this(nameIndex, length, legacyCodeFormat ? dataInput.readUnsignedByte() : dataInput.readUnsignedShort(),
legacyCodeFormat ? dataInput.readUnsignedByte() : dataInput.readUnsignedShort(), null, null, null, constantPool, legacyCodeFormat);
final int codeLength = Args.requireU4(legacyCodeFormat ? dataInput.readUnsignedShort() : dataInput.readInt(), 0, Const.MAX_CODE_SIZE,
"Code length attribute");
code = new byte[codeLength]; // Read byte code
dataInput.readFully(code);
/*
Expand All @@ -110,7 +122,7 @@ public Code(final Code code) {
final int attributesCount = dataInput.readUnsignedShort();
attributes = new Attribute[attributesCount];
for (int i = 0; i < attributesCount; i++) {
attributes[i] = readAttribute(dataInput, constantPool);
attributes[i] = readAttribute(dataInput, constantPool, legacyCodeFormat);
}
/*
* Adjust length, because of setAttributes in this(), s.b. length is incorrect, because it didn't take the internal attributes into account yet! Very
Expand All @@ -134,7 +146,13 @@ public Code(final Code code) {
*/
public Code(final int nameIndex, final int length, final int maxStack, final int maxLocals, final byte[] code, final CodeException[] exceptionTable,
final Attribute[] attributes, final ConstantPool constantPool) {
this(nameIndex, length, maxStack, maxLocals, code, exceptionTable, attributes, constantPool, false);
}

private Code(final int nameIndex, final int length, final int maxStack, final int maxLocals, final byte[] code, final CodeException[] exceptionTable,
final Attribute[] attributes, final ConstantPool constantPool, final boolean legacyCodeFormat) {
super(Const.ATTR_CODE, nameIndex, length, constantPool);
this.legacyCodeFormat = legacyCodeFormat;
this.maxStack = Args.requireU2(maxStack, "maxStack");
this.maxLocals = Args.requireU2(maxLocals, "maxLocals");
this.code = ArrayUtils.nullToEmpty(code);
Expand Down Expand Up @@ -197,10 +215,21 @@ public Attribute copy(final ConstantPool constantPool) {
*/
@Override
public void dump(final DataOutputStream file) throws IOException {
if (legacyCodeFormat) {
Args.requireU1(maxStack, "maxStack");
Args.requireU1(maxLocals, "maxLocals");
Args.requireU2(code.length, "Code length attribute");
}
super.dump(file);
file.writeShort(maxStack);
file.writeShort(maxLocals);
file.writeInt(code.length);
if (legacyCodeFormat) {
file.writeByte(maxStack);
file.writeByte(maxLocals);
file.writeShort(code.length);
} else {
file.writeShort(maxStack);
file.writeShort(maxLocals);
file.writeInt(code.length);
}
file.write(code, 0, code.length);
file.writeShort(Args.requireU2(exceptionTable.length, "exceptionTable.length"));
for (final CodeException exception : exceptionTable) {
Expand Down Expand Up @@ -247,7 +276,7 @@ public CodeException[] getExceptionTable() {
* @return The internal length of this code attribute (minus the first 6 bytes) and excluding all its attributes.
*/
private int getInternalLength() {
return 2 /* maxStack */ + 2 /* maxLocals */ + 4 /* code length */
return (legacyCodeFormat ? 4 : 8) /* maxStack, maxLocals, code length */
+ code.length /* byte-code */
+ 2 /* exception-table length */
+ 8 * (exceptionTable == null ? 0 : exceptionTable.length) /* exception table */
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/org/apache/bcel/classfile/Field.java
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,11 @@ public static void setComparator(final BCELComparator<Field> comparator) {
* @param constantPool The constant pool.
*/
Field(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException {
super(file, constantPool);
this(file, constantPool, false);
}

Field(final DataInput file, final ConstantPool constantPool, final boolean legacyCodeFormat) throws IOException {
super(file, constantPool, legacyCodeFormat);
}

/**
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/org/apache/bcel/classfile/FieldOrMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,15 @@ public abstract class FieldOrMethod extends AccessFlags implements Cloneable, No
* @throws IOException Thrown if an I/O error occurs.
*/
protected FieldOrMethod(final DataInput file, final ConstantPool constantPool) throws IOException {
this(file, constantPool, false);
}

FieldOrMethod(final DataInput file, final ConstantPool constantPool, final boolean legacyCodeFormat) throws IOException {
this(file.readUnsignedShort(), file.readUnsignedShort(), file.readUnsignedShort(), null, constantPool);
final int attributesCount = file.readUnsignedShort();
attributes = new Attribute[attributesCount];
for (int i = 0; i < attributesCount; i++) {
attributes[i] = Attribute.readAttribute(file, constantPool);
attributes[i] = Attribute.readAttribute(file, constantPool, legacyCodeFormat);
}
this.attributes_count = attributesCount; // init deprecated field
}
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/org/apache/bcel/classfile/Method.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,11 @@ public Method() {
* @throws ClassFormatException Thrown if a class is malformed or cannot be interpreted as a class file.
*/
Method(final DataInput file, final ConstantPool constantPool) throws IOException, ClassFormatException {
super(file, constantPool);
this(file, constantPool, false);
}

Method(final DataInput file, final ConstantPool constantPool, final boolean legacyCodeFormat) throws IOException {
super(file, constantPool, legacyCodeFormat);
}

/**
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/org/apache/bcel/classfile/Record.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ public final class Record extends Attribute {

private static final RecordComponentInfo[] EMPTY_RCI_ARRAY = {};

private static RecordComponentInfo[] readComponents(final DataInput input, final ConstantPool constantPool)
private static RecordComponentInfo[] readComponents(final DataInput input, final ConstantPool constantPool, final boolean legacyCodeFormat)
throws IOException {
final int classCount = input.readUnsignedShort();
final RecordComponentInfo[] components = new RecordComponentInfo[classCount];
for (int i = 0; i < classCount; i++) {
components[i] = new RecordComponentInfo(input, constantPool);
components[i] = new RecordComponentInfo(input, constantPool, legacyCodeFormat);
}
return components;
}
Expand All @@ -62,7 +62,12 @@ private static RecordComponentInfo[] readComponents(final DataInput input, final
*/
Record(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool)
throws IOException {
this(nameIndex, length, readComponents(input, constantPool), constantPool);
this(nameIndex, length, input, constantPool, false);
}

Record(final int nameIndex, final int length, final DataInput input, final ConstantPool constantPool, final boolean legacyCodeFormat)
throws IOException {
this(nameIndex, length, readComponents(input, constantPool, legacyCodeFormat), constantPool);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,16 @@ public class RecordComponentInfo implements Node {
* @throws IOException Thrown if an I/O error occurs.
*/
public RecordComponentInfo(final DataInput input, final ConstantPool constantPool) throws IOException {
this(input, constantPool, false);
}

RecordComponentInfo(final DataInput input, final ConstantPool constantPool, final boolean legacyCodeFormat) throws IOException {
this.index = input.readUnsignedShort();
this.descriptorIndex = input.readUnsignedShort();
final int attributesCount = input.readUnsignedShort();
this.attributes = new Attribute[attributesCount];
for (int j = 0; j < attributesCount; j++) {
attributes[j] = Attribute.readAttribute(input, constantPool);
attributes[j] = Attribute.readAttribute(input, constantPool, legacyCodeFormat);
}
this.constantPool = constantPool;
}
Expand Down
8 changes: 8 additions & 0 deletions src/test/java/org/apache/bcel/classfile/ClassParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@

package org.apache.bcel.classfile;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.io.IOException;

import org.junit.jupiter.api.Test;

/**
Expand All @@ -32,4 +35,9 @@ class ClassParserTest {
void testParseInvalidClass() {
assertThrows(ClassFormatException.class, () -> new ClassParser("src/test/resources/org/apache/commons/bcel/classfile/CodeCtor.class").parse());
}

@Test
void testParseOakClassFile() throws IOException {
assertNotNull(new ClassParser("src/test/resources/Java1OakExample.class").parse());
}
}
Binary file added src/test/resources/Java1OakExample.class
Binary file not shown.
5 changes: 5 additions & 0 deletions src/test/resources/Java1OakExample.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
public class Java1OakExample {
public static void main(String args[]) {
System.out.println("Hello Oak!");
}
}