Skip to content

Commit 36f7bf0

Browse files
authored
Merge pull request #22247 from chrisdennis/issue-14143-reflection-cache-poisoning
Prevent non-root accessible objects from poisoning the cache
2 parents f4c006a + ab6a8ce commit 36f7bf0

File tree

1 file changed

+83
-16
lines changed
  • jcl/src/java.base/share/classes/java/lang

1 file changed

+83
-16
lines changed

jcl/src/java.base/share/classes/java/lang/Class.java

Lines changed: 83 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,7 +1409,7 @@ private List<Method> cacheDeclaredPublicMethods(List<Method> methods, CacheKey c
14091409
methods.set(i, methodPut);
14101410
}
14111411
}
1412-
cache.insert(cacheKey, methods);
1412+
cache.insertRoot(cacheKey, methods);
14131413
} finally {
14141414
if (cache != null) {
14151415
cache.release();
@@ -4549,32 +4549,38 @@ private String getParameterTypesSignature(boolean throwException, String name, C
45494549
return signature.toString();
45504550
}
45514551

4552+
/*[IF JAVA_SPEC_VERSION <= 11] */
4553+
private static Method getRootMethod;
45524554
/*[IF JAVA_SPEC_VERSION == 8]*/
45534555
/*[PR CMVC 114820, CMVC 115873, CMVC 116166] add reflection cache */
45544556
private static Method copyMethod, copyField, copyConstructor;
4557+
private static Field rootField;
45554558
private static Field methodParameterTypesField;
45564559
private static Field constructorParameterTypesField;
4557-
private static final Object[] NoArgs = new Object[0];
45584560
/*[ENDIF] JAVA_SPEC_VERSION == 8 */
4561+
private static final Object[] NoArgs = new Object[0];
4562+
/*[ENDIF] JAVA_SPEC_VERSION <= 11 */
45594563

45604564
/*[PR JAZZ 107786] constructorParameterTypesField should be initialized regardless of reflectCacheEnabled or not */
45614565
static void initCacheIds(boolean cacheEnabled, boolean cacheDebug) {
45624566
reflectCacheEnabled = cacheEnabled;
45634567
reflectCacheDebug = cacheDebug;
4564-
/*[IF JAVA_SPEC_VERSION == 8]*/
4568+
/*[IF JAVA_SPEC_VERSION <= 11]*/
45654569
AccessController.doPrivileged(new PrivilegedAction<Void>() {
45664570
@Override
45674571
public Void run() {
45684572
doInitCacheIds();
45694573
return null;
45704574
}
45714575
});
4572-
/*[ENDIF] JAVA_SPEC_VERSION == 8 */
4576+
/*[ENDIF] JAVA_SPEC_VERSION <= 11 */
45734577
}
4578+
45744579
static void setReflectCacheAppOnly(boolean cacheAppOnly) {
45754580
reflectCacheAppOnly = cacheAppOnly;
45764581
}
4577-
/*[IF JAVA_SPEC_VERSION == 8]*/
4582+
4583+
/*[IF JAVA_SPEC_VERSION <= 11]*/
45784584
@SuppressWarnings("nls")
45794585
static void doInitCacheIds() {
45804586
/*
@@ -4585,6 +4591,7 @@ static void doInitCacheIds() {
45854591
* not done (we're in the process of initializing the caching mechanisms).
45864592
* We must ensure the classes that own the fields of interest are prepared.
45874593
*/
4594+
/*[IF JAVA_SPEC_VERSION == 8]*/
45884595
J9VMInternals.prepare(Constructor.class);
45894596
J9VMInternals.prepare(Method.class);
45904597
try {
@@ -4596,12 +4603,28 @@ static void doInitCacheIds() {
45964603
constructorParameterTypesField.setAccessible(true);
45974604
methodParameterTypesField.setAccessible(true);
45984605
if (reflectCacheEnabled) {
4606+
J9VMInternals.prepare(Executable.class);
4607+
J9VMInternals.prepare(Field.class);
45994608
copyConstructor = getAccessibleMethod(Constructor.class, "copy");
46004609
copyMethod = getAccessibleMethod(Method.class, "copy");
46014610
copyField = getAccessibleMethod(Field.class, "copy");
4611+
getRootMethod = getAccessibleMethod(Executable.class, "getRoot");
4612+
try {
4613+
rootField = Field.class.getDeclaredFieldImpl("root");
4614+
} catch (NoSuchFieldException e) {
4615+
throw newInternalError(e);
4616+
}
4617+
rootField.setAccessible(true);
46024618
}
4619+
/*[ELSE] JAVA_SPEC_VERSION == 8 */
4620+
J9VMInternals.prepare(AccessibleObject.class);
4621+
if (reflectCacheEnabled) {
4622+
getRootMethod = getAccessibleMethod(AccessibleObject.class, "getRoot");
4623+
}
4624+
/*[ENDIF] JAVA_SPEC_VERSION == 8 */
46034625
}
4604-
/*[ENDIF] JAVA_SPEC_VERSION == 8 */
4626+
/*[ENDIF] JAVA_SPEC_VERSION <= 11 */
4627+
46054628
private static Method getAccessibleMethod(Class<?> cls, String name) {
46064629
try {
46074630
Method method = cls.getDeclaredMethod(name, EmptyParameters);
@@ -4974,16 +4997,24 @@ Object find(CacheKey key) {
49744997
return ref != null ? ref.get() : null;
49754998
}
49764999

4977-
void insert(CacheKey key, Object value) {
5000+
void insert(CacheKey key, AccessibleObject value) {
5001+
insertRoot(key, retrieveRoot(value));
5002+
}
5003+
5004+
/*
5005+
* This method must only be called with known roots or aggregates of roots.
5006+
*/
5007+
void insertRoot(CacheKey key, Object value) {
49785008
put(key, new ReflectRef(this, key, value));
49795009
}
49805010

4981-
<T> T insertIfAbsent(CacheKey key, T value) {
4982-
ReflectRef newRef = new ReflectRef(this, key, value);
5011+
<T extends AccessibleObject> T insertIfAbsent(CacheKey key, T value) {
5012+
T rootValue = retrieveRoot(value);
5013+
ReflectRef newRef = new ReflectRef(this, key, rootValue);
49835014
for (;;) {
49845015
ReflectRef oldRef = putIfAbsent(key, newRef);
49855016
if (oldRef == null) {
4986-
return value;
5017+
return rootValue;
49875018
}
49885019
T oldValue = (T) oldRef.get();
49895020
if (oldValue != null) {
@@ -4992,11 +5023,23 @@ <T> T insertIfAbsent(CacheKey key, T value) {
49925023
// The entry addressed by key has been cleared, but not yet removed from this map.
49935024
// One thread will successfully replace the entry; the value stored will be shared.
49945025
if (replace(key, oldRef, newRef)) {
4995-
return value;
5026+
return rootValue;
49965027
}
49975028
}
49985029
}
49995030

5031+
private static <T extends AccessibleObject> T retrieveRoot(T value) {
5032+
T root = getRoot(value);
5033+
if (root != null) {
5034+
if (reflectCacheDebug) {
5035+
System.err.println("extracted root from non-root accessible object before caching: " + value);
5036+
}
5037+
return root;
5038+
} else {
5039+
return value;
5040+
}
5041+
}
5042+
50005043
void release() {
50015044
useCount.decrementAndGet();
50025045
}
@@ -5120,7 +5163,7 @@ private Method cacheMethod(Method method) {
51205163
try {
51215164
// cache the Method with the largest depth with a null returnType
51225165
CacheKey lookupKey = CacheKey.newMethodKey(method.getName(), parameterTypes, null);
5123-
cache.insert(lookupKey, method);
5166+
cache.insertRoot(lookupKey, method);
51245167
} finally {
51255168
cache.release();
51265169
}
@@ -5188,7 +5231,7 @@ private Field cacheField(Field field) {
51885231
if (declaringClass == this) {
51895232
// cache the Field returned from getField() with a null returnType
51905233
CacheKey lookupKey = CacheKey.newFieldKey(field.getName(), null);
5191-
cache.insert(lookupKey, field);
5234+
cache.insertRoot(lookupKey, field);
51925235
}
51935236
} finally {
51945237
cache.release();
@@ -5349,7 +5392,7 @@ private Method[] cacheMethods(Method[] methods, CacheKey cacheKey) {
53495392
if (cache == null) {
53505393
cache = acquireReflectCache();
53515394
}
5352-
cache.insert(cacheKey, methods);
5395+
cache.insertRoot(cacheKey, methods);
53535396
} finally {
53545397
if (cache != null) {
53555398
cache.release();
@@ -5433,7 +5476,7 @@ private Field[] cacheFields(Field[] fields, CacheKey cacheKey) {
54335476
if (cache == null) {
54345477
cache = acquireReflectCache();
54355478
}
5436-
cache.insert(cacheKey, fields);
5479+
cache.insertRoot(cacheKey, fields);
54375480
} finally {
54385481
if (cache != null) {
54395482
cache.release();
@@ -5499,7 +5542,7 @@ private Constructor<T>[] cacheConstructors(Constructor<T>[] constructors, CacheK
54995542
CacheKey key = CacheKey.newConstructorKey(getParameterTypes(constructors[i]));
55005543
constructors[i] = cache.insertIfAbsent(key, constructors[i]);
55015544
}
5502-
cache.insert(cacheKey, constructors);
5545+
cache.insertRoot(cacheKey, constructors);
55035546
} finally {
55045547
cache.release();
55055548
}
@@ -5548,6 +5591,30 @@ Object setMethodHandleCache(Object cache) {
55485591
return result;
55495592
}
55505593

5594+
static <T extends AccessibleObject> T getRoot(T object) {
5595+
/*[IF JAVA_SPEC_VERSION >= 17]*/
5596+
return SharedSecrets.getJavaLangReflectAccess().getRoot(object);
5597+
/*[ELSEIF JAVA_SPEC_VERSION >= 11] */
5598+
try {
5599+
return (T) getRootMethod.invoke(object, NoArgs);
5600+
} catch (IllegalAccessException | InvocationTargetException e) {
5601+
throw newInternalError(e);
5602+
}
5603+
/*[ELSE] JAVA_SPEC_VERSION >= 11 */
5604+
try {
5605+
if (object instanceof Executable) {
5606+
return (T) getRootMethod.invoke(object, NoArgs);
5607+
} else if (object instanceof Field) {
5608+
return (T) rootField.get(object);
5609+
} else {
5610+
throw newInternalError(new IllegalArgumentException("Unexpected AccessibleObject subclass: " + object.getClass().getSimpleName()));
5611+
}
5612+
} catch (IllegalAccessException | InvocationTargetException e) {
5613+
throw newInternalError(e);
5614+
}
5615+
/*[ENDIF] JAVA_SPEC_VERSION >= 17 */
5616+
}
5617+
55515618
ConstantPool getConstantPool() {
55525619
return SharedSecrets.getJavaLangAccess().getConstantPool(this);
55535620
}

0 commit comments

Comments
 (0)