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

Recursive annotations lead to StackOverflowError #99

Closed
yrodiere opened this issue Sep 23, 2024 · 0 comments
Closed

Recursive annotations lead to StackOverflowError #99

yrodiere opened this issue Sep 23, 2024 · 0 comments
Assignees
Milestone

Comments

@yrodiere
Copy link
Member

yrodiere commented Sep 23, 2024

Java annotations can be recursive, i.e. can be annotated with themselves.

One such annotation is java.lang.annotation.Target:

package java.lang.annotation;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE) // Annotated with itself!
public @interface Target {
    ElementType[] value();
}

For such annotations, StandardAnnotationDescriptor#buildUsagesMap can be problematic, as it will, during the creation of an annotation descriptor, create a map of meta-annotations (annotations on the current annotation)... using annotation descriptors.
You can see how this can be a problem for recursive annotation: creating the annotation descriptor will trigger the creation of the annotation descriptor, recursively.

Fortunately, for some recursive annotations, this is already avoided with a simple if:

// skip a few well-know ones that are irrelevant
if ( annotationTypeAnnotationType == Repeatable.class
|| annotationTypeAnnotationType == Target.class
|| annotationTypeAnnotationType == Retention.class
|| annotationTypeAnnotationType == Documented.class ) {
continue;
}
//noinspection rawtypes
final AnnotationDescriptor annotationDescriptor = annotationDescriptorRegistry.getDescriptor( annotationTypeAnnotationType );

Unfortunately, that if doesn't cover all possible recursive annotations. Any time hibernate-models will hit any of the following, it will trigger a StackOverflowError:

  1. kotlin.annotation.Target:
package kotlin.annotation

@kotlin.annotation.Target @kotlin.annotation.MustBeDocumented public final annotation class Target public constructor(vararg allowedTargets: kotlin.annotation.AnnotationTarget) : kotlin.Annotation {
    public final val allowedTargets: kotlin.Array<out kotlin.annotation.AnnotationTarget> /* compiled code */
}
  1. A custom self-annotated annotation, e.g.:
  @SelfAnnotated
  public @interface SelfAnnotated {}
  1. An annotation cycle, e.g.:
  @A
  public @interface B {}
  @B
  public @interface A {}

Disclaimer: I only tested the first one, because it's blocking the upgrade of Quarkus to Hibernate ORM 7: quarkusio/quarkus#41310. But I'm pretty sure the others would lead to the same behavior.

Suggested solution: delay the building of StandardAnnotationDescriptor#usagesMap. Maybe initialize it lazily?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants