This is an automated email from the ASF dual-hosted git repository.
garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-bcel.git
The following commit(s) were added to refs/heads/master by this push:
new 04f1eae5 ConstantPool.constantToString recurses forever on
self-referencing CONSTANT_MethodHandle (f010).
04f1eae5 is described below
commit 04f1eae50e54f99d580af4c060c7f9f9ae1cd4b2
Author: Gary Gregory <[email protected]>
AuthorDate: Fri Sep 4 16:13:36 2026 -0400
ConstantPool.constantToString recurses forever on self-referencing
CONSTANT_MethodHandle (f010).
---
src/changes/changes.xml | 1 +
src/main/java/org/apache/bcel/classfile/ConstantPool.java | 12 ++++++++++--
.../java/org/apache/bcel/classfile/ConstantPoolTest.java | 11 +++++++++++
3 files changed, 22 insertions(+), 2 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 7e1ad79e..a4d1bfea 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -97,6 +97,7 @@ The <action> type attribute can be add,update,fix,remove.
<action type="fix" dev="ggregory" due-to="Gary
Gregory">Cubic handlers × range × subroutines scan in Subroutines constructor
hangs JustIce Pass 3b (f007).</action>
<action type="fix" dev="ggregory" due-to="Gary
Gregory">Subroutines.noRecursiveCalls enumerates exponentially many JSR call
paths (f008).</action>
<action type="fix" dev="ggregory" due-to="Gary
Gregory">Disassembler and Class2HTML re-implement the unchecked switch-table
allocation (f009).</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">ConstantPool.constantToString recurses forever on self-referencing
CONSTANT_MethodHandle (f010).</action>
<!-- ADD -->
<action type="add" dev="ggregory" due-to="nbauma109,
Gary Gregory">Add support for permitted subclasses #493.</action>
<action type="add" dev="ggregory" due-to="nbauma109,
Gary Gregory">Add RecordComponentInfo.getAttribute(byte tag)#494.</action>
diff --git a/src/main/java/org/apache/bcel/classfile/ConstantPool.java
b/src/main/java/org/apache/bcel/classfile/ConstantPool.java
index cd8eb9f8..975030e6 100644
--- a/src/main/java/org/apache/bcel/classfile/ConstantPool.java
+++ b/src/main/java/org/apache/bcel/classfile/ConstantPool.java
@@ -166,8 +166,16 @@ public class ConstantPool implements Cloneable, Node,
Iterable<Constant> {
// Note that the ReferenceIndex may point to a Fieldref, Methodref
or
// InterfaceMethodref - so we need to peek ahead to get the actual
type.
final ConstantMethodHandle cmh = (ConstantMethodHandle) c;
- str = Const.getMethodHandleName(cmh.getReferenceKind()) + " "
- + constantToString(cmh.getReferenceIndex(),
getConstant(cmh.getReferenceIndex()).getTag());
+ final byte referenceTag =
getConstant(cmh.getReferenceIndex()).getTag();
+ // JVMS 4.4.8: the reference_index of a CONSTANT_MethodHandle must
point to a CONSTANT_Fieldref,
+ // CONSTANT_Methodref or CONSTANT_InterfaceMethodref entry.
Anything else is malformed; in particular, a
+ // CONSTANT_MethodHandle referencing another CONSTANT_MethodHandle
(such as itself) would otherwise make
+ // this method recurse without bound until a StackOverflowError.
+ if (referenceTag != Const.CONSTANT_Fieldref && referenceTag !=
Const.CONSTANT_Methodref && referenceTag != Const.CONSTANT_InterfaceMethodref) {
+ throw new ClassFormatException(
+ "Constant pool at index " + cmh.getReferenceIndex() +
" has an invalid tag " + referenceTag + " for a CONSTANT_MethodHandle
reference");
+ }
+ str = Const.getMethodHandleName(cmh.getReferenceKind()) + " " +
constantToString(cmh.getReferenceIndex(), referenceTag);
break;
case Const.CONSTANT_MethodType:
final ConstantMethodType cmt = (ConstantMethodType) c;
diff --git a/src/test/java/org/apache/bcel/classfile/ConstantPoolTest.java
b/src/test/java/org/apache/bcel/classfile/ConstantPoolTest.java
index bd484941..25ccd840 100644
--- a/src/test/java/org/apache/bcel/classfile/ConstantPoolTest.java
+++ b/src/test/java/org/apache/bcel/classfile/ConstantPoolTest.java
@@ -130,6 +130,17 @@ class ConstantPoolTest extends AbstractTest {
}
}
+ @Test
+ void testSelfReferencingConstantMethodHandle() {
+ // A malformed CONSTANT_MethodHandle whose reference_index points at
itself (JVMS 4.4.8 requires a
+ // CONSTANT_Fieldref, CONSTANT_Methodref or
CONSTANT_InterfaceMethodref there) must be reported
+ // instead of recursing until a StackOverflowError.
+ final Constant[] constants = new Constant[2];
+ constants[1] = new ConstantMethodHandle(Const.REF_invokeStatic, 1);
+ final ConstantPool pool = new ConstantPool(constants);
+ assertThrows(ClassFormatException.class, () ->
pool.constantToString(1, Const.CONSTANT_MethodHandle));
+ }
+
@Test
void testTooManyConstants() throws ClassNotFoundException {
final JavaClass clazz = getTestJavaClass(PACKAGE_BASE_NAME +
".data.SimpleClassWithDefaultConstructor");