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");

Reply via email to