Browse Source

MethodInvokeTypeProvider lazily invokes target method (avoiding deserialization exploits)

Issue: SPR-13656
(cherry picked from commit ea2843e)
pull/931/head
Juergen Hoeller 10 years ago
parent
commit
90a288513c
  1. 57
      spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java

57
spring-core/src/main/java/org/springframework/core/SerializableTypeWrapper.java

@ -1,5 +1,5 @@ @@ -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; @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -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 { @@ -254,6 +251,7 @@ abstract class SerializableTypeWrapper {
}
return result;
}
try {
return method.invoke(this.provider.getType(), args);
}
@ -376,21 +374,27 @@ abstract class SerializableTypeWrapper { @@ -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 { @@ -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()));
}
}

Loading…
Cancel
Save