diff --git a/Changes.md b/Changes.md index 9ec48803..09afbadb 100644 --- a/Changes.md +++ b/Changes.md @@ -1,7 +1,15 @@ ### Changes +### version 3.33 on August 23, 2026 + +* API changes suggested by `@waydeshi` and `@lucianjohnhouse` + +* GitHub PR #523 + ### version 3.32 on June 21, 2026 +* API change suggested by Wayde Shi (GitHub: `@waydeshi`). + * Excludes javassist.tools.{reflect,rmi,web}. They are now included in ./examples/src/main. * Disables javassist.runtime.Desc.useContextClassLoader diff --git a/build.xml b/build.xml index 881800c7..3adc945d 100644 --- a/build.xml +++ b/build.xml @@ -6,7 +6,7 @@ - + diff --git a/javassist.jar b/javassist.jar index 81ebf22e..205859ca 100644 Binary files a/javassist.jar and b/javassist.jar differ diff --git a/pom.xml b/pom.xml index e888206c..c3f7928c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.javassist javassist - 3.32.0-GA + 3.33.0-GA bundle Javassist diff --git a/src/main/javassist/CtClass.java b/src/main/javassist/CtClass.java index 3133d1cc..a8c09ed6 100644 --- a/src/main/javassist/CtClass.java +++ b/src/main/javassist/CtClass.java @@ -69,7 +69,7 @@ public abstract class CtClass { /** * The version number of this release. */ - public static final String version = "3.32.0-GA"; + public static final String version = "3.33.0-GA"; private int linesCount = 0; diff --git a/src/main/javassist/bytecode/AttributeInfo.java b/src/main/javassist/bytecode/AttributeInfo.java index 7c264816..66d5dd5c 100644 --- a/src/main/javassist/bytecode/AttributeInfo.java +++ b/src/main/javassist/bytecode/AttributeInfo.java @@ -67,13 +67,37 @@ protected AttributeInfo(ConstPool cp, int n, DataInputStream in) constPool = cp; name = n; int len = in.readInt(); + info = allocateBytes(len); + if (len > 0) + in.readFully(info); + } + + private static volatile int maxAttributeLength = 0x7FFFFFFD; + + /** + * Sets the maximum length of an attribute. + * The default value is 0x7FFFFFFD. + * The value must be greater than or equal to 0xFFFF. + * + * @param n + * @since 3.33.0 + */ + public static void setMaxAttributeLength(int n) { + if (n < 0xffff) + throw new IllegalArgumentException("invalid limit: " + n); + else + maxAttributeLength = n; + } + + protected static byte[] allocateBytes(int len) throws IOException { + if (len < 0 || len > maxAttributeLength) + throw new IOException("Bad attribute length: " + len); + try { - info = new byte[len]; + return new byte[len]; } catch (Throwable e) { - throw new IOException("Error reading attribute info for " + n + " with size " + len, e); + throw new IOException("fail to allocate a byte array: " + len, e); } - if (len > 0) - in.readFully(info); } static AttributeInfo read(ConstPool cp, DataInputStream in) diff --git a/src/main/javassist/bytecode/CodeAttribute.java b/src/main/javassist/bytecode/CodeAttribute.java index cf8806ef..051fb2f6 100644 --- a/src/main/javassist/bytecode/CodeAttribute.java +++ b/src/main/javassist/bytecode/CodeAttribute.java @@ -106,6 +106,9 @@ private CodeAttribute(ConstPool cp, CodeAttribute src, Map classn maxLocals = in.readUnsignedShort(); int code_len = in.readInt(); + if (code_len <= 0 || 0xffff < code_len) // JVM specification 4.7.3 + throw new IOException("bad code attribute length: " + code_len); + info = new byte[code_len]; in.readFully(info); diff --git a/src/main/javassist/bytecode/RecordAttribute.java b/src/main/javassist/bytecode/RecordAttribute.java index fca20069..e1310b29 100644 --- a/src/main/javassist/bytecode/RecordAttribute.java +++ b/src/main/javassist/bytecode/RecordAttribute.java @@ -45,7 +45,7 @@ public RecordAttribute(ConstPool cp, int nameIndex, DataInputStream in) throws I pos += 2; int attrLength = ByteArray.read32bit(info, pos); pos += 4; - byte[] attrInfo = new byte[attrLength]; + byte[] attrInfo = allocateBytes(attrLength); System.arraycopy(info, pos, attrInfo, 0, attrLength); pos += attrLength; diff --git a/src/test/javassist/bytecode/BytecodeTest.java b/src/test/javassist/bytecode/BytecodeTest.java index cbd8410a..afdb593f 100644 --- a/src/test/javassist/bytecode/BytecodeTest.java +++ b/src/test/javassist/bytecode/BytecodeTest.java @@ -857,6 +857,56 @@ public void testInvokeDynamicWithCopy() throws Exception { assertEquals("hello", destObj.getClass().getMethod("getString").invoke(destObj)); } + public void testLargeAttributeInfo() throws Exception { + int attrLen = 0x7fffffff; + try { + new ClassFile(buildBrokenClassfile(attrLen)); + fail("Attribute length " + attrLen + " should throw an exception"); + } + catch (IOException e) { + assertEquals("Bad attribute length: " + attrLen, e.getMessage()); + } + + try { + AttributeInfo.setMaxAttributeLength(0xfffe); + } + catch (IllegalArgumentException e) { + assertTrue(e.getMessage().startsWith("invalid limit:")); + } + + AttributeInfo.setMaxAttributeLength(0x7FFFFFFF); + try { + new ClassFile(buildBrokenClassfile(attrLen)); + fail("Attribute length " + attrLen + " should throw an exception"); + } + catch (IOException e) { + assertTrue(e.getMessage().startsWith("fail to allocate a byte array")); + } + } + + static DataInputStream buildBrokenClassfile(int attrLen) throws Exception { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + dos.writeInt(0xCAFEBABE); + dos.writeShort(0); + dos.writeShort(52); + dos.writeShort(6); + dos.writeByte(1); dos.writeUTF("TestClass"); + dos.writeByte(7); dos.writeShort(1); + dos.writeByte(1); dos.writeUTF("java/lang/Object"); + dos.writeByte(7); dos.writeShort(3); + dos.writeByte(1); dos.writeUTF("EvilAttr"); + dos.writeShort(0x0001); + dos.writeShort(2); + dos.writeShort(4); + dos.writeShort(0); dos.writeShort(0); dos.writeShort(0); + dos.writeShort(1); + dos.writeShort(5); + dos.writeInt(attrLen); + dos.flush(); + return new DataInputStream(new ByteArrayInputStream(baos.toByteArray())); + } + public static Test suite() { TestSuite suite = new TestSuite("Bytecode Tests"); suite.addTestSuite(BytecodeTest.class);