Skip to content

Commit b424803

Browse files
committed
Rewrite ClassUtils.getClass() without recursion to avoid
StackOverflowError on very long inputs. - This was found fuzz testing Apache Commons Text which relies on ClassUtils. - OssFuzz Issue 42522972: apache-commons-text:StringSubstitutorInterpolatorFuzzer: Security exception in org.apache.commons.lang3.ClassUtils.getClass
1 parent 2d9eddb commit b424803

File tree

3 files changed

+18
-19
lines changed

3 files changed

+18
-19
lines changed

src/changes/changes.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ The <action> type attribute can be add,update,fix,remove.
5050
<action type="fix" dev="ggregory" due-to="Gary Gregory">Fix flaky FileUtilsWaitForTest.testWaitForNegativeDuration().</action>
5151
<action type="fix" dev="ggregory" due-to="Gary Gregory">Pick up exec-maven-plugin version from parent POM.</action>
5252
<action type="fix" dev="ggregory" due-to="Gary Gregory">Speed up and sanitize StopWatchTest.</action>
53-
<action type="fix" dev="ggregory" due-to="Fabrice Benhamouda">Fix handling of non-ASCII letters and numbers in RandomStringUtils #1273.</action>
53+
<action type="fix" dev="ggregory" due-to="Fabrice Benhamouda">Fix handling of non-ASCII letters and numbers in RandomStringUtils #1273.</action>
54+
<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>
5455
<!-- ADD -->
5556
<action type="add" dev="ggregory" due-to="Gary Gregory">Add Strings and refactor StringUtils.</action>
5657
<!-- UPDATE -->

src/main/java/org/apache/commons/lang3/ClassUtils.java

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -527,24 +527,21 @@ public static Class<?> getClass(final ClassLoader classLoader, final String clas
527527
* @throws ClassNotFoundException if the class is not found
528528
*/
529529
public static Class<?> getClass(final ClassLoader classLoader, final String className, final boolean initialize) throws ClassNotFoundException {
530-
try {
531-
final Class<?> clazz = getPrimitiveClass(className);
532-
return clazz != null ? clazz : Class.forName(toCanonicalName(className), initialize, classLoader);
533-
} catch (final ClassNotFoundException ex) {
534-
// allow path separators (.) as inner class name separators
535-
final int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
536-
537-
if (lastDotIndex != -1) {
538-
try {
539-
return getClass(classLoader, className.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1),
540-
initialize);
541-
} catch (final ClassNotFoundException ignored) {
542-
// ignore exception
530+
// This method was re-written to avoid recursion and stack overflows found by fuzz testing.
531+
String next = className;
532+
int lastDotIndex = -1;
533+
do {
534+
try {
535+
final Class<?> clazz = getPrimitiveClass(next);
536+
return clazz != null ? clazz : Class.forName(toCanonicalName(next), initialize, classLoader);
537+
} catch (final ClassNotFoundException ex) {
538+
lastDotIndex = next.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
539+
if (lastDotIndex != -1) {
540+
next = next.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR_CHAR + next.substring(lastDotIndex + 1);
543541
}
544542
}
545-
546-
throw ex;
547-
}
543+
} while (lastDotIndex != -1);
544+
throw new ClassNotFoundException(next);
548545
}
549546

550547
/**
@@ -1504,9 +1501,10 @@ public static Class<?> primitiveToWrapper(final Class<?> cls) {
15041501
private static String toCanonicalName(final String className) {
15051502
String canonicalName = StringUtils.deleteWhitespace(className);
15061503
Objects.requireNonNull(canonicalName, "className");
1507-
if (canonicalName.endsWith("[]")) {
1504+
final String arrayMarker = "[]";
1505+
if (canonicalName.endsWith(arrayMarker)) {
15081506
final StringBuilder classNameBuffer = new StringBuilder();
1509-
while (canonicalName.endsWith("[]")) {
1507+
while (canonicalName.endsWith(arrayMarker)) {
15101508
canonicalName = canonicalName.substring(0, canonicalName.length() - 2);
15111509
classNameBuffer.append("[");
15121510
}
Binary file not shown.

0 commit comments

Comments
 (0)