Apache Commons Lang, Apache Commons Lang: ClassUtils.getClass(...) can throw a StackOverflowError on very long inputs
Description
Uncontrolled Recursion vulnerability in Apache Commons Lang.
This issue affects Apache Commons Lang: Starting with commons-lang:commons-lang 2.0 to 2.6, and, from org.apache.commons:commons-lang3 3.0 before 3.18.0.
The methods ClassUtils.getClass(...) can throw StackOverflowError on very long inputs. Because an Error is usually not handled by applications and libraries, a StackOverflowError could cause an application to stop.
Users are recommended to upgrade to version 3.18.0, which fixes the issue.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
Uncontrolled recursion in Apache Commons Lang's ClassUtils.getClass() can cause StackOverflowError on long inputs, leading to denial of service.
Vulnerability
CVE-2025-48924 is an uncontrolled recursion vulnerability in Apache Commons Lang's ClassUtils.getClass() methods. The issue affects commons-lang:commons-lang from version 2.0 to 2.6, and org.apache.commons:commons-lang3 from 3.0 before 3.18.0 [1][4]. The methods use recursion to process class names, and when provided with extremely long inputs, the recursion depth exceeds the Java stack limit, throwing a StackOverflowError [2].
Exploitation
An attacker can trigger the vulnerability by supplying a crafted, very long class name string to ClassUtils.getClass(). No authentication is required if the application accepts untrusted input that is passed to this method. The attack vector is simple: any mechanism that allows an attacker to control the class name argument can lead to the error [1][4].
Impact
Since StackOverflowError is an Error subclass, it is not typically caught by application or library error handling, causing the application to terminate. This results in a denial of service (DoS) condition. The vulnerability has a low CVSS severity due to the requirement of long input and the availability of a fix [1][4].
Mitigation
The fix was implemented in Apache Commons Lang 3.18.0 by rewriting the getClass() methods to avoid recursion [2]. Users are strongly recommended to upgrade to version 3.18.0 or later. For versions prior to 3.0 (i.e., the old commons-lang group), no patch is available as it is end-of-life; migration to commons-lang3 is advised [1][4].
AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.commons:commons-lang3Maven | >= 3.0, < 3.18.0 | 3.18.0 |
commons-lang:commons-langMaven | >= 2.0, <= 2.6 | — |
Affected products
2- Range: 2.0 - 2.6
- Apache Software Foundation/Apache Commons Langv5Range: 3.0
Patches
1b424803abdb2Rewrite ClassUtils.getClass() without recursion to avoid
3 files changed · +18 −19
src/changes/changes.xml+2 −1 modified@@ -50,7 +50,8 @@ The <action> type attribute can be add,update,fix,remove. <action type="fix" dev="ggregory" due-to="Gary Gregory">Fix flaky FileUtilsWaitForTest.testWaitForNegativeDuration().</action> <action type="fix" dev="ggregory" due-to="Gary Gregory">Pick up exec-maven-plugin version from parent POM.</action> <action type="fix" dev="ggregory" due-to="Gary Gregory">Speed up and sanitize StopWatchTest.</action> - <action type="fix" dev="ggregory" due-to="Fabrice Benhamouda">Fix handling of non-ASCII letters and numbers in RandomStringUtils #1273.</action> + <action type="fix" dev="ggregory" due-to="Fabrice Benhamouda">Fix handling of non-ASCII letters and numbers in RandomStringUtils #1273.</action> + <action type="fix" dev="ggregory" due-to="OSS-Fuzz, Gary Gregory">Rewrite ClassUtils.getClass(...) without recursion to avoid StackOverflowError on very long inputs. OSS-Fuzz Issue 42522972: apache-commons-text:StringSubstitutorInterpolatorFuzzer: Security exception in org.apache.commons.lang3.ClassUtils.getClass.</action> <!-- ADD --> <action type="add" dev="ggregory" due-to="Gary Gregory">Add Strings and refactor StringUtils.</action> <!-- UPDATE -->
src/main/java/org/apache/commons/lang3/ClassUtils.java+16 −18 modified@@ -527,24 +527,21 @@ public static Class<?> getClass(final ClassLoader classLoader, final String clas * @throws ClassNotFoundException if the class is not found */ public static Class<?> getClass(final ClassLoader classLoader, final String className, final boolean initialize) throws ClassNotFoundException { - try { - final Class<?> clazz = getPrimitiveClass(className); - return clazz != null ? clazz : Class.forName(toCanonicalName(className), initialize, classLoader); - } catch (final ClassNotFoundException ex) { - // allow path separators (.) as inner class name separators - final int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); - - if (lastDotIndex != -1) { - try { - return getClass(classLoader, className.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1), - initialize); - } catch (final ClassNotFoundException ignored) { - // ignore exception + // This method was re-written to avoid recursion and stack overflows found by fuzz testing. + String next = className; + int lastDotIndex = -1; + do { + try { + final Class<?> clazz = getPrimitiveClass(next); + return clazz != null ? clazz : Class.forName(toCanonicalName(next), initialize, classLoader); + } catch (final ClassNotFoundException ex) { + lastDotIndex = next.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + if (lastDotIndex != -1) { + next = next.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR_CHAR + next.substring(lastDotIndex + 1); } } - - throw ex; - } + } while (lastDotIndex != -1); + throw new ClassNotFoundException(next); } /** @@ -1504,9 +1501,10 @@ public static Class<?> primitiveToWrapper(final Class<?> cls) { private static String toCanonicalName(final String className) { String canonicalName = StringUtils.deleteWhitespace(className); Objects.requireNonNull(canonicalName, "className"); - if (canonicalName.endsWith("[]")) { + final String arrayMarker = "[]"; + if (canonicalName.endsWith(arrayMarker)) { final StringBuilder classNameBuffer = new StringBuilder(); - while (canonicalName.endsWith("[]")) { + while (canonicalName.endsWith(arrayMarker)) { canonicalName = canonicalName.substring(0, canonicalName.length() - 2); classNameBuffer.append("["); }
src/test/java/org/apache/commons/lang3/ClassUtilsOssFuzzTest.java+0 −0 added
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-j288-q9x7-2f5vghsaADVISORY
- lists.apache.org/thread/bgv0lpswokgol11tloxnjfzdl7yrc1g1ghsavendor-advisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2025-48924ghsaADVISORY
- www.openwall.com/lists/oss-security/2025/07/11/1ghsaWEB
- github.com/apache/commons-lang/commit/b424803abdb2bec818e4fbcb251ce031c22aca53ghsaWEB
- lists.debian.org/debian-lts-announce/2025/08/msg00000.htmlghsaWEB
- lists.debian.org/debian-lts-announce/2025/08/msg00026.htmlghsaWEB
- lists.debian.org/debian-lts-announce/2025/09/msg00032.htmlghsaWEB
- lists.debian.org/debian-lts-announce/2025/09/msg00036.htmlghsaWEB
News mentions
0No linked articles in our index yet.