RFR: 8364655: Loading class with nested annotations causes stack overflow in VM [v3]

Johan Sjölen jsjolen at openjdk.org
Thu Jan 15 12:41:25 UTC 2026


> Hi,
> 
> `skip_annotation` and `skip_annotation_value` are two mutually recursive functions calling each other in order to skip over classfile annotations. If a classfile contains a highly nested annotation, then this will lead to a stack overflow and a subsequent crash of the JVM. I propose that we insert a recursion limit to prevent this from happening. This recursion limit will make the annotation parsing to bail out on the JVM side, skipping the rest of the annotations present. 
> 
> An example of Java code where we end up with nested annotations is this:
> 
> 
> import java.lang.annotation.*;
> 
> @Retention(RetentionPolicy.RUNTIME)
> public @interface Foo { Bar value(); }
> 
> @Retention(RetentionPolicy.RUNTIME)
> @interface Bar { Baz value();}
> 
> @Retention(RetentionPolicy.RUNTIME)
> @interface Baz { BarBaz value(); }
> 
> @Retention(RetentionPolicy.RUNTIME)
> @interface BarBaz { End value(); }
> 
> @Retention(RetentionPolicy.RUNTIME)
> @interface End { int value(); }
> 
> 
> @Foo(value = @Bar(value = @Baz(value = @BarBaz(value = @End(value=1)))))
> class Lol {
> 
>     @Deprecated // <--- This annotation might be missed with my change, as the depth limit is 5
>     void foobar();
> };
> 
> 
> Today, it seems that Java disallows cyclic interface annotations, this is why in order to achieve the nesting in `Lol` we need to define multiple classes (`Foo`, `Bar`, and so on). Attempting to define a `Foo` with a `Foo value()` will fail compilation.
> 
> I think that such a high nesting of annotations is unlikely to occur in normal Java code. The only 'public' consumer of annotations is JFR, who looks for `@Deprecated` annotations. Typically, the JVM parses these annotations is to gain access to a select few JDK-internal annotations, and we trust our own code to construct classfiles without egregious nesting. To quote John Rose in the JBS issue:
> 
>> If a JVM-detected annotation is preceded by a toxic input, it will be masked by a bailout. So be it. JVM-detected annotations are for power users; such users can take measures that their code will not be polluted with toxic annotations. 
> 
> All classfile annotations are also parsed by Java-code, so we're not skipping general annotation parsing.
> 
> A regression test has been added, where we check that the JVM does not crash when provided with a large amount of nesting.
> 
> Thanks!

Johan Sjölen has updated the pull request incrementally with one additional commit since the last revision:

  Copyright

-------------

Changes:
  - all: https://git.openjdk.org/jdk/pull/28674/files
  - new: https://git.openjdk.org/jdk/pull/28674/files/5cceb010..a705bc1f

Webrevs:
 - full: https://webrevs.openjdk.org/?repo=jdk&pr=28674&range=02
 - incr: https://webrevs.openjdk.org/?repo=jdk&pr=28674&range=01-02

  Stats: 2 lines in 2 files changed: 0 ins; 0 del; 2 mod
  Patch: https://git.openjdk.org/jdk/pull/28674.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/28674/head:pull/28674

PR: https://git.openjdk.org/jdk/pull/28674


More information about the hotspot-runtime-dev mailing list