Protect against stack overflow when searching meta annotations
It is legal for an annotation to be annotated with itself. Previously, when searching for meta annotations this could lead to a stack overflow. This was likely to occur when using Kotlin as, like Java, its Target annotation is annotated with itself. A stack overflow doesn’t occur with Java’s Target annotation due to some short-circuiting logic for annotations in java.lang. This commit updates the logic for finding meta-annotations to short-circuit when an annotation that has already been seen is encountered. Closes gh-5902
This commit is contained in:
@@ -20,9 +20,11 @@ import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -58,13 +60,13 @@ public class AnnotationsPropertySource extends EnumerablePropertySource<Class<?>
|
||||
|
||||
private Map<String, Object> getProperties(Class<?> source) {
|
||||
Map<String, Object> properties = new LinkedHashMap<String, Object>();
|
||||
collectProperties(source, source, properties);
|
||||
collectProperties(source, source, properties, new HashSet<Class<?>>());
|
||||
return Collections.unmodifiableMap(properties);
|
||||
}
|
||||
|
||||
private void collectProperties(Class<?> root, Class<?> source,
|
||||
Map<String, Object> properties) {
|
||||
if (source != null) {
|
||||
Map<String, Object> properties, Set<Class<?>> seen) {
|
||||
if (source != null && seen.add(source)) {
|
||||
for (Annotation annotation : getMergedAnnotations(root, source)) {
|
||||
if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation)) {
|
||||
PropertyMapping typeMapping = annotation.annotationType()
|
||||
@@ -73,10 +75,11 @@ public class AnnotationsPropertySource extends EnumerablePropertySource<Class<?>
|
||||
.getDeclaredMethods()) {
|
||||
collectProperties(annotation, attribute, typeMapping, properties);
|
||||
}
|
||||
collectProperties(root, annotation.annotationType(), properties);
|
||||
collectProperties(root, annotation.annotationType(), properties,
|
||||
seen);
|
||||
}
|
||||
}
|
||||
collectProperties(root, source.getSuperclass(), properties);
|
||||
collectProperties(root, source.getSuperclass(), properties, seen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -164,6 +164,11 @@ public class AnnotationsPropertySourceTests {
|
||||
assertThat(source.getProperty("aliasing.value")).isEqualTo("baz");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selfAnnotatingAnnotationDoesNotCauseStackOverflow() {
|
||||
new AnnotationsPropertySource(PropertyMappedWithSelfAnnotatingAnnotation.class);
|
||||
}
|
||||
|
||||
static class NoAnnotation {
|
||||
|
||||
}
|
||||
@@ -327,7 +332,9 @@ public class AnnotationsPropertySourceTests {
|
||||
static @interface AttributeWithAliasAnnotation {
|
||||
|
||||
@AliasFor(annotation = AliasedAttributeAnnotation.class, attribute = "value")
|
||||
String value() default "foo";
|
||||
String value()
|
||||
|
||||
default "foo";
|
||||
|
||||
String someOtherAttribute() default "shouldNotBeMapped";
|
||||
|
||||
@@ -341,4 +348,15 @@ public class AnnotationsPropertySourceTests {
|
||||
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@SelfAnnotating
|
||||
static @interface SelfAnnotating {
|
||||
|
||||
}
|
||||
|
||||
@SelfAnnotating
|
||||
static class PropertyMappedWithSelfAnnotatingAnnotation {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user