Skip to content

Commit

Permalink
Merge master HEAD into openj9-staging
Browse files Browse the repository at this point in the history
Signed-off-by: J9 Build <[email protected]>
  • Loading branch information
j9build committed Sep 26, 2024
2 parents 0555d75 + 30f7e78 commit e4f5d23
Show file tree
Hide file tree
Showing 26 changed files with 1,040 additions and 198 deletions.
3 changes: 3 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# JDK Vulnerabilities

Please follow the process outlined in the [OpenJDK Vulnerability Policy](https://openjdk.org/groups/vulnerability/report) to disclose vulnerabilities in the JDK.
185 changes: 79 additions & 106 deletions src/java.base/share/classes/java/lang/invoke/MethodHandles.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@
import sun.reflect.misc.ReflectUtil;
import sun.security.util.SecurityConstants;

import java.lang.classfile.ClassFile;
import java.lang.classfile.ClassModel;
import java.lang.constant.ClassDesc;
import java.lang.constant.ConstantDescs;
import java.lang.invoke.LambdaForm.BasicType;
import java.lang.invoke.MethodHandleImpl.Intrinsic;
Expand Down Expand Up @@ -2248,85 +2250,70 @@ private static ClassFileDumper defaultDumper() {
private static final ClassFileDumper DEFAULT_DUMPER = ClassFileDumper.getInstance(
"jdk.invoke.MethodHandle.dumpClassFiles", "DUMP_CLASS_FILES");

static class ClassFile {
final String name; // internal name
final int accessFlags;
final byte[] bytes;
ClassFile(String name, int accessFlags, byte[] bytes) {
this.name = name;
this.accessFlags = accessFlags;
this.bytes = bytes;
/**
* This method checks the class file version and the structure of `this_class`.
* and checks if the bytes is a class or interface (ACC_MODULE flag not set)
* that is in the named package.
*
* @throws IllegalArgumentException if ACC_MODULE flag is set in access flags
* or the class is not in the given package name.
*/
static String validateAndFindInternalName(byte[] bytes, String pkgName) {
int magic = readInt(bytes, 0);
if (magic != ClassFile.MAGIC_NUMBER) {
throw new ClassFormatError("Incompatible magic value: " + magic);
}
// We have to read major and minor this way as ClassFile API throws IAE
// yet we want distinct ClassFormatError and UnsupportedClassVersionError
int minor = readUnsignedShort(bytes, 4);
int major = readUnsignedShort(bytes, 6);

static ClassFile newInstanceNoCheck(String name, byte[] bytes) {
return new ClassFile(name, 0, bytes);
if (!VM.isSupportedClassFileVersion(major, minor)) {
throw new UnsupportedClassVersionError("Unsupported class file version " + major + "." + minor);
}

/**
* This method checks the class file version and the structure of `this_class`.
* and checks if the bytes is a class or interface (ACC_MODULE flag not set)
* that is in the named package.
*
* @throws IllegalArgumentException if ACC_MODULE flag is set in access flags
* or the class is not in the given package name.
*/
static ClassFile newInstance(byte[] bytes, String pkgName) {
var cf = readClassFile(bytes);

// check if it's in the named package
int index = cf.name.lastIndexOf('/');
String pn = (index == -1) ? "" : cf.name.substring(0, index).replace('/', '.');
if (!pn.equals(pkgName)) {
throw newIllegalArgumentException(cf.name + " not in same package as lookup class");
}
return cf;
String name;
ClassDesc sym;
int accessFlags;
try {
ClassModel cm = ClassFile.of().parse(bytes);
var thisClass = cm.thisClass();
name = thisClass.asInternalName();
sym = thisClass.asSymbol();
accessFlags = cm.flags().flagsMask();
} catch (IllegalArgumentException e) {
ClassFormatError cfe = new ClassFormatError();
cfe.initCause(e);
throw cfe;
}
// must be a class or interface
if ((accessFlags & ACC_MODULE) != 0) {
throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set");
}

private static ClassFile readClassFile(byte[] bytes) {
int magic = readInt(bytes, 0);
if (magic != 0xCAFEBABE) {
throw new ClassFormatError("Incompatible magic value: " + magic);
}
int minor = readUnsignedShort(bytes, 4);
int major = readUnsignedShort(bytes, 6);
if (!VM.isSupportedClassFileVersion(major, minor)) {
throw new UnsupportedClassVersionError("Unsupported class file version " + major + "." + minor);
}

String name;
int accessFlags;
try {
ClassModel cm = java.lang.classfile.ClassFile.of().parse(bytes);
name = cm.thisClass().asInternalName();
accessFlags = cm.flags().flagsMask();
} catch (IllegalArgumentException e) {
ClassFormatError cfe = new ClassFormatError();
cfe.initCause(e);
throw cfe;
}
// must be a class or interface
if ((accessFlags & ACC_MODULE) != 0) {
throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set");
}
return new ClassFile(name, accessFlags, bytes);
String pn = sym.packageName();
if (!pn.equals(pkgName)) {
throw newIllegalArgumentException(name + " not in same package as lookup class");
}

private static int readInt(byte[] bytes, int offset) {
if ((offset+4) > bytes.length) {
throw new ClassFormatError("Invalid ClassFile structure");
}
return ((bytes[offset] & 0xFF) << 24)
| ((bytes[offset + 1] & 0xFF) << 16)
| ((bytes[offset + 2] & 0xFF) << 8)
| (bytes[offset + 3] & 0xFF);
return name;
}

private static int readInt(byte[] bytes, int offset) {
if ((offset + 4) > bytes.length) {
throw new ClassFormatError("Invalid ClassFile structure");
}
return ((bytes[offset] & 0xFF) << 24)
| ((bytes[offset + 1] & 0xFF) << 16)
| ((bytes[offset + 2] & 0xFF) << 8)
| (bytes[offset + 3] & 0xFF);
}

private static int readUnsignedShort(byte[] bytes, int offset) {
if ((offset+2) > bytes.length) {
throw new ClassFormatError("Invalid ClassFile structure");
}
return ((bytes[offset] & 0xFF) << 8) | (bytes[offset + 1] & 0xFF);
private static int readUnsignedShort(byte[] bytes, int offset) {
if ((offset+2) > bytes.length) {
throw new ClassFormatError("Invalid ClassFile structure");
}
return ((bytes[offset] & 0xFF) << 8) | (bytes[offset + 1] & 0xFF);
}

/*
Expand All @@ -2340,23 +2327,22 @@ private static int readUnsignedShort(byte[] bytes, int offset) {
* {@code bytes} denotes a class in a different package than the lookup class
*/
private ClassDefiner makeClassDefiner(byte[] bytes) {
ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName());
return new ClassDefiner(this, cf, STRONG_LOADER_LINK, defaultDumper());
var internalName = validateAndFindInternalName(bytes, lookupClass().getPackageName());
return new ClassDefiner(this, internalName, bytes, STRONG_LOADER_LINK, defaultDumper());
}

/**
* Returns a ClassDefiner that creates a {@code Class} object of a normal class
* from the given bytes. No package name check on the given bytes.
*
* @param name internal name
* @param internalName internal name
* @param bytes class bytes
* @param dumper dumper to write the given bytes to the dumper's output directory
* @return ClassDefiner that defines a normal class of the given bytes.
*/
ClassDefiner makeClassDefiner(String name, byte[] bytes, ClassFileDumper dumper) {
ClassDefiner makeClassDefiner(String internalName, byte[] bytes, ClassFileDumper dumper) {
// skip package name validation
ClassFile cf = ClassFile.newInstanceNoCheck(name, bytes);
return new ClassDefiner(this, cf, STRONG_LOADER_LINK, dumper);
return new ClassDefiner(this, internalName, bytes, STRONG_LOADER_LINK, dumper);
}

/**
Expand All @@ -2374,8 +2360,8 @@ ClassDefiner makeClassDefiner(String name, byte[] bytes, ClassFileDumper dumper)
* {@code bytes} denotes a class in a different package than the lookup class
*/
ClassDefiner makeHiddenClassDefiner(byte[] bytes, ClassFileDumper dumper) {
ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName());
return makeHiddenClassDefiner(cf, false, dumper, 0);
var internalName = validateAndFindInternalName(bytes, lookupClass().getPackageName());
return makeHiddenClassDefiner(internalName, bytes, false, dumper, 0);
}

/**
Expand All @@ -2397,51 +2383,53 @@ ClassDefiner makeHiddenClassDefiner(byte[] bytes, ClassFileDumper dumper) {
private ClassDefiner makeHiddenClassDefiner(byte[] bytes,
boolean accessVmAnnotations,
int flags) {
ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName());
return makeHiddenClassDefiner(cf, accessVmAnnotations, defaultDumper(), flags);
var internalName = validateAndFindInternalName(bytes, lookupClass().getPackageName());
return makeHiddenClassDefiner(internalName, bytes, accessVmAnnotations, defaultDumper(), flags);
}

/**
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
* from the given bytes and the given options. No package name check on the given bytes.
*
* @param name internal name that specifies the prefix of the hidden class
* @param internalName internal name that specifies the prefix of the hidden class
* @param bytes class bytes
* @param dumper dumper to write the given bytes to the dumper's output directory
* @return ClassDefiner that defines a hidden class of the given bytes and options.
*/
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, ClassFileDumper dumper) {
ClassDefiner makeHiddenClassDefiner(String internalName, byte[] bytes, ClassFileDumper dumper) {
Objects.requireNonNull(dumper);
// skip name and access flags validation
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), false, dumper, 0);
return makeHiddenClassDefiner(internalName, bytes, false, dumper, 0);
}

/**
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
* from the given bytes and the given options. No package name check on the given bytes.
*
* @param name internal name that specifies the prefix of the hidden class
* @param internalName internal name that specifies the prefix of the hidden class
* @param bytes class bytes
* @param flags class options flag mask
* @param dumper dumper to write the given bytes to the dumper's output directory
* @return ClassDefiner that defines a hidden class of the given bytes and options.
*/
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, ClassFileDumper dumper, int flags) {
ClassDefiner makeHiddenClassDefiner(String internalName, byte[] bytes, ClassFileDumper dumper, int flags) {
Objects.requireNonNull(dumper);
// skip name and access flags validation
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), false, dumper, flags);
return makeHiddenClassDefiner(internalName, bytes, false, dumper, flags);
}

/**
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
* from the given class file and options.
*
* @param cf ClassFile
* @param internalName internal name
* @param bytes Class byte array
* @param flags class option flag mask
* @param accessVmAnnotations true to give the hidden class access to VM annotations
* @param dumper dumper to write the given bytes to the dumper's output directory
*/
private ClassDefiner makeHiddenClassDefiner(ClassFile cf,
private ClassDefiner makeHiddenClassDefiner(String internalName,
byte[] bytes,
boolean accessVmAnnotations,
ClassFileDumper dumper,
int flags) {
Expand All @@ -2452,27 +2440,12 @@ private ClassDefiner makeHiddenClassDefiner(ClassFile cf,
flags |= ACCESS_VM_ANNOTATIONS;
}

return new ClassDefiner(this, cf, flags, dumper);
return new ClassDefiner(this, internalName, bytes, flags, dumper);
}

static class ClassDefiner {
private final Lookup lookup;
private final String name; // internal name
private final byte[] bytes;
private final int classFlags;
private final ClassFileDumper dumper;

private ClassDefiner(Lookup lookup, ClassFile cf, int flags, ClassFileDumper dumper) {
assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK);
this.lookup = lookup;
this.bytes = cf.bytes;
this.name = cf.name;
this.classFlags = flags;
this.dumper = dumper;
}

String internalName() {
return name;
record ClassDefiner(Lookup lookup, String internalName, byte[] bytes, int classFlags, ClassFileDumper dumper) {
ClassDefiner {
assert ((classFlags & HIDDEN_CLASS) != 0 || (classFlags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK);
}

Class<?> defineClass(boolean initialize) {
Expand Down Expand Up @@ -2501,7 +2474,7 @@ Class<?> defineClass(boolean initialize, Object classData) {
Class<?> c = null;
try {
c = SharedSecrets.getJavaLangAccess()
.defineClass(loader, lookupClass, name, bytes, pd, initialize, classFlags, classData);
.defineClass(loader, lookupClass, internalName, bytes, pd, initialize, classFlags, classData);
assert !isNestmate() || c.getNestHost() == lookupClass.getNestHost();
return c;
} finally {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 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
Expand All @@ -26,7 +26,8 @@
package java.lang.invoke;

import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

import static java.lang.invoke.MethodHandleStatics.UNSAFE;

/**
* A {@code MutableCallSite} is a {@link CallSite} whose target variable
Expand Down Expand Up @@ -274,11 +275,10 @@ public final MethodHandle dynamicInvoker() {
*/
public static void syncAll(MutableCallSite[] sites) {
if (sites.length == 0) return;
STORE_BARRIER.lazySet(0);
UNSAFE.storeFence();
for (MutableCallSite site : sites) {
Objects.requireNonNull(site); // trigger NPE on first null
}
// FIXME: NYI
}
private static final AtomicInteger STORE_BARRIER = new AtomicInteger();
}
Loading

0 comments on commit e4f5d23

Please sign in to comment.