|
| 1 | +From b424803abdb2bec818e4fbcb251ce031c22aca53 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Gary Gregory < [email protected]> |
| 3 | +Date: Sat, 21 Sep 2024 17:23:08 -0400 |
| 4 | +Subject: [PATCH] Rewrite ClassUtils.getClass() without recursion to avoid |
| 5 | + StackOverflowError on very long inputs. |
| 6 | + |
| 7 | +- This was found fuzz testing Apache Commons Text which relies on |
| 8 | +ClassUtils. |
| 9 | +- OssFuzz Issue 42522972: |
| 10 | +apache-commons-text:StringSubstitutorInterpolatorFuzzer: Security |
| 11 | +exception in org.apache.commons.lang3.ClassUtils.getClass |
| 12 | + |
| 13 | +Upstream Patch Reference: https://github.com/apache/commons-lang/commit/b424803abdb2bec818e4fbcb251ce031c22aca53.patch |
| 14 | +--- |
| 15 | + src/changes/changes.xml | 1 + |
| 16 | + .../org/apache/commons/lang3/ClassUtils.java | 46 +++++++++---------- |
| 17 | + 2 files changed, 23 insertions(+), 24 deletions(-) |
| 18 | + |
| 19 | +diff --git a/src/changes/changes.xml b/src/changes/changes.xml |
| 20 | +index 5731324..dd2577b 100644 |
| 21 | +--- a/src/changes/changes.xml |
| 22 | ++++ b/src/changes/changes.xml |
| 23 | +@@ -47,6 +47,7 @@ The <action> type attribute can be add,update,fix,remove. |
| 24 | + |
| 25 | + <release version="3.8.1" date="2018-09-19" description="This release is a bugfix for Restoring Bundle-SymbolicName in the MANIFEST.mf file."> |
| 26 | + <action issue="LANG-1419" type="fix" dev="chtompki">Restore BundleSymbolicName for OSGi</action> |
| 27 | ++ <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> |
| 28 | + </release> |
| 29 | + |
| 30 | + <release version="3.8" date="2018-08-15" description="New features and bug fixes. Requires Java 7, supports Java 8, 9, 10."> |
| 31 | +diff --git a/src/main/java/org/apache/commons/lang3/ClassUtils.java b/src/main/java/org/apache/commons/lang3/ClassUtils.java |
| 32 | +index be9f0dd..a9ec195 100644 |
| 33 | +--- a/src/main/java/org/apache/commons/lang3/ClassUtils.java |
| 34 | ++++ b/src/main/java/org/apache/commons/lang3/ClassUtils.java |
| 35 | +@@ -985,30 +985,27 @@ public class ClassUtils { |
| 36 | + */ |
| 37 | + public static Class<?> getClass( |
| 38 | + final ClassLoader classLoader, final String className, final boolean initialize) throws ClassNotFoundException { |
| 39 | +- try { |
| 40 | +- Class<?> clazz; |
| 41 | +- if (namePrimitiveMap.containsKey(className)) { |
| 42 | +- clazz = namePrimitiveMap.get(className); |
| 43 | +- } else { |
| 44 | +- clazz = Class.forName(toCanonicalName(className), initialize, classLoader); |
| 45 | +- } |
| 46 | +- return clazz; |
| 47 | +- } catch (final ClassNotFoundException ex) { |
| 48 | +- // allow path separators (.) as inner class name separators |
| 49 | +- final int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); |
| 50 | +- |
| 51 | +- if (lastDotIndex != -1) { |
| 52 | +- try { |
| 53 | +- return getClass(classLoader, className.substring(0, lastDotIndex) + |
| 54 | +- INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1), |
| 55 | +- initialize); |
| 56 | +- } catch (final ClassNotFoundException ex2) { // NOPMD |
| 57 | +- // ignore exception |
| 58 | ++ // This method was re-written to avoid recursion and stack overflows found by fuzz testing. |
| 59 | ++ String next = className; |
| 60 | ++ int lastDotIndex = -1; |
| 61 | ++ do { |
| 62 | ++ try { |
| 63 | ++ Class<?> clazz; |
| 64 | ++ if (namePrimitiveMap.containsKey(next)) { |
| 65 | ++ clazz = namePrimitiveMap.get(next); |
| 66 | ++ } else { |
| 67 | ++ clazz = Class.forName(toCanonicalName(next), initialize, classLoader); |
| 68 | ++ } |
| 69 | ++ return clazz; |
| 70 | ++ } catch (final ClassNotFoundException ex) { |
| 71 | ++ lastDotIndex = next.lastIndexOf(PACKAGE_SEPARATOR_CHAR); |
| 72 | ++ if (lastDotIndex != -1) { |
| 73 | ++ next = next.substring(0, lastDotIndex) + |
| 74 | ++ INNER_CLASS_SEPARATOR_CHAR + next.substring(lastDotIndex + 1); |
| 75 | + } |
| 76 | + } |
| 77 | +- |
| 78 | +- throw ex; |
| 79 | +- } |
| 80 | ++ } while (lastDotIndex != -1); |
| 81 | ++ throw new ClassNotFoundException(next); |
| 82 | + } |
| 83 | + |
| 84 | + /** |
| 85 | +@@ -1124,9 +1121,10 @@ public class ClassUtils { |
| 86 | + private static String toCanonicalName(String className) { |
| 87 | + className = StringUtils.deleteWhitespace(className); |
| 88 | + Validate.notNull(className, "className must not be null."); |
| 89 | +- if (className.endsWith("[]")) { |
| 90 | ++ final String arrayMarker = "[]"; |
| 91 | ++ if (className.endsWith(arrayMarker)) { |
| 92 | + final StringBuilder classNameBuffer = new StringBuilder(); |
| 93 | +- while (className.endsWith("[]")) { |
| 94 | ++ while (className.endsWith(arrayMarker)) { |
| 95 | + className = className.substring(0, className.length() - 2); |
| 96 | + classNameBuffer.append("["); |
| 97 | + } |
| 98 | +-- |
| 99 | +2.34.1 |
| 100 | + |
0 commit comments