From 4244682309e7ae1be892280dfd6a6f70ccecc760 Mon Sep 17 00:00:00 2001 From: Nizar Benalla Date: Thu, 7 Nov 2024 10:30:12 +0000 Subject: [PATCH] 8339190: Parameter arrays that are capped during annotation processing report incorrect length Reviewed-by: vromero --- .../com/sun/tools/javac/jvm/ClassFile.java | 1 + .../com/sun/tools/javac/jvm/ClassWriter.java | 8 + .../tools/javac/resources/compiler.properties | 5 + .../annotations/ParameterArrayLimit.java | 150 ++++++++++++++++++ .../tools/javac/diags/examples.not-yet.txt | 1 + 5 files changed, 165 insertions(+) create mode 100644 test/langtools/tools/javac/annotations/ParameterArrayLimit.java diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java index 7e8b96d5856..c7e71f74d8a 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassFile.java @@ -103,6 +103,7 @@ public class ClassFile { public static final int MAX_STACK = 0xffff; public static final int PREVIEW_MINOR_VERSION = 0xffff; + public static final int MAX_ANNOTATIONS = 0xffff; public enum Version { V45_3(45, 3), // base level for all attributes diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java index 6679bb43fb8..d70032353b6 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/jvm/ClassWriter.java @@ -649,11 +649,19 @@ public class ClassWriter extends ClassFile { databuf.appendChar(poolWriter.putDescriptor(c.type)); databuf.appendChar(c.values.length()); for (Pair p : c.values) { + checkAnnotationArraySizeInternal(p); databuf.appendChar(poolWriter.putName(p.fst.name)); p.snd.accept(awriter); } } + private void checkAnnotationArraySizeInternal(Pair p) { + if (p.snd instanceof Attribute.Array arrAttr && + arrAttr.values.length > ClassFile.MAX_ANNOTATIONS) { + log.error(Errors.AnnotationArrayTooLarge(p.fst.owner)); + } + } + void writeTypeAnnotation(Attribute.TypeCompound c) { writePosition(c.position); writeCompoundAttribute(c); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties index a991ae60601..a24dd2f95bd 100644 --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -887,6 +887,10 @@ compiler.err.limit.stack=\ compiler.err.limit.string=\ constant string too long +# 0: symbol +compiler.err.annotation.array.too.large=\ + Annotation array element too large in \"{0}\" + # 0: string compiler.err.limit.string.overflow=\ UTF8 representation for string \"{0}...\" is too long for the constant pool @@ -2235,6 +2239,7 @@ compiler.warn.proc.duplicate.option.name=\ compiler.warn.proc.duplicate.supported.annotation=\ Duplicate supported annotation interface ''{0}'' returned by annotation processor ''{1}'' + # 0: string compiler.warn.proc.redundant.types.with.wildcard=\ Annotation processor ''{0}'' redundantly supports both ''*'' and other annotation interfaces diff --git a/test/langtools/tools/javac/annotations/ParameterArrayLimit.java b/test/langtools/tools/javac/annotations/ParameterArrayLimit.java new file mode 100644 index 00000000000..6e782dd0b83 --- /dev/null +++ b/test/langtools/tools/javac/annotations/ParameterArrayLimit.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary Check if error is thrown if annotation array exceeds limit + * @library /tools/lib + * @run main ParameterArrayLimit + */ + +import java.io.BufferedWriter; +import java.io.IOException; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.List; +import javax.tools.*; + +import com.sun.source.util.JavacTask; + +public class ParameterArrayLimit { + + public static void main(String[] args) throws IOException { + + int[] values = new int[]{65536, 65537, 512000}; + String[] retPolicies = {"RUNTIME", "CLASS"}; + + for (var value : values) { + Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir")); + + for (String retPolicy : retPolicies) { + String className = MessageFormat.format("ClassAnnotationWithLength_{0,number,#}_{1}.java", + value, + retPolicy); + Path out = tmpDir.resolve(className); + createAnnotationFile(out, value, retPolicy, false); + checkParamArrayWarning(className, out); + } + + for (String retPolicy : retPolicies) { + String className = MessageFormat.format("TypeAnnotationWithLength_{0,number,#}_{1}.java", + value, + retPolicy); + Path out = tmpDir.resolve(className); + createAnnotationFile(out, value, retPolicy, true); + checkParamArrayWarning(className, out); + } + } + } + + private static void checkParamArrayWarning(String className, Path out) throws IOException { + JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler(); + DiagnosticCollector d = new DiagnosticCollector<>(); + JavacTask task = (JavacTask) javaCompiler.getTask( + null, + null, + d, + null, + null, + Collections.singletonList( + SimpleJavaFileObject.forSource( + URI.create("myfo:/" + className), + Files.readString(out) + ))); + task.call(); + + List> diagnosticList = d.getDiagnostics(); + if (diagnosticList.isEmpty()) { + throw new RuntimeException("No diagnostic found"); + } + + for (Diagnostic diagnostic : diagnosticList) { + if (!(diagnostic.getKind() == Diagnostic.Kind.ERROR + && diagnostic.getCode() + .equals("compiler.err.annotation.array.too.large"))) { + throw new RuntimeException("Unexpected diagnostic: " + diagnostic.getMessage(null)); + } + } + } + + private static void createAnnotationFile(Path out, int value, String retPolicy, boolean isTypeAnnotation) throws IOException { + StringBuilder sb = new StringBuilder(); + + if (isTypeAnnotation) { + sb.append(MessageFormat.format(""" + import java.lang.annotation.*; + @Retention(RetentionPolicy.{0}) + @Target(ElementType.TYPE_USE) + @interface TypeAnno '{' + long[] arr(); + '}' + """, retPolicy)); + sb.append(MessageFormat.format(""" + public class TypeAnnotationWithLength_{0,number,#}_{1}'{' + @TypeAnno(arr = '{' + """, value, retPolicy)); + } else { + sb.append(MessageFormat.format(""" + import java.lang.annotation.*; + @Retention(RetentionPolicy.{0}) + @interface MyCustomAnno '{' + String value() default "default value"; + long[] arr(); + int count() default 0; + '}' + """, retPolicy)); + sb.append(MessageFormat.format(""" + public class ClassAnnotationWithLength_{0,number,#}_{1}'{' + @MyCustomAnno(value = "custom", count = 42, arr = '{' + """, value, retPolicy)); + } + + sb.append("-1,".repeat(Math.max(0, value - 1))); + sb.append("-1})"); + + sb.append(""" + static int x = 3; + + public void myAnnotatedMethod() { } + } + """); + + try (BufferedWriter bufferedWriter = Files.newBufferedWriter(out)) { + bufferedWriter.write(sb.toString()); + } + } +} diff --git a/test/langtools/tools/javac/diags/examples.not-yet.txt b/test/langtools/tools/javac/diags/examples.not-yet.txt index 329e716a780..e158366a171 100644 --- a/test/langtools/tools/javac/diags/examples.not-yet.txt +++ b/test/langtools/tools/javac/diags/examples.not-yet.txt @@ -16,6 +16,7 @@ compiler.err.limit.code # Code compiler.err.limit.code.too.large.for.try.stmt # Gen compiler.err.limit.dimensions # Gen compiler.err.limit.locals # Code +compiler.err.annotation.array.too.large # Code compiler.err.limit.parameters # Gen compiler.err.limit.pool # Gen,JavaCompiler compiler.err.limit.pool.in.class # UNUSED?