Skip to content

Commit

Permalink
Moves nestmates host loading to visibility check
Browse files Browse the repository at this point in the history
Earlier nest mates spec draft loaded a class's nest host during class
loading. More recent nest mates spec puts off host loading until
required for an access check. This commit moves the nest host loading
and verification to the visibility check.

The spec draft for checking visibility states:

To determine whether a class or interface C belongs to the same nest as
D (that is, whether C and D are nestmates), the following steps are
performed:
1. If C and D are the same class or interface, they belong to the same
nest.
2. Otherwise, the nest host of D, H, is determined. If an exception is
thrown, the nest membership test fails for the same reason.
3. Otherwise, the nest host of C, H', is determined. If an exception is
thrown, the nest membership test fails for the same reason.
4. Otherwise, C and D belong to the same nest if H and H' are the same
class or interface.
To determine the nest host of a class M, the following steps are
performed:
1. If M lacks a NestHost attribute (4.7.28), M is its own nest host.
2. Otherwise, where i is the host_class_index item of M's NestHost
attribute, the symbolic reference at index i of M's run-time constant
pool is resolved to a class or interface H (5.4.3.1). Any of the
exceptions pertaining to class or interface resolution can be thrown.
3. If resolution of H succeeds, but H is not declared in the same
run-time package as M, an IncompatibleClassChangeError is thrown.
4. Otherwise, if H lacks a NestMembers attribute (4.7.29), or if, where
M has name N, there exists no entry in the classes array of the
NestMembers attribute of H that refers to a class or interface with name
N, an IncompatibleClassChangeError is thrown.
5. Otherwise, H is the nest host of M.

As taken from here: http://cr.openjdk.java.net/~dlsmith/nestmates.html

Signed-off-by: Talia McCormick <Talia.McCormick@ibm.com>
  • Loading branch information
Talia McCormick authored and Talia McCormick committed Mar 6, 2018
1 parent f07f963 commit 6a882d3
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 113 deletions.
13 changes: 12 additions & 1 deletion runtime/nls/j9vm/j9vm.nls
Original file line number Diff line number Diff line change
Expand Up @@ -1729,7 +1729,7 @@ J9NLS_VM_NEST_HOST_HAS_DIFFERENT_PACKAGE.user_response=Load class and nest host
# Second argument is class name
# Third argument is nest host name length
# Fourth argument is nest host name
J9NLS_VM_NEST_MEMBER_NOT_CLAIMED_BY_NEST_HOST=Class %2$.*1$s must be claimed by its nest host %4$.*1$s.
J9NLS_VM_NEST_MEMBER_NOT_CLAIMED_BY_NEST_HOST=Class %2$.*1$s must be claimed by its nest host %4$.*3$s
# START NON-TRANSLATABLE
J9NLS_VM_NEST_MEMBER_NOT_CLAIMED_BY_NEST_HOST.explanation=Class was not claimed by its nest host class.
J9NLS_VM_NEST_MEMBER_NOT_CLAIMED_BY_NEST_HOST.system_action=The JVM will throw a java/lang/VerifyError
Expand Down Expand Up @@ -1782,3 +1782,14 @@ J9NLS_VM_LOADING_CONSTRAINT_OVERRIDE_VIOLATION.explanation=A class loading const
J9NLS_VM_LOADING_CONSTRAINT_OVERRIDE_VIOLATION.system_action=A java/lang/LinkageError will be thrown
J9NLS_VM_LOADING_CONSTRAINT_OVERRIDE_VIOLATION.user_response=Examine the use of ClassLoaders to determine why a class of the same name is loaded in two different loaders
# END NON-TRANSLATABLE

# First argument is class name length
# Second argument is class name
# Third argument is nest host name length
# Fourth argument is nest host name
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD=Class %2$.*1$s must be able to load its nest host %4$.*3$s.
# START NON-TRANSLATABLE
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD.explanation=Class and nest host class were loaded with different class loaders.
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD.system_action=The JVM will throw a java/lang/VerifyError
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD.user_response=Load class and nest host class with same classloader.
# END NON-TRANSLATABLE
5 changes: 5 additions & 0 deletions runtime/oti/j9nonbuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,11 @@
#define J9_VISIBILITY_NON_MODULE_ACCESS_ERROR 0
#define J9_VISIBILITY_MODULE_READ_ACCESS_ERROR -1
#define J9_VISIBILITY_MODULE_PACKAGE_EXPORT_ERROR -2
#if defined(J9VM_OPT_VALHALLA_NESTMATES)
#define J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR -3
#define J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR -4
#define J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR -5
#endif /* defined(J9VM_OPT_VALHALLA_NESTMATES) */

/* @ddr_namespace: map_to_type=J9CfrError */

Expand Down
65 changes: 1 addition & 64 deletions runtime/vm/ClassInitialization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,6 @@ static char const *statusNames[] = {

static j9object_t setInitStatus(J9VMThread *currentThread, J9Class *clazz, UDATA status, j9object_t initializationLock);
static void classInitStateMachine(J9VMThread *currentThread, J9Class *clazz, J9ClassInitState desiredState);
#if defined(J9VM_OPT_VALHALLA_NESTMATES)
static bool verifyNestHost(J9Class *clazz, J9VMThread *vmThread);
#endif /* J9VM_OPT_VALHALLA_NESTMATES */

void
initializeImpl(J9VMThread *currentThread, J9Class *clazz)
Expand Down Expand Up @@ -175,11 +172,7 @@ performVerification(J9VMThread *currentThread, J9Class *clazz)
setCurrentException(currentThread, J9VMCONSTANTPOOL_JAVALANGVERIFYERROR, (UDATA*)verifyErrorStringObject);
goto done;
}
#if defined(J9VM_OPT_VALHALLA_NESTMATES)
if (false == verifyNestHost(clazz, currentThread)) {
goto done;
}
#endif /* J9VM_OPT_VALHALLA_NESTMATES */

Trc_VM_verification_End(currentThread, J9UTF8_LENGTH(J9ROMCLASS_CLASSNAME(clazz->romClass)), J9UTF8_DATA(J9ROMCLASS_CLASSNAME(clazz->romClass)), clazz->classLoader);
} else {
Trc_VM_performVerification_unverifiable(currentThread);
Expand Down Expand Up @@ -619,60 +612,4 @@ classInitStateMachine(J9VMThread *currentThread, J9Class *clazz, J9ClassInitStat
Trc_VM_classInitStateMachine_Exit(currentThread);
return;
}

#if defined(J9VM_OPT_VALHALLA_NESTMATES)
static bool
verifyNestHost(J9Class *clazz, J9VMThread *vmThread)
{
J9Class *nestHost = clazz->nestHost;
bool verified = false;

/* Verification only needed if class's nest host is not itself */
if (clazz == nestHost) {
verified = true;
} else {
J9ROMClass *romClass = clazz->romClass;
J9UTF8 *className = J9ROMCLASS_CLASSNAME(romClass);
U_32 moduleName = 0;
U_32 nlsNumber = 0;

/* Nest host must have same classloader & package */
if (clazz->classLoader != nestHost->classLoader) {
Trc_VM_CreateRAMClassFromROMClass_nestHostNotSameClassLoader(vmThread, nestHost, nestHost->classLoader, clazz->classLoader);
moduleName = J9NLS_VM_NEST_HOST_HAS_DIFFERENT_CLASSLOADER__MODULE;
nlsNumber = J9NLS_VM_NEST_HOST_HAS_DIFFERENT_CLASSLOADER__ID;
} else if (clazz->packageID != nestHost->packageID) {
Trc_VM_CreateRAMClassFromROMClass_nestHostNotSamePackage(vmThread, nestHost, nestHost->classLoader, clazz->classLoader);
moduleName = J9NLS_VM_NEST_HOST_HAS_DIFFERENT_PACKAGE__MODULE;
nlsNumber = J9NLS_VM_NEST_HOST_HAS_DIFFERENT_PACKAGE__ID;
} else {
/* The nest host must have a nestmembers attribute that includes this class. */
J9SRP *nestMembers = J9ROMCLASS_NESTMEMBERS(nestHost->romClass);
U_16 nestMemberCount = nestHost->romClass->nestMemberCount;
for (U_16 i = 0; i < nestMemberCount; i++) {
J9UTF8 *nestMemberName = NNSRP_GET(nestMembers[i], J9UTF8*);
if (J9UTF8_EQUALS(className, nestMemberName)) {
verified = true;
break;
}
}
if (!verified) {
Trc_VM_CreateRAMClassFromROMClass_nestHostNotVerified(vmThread, nestHost, nestHost->classLoader, clazz->classLoader, className);
moduleName = J9NLS_VM_NEST_MEMBER_NOT_CLAIMED_BY_NEST_HOST__MODULE;
nlsNumber = J9NLS_VM_NEST_MEMBER_NOT_CLAIMED_BY_NEST_HOST__ID;
}
}

if (!verified) {
J9UTF8 *nestHostName = J9ROMCLASS_NESTHOSTNAME(romClass);
setCurrentExceptionNLSWithArgs(vmThread,
moduleName, nlsNumber,
J9VMCONSTANTPOOL_JAVALANGVERIFYERROR,
J9UTF8_LENGTH(className),J9UTF8_DATA(className),
J9UTF8_LENGTH(nestHostName), J9UTF8_DATA(className));
}
}
return verified;
}
#endif /* J9VM_OPT_VALHALLA_NESTMATES */
} /* extern "C" */
41 changes: 0 additions & 41 deletions runtime/vm/createramclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,6 @@ static void setCurrentExceptionForBadClass(J9VMThread *vmThread, J9UTF8 *badClas
static BOOLEAN verifyClassLoadingStack(J9VMThread *vmThread, J9ClassLoader *classLoader, J9ROMClass *romClass);
static void popFromClassLoadingStack(J9VMThread *vmThread);
static VMINLINE BOOLEAN loadSuperClassAndInterfaces(J9VMThread *vmThread, J9ClassLoader *classLoader, J9ROMClass *romClass, J9Class *elementClass, UDATA packageID, BOOLEAN hotswapping, UDATA classPreloadFlags, J9Class **superclassOut, J9Module *module);
#if defined(J9VM_OPT_VALHALLA_NESTMATES)
static J9Class *loadNestHost(J9VMThread *vmThread, J9ClassLoader *classLoader, J9UTF8 *nestHostName, UDATA classPreloadFlags);
#endif /* defined(J9VM_OPT_VALHALLA_NESTMATES) */
static J9Class* internalCreateRAMClassDropAndReturn(J9VMThread *vmThread, J9ROMClass *romClass, J9CreateRAMClassState *state);
static J9Class* internalCreateRAMClassDoneNoMutex(J9VMThread *vmThread, J9ROMClass *romClass, UDATA options, J9CreateRAMClassState *state);
static J9Class* internalCreateRAMClassDone(J9VMThread *vmThread, J9ClassLoader *classLoader, J9ROMClass *romClass, UDATA options, J9Class *elementClass,
Expand Down Expand Up @@ -1596,16 +1593,6 @@ loadSuperClassAndInterfaces(J9VMThread *vmThread, J9ClassLoader *classLoader, J9
return TRUE;
}

#if defined(J9VM_OPT_VALHALLA_NESTMATES)
static J9Class *
loadNestHost(J9VMThread *vmThread, J9ClassLoader *classLoader, J9UTF8 *nestHostName, UDATA classPreloadFlags)
{
J9Class *nestHost = internalFindClassUTF8(vmThread, J9UTF8_DATA(nestHostName), J9UTF8_LENGTH(nestHostName), classLoader, classPreloadFlags);
Trc_VM_CreateRAMClassFromROMClass_nestHostLoaded(vmThread, J9UTF8_LENGTH(nestHostName), J9UTF8_DATA(nestHostName), nestHost);
return nestHost;
}
#endif

static J9Class*
internalCreateRAMClassDropAndReturn(J9VMThread *vmThread, J9ROMClass *romClass, J9CreateRAMClassState *state)
{
Expand Down Expand Up @@ -2307,34 +2294,6 @@ internalCreateRAMClassFromROMClassImpl(J9VMThread *vmThread, J9ClassLoader *clas
ramClass->hostClass = ramClass;
}

#if defined(J9VM_OPT_VALHALLA_NESTMATES)
{
J9Class *nestHost = NULL;
J9UTF8 *nestHostName = J9ROMCLASS_NESTHOSTNAME(romClass);

/* If no nest host is named, class is own nest host */
if (NULL == nestHostName) {
nestHost = ramClass;
} else {
UDATA nestHostClassPreloadFlags = 0;
if (hotswapping) {
nestHostClassPreloadFlags = J9_FINDCLASS_FLAG_EXISTING_ONLY;
} else {
nestHostClassPreloadFlags = J9_FINDCLASS_FLAG_THROW_ON_FAIL;
if (classLoader != javaVM->systemClassLoader) {
nestHostClassPreloadFlags |= J9_FINDCLASS_FLAG_CHECK_PKG_ACCESS;
}
}
nestHost = loadNestHost(vmThread, hostClassLoader, nestHostName, nestHostClassPreloadFlags);
}
/* If nest host loading failed, an exception has been set; end loading early */
if (NULL == nestHost) {
return internalCreateRAMClassDone(vmThread, classLoader, romClass, options, elementClass, className, state);
}
ramClass->nestHost = nestHost;
}
#endif /* defined(J9VM_OPT_VALHALLA_NESTMATES) */

/* Initialize the methods. */
if (romClass->romMethodCount != 0) {
J9Method *currentRAMMethod = ramClass->ramMethods;
Expand Down
70 changes: 67 additions & 3 deletions runtime/vm/lookupmethod.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 1991, 2017 IBM Corp. and others
* Copyright (c) 1991, 2018 IBM Corp. and others
*
* This program and the accompanying materials are made available under
* the terms of the Eclipse Public License 2.0 which accompanies this
Expand Down Expand Up @@ -193,6 +193,7 @@ processMethod(J9VMThread * currentThread, UDATA lookupOptions, J9Method * method
} else {
*exception = J9VMCONSTANTPOOL_JAVALANGILLEGALACCESSERROR;
*exceptionClass = methodClass;
*errorType = J9_VISIBILITY_NON_MODULE_ACCESS_ERROR;
return NULL;
}
}
Expand Down Expand Up @@ -743,6 +744,7 @@ javaLookupMethodImpl(J9VMThread *currentThread, J9Class *targetClass, J9ROMNameA

exception = data.exception;
exceptionClass = data.exceptionClass;
errorType = data.errorType;

if ((NULL == resultMethod) && data.elements > 1) {
if (NULL != foundDefaultConflicts) {
Expand Down Expand Up @@ -818,8 +820,7 @@ javaLookupMethodImpl(J9VMThread *currentThread, J9Class *targetClass, J9ROMNameA
char *buf = NULL;

PORT_ACCESS_FROM_VMC(currentThread);

buf = illegalAccessMessage(currentThread, badRomMethod->modifiers, senderClass, targetClass, J9_VISIBILITY_NON_MODULE_ACCESS_ERROR);
buf = illegalAccessMessage(currentThread, badRomMethod->modifiers, senderClass, targetClass, errorType);

setCurrentExceptionUTF(currentThread, exception, buf);

Expand Down Expand Up @@ -1035,6 +1036,69 @@ illegalAccessMessage(J9VMThread *currentThread, IDATA badMemberModifier, J9Class
PORT_ACCESS_FROM_VMC(currentThread);
Trc_VM_illegalAccessMessage_Entry(currentThread, J9UTF8_LENGTH(senderClassNameUTF), J9UTF8_DATA(senderClassNameUTF),
J9UTF8_LENGTH(targetClassNameUTF), J9UTF8_DATA(targetClassNameUTF), badMemberModifier);

#if defined(J9VM_OPT_VALHALLA_NESTMATES)
/* If an issue with the nest host loading and verification occurred, then it will be one of:
* J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR
* J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR
* J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR
* Otherwise, it is one of:
* J9_VISIBILITY_NON_MODULE_ACCESS_ERROR
* J9_VISIBILITY_MODULE_READ_ACCESS_ERROR
* J9_VISIBILITY_MODULE_PACKAGE_EXPORT_ERROR
*/
if ((J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR == errorType)
|| (J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR == errorType)
|| (J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR == errorType)) {
J9Class *unverifiedNestMemberClass = NULL;
J9ROMClass *romClass = NULL;
J9UTF8 *nestMemberNameUTF = NULL;
J9UTF8 *nestHostNameUTF = NULL;

if (NULL == senderClass->nestHost) {
unverifiedNestMemberClass = senderClass;
} else {
unverifiedNestMemberClass = targetClass;
}

romClass = unverifiedNestMemberClass->romClass;
nestMemberNameUTF = J9ROMCLASS_CLASSNAME(romClass);
nestHostNameUTF = J9ROMCLASS_NESTHOSTNAME(romClass);

if (J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR == errorType) {
errorMsg = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE,
J9NLS_VM_NEST_HOST_FAILED_TO_LOAD,
NULL);
} else if (J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR == errorType) {
errorMsg = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE,
J9NLS_VM_NEST_HOST_HAS_DIFFERENT_PACKAGE,
NULL);
} else if (J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR == errorType) {
errorMsg = j9nls_lookup_message(J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE,
J9NLS_VM_NEST_MEMBER_NOT_CLAIMED_BY_NEST_HOST,
NULL);
}

bufLen = j9str_printf(PORTLIB, NULL, 0, errorMsg,
J9UTF8_LENGTH(nestMemberNameUTF),
J9UTF8_DATA(nestMemberNameUTF),
J9UTF8_LENGTH(nestHostNameUTF),
J9UTF8_DATA(nestHostNameUTF));

if (bufLen > 0) {
buf = j9mem_allocate_memory(bufLen, OMRMEM_CATEGORY_VM);
if (NULL != buf) {
j9str_printf(PORTLIB, buf, bufLen, errorMsg,
J9UTF8_LENGTH(nestMemberNameUTF),
J9UTF8_DATA(nestMemberNameUTF),
J9UTF8_LENGTH(nestHostNameUTF),
J9UTF8_DATA(nestHostNameUTF));
} else {
goto allocationFailure;
}
}
} else
#endif /* defined(J9VM_OPT_VALHALLA_NESTMATES) */
if (J9_VISIBILITY_NON_MODULE_ACCESS_ERROR != errorType) {
/* illegal module access */
j9object_t srcModuleObject = J9VMJAVALANGCLASS_MODULE(currentThread, senderClass->classObject);
Expand Down
70 changes: 66 additions & 4 deletions runtime/vm/visible.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
#include "ut_j9vm.h"
#include "util_api.h"
#include "vm_internal.h"
#include "j9protos.h"

#if defined(J9VM_OPT_VALHALLA_NESTMATES)
static UDATA loadAndVerifyNestHost(J9VMThread *vmThread, J9Class *clazz);
#endif /* defined(J9VM_OPT_VALHALLA_NESTMATES) */

/**
* Check visibility from sourceClass to destClass with modifiers specified
Expand Down Expand Up @@ -100,12 +105,21 @@ checkVisibility(J9VMThread *currentThread, J9Class* sourceClass, J9Class* destCl
}
} else if (modifiers & J9AccPrivate) {
/* Private */
if ((sourceClass != destClass)
if (sourceClass != destClass) {
#if defined(J9VM_OPT_VALHALLA_NESTMATES)
&& (sourceClass->nestHost != destClass->nestHost)
/* loadAndVerifyNestHost returns an error if setting nest host field fails */
if (NULL == destClass->nestHost) {
result = loadAndVerifyNestHost(currentThread, destClass);
}
if ((J9_VISIBILITY_ALLOWED == result) && (NULL == sourceClass->nestHost)) {
result = loadAndVerifyNestHost(currentThread, sourceClass);
}
if ((J9_VISIBILITY_ALLOWED == result) && (sourceClass->nestHost != destClass->nestHost)) {
#endif /* defined(J9VM_OPT_VALHALLA_NESTMATES) */
result = J9_VISIBILITY_NON_MODULE_ACCESS_ERROR;
#if defined(J9VM_OPT_VALHALLA_NESTMATES)
}
#endif /* defined(J9VM_OPT_VALHALLA_NESTMATES) */
) {
result = J9_VISIBILITY_NON_MODULE_ACCESS_ERROR;
}
} else if (modifiers & J9AccProtected) {
/* Protected */
Expand Down Expand Up @@ -142,3 +156,51 @@ checkVisibility(J9VMThread *currentThread, J9Class* sourceClass, J9Class* destCl

return result;
}

#if defined(J9VM_OPT_VALHALLA_NESTMATES)
static UDATA loadAndVerifyNestHost(J9VMThread *vmThread, J9Class *clazz) {
J9Class *nestHost = NULL;
UDATA result = J9_VISIBILITY_ALLOWED;
J9ROMClass *romClass = clazz->romClass;
J9UTF8 *nestHostName = J9ROMCLASS_NESTHOSTNAME(romClass);

/* If no nest host is named, class is own nest host */
if (NULL == nestHostName) {
nestHost = clazz;
} else {
UDATA classLoadingFlags = J9_FINDCLASS_FLAG_THROW_ON_FAIL;
nestHost = internalFindClassUTF8(vmThread, J9UTF8_DATA(nestHostName), J9UTF8_LENGTH(nestHostName), clazz->classLoader, classLoadingFlags);

/* Nest host must be successfully loaded by the same classloader in the same package & verify the nest member */
if (NULL == nestHost) {
result = J9_VISIBILITY_NEST_HOST_LOADING_FAILURE_ERROR;
} else if (clazz->packageID != nestHost->packageID) {
result = J9_VISIBILITY_NEST_HOST_DIFFERENT_PACKAGE_ERROR;
} else {
/* The nest host must have a nestmembers attribute that claims this class. */
BOOLEAN verified = FALSE;
J9UTF8 *className = J9ROMCLASS_CLASSNAME(romClass);
J9SRP *nestMembers = J9ROMCLASS_NESTMEMBERS(nestHost->romClass);
U_16 nestMemberCount = nestHost->romClass->nestMemberCount;
U_16 i = 0;

for (i = 0; i < nestMemberCount; i++) {
J9UTF8 *nestMemberName = NNSRP_GET(nestMembers[i], J9UTF8*);
if (J9UTF8_EQUALS(className, nestMemberName)) {
verified = TRUE;
break;
}
}
if (!verified) {
result = J9_VISIBILITY_NEST_MEMBER_NOT_CLAIMED_ERROR;
}
}
}

/* If a problem occurred in nest host verification then the nest host value is invalid */
if ((J9_VISIBILITY_ALLOWED == result) && (nestHost != NULL)) {
clazz->nestHost = nestHost;
}
return result;
}
#endif /* defined(J9VM_OPT_VALHALLA_NESTMATES) */

0 comments on commit 6a882d3

Please sign in to comment.