undefect. CWE-407 — 63 sites patched across 27 ecosystems
Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
This commit is contained in:
commit
0a580b313d
70422 changed files with 17213626 additions and 0 deletions
228
test/setup_aot/JavacBenchApp.java
Normal file
228
test/setup_aot/JavacBenchApp.java
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* Copyright (c) 2023, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.DiagnosticCollector;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.ForwardingJavaFileManager;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileManager;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.SimpleJavaFileObject;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
/**
|
||||
* This program tries to compile a large number of classes that exercise a fair amount of
|
||||
* features in javac.
|
||||
*/
|
||||
public class JavacBenchApp {
|
||||
static class ClassFile extends SimpleJavaFileObject {
|
||||
private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
protected ClassFile(String name) {
|
||||
super(URI.create("memo:///" + name.replace('.', '/') + Kind.CLASS.extension), Kind.CLASS);
|
||||
}
|
||||
@Override
|
||||
public ByteArrayOutputStream openOutputStream() {
|
||||
return this.baos;
|
||||
}
|
||||
byte[] toByteArray() {
|
||||
return baos.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
static class FileManager extends ForwardingJavaFileManager<JavaFileManager> {
|
||||
private Map<String, ClassFile> classesMap = new HashMap<String, ClassFile>();
|
||||
protected FileManager(JavaFileManager fileManager) {
|
||||
super(fileManager);
|
||||
}
|
||||
@Override
|
||||
public ClassFile getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject source) {
|
||||
ClassFile classFile = new ClassFile(name);
|
||||
classesMap.put(name, classFile);
|
||||
return classFile;
|
||||
}
|
||||
public Map<String, byte[]> getCompiledClasses() {
|
||||
Map<String, byte[]> result = new HashMap<>();
|
||||
for (Map.Entry<String, ClassFile> entry : classesMap.entrySet()) {
|
||||
result.put(entry.getKey(), entry.getValue().toByteArray());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
static class SourceFile extends SimpleJavaFileObject {
|
||||
private CharSequence sourceCode;
|
||||
public SourceFile(String name, CharSequence sourceCode) {
|
||||
super(URI.create("memo:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE);
|
||||
this.sourceCode = sourceCode;
|
||||
}
|
||||
@Override
|
||||
public CharSequence getCharContent(boolean ignore) {
|
||||
return this.sourceCode;
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, byte[]> compile() {
|
||||
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
|
||||
DiagnosticCollector<JavaFileObject> ds = new DiagnosticCollector<>();
|
||||
Collection<SourceFile> sourceFiles = sources;
|
||||
|
||||
try (FileManager fileManager = new FileManager(compiler.getStandardFileManager(ds, null, null))) {
|
||||
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, sourceFiles);
|
||||
if (task.call()) {
|
||||
return fileManager.getCompiledClasses();
|
||||
} else {
|
||||
for (Diagnostic<? extends JavaFileObject> d : ds.getDiagnostics()) {
|
||||
System.out.format("Line: %d, %s in %s", d.getLineNumber(), d.getMessage(null), d.getSource().getName());
|
||||
}
|
||||
throw new InternalError("compilation failure");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
List<SourceFile> sources;
|
||||
|
||||
static final String imports = """
|
||||
import java.lang.*;
|
||||
import java.util.*;
|
||||
""";
|
||||
|
||||
static final String testClassBody = """
|
||||
// Some comments
|
||||
static long x;
|
||||
static final long y;
|
||||
static {
|
||||
y = System.currentTimeMillis();
|
||||
}
|
||||
/* More comments */
|
||||
@Deprecated
|
||||
String func() { return "String " + this + y; }
|
||||
public static void main(String args[]) {
|
||||
try {
|
||||
x = Long.parseLong(args[0]);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
doit(() -> {
|
||||
System.out.println("Hello Lambda");
|
||||
Thread.dumpStack();
|
||||
});
|
||||
}
|
||||
static List<String> list = List.of("1", "2");
|
||||
class InnerClass1 {
|
||||
static final long yy = y;
|
||||
}
|
||||
static void doit(Runnable r) {
|
||||
for (var x : list) {
|
||||
r.run();
|
||||
}
|
||||
}
|
||||
static String patternMatch(String arg, Object o) {
|
||||
if (o instanceof String s) {
|
||||
return "1234";
|
||||
}
|
||||
final String b = "B";
|
||||
return switch (arg) {
|
||||
case "A" -> "a";
|
||||
case b -> "b";
|
||||
default -> "c";
|
||||
};
|
||||
}
|
||||
public sealed class SealedInnerClass {}
|
||||
public final class Foo extends SealedInnerClass {}
|
||||
enum Expression {
|
||||
ADDITION,
|
||||
SUBTRACTION,
|
||||
MULTIPLICATION,
|
||||
DIVISION
|
||||
}
|
||||
public record Point(int x, int y) {
|
||||
public Point(int x) {
|
||||
this(x, 0);
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
String sanitySource = """
|
||||
public class Sanity implements java.util.concurrent.Callable<String> {
|
||||
public String call() {
|
||||
return "this is a test";
|
||||
}
|
||||
}
|
||||
""";
|
||||
|
||||
void setup(int count) {
|
||||
sources = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
String source = imports + "public class Test" + i + " {" + testClassBody + "}";
|
||||
sources.add(new SourceFile("Test" + i, source));
|
||||
}
|
||||
|
||||
sources.add(new SourceFile("Sanity", sanitySource));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static void validate(byte[] sanityClassFile) throws Throwable {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
Class<?> cls = lookup.defineClass(sanityClassFile);
|
||||
Callable<String> obj = (Callable<String>)cls.getDeclaredConstructor().newInstance();
|
||||
String s = obj.call();
|
||||
if (!s.equals("this is a test")) {
|
||||
throw new RuntimeException("Expected \"this is a test\", but got \"" + s + "\"");
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String args[]) throws Throwable {
|
||||
long started = System.currentTimeMillis();
|
||||
JavacBenchApp bench = new JavacBenchApp();
|
||||
|
||||
int count = 0;
|
||||
if (args.length > 0) {
|
||||
count = Integer.parseInt(args[0]);
|
||||
if (count >= 0) {
|
||||
bench.setup(count);
|
||||
Map<String, byte[]> allClasses = bench.compile();
|
||||
validate(allClasses.get("Sanity"));
|
||||
}
|
||||
}
|
||||
if (System.getProperty("JavacBenchApp.silent") == null) {
|
||||
// Set this property when running with "perf stat", etc
|
||||
long elapsed = System.currentTimeMillis() - started;
|
||||
System.out.println("Generated source code for " + bench.sources.size() + " classes and compiled them in " + elapsed + " ms");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
278
test/setup_aot/TestSetupAOT.java
Normal file
278
test/setup_aot/TestSetupAOT.java
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
* Copyright (c) 2025, 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. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.spi.ToolProvider;
|
||||
import java.util.stream.Stream;
|
||||
import static java.util.stream.Collectors.*;
|
||||
|
||||
// This program is executed by make/RunTests.gmk to support running HotSpot tests
|
||||
// in the "AOT mode", for example:
|
||||
//
|
||||
// make test JTREG=AOT_JDK=onestep TEST=open/test/hotspot/jtreg/runtime/invokedynamic
|
||||
// make test JTREG=AOT_JDK=twostep TEST=open/test/hotspot/jtreg/runtime/invokedynamic
|
||||
//
|
||||
// The onestep and twostep options specify whether the AOT cache is created with
|
||||
// a single JVM command (java -XX:AOTMode=record -XX:AOTCacheOutput=jdk.aotcache ...) or
|
||||
// two JVM commands (java -XX:AOTMode=record ...; java -XX:AOTMode=create -XX:AOTCache=jdk.aotcache ...)
|
||||
//
|
||||
// All JDK classes touched by this program will be stored into a customized AOT cache.
|
||||
// This is a larger set of classes than those stored in the JDK's default CDS archive.
|
||||
// This customized cache can also have additional optimizations that are not
|
||||
// enabled in the default CDS archive. For example, AOT-linked classes and lambda
|
||||
// expressions. In the future, it can also contain AOT profiles and AOT compiled methods.
|
||||
//
|
||||
// We can use this customized AOT cache to run various HotSpot tests to improve
|
||||
// coverage on AOT.
|
||||
//
|
||||
// Note that make/RunTests.gmk loads this class using an implicit classpath of ".", so
|
||||
// this class will be excluded from the customized AOT cache. As a result,
|
||||
// the customized AOT cache contains *only* classes from the JDK itself.
|
||||
|
||||
public class TestSetupAOT {
|
||||
private static final Logger LOGGER = Logger.getLogger("Hello");
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
runJDKTools(args);
|
||||
invokedynamicTests(args);
|
||||
LOGGER.log(Level.FINE, "Done");
|
||||
}
|
||||
|
||||
static class ToolOutput {
|
||||
ByteArrayOutputStream baos;
|
||||
PrintStream ps;
|
||||
String output;
|
||||
|
||||
ToolOutput() throws Exception {
|
||||
baos = new ByteArrayOutputStream();
|
||||
ps = new PrintStream(baos, true, StandardCharsets.UTF_8.name());
|
||||
}
|
||||
void finish() throws Exception {
|
||||
output = baos.toString(StandardCharsets.UTF_8.name());
|
||||
System.out.println(output);
|
||||
}
|
||||
|
||||
ToolOutput shouldContain(String... substrings) {
|
||||
for (String s : substrings) {
|
||||
if (!output.contains(s)) {
|
||||
throw new RuntimeException("\"" + s + "\" missing from tool output");
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
ToolOutput shouldMatch(String... regexps) {
|
||||
for (String regexp : regexps) {
|
||||
Pattern pattern = Pattern.compile(regexp, Pattern.MULTILINE);
|
||||
if (!pattern.matcher(output).find()) {
|
||||
throw new RuntimeException("Pattern \"" + regexp + "\" missing from tool output");
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
static void runJDKTools(String[] args) throws Throwable {
|
||||
String tmpDir = args[0];
|
||||
System.out.println("Working Directory = " + System.getProperty("user.dir"));
|
||||
System.out.println("Temp output dir = " + tmpDir);
|
||||
|
||||
// ------------------------------
|
||||
// javac
|
||||
|
||||
execTool("javac", "--help")
|
||||
.shouldContain("Usage: javac <options> <source files>");
|
||||
|
||||
JavacBenchApp.main(new String[] {"5"});
|
||||
|
||||
// ------------------------------
|
||||
// javap
|
||||
|
||||
execTool("javap", "--help")
|
||||
.shouldContain("Show package/protected/public classes");
|
||||
execTool("javap", "-c", "-private", "-v", "-verify",
|
||||
"java.lang.System",
|
||||
"java/util/stream/IntStream",
|
||||
"jdk.internal.module.ModuleBootstrap")
|
||||
.shouldContain("Compiled from \"System.java\"",
|
||||
"public static java.io.Console console()");
|
||||
|
||||
// ------------------------------
|
||||
// jlink
|
||||
|
||||
String jlinkOutput = tmpDir + File.separator + "jlinkOutput";
|
||||
|
||||
execTool("jlink", "--help")
|
||||
.shouldContain("Compress all resources in the output image");
|
||||
execTool("jlink", "--list-plugins")
|
||||
.shouldContain("List of available plugins",
|
||||
"--generate-cds-archive ");
|
||||
|
||||
deleteAll(jlinkOutput);
|
||||
execTool("jlink", "--add-modules", "java.base", "--strip-debug", "--output", jlinkOutput);
|
||||
deleteAll(jlinkOutput);
|
||||
|
||||
// ------------------------------
|
||||
// jar
|
||||
|
||||
String jarOutput = tmpDir + File.separator + "tmp.jar";
|
||||
|
||||
execTool("jar", "--help")
|
||||
.shouldContain("--main-class=CLASSNAME");
|
||||
|
||||
deleteAll(jarOutput);
|
||||
execTool("jar", "cvf", jarOutput, "TestSetupAOT.class")
|
||||
.shouldContain("adding: TestSetupAOT.class");
|
||||
execTool("jar", "uvf", jarOutput, "TestSetupAOT.class")
|
||||
.shouldContain("adding: TestSetupAOT.class");
|
||||
execTool("jar", "tvf", jarOutput)
|
||||
.shouldContain("META-INF/MANIFEST.MF");
|
||||
execTool("jar", "--describe-module", "--file=" + jarOutput)
|
||||
.shouldMatch("Unable to derive module descriptor for: .*tmp.jar");
|
||||
deleteAll(jarOutput);
|
||||
|
||||
// ------------------------------
|
||||
// jdeps
|
||||
|
||||
execTool("jdeps", "--help")
|
||||
.shouldContain("--ignore-missing-deps");
|
||||
execTool("jdeps", "-v", "TestSetupAOT.class")
|
||||
.shouldContain("-> JavacBenchApp");
|
||||
}
|
||||
|
||||
static void deleteAll(String f) {
|
||||
deleteAll(new File(f));
|
||||
}
|
||||
|
||||
static void deleteAll(File f) {
|
||||
File[] files = f.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
deleteAll(file);
|
||||
}
|
||||
}
|
||||
System.out.println("Deleting: " + f);
|
||||
f.delete();
|
||||
}
|
||||
|
||||
static ToolOutput execTool(String tool, String... args) throws Throwable {
|
||||
System.out.println("== Running tool ======================================================");
|
||||
System.out.print(tool);
|
||||
for (String s : args) {
|
||||
System.out.print(" " + s);
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println("======================================================================");
|
||||
|
||||
ToolOutput output = new ToolOutput();
|
||||
ToolProvider t = ToolProvider.findFirst(tool)
|
||||
.orElseThrow(() -> new RuntimeException(tool + " not found"));
|
||||
t.run(output.ps, output.ps, args);
|
||||
|
||||
output.finish();
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
// Run some operations with java.util.stream, lambda expressions and string concatenation. This
|
||||
// will lead to AOT resolution of invokedynamic call sites.
|
||||
static void invokedynamicTests(String args[]) {
|
||||
List<String> strings = Arrays.asList("Hello", "World!");
|
||||
|
||||
String helloWorld = strings.parallelStream()
|
||||
.filter(s -> s.contains("o"))
|
||||
.map(s -> s.toLowerCase(Locale.ROOT))
|
||||
.collect(joining(","));
|
||||
|
||||
Stream.of(helloWorld.split("([,x-z]{1,3})([\\s]*)"))
|
||||
.map(String::toString)
|
||||
.forEach(System.out::println);
|
||||
|
||||
// Common concatenation patterns
|
||||
int i = args.length * 12357; // Seed with this so javac will not perform constant folding.
|
||||
String s = String.valueOf(i);
|
||||
|
||||
String SS = s + s;
|
||||
String CS = "string" + s;
|
||||
String SC = s + "string";
|
||||
String SCS = s + "string" + s;
|
||||
String CSS = "string" + s + s;
|
||||
String CSC = "string" + s + "string";
|
||||
String SSC = s + s + "string";
|
||||
String CSCS = "string" + s + "string" + s;
|
||||
String SCSC = s + "string" + s + "string";
|
||||
String CSCSC = "string" + s + "string" + s + "string";
|
||||
String SCSCS = s + "string" + s + "string" + s;
|
||||
String SSCSS = s + s + "string" + s + s;
|
||||
String S5 = s + s + s + s + s;
|
||||
String S6 = s + s + s + s + s + s;
|
||||
String S7 = s + s + s + s + s + s + s;
|
||||
String S8 = s + s + s + s + s + s + s + s;
|
||||
String S9 = s + s + s + s + s + s + s + s + s;
|
||||
String S10 = s + s + s + s + s + s + s + s + s + s;
|
||||
|
||||
String CI = "string" + i;
|
||||
String IC = i + "string";
|
||||
String SI = s + i;
|
||||
String IS = i + s;
|
||||
String CIS = "string" + i + s;
|
||||
String CSCI = "string" + s + "string" + i;
|
||||
String CIC = "string" + i + "string";
|
||||
String CICI = "string" + i + "string" + i;
|
||||
|
||||
float f = 0.1f;
|
||||
String CF = "string" + f;
|
||||
String CFS = "string" + f + s;
|
||||
String CSCF = "string" + s + "string" + f;
|
||||
|
||||
char c = 'a';
|
||||
String CC = "string" + c;
|
||||
String CCS = "string" + c + s;
|
||||
String CSCC = "string" + s + "string" + c;
|
||||
|
||||
long l = i + 12345678;
|
||||
String CJ = "string" + l;
|
||||
String JC = l + "string";
|
||||
String CJC = "string" + l + "string";
|
||||
String CJCJ = "string" + l + "string" + l;
|
||||
String CJCJC = "string" + l + "string" + l + "string";
|
||||
double d = i / 2.0;
|
||||
String CD = "string" + d;
|
||||
String CDS = "string" + d + s;
|
||||
String CSCD = "string" + s + "string" + d;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue