Skip to content

Commit 84751cb

Browse files
committed
8340831: Simplify simple validation for class definition in MethodHandles.Lookup
Reviewed-by: redestad
1 parent df1959f commit 84751cb

File tree

2 files changed

+85
-128
lines changed

2 files changed

+85
-128
lines changed

src/java.base/share/classes/java/lang/invoke/MethodHandles.java

Lines changed: 79 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@
3939
import sun.reflect.misc.ReflectUtil;
4040
import sun.security.util.SecurityConstants;
4141

42+
import java.lang.classfile.ClassFile;
4243
import java.lang.classfile.ClassModel;
44+
import java.lang.constant.ClassDesc;
4345
import java.lang.constant.ConstantDescs;
4446
import java.lang.invoke.LambdaForm.BasicType;
4547
import java.lang.invoke.MethodHandleImpl.Intrinsic;
@@ -2242,85 +2244,70 @@ private static ClassFileDumper defaultDumper() {
22422244
private static final ClassFileDumper DEFAULT_DUMPER = ClassFileDumper.getInstance(
22432245
"jdk.invoke.MethodHandle.dumpClassFiles", "DUMP_CLASS_FILES");
22442246

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

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

2259-
/**
2260-
* This method checks the class file version and the structure of `this_class`.
2261-
* and checks if the bytes is a class or interface (ACC_MODULE flag not set)
2262-
* that is in the named package.
2263-
*
2264-
* @throws IllegalArgumentException if ACC_MODULE flag is set in access flags
2265-
* or the class is not in the given package name.
2266-
*/
2267-
static ClassFile newInstance(byte[] bytes, String pkgName) {
2268-
var cf = readClassFile(bytes);
2269-
2270-
// check if it's in the named package
2271-
int index = cf.name.lastIndexOf('/');
2272-
String pn = (index == -1) ? "" : cf.name.substring(0, index).replace('/', '.');
2273-
if (!pn.equals(pkgName)) {
2274-
throw newIllegalArgumentException(cf.name + " not in same package as lookup class");
2275-
}
2276-
return cf;
2269+
String name;
2270+
ClassDesc sym;
2271+
int accessFlags;
2272+
try {
2273+
ClassModel cm = ClassFile.of().parse(bytes);
2274+
var thisClass = cm.thisClass();
2275+
name = thisClass.asInternalName();
2276+
sym = thisClass.asSymbol();
2277+
accessFlags = cm.flags().flagsMask();
2278+
} catch (IllegalArgumentException e) {
2279+
ClassFormatError cfe = new ClassFormatError();
2280+
cfe.initCause(e);
2281+
throw cfe;
2282+
}
2283+
// must be a class or interface
2284+
if ((accessFlags & ACC_MODULE) != 0) {
2285+
throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set");
22772286
}
22782287

2279-
private static ClassFile readClassFile(byte[] bytes) {
2280-
int magic = readInt(bytes, 0);
2281-
if (magic != 0xCAFEBABE) {
2282-
throw new ClassFormatError("Incompatible magic value: " + magic);
2283-
}
2284-
int minor = readUnsignedShort(bytes, 4);
2285-
int major = readUnsignedShort(bytes, 6);
2286-
if (!VM.isSupportedClassFileVersion(major, minor)) {
2287-
throw new UnsupportedClassVersionError("Unsupported class file version " + major + "." + minor);
2288-
}
2289-
2290-
String name;
2291-
int accessFlags;
2292-
try {
2293-
ClassModel cm = java.lang.classfile.ClassFile.of().parse(bytes);
2294-
name = cm.thisClass().asInternalName();
2295-
accessFlags = cm.flags().flagsMask();
2296-
} catch (IllegalArgumentException e) {
2297-
ClassFormatError cfe = new ClassFormatError();
2298-
cfe.initCause(e);
2299-
throw cfe;
2300-
}
2301-
// must be a class or interface
2302-
if ((accessFlags & ACC_MODULE) != 0) {
2303-
throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set");
2304-
}
2305-
return new ClassFile(name, accessFlags, bytes);
2288+
String pn = sym.packageName();
2289+
if (!pn.equals(pkgName)) {
2290+
throw newIllegalArgumentException(name + " not in same package as lookup class");
23062291
}
23072292

2308-
private static int readInt(byte[] bytes, int offset) {
2309-
if ((offset+4) > bytes.length) {
2310-
throw new ClassFormatError("Invalid ClassFile structure");
2311-
}
2312-
return ((bytes[offset] & 0xFF) << 24)
2313-
| ((bytes[offset + 1] & 0xFF) << 16)
2314-
| ((bytes[offset + 2] & 0xFF) << 8)
2315-
| (bytes[offset + 3] & 0xFF);
2293+
return name;
2294+
}
2295+
2296+
private static int readInt(byte[] bytes, int offset) {
2297+
if ((offset + 4) > bytes.length) {
2298+
throw new ClassFormatError("Invalid ClassFile structure");
23162299
}
2300+
return ((bytes[offset] & 0xFF) << 24)
2301+
| ((bytes[offset + 1] & 0xFF) << 16)
2302+
| ((bytes[offset + 2] & 0xFF) << 8)
2303+
| (bytes[offset + 3] & 0xFF);
2304+
}
23172305

2318-
private static int readUnsignedShort(byte[] bytes, int offset) {
2319-
if ((offset+2) > bytes.length) {
2320-
throw new ClassFormatError("Invalid ClassFile structure");
2321-
}
2322-
return ((bytes[offset] & 0xFF) << 8) | (bytes[offset + 1] & 0xFF);
2306+
private static int readUnsignedShort(byte[] bytes, int offset) {
2307+
if ((offset+2) > bytes.length) {
2308+
throw new ClassFormatError("Invalid ClassFile structure");
23232309
}
2310+
return ((bytes[offset] & 0xFF) << 8) | (bytes[offset + 1] & 0xFF);
23242311
}
23252312

23262313
/*
@@ -2334,23 +2321,22 @@ private static int readUnsignedShort(byte[] bytes, int offset) {
23342321
* {@code bytes} denotes a class in a different package than the lookup class
23352322
*/
23362323
private ClassDefiner makeClassDefiner(byte[] bytes) {
2337-
ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName());
2338-
return new ClassDefiner(this, cf, STRONG_LOADER_LINK, defaultDumper());
2324+
var internalName = validateAndFindInternalName(bytes, lookupClass().getPackageName());
2325+
return new ClassDefiner(this, internalName, bytes, STRONG_LOADER_LINK, defaultDumper());
23392326
}
23402327

23412328
/**
23422329
* Returns a ClassDefiner that creates a {@code Class} object of a normal class
23432330
* from the given bytes. No package name check on the given bytes.
23442331
*
2345-
* @param name internal name
2332+
* @param internalName internal name
23462333
* @param bytes class bytes
23472334
* @param dumper dumper to write the given bytes to the dumper's output directory
23482335
* @return ClassDefiner that defines a normal class of the given bytes.
23492336
*/
2350-
ClassDefiner makeClassDefiner(String name, byte[] bytes, ClassFileDumper dumper) {
2337+
ClassDefiner makeClassDefiner(String internalName, byte[] bytes, ClassFileDumper dumper) {
23512338
// skip package name validation
2352-
ClassFile cf = ClassFile.newInstanceNoCheck(name, bytes);
2353-
return new ClassDefiner(this, cf, STRONG_LOADER_LINK, dumper);
2339+
return new ClassDefiner(this, internalName, bytes, STRONG_LOADER_LINK, dumper);
23542340
}
23552341

23562342
/**
@@ -2368,8 +2354,8 @@ ClassDefiner makeClassDefiner(String name, byte[] bytes, ClassFileDumper dumper)
23682354
* {@code bytes} denotes a class in a different package than the lookup class
23692355
*/
23702356
ClassDefiner makeHiddenClassDefiner(byte[] bytes, ClassFileDumper dumper) {
2371-
ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName());
2372-
return makeHiddenClassDefiner(cf, false, dumper, 0);
2357+
var internalName = validateAndFindInternalName(bytes, lookupClass().getPackageName());
2358+
return makeHiddenClassDefiner(internalName, bytes, false, dumper, 0);
23732359
}
23742360

23752361
/**
@@ -2391,51 +2377,53 @@ ClassDefiner makeHiddenClassDefiner(byte[] bytes, ClassFileDumper dumper) {
23912377
private ClassDefiner makeHiddenClassDefiner(byte[] bytes,
23922378
boolean accessVmAnnotations,
23932379
int flags) {
2394-
ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName());
2395-
return makeHiddenClassDefiner(cf, accessVmAnnotations, defaultDumper(), flags);
2380+
var internalName = validateAndFindInternalName(bytes, lookupClass().getPackageName());
2381+
return makeHiddenClassDefiner(internalName, bytes, accessVmAnnotations, defaultDumper(), flags);
23962382
}
23972383

23982384
/**
23992385
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
24002386
* from the given bytes and the given options. No package name check on the given bytes.
24012387
*
2402-
* @param name internal name that specifies the prefix of the hidden class
2388+
* @param internalName internal name that specifies the prefix of the hidden class
24032389
* @param bytes class bytes
24042390
* @param dumper dumper to write the given bytes to the dumper's output directory
24052391
* @return ClassDefiner that defines a hidden class of the given bytes and options.
24062392
*/
2407-
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, ClassFileDumper dumper) {
2393+
ClassDefiner makeHiddenClassDefiner(String internalName, byte[] bytes, ClassFileDumper dumper) {
24082394
Objects.requireNonNull(dumper);
24092395
// skip name and access flags validation
2410-
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), false, dumper, 0);
2396+
return makeHiddenClassDefiner(internalName, bytes, false, dumper, 0);
24112397
}
24122398

24132399
/**
24142400
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
24152401
* from the given bytes and the given options. No package name check on the given bytes.
24162402
*
2417-
* @param name internal name that specifies the prefix of the hidden class
2403+
* @param internalName internal name that specifies the prefix of the hidden class
24182404
* @param bytes class bytes
24192405
* @param flags class options flag mask
24202406
* @param dumper dumper to write the given bytes to the dumper's output directory
24212407
* @return ClassDefiner that defines a hidden class of the given bytes and options.
24222408
*/
2423-
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes, ClassFileDumper dumper, int flags) {
2409+
ClassDefiner makeHiddenClassDefiner(String internalName, byte[] bytes, ClassFileDumper dumper, int flags) {
24242410
Objects.requireNonNull(dumper);
24252411
// skip name and access flags validation
2426-
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), false, dumper, flags);
2412+
return makeHiddenClassDefiner(internalName, bytes, false, dumper, flags);
24272413
}
24282414

24292415
/**
24302416
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
24312417
* from the given class file and options.
24322418
*
2433-
* @param cf ClassFile
2419+
* @param internalName internal name
2420+
* @param bytes Class byte array
24342421
* @param flags class option flag mask
24352422
* @param accessVmAnnotations true to give the hidden class access to VM annotations
24362423
* @param dumper dumper to write the given bytes to the dumper's output directory
24372424
*/
2438-
private ClassDefiner makeHiddenClassDefiner(ClassFile cf,
2425+
private ClassDefiner makeHiddenClassDefiner(String internalName,
2426+
byte[] bytes,
24392427
boolean accessVmAnnotations,
24402428
ClassFileDumper dumper,
24412429
int flags) {
@@ -2446,27 +2434,12 @@ private ClassDefiner makeHiddenClassDefiner(ClassFile cf,
24462434
flags |= ACCESS_VM_ANNOTATIONS;
24472435
}
24482436

2449-
return new ClassDefiner(this, cf, flags, dumper);
2437+
return new ClassDefiner(this, internalName, bytes, flags, dumper);
24502438
}
24512439

2452-
static class ClassDefiner {
2453-
private final Lookup lookup;
2454-
private final String name; // internal name
2455-
private final byte[] bytes;
2456-
private final int classFlags;
2457-
private final ClassFileDumper dumper;
2458-
2459-
private ClassDefiner(Lookup lookup, ClassFile cf, int flags, ClassFileDumper dumper) {
2460-
assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK);
2461-
this.lookup = lookup;
2462-
this.bytes = cf.bytes;
2463-
this.name = cf.name;
2464-
this.classFlags = flags;
2465-
this.dumper = dumper;
2466-
}
2467-
2468-
String internalName() {
2469-
return name;
2440+
record ClassDefiner(Lookup lookup, String internalName, byte[] bytes, int classFlags, ClassFileDumper dumper) {
2441+
ClassDefiner {
2442+
assert ((classFlags & HIDDEN_CLASS) != 0 || (classFlags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK);
24702443
}
24712444

24722445
Class<?> defineClass(boolean initialize) {
@@ -2495,7 +2468,7 @@ Class<?> defineClass(boolean initialize, Object classData) {
24952468
Class<?> c = null;
24962469
try {
24972470
c = SharedSecrets.getJavaLangAccess()
2498-
.defineClass(loader, lookupClass, name, bytes, pd, initialize, classFlags, classData);
2471+
.defineClass(loader, lookupClass, internalName, bytes, pd, initialize, classFlags, classData);
24992472
assert !isNestmate() || c.getNestHost() == lookupClass.getNestHost();
25002473
return c;
25012474
} finally {

src/java.base/share/classes/jdk/internal/misc/VM.java

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static java.lang.Thread.State.*;
2929

3030
import java.io.PrintStream;
31+
import java.lang.classfile.ClassFile;
3132
import java.util.ArrayList;
3233
import java.util.Collections;
3334
import java.util.List;
@@ -158,10 +159,6 @@ public static boolean isDirectMemoryPageAligned() {
158159
return pageAlignDirectMemory;
159160
}
160161

161-
private static int classFileMajorVersion;
162-
private static int classFileMinorVersion;
163-
private static final int PREVIEW_MINOR_VERSION = 65535;
164-
165162
/**
166163
* Tests if the given version is a supported {@code class}
167164
* file version.
@@ -175,11 +172,11 @@ public static boolean isDirectMemoryPageAligned() {
175172
* @jvms 4.1 Table 4.1-A. class file format major versions
176173
*/
177174
public static boolean isSupportedClassFileVersion(int major, int minor) {
178-
if (major < 45 || major > classFileMajorVersion) return false;
175+
if (major < ClassFile.JAVA_1_VERSION || major > ClassFile.latestMajorVersion()) return false;
179176
// for major version is between 45 and 55 inclusive, the minor version may be any value
180-
if (major < 56) return true;
177+
if (major < ClassFile.JAVA_12_VERSION) return true;
181178
// otherwise, the minor version must be 0 or 65535
182-
return minor == 0 || minor == PREVIEW_MINOR_VERSION;
179+
return minor == 0 || (minor == ClassFile.PREVIEW_MINOR_VERSION && major == ClassFile.latestMajorVersion());
183180
}
184181

185182
/**
@@ -189,12 +186,8 @@ public static boolean isSupportedClassFileVersion(int major, int minor) {
189186
* major.minor version >= 53.0
190187
*/
191188
public static boolean isSupportedModuleDescriptorVersion(int major, int minor) {
192-
if (major < 53 || major > classFileMajorVersion) return false;
193-
// for major version is between 45 and 55 inclusive, the minor version may be any value
194-
if (major < 56) return true;
195-
// otherwise, the minor version must be 0 or 65535
196-
// preview features do not apply to module-info.class but JVMS allows it
197-
return minor == 0 || minor == PREVIEW_MINOR_VERSION;
189+
if (major < ClassFile.JAVA_9_VERSION) return false;
190+
return isSupportedClassFileVersion(major, minor);
198191
}
199192

200193
/**
@@ -271,15 +264,6 @@ public static void saveProperties(Map<String, String> props) {
271264
s = props.get("sun.nio.PageAlignDirectMemory");
272265
if ("true".equals(s))
273266
pageAlignDirectMemory = true;
274-
275-
s = props.get("java.class.version");
276-
int index = s.indexOf('.');
277-
try {
278-
classFileMajorVersion = Integer.parseInt(s.substring(0, index));
279-
classFileMinorVersion = Integer.parseInt(s.substring(index + 1));
280-
} catch (NumberFormatException e) {
281-
throw new InternalError(e);
282-
}
283267
}
284268

285269
// Initialize any miscellaneous operating system settings that need to be

0 commit comments

Comments
 (0)