ClassReader fails to parse class file due to InputStream optimization
Introductory information
In my project, after switching from using spring framework 5.3.7 to 5.3.10 at the very beginning of start I'm getting following exception:
java.lang.IllegalArgumentException: Unsupported class file major version 21845
at org.springframework.asm.ClassReader.<init>(ClassReader.java:199)
at org.springframework.asm.ClassReader.<init>(ClassReader.java:180)
at org.springframework.asm.ClassReader.<init>(ClassReader.java:166)
at org.springframework.asm.ClassReader.<init>(ClassReader.java:287)
at org.springframework.core.type.classreading.SimpleMetadataReader.getClassReader(SimpleMetadataReader.java:57)
... 39 common frames omitted
Wrapped by: org.springframework.core.NestedIOException: ASM ClassReader failed to parse class file - probably due to a new Java class file version that isn't supported yet: class path resource [com/example/demo/HealthConfig.class]; nested exception is java.lang.IllegalArgumentException: Unsupported class file major version 21845
at org.springframework.core.type.classreading.SimpleMetadataReader.getClassReader(SimpleMetadataReader.java:60)
at org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:49)
at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:103)
at org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory.createMetadataReader(ConcurrentReferenceCachingMetadataReaderFactory.java:86)
at org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory.getMetadataReader(ConcurrentReferenceCachingMetadataReaderFactory.java:73)
at org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:81)
at org.springframework.context.annotation.ConfigurationClassParser.asSourceClass(ConfigurationClassParser.java:696)
at org.springframework.context.annotation.ConfigurationClassParser$SourceClass.getRelated(ConfigurationClassParser.java:1090)
at org.springframework.context.annotation.ConfigurationClassParser$SourceClass.getAnnotationAttributes(ConfigurationClassParser.java:1071)
at org.springframework.context.annotation.ConfigurationClassParser.collectImports(ConfigurationClassParser.java:549)
at org.springframework.context.annotation.ConfigurationClassParser.getImports(ConfigurationClassParser.java:522)
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:311)
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:250)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:199)
at org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:304)
at org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:250)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:207)
at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:175)
... 22 common frames omitted
We reported this issue in the spring-framework project (https://github.com/spring-projects/spring-framework/issues/27429). It turned out that the exception comes from a class that is a fork the asm project.
The environment in which the problem occurs:
The environment we use uses the cloudflare's zlib fork (https://github.com/cloudflare/zlib).
As long as uses zlib distributed with a system, everything is fine.
The problem appears when uses the mentioned zlib fork.
What is the problem:
Commit which introduces the problematic change is: cfda364c
In the mentioned environment the last call to inputStream.read(data, 0, bufferSize))
returns -1, and and despite that the passed data buffer has been modified.
For this reason, the marked if statement in the following code snippet returns invalid data, and consequently the exception shown in the preliminary information.
while ((bytesRead = inputStream.read(data, 0, bufferSize)) != -1) {
outputStream.write(data, 0, bytesRead);
readCount++;
}
outputStream.flush();
if (readCount == 1) { // <-------------------
return data;
}
return outputStream.toByteArray();
Reproduction steps:
I created simple reproduction steps using spring framework. https://github.com/180254/spring-framework-issue-27429
By running scripts indexed 0, 1 and 2, you can see that the problem occurs after the problematic commit has been made to spring-framework.
By running scripts indexed 3 and 4, you can see proof the the buffer was modified in the last pass of the loop on the environment with cloudflare zlib fork.
Final words:
The problem is somewhere between sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream - asm - cloudflare-gzip-fork. In such an environment a InputStream implementation has a very surprising optimization. Despite returning -1, the passed buffer has been modified.
The problem appears on a custom environment. However it seems to me, that cloudflare's zlib fork is so good and popular, that it is worth to take this problem and continue compatibility of spring-boot - asm - cloudflare-zlib-fork.
Please pick up the issue and consider supporting such behavior.