8245061: Lookup::defineHiddenClass should throw ClassFormatError if this_class is not Class_info structure
8245432: Lookup::defineHiddenClass should throw UnsupportedClassVersionError if bytes are of an unsupported major or minor version 8245596: Clarify Lookup::defineHiddenClass spec @throws IAE if the bytes has ACC_MODULE flag set Reviewed-by: alanb, dholmes
This commit is contained in:
parent
1f698a35f2
commit
5e5880d4f1
@ -314,7 +314,7 @@ class InvokerBytecodeGenerator {
|
||||
* Extract the MemberName of a newly-defined method.
|
||||
*/
|
||||
private MemberName loadMethod(byte[] classFile) {
|
||||
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(classFile)
|
||||
Class<?> invokerClass = LOOKUP.makeHiddenClassDefiner(className(), classFile)
|
||||
.defineClass(true, classDataValues());
|
||||
return resolveInvokerMember(invokerClass, invokerName, invokerType);
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ import jdk.internal.misc.VM;
|
||||
import jdk.internal.module.IllegalAccessLogger;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.org.objectweb.asm.Type;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
@ -1661,8 +1662,9 @@ public class MethodHandles {
|
||||
* @return the {@code Class} object for the class
|
||||
* @throws IllegalAccessException if this lookup does not have {@code PACKAGE} access
|
||||
* @throws ClassFormatError if {@code bytes} is not a {@code ClassFile} structure
|
||||
* @throws IllegalArgumentException the bytes are for a class in a different package
|
||||
* to the lookup class
|
||||
* @throws IllegalArgumentException if {@code bytes} denotes a class in a different package
|
||||
* than the lookup class or {@code bytes} is not a class or interface
|
||||
* ({@code ACC_MODULE} flag is set in the value of the {@code access_flags} item)
|
||||
* @throws VerifyError if the newly created class cannot be verified
|
||||
* @throws LinkageError if the newly created class cannot be linked for any other reason
|
||||
* @throws SecurityException if a security manager is present and it
|
||||
@ -1923,8 +1925,9 @@ public class MethodHandles {
|
||||
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
|
||||
* @throws ClassFormatError if {@code bytes} is not a {@code ClassFile} structure
|
||||
* @throws UnsupportedClassVersionError if {@code bytes} is not of a supported major or minor version
|
||||
* @throws IllegalArgumentException if {@code bytes} is not a class or interface or
|
||||
* {@bytes} denotes a class in a different package than the lookup class
|
||||
* @throws IllegalArgumentException if {@code bytes} denotes a class in a different package
|
||||
* than the lookup class or {@code bytes} is not a class or interface
|
||||
* ({@code ACC_MODULE} flag is set in the value of the {@code access_flags} item)
|
||||
* @throws IncompatibleClassChangeError if the class or interface named as
|
||||
* the direct superclass of {@code C} is in fact an interface, or if any of the classes
|
||||
* or interfaces named as direct superinterfaces of {@code C} are not in fact interfaces
|
||||
@ -1987,8 +1990,9 @@ public class MethodHandles {
|
||||
* <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
|
||||
* @throws ClassFormatError if {@code bytes} is not a {@code ClassFile} structure
|
||||
* @throws UnsupportedClassVersionError if {@code bytes} is not of a supported major or minor version
|
||||
* @throws IllegalArgumentException if {@code bytes} is not a class or interface or
|
||||
* {@bytes} denotes a class in a different package than the lookup class
|
||||
* @throws IllegalArgumentException if {@code bytes} denotes a class in a different package
|
||||
* than the lookup class or {@code bytes} is not a class or interface
|
||||
* ({@code ACC_MODULE} flag is set in the value of the {@code access_flags} item)
|
||||
* @throws IncompatibleClassChangeError if the class or interface named as
|
||||
* the direct superclass of {@code C} is in fact an interface, or if any of the classes
|
||||
* or interfaces named as direct superinterfaces of {@code C} are not in fact interfaces
|
||||
@ -2018,37 +2022,96 @@ public class MethodHandles {
|
||||
.defineClassAsLookup(true, classData);
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates the given bytes to be a class or interface and the class name
|
||||
* is in the same package as the lookup class.
|
||||
*
|
||||
* This method returns the class name.
|
||||
*/
|
||||
private String validateAndGetClassName(byte[] bytes) {
|
||||
try {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
if ((reader.getAccess() & Opcodes.ACC_MODULE) != 0) {
|
||||
static class ClassFile {
|
||||
final String name;
|
||||
final int accessFlags;
|
||||
final byte[] bytes;
|
||||
ClassFile(String name, int accessFlags, byte[] bytes) {
|
||||
this.name = name;
|
||||
this.accessFlags = accessFlags;
|
||||
this.bytes = bytes;
|
||||
}
|
||||
|
||||
static ClassFile newInstanceNoCheck(String name, byte[] bytes) {
|
||||
return new ClassFile(name, 0, 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 ClassFile newInstance(byte[] bytes, String pkgName) {
|
||||
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 {
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
// ClassReader::getClassName does not check if `this_class` is CONSTANT_Class_info
|
||||
// workaround to read `this_class` using readConst and validate the value
|
||||
int thisClass = reader.readUnsignedShort(reader.header + 2);
|
||||
Object constant = reader.readConst(thisClass, new char[reader.getMaxStringLength()]);
|
||||
if (!(constant instanceof Type)) {
|
||||
throw new ClassFormatError("this_class item: #" + thisClass + " not a CONSTANT_Class_info");
|
||||
}
|
||||
Type type = ((Type) constant);
|
||||
if (!type.getDescriptor().startsWith("L")) {
|
||||
throw new ClassFormatError("this_class item: #" + thisClass + " not a CONSTANT_Class_info");
|
||||
}
|
||||
name = type.getClassName();
|
||||
accessFlags = reader.readUnsignedShort(reader.header);
|
||||
} catch (RuntimeException e) {
|
||||
// ASM exceptions are poorly specified
|
||||
ClassFormatError cfe = new ClassFormatError();
|
||||
cfe.initCause(e);
|
||||
throw cfe;
|
||||
}
|
||||
|
||||
// must be a class or interface
|
||||
if ((accessFlags & Opcodes.ACC_MODULE) != 0) {
|
||||
throw newIllegalArgumentException("Not a class or interface: ACC_MODULE flag is set");
|
||||
}
|
||||
String name = reader.getClassName().replace('/', '.');
|
||||
|
||||
// check if it's in the named package
|
||||
int index = name.lastIndexOf('.');
|
||||
String pn = (index == -1) ? "" : name.substring(0, index);
|
||||
if (!pn.equals(lookupClass.getPackageName())) {
|
||||
throw newIllegalArgumentException(name + " not in same package as lookup class: " +
|
||||
lookupClass.getName());
|
||||
if (!pn.equals(pkgName)) {
|
||||
throw newIllegalArgumentException(name + " not in same package as lookup class");
|
||||
}
|
||||
return name;
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
// ASM exceptions are poorly specified
|
||||
ClassFormatError cfe = new ClassFormatError();
|
||||
cfe.initCause(e);
|
||||
throw cfe;
|
||||
|
||||
return new ClassFile(name, accessFlags, bytes);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns a ClassDefiner that creates a {@code Class} object of a normal class
|
||||
* from the given bytes.
|
||||
@ -2060,7 +2123,8 @@ public class MethodHandles {
|
||||
* {@bytes} denotes a class in a different package than the lookup class
|
||||
*/
|
||||
private ClassDefiner makeClassDefiner(byte[] bytes) {
|
||||
return new ClassDefiner(this, validateAndGetClassName(bytes), bytes, STRONG_LOADER_LINK);
|
||||
ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName());
|
||||
return new ClassDefiner(this, cf, STRONG_LOADER_LINK);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2077,7 +2141,8 @@ public class MethodHandles {
|
||||
* {@bytes} denotes a class in a different package than the lookup class
|
||||
*/
|
||||
ClassDefiner makeHiddenClassDefiner(byte[] bytes) {
|
||||
return makeHiddenClassDefiner(validateAndGetClassName(bytes), bytes, Set.of(), false);
|
||||
ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName());
|
||||
return makeHiddenClassDefiner(cf, Set.of(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2099,7 +2164,8 @@ public class MethodHandles {
|
||||
ClassDefiner makeHiddenClassDefiner(byte[] bytes,
|
||||
Set<ClassOption> options,
|
||||
boolean accessVmAnnotations) {
|
||||
return makeHiddenClassDefiner(validateAndGetClassName(bytes), bytes, options, accessVmAnnotations);
|
||||
ClassFile cf = ClassFile.newInstance(bytes, lookupClass().getPackageName());
|
||||
return makeHiddenClassDefiner(cf, options, accessVmAnnotations);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2111,22 +2177,21 @@ public class MethodHandles {
|
||||
* @return ClassDefiner that defines a hidden class of the given bytes.
|
||||
*/
|
||||
ClassDefiner makeHiddenClassDefiner(String name, byte[] bytes) {
|
||||
return makeHiddenClassDefiner(name, bytes, Set.of(), false);
|
||||
// skip name and access flags validation
|
||||
return makeHiddenClassDefiner(ClassFile.newInstanceNoCheck(name, bytes), Set.of(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ClassDefiner that creates a {@code Class} object of a hidden class
|
||||
* from the given bytes and options. No package name check on the given name.
|
||||
* from the given class file and options.
|
||||
*
|
||||
* @param name the name of the class and the name in the class bytes is ignored.
|
||||
* @param bytes class bytes
|
||||
* @param cf ClassFile
|
||||
* @param options class options
|
||||
* @param accessVmAnnotations true to give the hidden class access to VM annotations
|
||||
*/
|
||||
ClassDefiner makeHiddenClassDefiner(String name,
|
||||
byte[] bytes,
|
||||
Set<ClassOption> options,
|
||||
boolean accessVmAnnotations) {
|
||||
private ClassDefiner makeHiddenClassDefiner(ClassFile cf,
|
||||
Set<ClassOption> options,
|
||||
boolean accessVmAnnotations) {
|
||||
int flags = HIDDEN_CLASS | ClassOption.optionsToFlag(options);
|
||||
if (accessVmAnnotations | VM.isSystemDomainLoader(lookupClass.getClassLoader())) {
|
||||
// jdk.internal.vm.annotations are permitted for classes
|
||||
@ -2134,7 +2199,7 @@ public class MethodHandles {
|
||||
flags |= ACCESS_VM_ANNOTATIONS;
|
||||
}
|
||||
|
||||
return new ClassDefiner(this, name, bytes, flags);
|
||||
return new ClassDefiner(this, cf, flags);
|
||||
}
|
||||
|
||||
static class ClassDefiner {
|
||||
@ -2143,12 +2208,12 @@ public class MethodHandles {
|
||||
private final byte[] bytes;
|
||||
private final int classFlags;
|
||||
|
||||
private ClassDefiner(Lookup lookup, String name, byte[] bytes, int flags) {
|
||||
private ClassDefiner(Lookup lookup, ClassFile cf, int flags) {
|
||||
assert ((flags & HIDDEN_CLASS) != 0 || (flags & STRONG_LOADER_LINK) == STRONG_LOADER_LINK);
|
||||
this.lookup = lookup;
|
||||
this.bytes = bytes;
|
||||
this.bytes = cf.bytes;
|
||||
this.name = cf.name;
|
||||
this.classFlags = flags;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
String className() {
|
||||
|
@ -27,11 +27,11 @@ package jdk.internal.misc;
|
||||
|
||||
import static java.lang.Thread.State.*;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
|
||||
@ -46,7 +46,6 @@ public class VM {
|
||||
private static final int SYSTEM_BOOTED = 4;
|
||||
private static final int SYSTEM_SHUTDOWN = 5;
|
||||
|
||||
|
||||
// 0, 1, 2, ...
|
||||
private static volatile int initLevel;
|
||||
private static final Object lock = new Object();
|
||||
@ -148,6 +147,45 @@ public class VM {
|
||||
return pageAlignDirectMemory;
|
||||
}
|
||||
|
||||
private static int classFileMajorVersion;
|
||||
private static int classFileMinorVersion;
|
||||
private static final int PREVIEW_MINOR_VERSION = 65535;
|
||||
|
||||
/**
|
||||
* Tests if the given version is a supported {@code class}
|
||||
* file version.
|
||||
*
|
||||
* A {@code class} file depends on the preview features of Java SE {@code N}
|
||||
* if the major version is {@code N} and the minor version is 65535.
|
||||
* This method returns {@code true} if the given version is a supported
|
||||
* {@code class} file version regardless of whether the preview features
|
||||
* are enabled or not.
|
||||
*
|
||||
* @jvms 4.1 Table 4.1-A. class file format major versions
|
||||
*/
|
||||
public static boolean isSupportedClassFileVersion(int major, int minor) {
|
||||
if (major < 45 || major > classFileMajorVersion) return false;
|
||||
// for major version is between 45 and 55 inclusive, the minor version may be any value
|
||||
if (major < 56) return true;
|
||||
// otherwise, the minor version must be 0 or 65535
|
||||
return minor == 0 || minor == PREVIEW_MINOR_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given version is a supported {@code class}
|
||||
* file version for module descriptor.
|
||||
*
|
||||
* major.minor version >= 53.0
|
||||
*/
|
||||
public static boolean isSupportedModuleDescriptorVersion(int major, int minor) {
|
||||
if (major < 53 || major > classFileMajorVersion) return false;
|
||||
// for major version is between 45 and 55 inclusive, the minor version may be any value
|
||||
if (major < 56) return true;
|
||||
// otherwise, the minor version must be 0 or 65535
|
||||
// preview features do not apply to module-info.class but JVMS allows it
|
||||
return minor == 0 || minor == PREVIEW_MINOR_VERSION;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given class loader is the bootstrap class loader
|
||||
* or the platform class loader.
|
||||
@ -222,6 +260,15 @@ public class VM {
|
||||
s = props.get("sun.nio.PageAlignDirectMemory");
|
||||
if ("true".equals(s))
|
||||
pageAlignDirectMemory = true;
|
||||
|
||||
s = props.get("java.class.version");
|
||||
int index = s.indexOf('.');
|
||||
try {
|
||||
classFileMajorVersion = Integer.valueOf(s.substring(0, index));
|
||||
classFileMinorVersion = Integer.valueOf(s.substring(index+1, s.length()));
|
||||
} catch (NumberFormatException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize any miscellaneous operating system settings that need to be
|
||||
|
@ -49,6 +49,7 @@ import java.util.function.Supplier;
|
||||
|
||||
import jdk.internal.access.JavaLangModuleAccess;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.misc.VM;
|
||||
|
||||
import static jdk.internal.module.ClassFileConstants.*;
|
||||
|
||||
@ -62,9 +63,6 @@ import static jdk.internal.module.ClassFileConstants.*;
|
||||
|
||||
public final class ModuleInfo {
|
||||
|
||||
private final int JAVA_MIN_SUPPORTED_VERSION = 53;
|
||||
private final int JAVA_MAX_SUPPORTED_VERSION = 59;
|
||||
|
||||
private static final JavaLangModuleAccess JLMA
|
||||
= SharedSecrets.getJavaLangModuleAccess();
|
||||
|
||||
@ -190,8 +188,7 @@ public final class ModuleInfo {
|
||||
|
||||
int minor_version = in.readUnsignedShort();
|
||||
int major_version = in.readUnsignedShort();
|
||||
if (major_version < JAVA_MIN_SUPPORTED_VERSION ||
|
||||
major_version > JAVA_MAX_SUPPORTED_VERSION) {
|
||||
if (!VM.isSupportedModuleDescriptorVersion(major_version, minor_version)) {
|
||||
throw invalidModuleDescriptor("Unsupported major.minor version "
|
||||
+ major_version + "." + minor_version);
|
||||
}
|
||||
|
@ -242,6 +242,11 @@ public class DefineClassTest {
|
||||
lookup().defineClass(generateNonLinkableClass(THIS_PACKAGE + ".NonLinkableClass"));
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { IllegalArgumentException.class })
|
||||
public void testModuleInfo() throws Exception {
|
||||
lookup().defineClass(generateModuleInfo());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a class file with the given class name
|
||||
*/
|
||||
@ -366,6 +371,23 @@ public class DefineClassTest {
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a class file with the given class name
|
||||
*/
|
||||
byte[] generateModuleInfo() {
|
||||
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS
|
||||
+ ClassWriter.COMPUTE_FRAMES);
|
||||
cw.visit(V14,
|
||||
ACC_MODULE,
|
||||
"module-info",
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
|
||||
cw.visitEnd();
|
||||
return cw.toByteArray();
|
||||
}
|
||||
|
||||
private int nextNumber() {
|
||||
return ++nextNumber;
|
||||
}
|
||||
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
// this_class is modified to CONSTANT_NameAndType_info
|
||||
class BadClassFile {
|
||||
0xCAFEBABE;
|
||||
0; // minor version
|
||||
58; // version
|
||||
[] { // Constant Pool
|
||||
; // first element is empty
|
||||
Method #2 #3; // #1
|
||||
class #4; // #2
|
||||
NameAndType #5 #6; // #3
|
||||
Utf8 "java/lang/Object"; // #4
|
||||
Utf8 "<init>"; // #5
|
||||
Utf8 "()V"; // #6
|
||||
NameAndType #5 #6; // #7
|
||||
Utf8 "BadClassFile"; // #8
|
||||
Utf8 "Code"; // #9
|
||||
Utf8 "LineNumberTable"; // #10
|
||||
Utf8 "SourceFile"; // #11
|
||||
Utf8 "BadClassFile.java"; // #12
|
||||
} // Constant Pool
|
||||
|
||||
0x0021; // access
|
||||
#7;// this_cpx
|
||||
#2;// super_cpx
|
||||
|
||||
[] { // Interfaces
|
||||
} // Interfaces
|
||||
|
||||
[] { // fields
|
||||
} // fields
|
||||
|
||||
[] { // methods
|
||||
{ // Member
|
||||
0x0001; // access
|
||||
#5; // name_cpx
|
||||
#6; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#9) { // Code
|
||||
1; // max_stack
|
||||
1; // max_locals
|
||||
Bytes[]{
|
||||
0x2AB70001B1;
|
||||
}
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
Attr(#10) { // LineNumberTable
|
||||
[] { // LineNumberTable
|
||||
0 28;
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
} // methods
|
||||
|
||||
[] { // Attributes
|
||||
Attr(#11) { // SourceFile
|
||||
#12;
|
||||
} // end SourceFile
|
||||
} // Attributes
|
||||
} // end class BadClassFile
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
// this_class is modified to CONSTANT_String_info
|
||||
|
||||
class BadClassFile2 {
|
||||
0xCAFEBABE;
|
||||
0; // minor version
|
||||
58; // version
|
||||
[] { // Constant Pool
|
||||
; // first element is empty
|
||||
Method #2 #3; // #1
|
||||
class #4; // #2
|
||||
NameAndType #5 #6; // #3
|
||||
Utf8 "java/lang/Object"; // #4
|
||||
Utf8 "<init>"; // #5
|
||||
Utf8 "()V"; // #6
|
||||
String #13; // #7
|
||||
Utf8 "BadClassFile2"; // #8
|
||||
Utf8 "Code"; // #9
|
||||
Utf8 "LineNumberTable"; // #10
|
||||
Utf8 "SourceFile"; // #11
|
||||
Utf8 "BadClassFile2.java"; // #12
|
||||
String "BadClassFile2.java"; // #13
|
||||
Utf8 "bad/Package/ClassFile"; // #14
|
||||
} // Constant Pool
|
||||
|
||||
0x0021; // access
|
||||
#7;// this_cpx
|
||||
#2;// super_cpx
|
||||
|
||||
[] { // Interfaces
|
||||
} // Interfaces
|
||||
|
||||
[] { // fields
|
||||
} // fields
|
||||
|
||||
[] { // methods
|
||||
{ // Member
|
||||
0x0001; // access
|
||||
#5; // name_cpx
|
||||
#6; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#9) { // Code
|
||||
1; // max_stack
|
||||
1; // max_locals
|
||||
Bytes[]{
|
||||
0x2AB70001B1;
|
||||
}
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
Attr(#10) { // LineNumberTable
|
||||
[] { // LineNumberTable
|
||||
0 28;
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
} // methods
|
||||
|
||||
[] { // Attributes
|
||||
Attr(#11) { // SourceFile
|
||||
#12;
|
||||
} // end SourceFile
|
||||
} // Attributes
|
||||
} // end class BadClassFile2
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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.
|
||||
*/
|
||||
|
||||
// major version modified to 100
|
||||
class BadClassFileVersion {
|
||||
0xCAFEBABE;
|
||||
0; // minor version
|
||||
100; // version
|
||||
[] { // Constant Pool
|
||||
; // first element is empty
|
||||
Method #2 #3; // #1
|
||||
class #4; // #2
|
||||
NameAndType #5 #6; // #3
|
||||
Utf8 "java/lang/Object"; // #4
|
||||
Utf8 "<init>"; // #5
|
||||
Utf8 "()V"; // #6
|
||||
class #8; // #7
|
||||
Utf8 "BadClassFileVersion"; // #8
|
||||
Utf8 "Code"; // #9
|
||||
Utf8 "LineNumberTable"; // #10
|
||||
Utf8 "SourceFile"; // #11
|
||||
Utf8 "BadClassFileVersion.java"; // #12
|
||||
} // Constant Pool
|
||||
|
||||
0x0021; // access
|
||||
#7;// this_cpx
|
||||
#2;// super_cpx
|
||||
|
||||
[] { // Interfaces
|
||||
} // Interfaces
|
||||
|
||||
[] { // fields
|
||||
} // fields
|
||||
|
||||
[] { // methods
|
||||
{ // Member
|
||||
0x0001; // access
|
||||
#5; // name_cpx
|
||||
#6; // sig_cpx
|
||||
[] { // Attributes
|
||||
Attr(#9) { // Code
|
||||
1; // max_stack
|
||||
1; // max_locals
|
||||
Bytes[]{
|
||||
0x2AB70001B1;
|
||||
}
|
||||
[] { // Traps
|
||||
} // end Traps
|
||||
[] { // Attributes
|
||||
Attr(#10) { // LineNumberTable
|
||||
[] { // LineNumberTable
|
||||
0 28;
|
||||
}
|
||||
} // end LineNumberTable
|
||||
} // Attributes
|
||||
} // end Code
|
||||
} // Attributes
|
||||
} // Member
|
||||
} // methods
|
||||
|
||||
[] { // Attributes
|
||||
Attr(#11) { // SourceFile
|
||||
#12;
|
||||
} // end SourceFile
|
||||
} // Attributes
|
||||
} // end class BadClassFileVersion
|
@ -26,6 +26,9 @@
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* jdk.compiler
|
||||
* @library /test/lib
|
||||
* @compile BadClassFile.jcod
|
||||
* BadClassFile2.jcod
|
||||
* BadClassFileVersion.jcod
|
||||
* @build jdk.test.lib.Utils
|
||||
* jdk.test.lib.compiler.CompilerUtils
|
||||
* @run testng/othervm --enable-preview BasicTest
|
||||
@ -360,7 +363,7 @@ public class BasicTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {IllegalArgumentException.class})
|
||||
@Test(expectedExceptions = { IllegalArgumentException.class })
|
||||
public void cantDefineModule() throws Throwable {
|
||||
Path src = Paths.get("module-info.java");
|
||||
Path dir = CLASSES_DIR.resolve("m");
|
||||
@ -371,7 +374,7 @@ public class BasicTest {
|
||||
lookup().defineHiddenClass(bytes, false);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {IllegalArgumentException.class})
|
||||
@Test(expectedExceptions = { IllegalArgumentException.class })
|
||||
public void cantDefineClassInAnotherPackage() throws Throwable {
|
||||
Path src = Paths.get("ClassInAnotherPackage.java");
|
||||
Files.write(src, List.of("package p;", "public class ClassInAnotherPackage {}"), StandardCharsets.UTF_8);
|
||||
@ -381,12 +384,38 @@ public class BasicTest {
|
||||
lookup().defineHiddenClass(bytes, false);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = {IllegalAccessException.class})
|
||||
@Test(expectedExceptions = { IllegalAccessException.class })
|
||||
public void lessPrivilegedLookup() throws Throwable {
|
||||
Lookup lookup = lookup().dropLookupMode(Lookup.PRIVATE);
|
||||
lookup.defineHiddenClass(hiddenClassBytes, false);
|
||||
}
|
||||
|
||||
@Test(expectedExceptions = { UnsupportedClassVersionError.class })
|
||||
public void badClassFileVersion() throws Throwable {
|
||||
Path dir = Paths.get(System.getProperty("test.classes", "."));
|
||||
byte[] bytes = Files.readAllBytes(dir.resolve("BadClassFileVersion.class"));
|
||||
lookup().defineHiddenClass(bytes, false);
|
||||
}
|
||||
|
||||
// malformed class files
|
||||
@DataProvider(name = "malformedClassFiles")
|
||||
private Object[][] malformedClassFiles() throws IOException {
|
||||
Path dir = Paths.get(System.getProperty("test.classes", "."));
|
||||
return new Object[][] {
|
||||
// `this_class` has invalid CP entry
|
||||
new Object[] { Files.readAllBytes(dir.resolve("BadClassFile.class")) },
|
||||
new Object[] { Files.readAllBytes(dir.resolve("BadClassFile2.class")) },
|
||||
// truncated file
|
||||
new Object[] { new byte[0] },
|
||||
new Object[] { new byte[] {(byte) 0xCA, (byte) 0xBA, (byte) 0xBE, (byte) 0x00} },
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "malformedClassFiles", expectedExceptions = ClassFormatError.class)
|
||||
public void badClassFile(byte[] bytes) throws Throwable {
|
||||
lookup().defineHiddenClass(bytes, false);
|
||||
}
|
||||
|
||||
@DataProvider(name = "nestedTypesOrAnonymousClass")
|
||||
private Object[][] nestedTypesOrAnonymousClass() {
|
||||
return new Object[][] {
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 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
|
||||
* @bug 8245432
|
||||
* @modules java.base/jdk.internal.org.objectweb.asm
|
||||
* jdk.compiler
|
||||
* @library /test/lib
|
||||
* @build jdk.test.lib.Utils
|
||||
* jdk.test.lib.compiler.CompilerUtils
|
||||
* @run testng PreviewHiddenClass
|
||||
* @summary verify UnsupportedClassVersionError thrown when defining a hidden class
|
||||
* with preview minor version but --enable-preview is not set
|
||||
*/
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.test.lib.compiler.CompilerUtils;
|
||||
import jdk.test.lib.Utils;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class PreviewHiddenClass {
|
||||
|
||||
private static final Path SRC_DIR = Paths.get(Utils.TEST_SRC, "src");
|
||||
private static final Path CLASSES_DIR = Paths.get("classes");
|
||||
|
||||
@Test(expectedExceptions = { UnsupportedClassVersionError.class })
|
||||
public void previewNotEnabled() throws Exception {
|
||||
// compile a class with --enable-preview
|
||||
Path sourceFile = SRC_DIR.resolve("HiddenInterface.java");
|
||||
String[] options = new String[] {
|
||||
"--enable-preview", "-source", String.valueOf(Runtime.version().feature()) };
|
||||
if (!CompilerUtils.compile(sourceFile, CLASSES_DIR, options)) {
|
||||
throw new RuntimeException("Compilation of the test failed: " + sourceFile);
|
||||
}
|
||||
|
||||
byte[] bytes = Files.readAllBytes(CLASSES_DIR.resolve("HiddenInterface.class"));
|
||||
ClassReader reader = new ClassReader(bytes);
|
||||
int minor = reader.readUnsignedShort(4);
|
||||
assertTrue(minor == 65535);
|
||||
MethodHandles.lookup().defineHiddenClass(bytes, false);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user