Skip to content

Commit 90a2885

Browse files
committed
MethodInvokeTypeProvider lazily invokes target method (avoiding deserialization exploits)
Issue: SPR-13656 (cherry picked from commit ea2843e)
1 parent df49b11 commit 90a2885

File tree

1 file changed

+31
-26
lines changed

1 file changed

+31
-26
lines changed

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

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2015 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -51,22 +51,18 @@
5151
* {@link GenericArrayType#getGenericComponentType()}) will be automatically wrapped.
5252
*
5353
* @author Phillip Webb
54+
* @author Juergen Hoeller
5455
* @since 4.0
5556
*/
5657
abstract class SerializableTypeWrapper {
5758

5859
private static final Class<?>[] SUPPORTED_SERIALIZABLE_TYPES = {
5960
GenericArrayType.class, ParameterizedType.class, TypeVariable.class, WildcardType.class};
6061

61-
private static final Method EQUALS_METHOD = ReflectionUtils.findMethod(Object.class,
62-
"equals", Object.class);
63-
64-
private static final Method GET_TYPE_PROVIDER_METHOD = ReflectionUtils.findMethod(
65-
SerializableTypeProxy.class, "getTypeProvider");
66-
6762
private static final ConcurrentReferenceHashMap<Type, Type> cache =
6863
new ConcurrentReferenceHashMap<Type, Type>(256);
6964

65+
7066
/**
7167
* Return a {@link Serializable} variant of {@link Field#getGenericType()}.
7268
*/
@@ -161,35 +157,33 @@ static Type forTypeProvider(final TypeProvider provider) {
161157
for (Class<?> type : SUPPORTED_SERIALIZABLE_TYPES) {
162158
if (type.isAssignableFrom(provider.getType().getClass())) {
163159
ClassLoader classLoader = provider.getClass().getClassLoader();
164-
Class<?>[] interfaces = new Class<?>[] { type,
165-
SerializableTypeProxy.class, Serializable.class };
160+
Class<?>[] interfaces = new Class<?>[] {type, SerializableTypeProxy.class, Serializable.class};
166161
InvocationHandler handler = new TypeProxyInvocationHandler(provider);
167162
cached = (Type) Proxy.newProxyInstance(classLoader, interfaces, handler);
168163
cache.put(provider.getType(), cached);
169164
return cached;
170165
}
171166
}
172-
throw new IllegalArgumentException("Unsupported Type class " + provider.getType().getClass().getName());
167+
throw new IllegalArgumentException("Unsupported Type class: " + provider.getType().getClass().getName());
173168
}
174169

175170

176171
/**
177172
* Additional interface implemented by the type proxy.
178173
*/
179-
static interface SerializableTypeProxy {
174+
interface SerializableTypeProxy {
180175

181176
/**
182177
* Return the underlying type provider.
183178
*/
184179
TypeProvider getTypeProvider();
185-
186180
}
187181

188182

189183
/**
190184
* A {@link Serializable} interface providing access to a {@link Type}.
191185
*/
192-
static interface TypeProvider extends Serializable {
186+
interface TypeProvider extends Serializable {
193187

194188
/**
195189
* Return the (possibly non {@link Serializable}) {@link Type}.
@@ -213,12 +207,11 @@ private static abstract class DefaultTypeProvider implements TypeProvider {
213207
public Object getSource() {
214208
return null;
215209
}
216-
217210
}
218211

219212

220213
/**
221-
* {@link Serializable} {@link InvocationHandler} used by the Proxied {@link Type}.
214+
* {@link Serializable} {@link InvocationHandler} used by the proxied {@link Type}.
222215
* Provides serialization support and enhances any methods that return {@code Type}
223216
* or {@code Type[]}.
224217
*/
@@ -233,17 +226,21 @@ public TypeProxyInvocationHandler(TypeProvider provider) {
233226

234227
@Override
235228
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
236-
if (GET_TYPE_PROVIDER_METHOD.equals(method)) {
237-
return this.provider;
238-
}
239-
if (EQUALS_METHOD.equals(method)) {
229+
if (method.getName().equals("equals")) {
240230
Object other = args[0];
241231
// Unwrap proxies for speed
242232
if (other instanceof Type) {
243233
other = unwrap((Type) other);
244234
}
245235
return this.provider.getType().equals(other);
246236
}
237+
else if (method.getName().equals("hashCode")) {
238+
return this.provider.getType().hashCode();
239+
}
240+
else if (method.getName().equals("getTypeProvider")) {
241+
return this.provider;
242+
}
243+
247244
if (Type.class.equals(method.getReturnType()) && args == null) {
248245
return forTypeProvider(new MethodInvokeTypeProvider(this.provider, method, -1));
249246
}
@@ -254,6 +251,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
254251
}
255252
return result;
256253
}
254+
257255
try {
258256
return method.invoke(this.provider.getType(), args);
259257
}
@@ -376,21 +374,27 @@ static class MethodInvokeTypeProvider implements TypeProvider {
376374

377375
private final int index;
378376

379-
private transient Object result;
377+
private transient Method method;
378+
379+
private transient volatile Object result;
380380

381381
public MethodInvokeTypeProvider(TypeProvider provider, Method method, int index) {
382382
this.provider = provider;
383383
this.methodName = method.getName();
384384
this.index = index;
385-
this.result = ReflectionUtils.invokeMethod(method, provider.getType());
385+
this.method = method;
386386
}
387387

388388
@Override
389389
public Type getType() {
390-
if (this.result instanceof Type || this.result == null) {
391-
return (Type) this.result;
390+
Object result = this.result;
391+
if (result == null) {
392+
// Lazy invocation of the target method on the provided type
393+
result = ReflectionUtils.invokeMethod(this.method, this.provider.getType());
394+
// Cache the result for further calls to getType()
395+
this.result = result;
392396
}
393-
return ((Type[])this.result)[this.index];
397+
return (result instanceof Type[] ? ((Type[]) result)[this.index] : (Type) result);
394398
}
395399

396400
@Override
@@ -400,8 +404,9 @@ public Object getSource() {
400404

401405
private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException {
402406
inputStream.defaultReadObject();
403-
Method method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);
404-
this.result = ReflectionUtils.invokeMethod(method, this.provider.getType());
407+
this.method = ReflectionUtils.findMethod(this.provider.getType().getClass(), this.methodName);
408+
Assert.state(Type.class.equals(this.method.getReturnType()) ||
409+
Type[].class.equals(this.method.getReturnType()));
405410
}
406411
}
407412

0 commit comments

Comments
 (0)