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 ae0a0c5f Signature.matchGJIdent recurses unboundedly on nested generic
signatures (Signature.translate) (f020).
ae0a0c5f is described below
commit ae0a0c5fedd307ada0eadc87cc22085f3c291d9b
Author: Gary Gregory <[email protected]>
AuthorDate: Fri Sep 4 17:56:41 2026 -0400
Signature.matchGJIdent recurses unboundedly on nested generic signatures
(Signature.translate) (f020).
---
src/changes/changes.xml | 1 +
.../java/org/apache/bcel/classfile/Signature.java | 19 ++++++++++++++++---
.../java/org/apache/bcel/classfile/SignatureTest.java | 13 +++++++++++++
3 files changed, 30 insertions(+), 3 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 6062f03e..1169a340 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -105,6 +105,7 @@ The <action> type attribute can be add,update,fix,remove.
<action type="fix" dev="ggregory" due-to="Gary
Gregory">Class2HTML builds output file paths from the unvalidated class name
(f015).</action>
<action type="fix" dev="ggregory" due-to="Gary
Gregory">ClassPath.getBytes() sizes its buffer from the forged ZIP
uncompressed-size field (f016).</action>
<action type="fix" dev="ggregory" due-to="Gary
Gregory">PMG attribute ignores declared length, enabling BCEL-vs-JVM parse
divergence (f019).</action>
+ <action type="fix" dev="ggregory" due-to="Gary
Gregory">Signature.matchGJIdent recurses unboundedly on nested generic
signatures (Signature.translate) (f020).</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/Signature.java
b/src/main/java/org/apache/bcel/classfile/Signature.java
index 1d40ef7d..68b12c88 100644
--- a/src/main/java/org/apache/bcel/classfile/Signature.java
+++ b/src/main/java/org/apache/bcel/classfile/Signature.java
@@ -81,14 +81,27 @@ public final class Signature extends Attribute {
return s.startsWith("<") && s.indexOf(':') > 0;
}
+ /**
+ * The maximum nesting depth of a signature accepted by {@link
#translate(String)}. Guards against a
+ * {@link StackOverflowError} from deeply nested, attacker-supplied
generic signatures.
+ */
+ private static final int MAX_NESTING_DEPTH = 512;
+
private static void matchGJIdent(final MyByteArrayInputStream in, final
StringBuilder buf) {
+ matchGJIdent(in, buf, 0);
+ }
+
+ private static void matchGJIdent(final MyByteArrayInputStream in, final
StringBuilder buf, final int depth) {
+ if (depth > MAX_NESTING_DEPTH) {
+ throw new IllegalArgumentException("Illegal signature: " +
in.getData() + " exceeds maximum nesting depth " + MAX_NESTING_DEPTH);
+ }
int ch;
matchIdent(in, buf);
ch = in.read();
if (ch == '<' || ch == '(') { // Parameterized or method
// System.out.println("Enter <");
buf.append((char) ch);
- matchGJIdent(in, buf);
+ matchGJIdent(in, buf, depth + 1);
while ((ch = in.read()) != '>' && ch != ')') { // List of
parameters
if (ch == -1) {
throw new IllegalArgumentException("Illegal signature: " +
in.getData() + " reaching EOF");
@@ -96,7 +109,7 @@ public final class Signature extends Attribute {
// System.out.println("Still no >");
buf.append(", ");
in.unread();
- matchGJIdent(in, buf); // Recursive call
+ matchGJIdent(in, buf, depth + 1); // Recursive call
}
// System.out.println("Exit >");
buf.append((char) ch);
@@ -106,7 +119,7 @@ public final class Signature extends Attribute {
ch = in.read();
if (identStart(ch)) {
in.unread();
- matchGJIdent(in, buf);
+ matchGJIdent(in, buf, depth + 1);
} else if (ch == ')') {
in.unread();
} else if (ch != ';') {
diff --git a/src/test/java/org/apache/bcel/classfile/SignatureTest.java
b/src/test/java/org/apache/bcel/classfile/SignatureTest.java
index 94697134..dac7fc8a 100644
--- a/src/test/java/org/apache/bcel/classfile/SignatureTest.java
+++ b/src/test/java/org/apache/bcel/classfile/SignatureTest.java
@@ -41,6 +41,19 @@ class SignatureTest extends AbstractTest {
assertThrowsExactly(IllegalArgumentException.class, () ->
Signature.translate("<>"));
}
+ /**
+ * Deeply nested attacker-supplied signatures must fail fast instead of
overflowing the stack.
+ */
+ @Test
+ void testDeeplyNestedSignature() {
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < 100_000; i++) {
+ sb.append("A<");
+ }
+ final String deep = sb.toString();
+ assertThrowsExactly(IllegalArgumentException.class, () ->
Signature.translate(deep));
+ }
+
@Test
void testMap() throws Exception {
final JavaClass jc = Repository.lookupClass(Map.class);