MethodInvokeTypeProvider lazily invokes target method (avoiding deserialization exploits)
Issue: SPR-13656
(cherry picked from commit ea2843e)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2014 the original author or authors.
|
||||
* Copyright 2002-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -51,6 +51,7 @@ import org.springframework.util.ReflectionUtils;
|
||||
* {@link GenericArrayType#getGenericComponentType()}) will be automatically wrapped.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.0
|
||||
*/
|
||||
abstract class SerializableTypeWrapper {
|
||||
@@ -58,15 +59,10 @@ abstract class SerializableTypeWrapper {
|
||||
private static final Class<?>[] SUPPORTED_SERIALIZABLE_TYPES = {
|
||||
GenericArrayType.class, ParameterizedType.class, TypeVariable.class, WildcardType.class};
|
||||
|
||||
private static final Method EQUALS_METHOD = ReflectionUtils.findMethod(Object.class,
|
||||
"equals", Object.class);
|
||||
|
||||
private static final Method GET_TYPE_PROVIDER_METHOD = ReflectionUtils.findMethod(
|
||||
SerializableTypeProxy.class, "getTypeProvider");
|
||||
|
||||
private static final ConcurrentReferenceHashMap<Type, Type> cache =
|
||||
new ConcurrentReferenceHashMap<Type, Type>(256);
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link Serializable} variant of {@link Field#getGenericType()}.
|
||||
*/
|
||||
@@ -161,35 +157,33 @@ abstract class SerializableTypeWrapper {
|
||||
for (Class<?> type : SUPPORTED_SERIALIZABLE_TYPES) {
|
||||
if (type.isAssignableFrom(provider.getType().getClass())) {
|
||||
ClassLoader classLoader = provider.getClass().getClassLoader();
|
||||
Class<?>[] interfaces = new Class<?>[] { type,
|
||||
SerializableTypeProxy.class, Serializable.class };
|
||||
Class<?>[] interfaces = new Class<?>[] {type, SerializableTypeProxy.class, Serializable.class};
|
||||
InvocationHandler handler = new TypeProxyInvocationHandler(provider);
|
||||
cached = (Type) Proxy.newProxyInstance(classLoader, interfaces, handler);
|
||||
cache.put(provider.getType(), cached);
|
||||
return cached;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unsupported Type class " + provider.getType().getClass().getName());
|
||||
throw new IllegalArgumentException("Unsupported Type class: " + provider.getType().getClass().getName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Additional interface implemented by the type proxy.
|
||||
*/
|
||||
static interface SerializableTypeProxy {
|
||||
interface SerializableTypeProxy {
|
||||
|
||||
/**
|
||||
* Return the underlying type provider.
|
||||
*/
|
||||
TypeProvider getTypeProvider();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A {@link Serializable} interface providing access to a {@link Type}.
|
||||
*/
|
||||
static interface TypeProvider extends Serializable {
|
||||
interface TypeProvider extends Serializable {
|
||||
|
||||
/**
|
||||
* Return the (possibly non {@link Serializable}) {@link Type}.
|
||||
@@ -213,12 +207,11 @@ abstract class SerializableTypeWrapper {
|
||||
public Object getSource() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@link Serializable} {@link InvocationHandler} used by the Proxied {@link Type}.
|
||||
* {@link Serializable} {@link InvocationHandler} used by the proxied {@link Type}.
|
||||
* Provides serialization support and enhances any methods that return {@code Type}
|
||||
* or {@code Type[]}.
|
||||
*/
|
||||
@@ -233,10 +226,7 @@ abstract class SerializableTypeWrapper {
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (GET_TYPE_PROVIDER_METHOD.equals(method)) {
|
||||
return this.provider;
|
||||
}
|
||||
if (EQUALS_METHOD.equals(method)) {
|
||||
if (method.getName().equals("equals")) {
|
||||
Object other = args[0];
|
||||
// Unwrap proxies for speed
|
||||
if (other instanceof Type) {
|
||||
@@ -244,6 +234,13 @@ abstract class SerializableTypeWrapper {
|
||||
}
|
||||
return this.provider.getType().equals(other);
|
||||
}
|
||||
else if (method.getName().equals("hashCode")) {
|
||||
return this.provider.getType().hashCode();
|
||||
}
|
||||
else if (method.getName().equals("getTypeProvider")) {
|
||||
return this.provider;
|
||||
}
|
||||
|
||||
if (Type.class.equals(method.getReturnType()) && args == null) {
|
||||
return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1));
|
||||
}
|
||||
@@ -254,6 +251,7 @@ abstract class SerializableTypeWrapper {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
return method.invoke(this.provider.getType(), args);
|
||||
}
|
||||
@@ -376,21 +374,27 @@ abstract class SerializableTypeWrapper {
|
||||
|
||||
private final int index;
|
||||
|
||||
private transient Object result;
|
||||
private transient Method method;
|
||||
|
||||
private transient volatile Object result;
|
||||
|
||||
public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) {
|
||||
this.provider = provider;
|
||||
this.methodName = method.getName();
|
||||
this.index = index;
|
||||
this.result = ReflectionUtils.invokeMethod(method, provider.getType());
|
||||
this.method = method;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
if (this.result instanceof Type || this.result == null) {
|
||||
return (Type) this.result;
|
||||
Object result = this.result;
|
||||
if (result == null) {
|
||||
// Lazy invocation of the target method on the provided type
|
||||
result = ReflectionUtils.invokeMethod(this.method, this.provider.getType());
|
||||
// Cache the result for further calls to getType()
|
||||
this.result = result;
|
||||
}
|
||||
return ((Type[])this.result)[this.index];
|
||||
return (result instanceof Type[] ? ((Type[]) result)[this.index] : (Type) result);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -400,8 +404,9 @@ abstract class SerializableTypeWrapper {
|
||||
|
||||
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
|
||||
inputStream.defaultReadObject();
|
||||
Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);
|
||||
this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());
|
||||
this.method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);
|
||||
Assert.state(Type.class.equals(this.method.getReturnType()) ||
|
||||
Type[].class.equals(this.method.getReturnType()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user