Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infinite recursion in equals() and hashCode() for ResolvableType with recursive generics #33932

Open
juancarrey opened this issue Nov 21, 2024 · 3 comments
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Milestone

Comments

@juancarrey
Copy link

juancarrey commented Nov 21, 2024

During SpEL evaluation, if the TypeDescriptor is recursive, the evaluation results in infinite recursion causing stack overflow.

This line:

ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), otherDesc.getMapValueTypeDescriptor()));

Evaluated object sample where we have this issue is a map:

public class MyClass implements Map<String, MyClass>

This causes the evaluation of equality to check the type, and recursively the inner types of the map.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Nov 21, 2024
@sbrannen sbrannen changed the title Infinite recurssion in sPEL for Types whose TypeDefinition is recursive Infinite recursion in SpEL for types with recursive TypeDefinition Nov 21, 2024
@sbrannen sbrannen added the in: core Issues in core modules (aop, beans, core, context, expression) label Nov 21, 2024
@sbrannen sbrannen changed the title Infinite recursion in SpEL for types with recursive TypeDefinition Infinite recursion in SpEL for type with recursive TypeDescriptor Nov 21, 2024
@sbrannen
Copy link
Member

Hi @juancarrey,

Congratulations on submitting your first issue for the Spring Framework! 👍

Unfortunately you have not provided enough information for us to reproduce the issue.

If you would like us to investigate this, please provide a minimal example that we can run -- for example, a stand-alone JUnit test class or a minimal sample application made available via a Git repository or a ZIP file attached to this issue.

Thanks


In any case, it appears that this may be an issue with TypeDescriptor in spring-core and therefore not specific to SpEL.

@sbrannen sbrannen self-assigned this Nov 21, 2024
@sbrannen sbrannen added the status: waiting-for-feedback We need additional information before we can continue label Nov 21, 2024
@sbrannen
Copy link
Member

sbrannen commented Nov 21, 2024

In any case, it appears that this may be an issue with TypeDescriptor in spring-core and therefore not specific to SpEL.

Indeed, the following results in a StackOverflowError.

class RecursiveTypeDescriptorTests {

	@Test
	void recursiveTypeDescriptor() {
		TypeDescriptor typeDescriptor1 =
				TypeDescriptor.map(Map.class,
						TypeDescriptor.valueOf(String.class),
						TypeDescriptor.valueOf(RecursiveMap.class));
		TypeDescriptor typeDescriptor2 =
				TypeDescriptor.map(Map.class,
						TypeDescriptor.valueOf(String.class),
						TypeDescriptor.valueOf(RecursiveMap.class));
		assertThat(typeDescriptor1).isEqualTo(typeDescriptor2);
	}


	static class RecursiveMap extends HashMap<String, RecursiveMap> {
	}

}

That results in a stack trace like this:

java.lang.StackOverflowError
	at org.springframework.util.ObjectUtils.nullSafeEquals(ObjectUtils.java:346)
	at org.springframework.core.ResolvableType.equals(ResolvableType.java:1023)
	at org.springframework.util.ObjectUtils.nullSafeEquals(ObjectUtils.java:342)
	at org.springframework.core.ResolvableType.equals(ResolvableType.java:1023)
	at org.springframework.util.ObjectUtils.nullSafeEquals(ObjectUtils.java:342)
	at org.springframework.core.ResolvableType.equals(ResolvableType.java:1023)
	at org.springframework.util.ObjectUtils.nullSafeEquals(ObjectUtils.java:342)
	at org.springframework.core.ResolvableType.equals(ResolvableType.java:1023)
	at org.springframework.util.ObjectUtils.nullSafeEquals(ObjectUtils.java:342)

But if we change the declaration of RecursiveMap to the following:

static class RecursiveMap extends HashMap<String, RecursiveMap>
	implements Map<String, RecursiveMap> {
}

... we then see a stack trace like this:

java.lang.StackOverflowError
	at org.springframework.util.ObjectUtils.nullSafeHashCode(ObjectUtils.java:452)
	at org.springframework.core.ResolvableType.calculateHashCode(ResolvableType.java:1056)
	at org.springframework.core.ResolvableType.hashCode(ResolvableType.java:1044)
	at org.springframework.util.ObjectUtils.nullSafeHashCode(ObjectUtils.java:452)
	at org.springframework.core.ResolvableType.calculateHashCode(ResolvableType.java:1056)
	at org.springframework.core.ResolvableType.hashCode(ResolvableType.java:1044)
	at org.springframework.util.ObjectUtils.nullSafeHashCode(ObjectUtils.java:452)
	at org.springframework.core.ResolvableType.calculateHashCode(ResolvableType.java:1056)
	at org.springframework.core.ResolvableType.hashCode(ResolvableType.java:1044)

Note the recursion in ResolvableType.equals vs. ResolvableType.hashCode.

@sbrannen sbrannen added type: bug A general bug and removed status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged or decided on labels Nov 21, 2024
@sbrannen sbrannen removed their assignment Nov 21, 2024
@sbrannen sbrannen added this to the 6.2.1 milestone Nov 21, 2024
@sbrannen
Copy link
Member

sbrannen commented Nov 21, 2024

We like to avoid infinite recursion in such scenarios, potentially by checking upfront if the generics are equal without recursing, or potentially by tracking which types have been visited and throwing an exception if a cycle is detected.

Tentatively assigned to 6.2.1, but may be backported to 6.1.x depending on the solution.

Note: their might be recursion issues in TypeDescriptor as well as in ResolvableType. So, we should write tests for recursive generics with both.

@sbrannen sbrannen changed the title Infinite recursion in SpEL for type with recursive TypeDescriptor Infinite recursion for equals() and hashCode() in recursive TypeDescriptor Nov 21, 2024
@sbrannen sbrannen changed the title Infinite recursion for equals() and hashCode() in recursive TypeDescriptor Infinite recursion in equals() and hashCode() for ResolvableType with recursive generics Nov 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

3 participants