Path: blob/master/runtime/bcverify/clconstraints.c
5986 views
/*******************************************************************************1* Copyright (c) 1991, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/2122#include <string.h>2324#include "j9cfg.h"25#include "j9protos.h"26#include "j9consts.h"27#include "rommeth.h"28#include "j9cp.h"29#include "j9modron.h"30#include "ut_j9bcverify.h"31#include "omrlinkedlist.h"3233static J9ClassLoadingConstraint* findClassLoadingConstraint (J9VMThread* vmThread, J9ClassLoader* loader, U_8* name, UDATA length);34static J9ClassLoadingConstraint* registerClassLoadingConstraint (J9VMThread* vmThread, J9ClassLoader* loader, U_8* name, UDATA length, UDATA copyName);35static void validateArgs (J9VMThread* vmThread, J9ClassLoader* loader1, J9ClassLoader* loader2, U_8* name1, U_8* name2, UDATA length);36static void constrainList (J9ClassLoadingConstraint* constraint, J9Class* clazz);37static UDATA constraintHashFn(void *key, void *userData);38static UDATA constraintHashEqualFn(void *leftKey, void *rightKey, void *userData);394041/* This is a helper function used by Assert_RTV_validateClassLoadingConstraints */42static void43validateArgs (J9VMThread* vmThread, J9ClassLoader* loader1, J9ClassLoader* loader2, U_8* name1, U_8* name2, UDATA length)44{45J9MemorySegment *seg;4647Assert_RTV_notEqual(loader1, loader2);48Assert_RTV_true(0 == memcmp(name1, name2, length));4950/* verify that the name is actually in the correct segment. If it's not, the name could get51* garbage collected while the constraint object is still alive52*/53seg = vmThread->javaVM->classMemorySegments->nextSegment;54while (seg) {55if (seg->heapBase <= name1 && seg->heapTop >= name1) {56Assert_RTV_true( (seg->classLoader == loader1) || (seg->classLoader->flags & J9CLASSLOADER_INVARIANTS_SHARABLE) );57}58if (seg->heapBase <= name2 && seg->heapTop >= name2) {59Assert_RTV_true( (seg->classLoader == loader2) || (seg->classLoader->flags & J9CLASSLOADER_INVARIANTS_SHARABLE) );60}61seg = seg->nextSegment;62}63}646566/*67* sig1 must come from a ROMSegment in loader1.68* sig2 must come from a ROMSegment in loader2.69* sig1 and sig2 must contain identical bytes. The signatures may be method or field signatures.70* return 0 if no class loading constraints have been violated, or non-zero if they have been.71*/72UDATA73j9bcv_checkClassLoadingConstraintsForSignature (J9VMThread* vmThread, J9ClassLoader* loader1, J9ClassLoader* loader2, J9UTF8* sig1, J9UTF8* sig2)74{75U_32 index = 0, endIndex;76U_32 length = J9UTF8_LENGTH(sig1);77UDATA rc = 0;78J9JavaVM *javaVM = vmThread->javaVM;79JavaVM* jniVM = (JavaVM*)javaVM;8081Trc_RTV_checkClassLoadingConstraintsForSignature_Entry(vmThread, loader1, loader2, sig1, sig2, J9UTF8_LENGTH(sig1), J9UTF8_DATA(sig1));82Assert_RTV_true(J9UTF8_LENGTH(sig1) == J9UTF8_LENGTH(sig2));83Assert_RTV_validateClassLoadingConstraints(vmThread, loader1, loader2, J9UTF8_DATA(sig1), J9UTF8_DATA(sig2), J9UTF8_LENGTH(sig1));8485omrthread_monitor_enter(javaVM->classTableMutex);86for (;;) {87/* find a 'L', indicating the beginning of a class name */88while (index < length && J9UTF8_DATA(sig1)[index] != 'L') {89index++;90}91if (index >= length) {92break;93}9495/* skip the 'L'; */96index++;9798/* find the ';' marking the end of the class name */99endIndex = index;100while (J9UTF8_DATA(sig1)[endIndex] != ';') {101endIndex++;102}103104rc = j9bcv_checkClassLoadingConstraintForName (vmThread, loader1, loader2, &J9UTF8_DATA(sig1)[index], &J9UTF8_DATA(sig2)[index], endIndex - index, FALSE);105if (rc) {106break;107}108109index = endIndex;110}111omrthread_monitor_exit(javaVM->classTableMutex);112113Trc_RTV_checkClassLoadingConstraintsForSignature_Exit(vmThread, rc);114115return rc;116}117118119/* NOTE: the current thread must own the class table mutex */120121UDATA122j9bcv_checkClassLoadingConstraintForName (J9VMThread* vmThread, J9ClassLoader* loader1, J9ClassLoader* loader2, U_8* name1, U_8* name2, UDATA length, UDATA copyUTFs)123{124J9Class *class1;125J9Class *class2;126J9ClassLoadingConstraint *const1 = NULL;127J9ClassLoadingConstraint *const2 = NULL;128J9InternalVMFunctions const *vmFuncs = vmThread->javaVM->internalVMFunctions;129130Trc_RTV_checkClassLoadingConstraintForName(vmThread, loader1, loader2, length, name1);131Assert_RTV_validateClassLoadingConstraints(vmThread, loader1, loader2, name1, name2, length);132133/* peek at the class tables to see if the class has been loaded yet */134class1 = vmFuncs->hashClassTableAt (loader1, name1, length);135class2 = vmFuncs->hashClassTableAt (loader2, name2, length);136137if (class1 && class2) {138if (class1 != class2) {139return 1;140}141} else if (class1 == NULL && class2 != NULL) {142const1 = registerClassLoadingConstraint (vmThread, loader1, name1, length, copyUTFs);143if (const1 == NULL) return 1;144if (const1->clazz != NULL) {145if (const1->clazz != class2) {146return 1;147}148} else {149Assert_RTV_true(J9_ARE_NO_BITS_SET(class2->classFlags, J9ClassIsAnonymous));150const1->clazz = class2;151}152} else if (class2 == NULL && class1 != NULL) {153const2 = registerClassLoadingConstraint (vmThread, loader2, name2, length, copyUTFs);154if (const2->clazz != NULL) {155if (const2->clazz != class1) {156return 1;157}158} else {159const2->clazz = class1;160Assert_RTV_true(J9_ARE_NO_BITS_SET(class1->classFlags, J9ClassIsAnonymous));161}162} else { /* class1 == NULL && class2 == NULL */163J9ClassLoadingConstraint *tempNext;164J9ClassLoadingConstraint *tempPrevious;165166const1 = registerClassLoadingConstraint (vmThread, loader1, name1, length, copyUTFs);167if (const1 == NULL) {168return 1;169}170const2 = registerClassLoadingConstraint (vmThread, loader2, name2, length, copyUTFs);171if (const2 == NULL) {172return 1;173}174175if (const1->clazz != const2->clazz) {176/* need to merge two constraint chains with different constraints.177* This is only solvable if one of them is NULL178*/179if (const1->clazz == NULL) {180constrainList(const1, const2->clazz);181} else if (const2->clazz == NULL) {182constrainList(const2, const1->clazz);183} else {184/* both constraints are satisfied, but in an incompatible manner */185return 1;186}187}188189/* now link them up, keeping in mind that one or either of them might have already been in a list */190tempNext = const1->linkNext;191tempPrevious = const2->linkPrevious;192const1->linkNext = const2;193const2->linkPrevious = const1;194tempNext->linkPrevious = tempPrevious;195tempPrevious->linkNext = tempNext;196}197198return 0;199}200201202/* NOTE: the current thread must own the class table mutex */203204static J9ClassLoadingConstraint*205registerClassLoadingConstraint (J9VMThread* vmThread, J9ClassLoader* loader, U_8* name, UDATA length, UDATA copyName)206{207PORT_ACCESS_FROM_VMC (vmThread);208J9JavaVM* vm = vmThread->javaVM;209J9ClassLoadingConstraint* constraint;210J9ClassLoadingConstraint exemplar;211212Trc_RTV_registerClassLoadingConstraint_Entry(vmThread, length, name, loader);213214if (vm->classLoadingConstraints == NULL) {215Trc_RTV_registerClassLoadingConstraint_AllocatingTable(vmThread);216vm->classLoadingConstraints = hashTableNew(OMRPORT_FROM_J9PORT(PORTLIB), J9_GET_CALLSITE(), 256, sizeof(J9ClassLoadingConstraint), sizeof(char *), 0, J9MEM_CATEGORY_CLASSES, constraintHashFn, constraintHashEqualFn, NULL, vm);217if (vm->classLoadingConstraints == NULL) {218Trc_RTV_registerClassLoadingConstraint_TableAllocationFailed(vmThread);219Trc_RTV_registerClassLoadingConstraint_Exit(vmThread, NULL);220return NULL;221}222}223224exemplar.classLoader = loader;225exemplar.name = name;226exemplar.nameLength = length;227exemplar.clazz = NULL;228exemplar.linkNext = NULL;229exemplar.linkPrevious = NULL;230exemplar.freeName = FALSE;231232constraint = hashTableAdd(vm->classLoadingConstraints, &exemplar);233if (constraint == NULL) {234oom:235Trc_RTV_registerClassLoadingConstraint_EntryAllocationFailed(vmThread);236} else if (constraint->linkNext == NULL) {237/* this must be a newly added constraint. Link it up to itself */238constraint->linkNext = constraint->linkPrevious = constraint;239if (copyName) {240U_8 *nameCopy = j9mem_allocate_memory(length, J9MEM_CATEGORY_CLASSES);241if (NULL == nameCopy) {242hashTableRemove(vm->classLoadingConstraints, constraint);243constraint = NULL;244goto oom;245}246memcpy(nameCopy, name, length);247constraint->name = nameCopy;248constraint->freeName = TRUE;249}250Trc_RTV_registerClassLoadingConstraint_AllocatedEntry(vmThread, constraint, length, name, loader);251}252253Trc_RTV_registerClassLoadingConstraint_Exit(vmThread, constraint);254return constraint;255}256257258/* NOTE: the current thread must own the class table mutex */259260static J9ClassLoadingConstraint*261findClassLoadingConstraint (J9VMThread* vmThread, J9ClassLoader* loader, U_8* name, UDATA length)262{263J9ClassLoadingConstraint *constraint = NULL;264J9JavaVM* vm = vmThread->javaVM;265266Trc_RTV_findClassLoadingConstraint_Entry(vmThread, length, name, loader);267268if (vm->classLoadingConstraints != NULL) {269J9ClassLoadingConstraint exemplar;270271exemplar.classLoader = loader;272exemplar.name = name;273exemplar.nameLength = length;274exemplar.clazz = NULL;275exemplar.linkNext = NULL;276exemplar.linkPrevious = NULL;277278constraint = hashTableFind(vm->classLoadingConstraints, &exemplar);279}280281Trc_RTV_findClassLoadingConstraint_Exit(vmThread, constraint);282283return constraint;284}285286287/* NOTE: this function must only be called while the current thread owns the class table mutex */288289J9Class *290j9bcv_satisfyClassLoadingConstraint (J9VMThread* vmThread, J9ClassLoader* loader, J9Class* clazz)291{292J9ROMClass* romClass = clazz->romClass;293J9UTF8* name = J9ROMCLASS_CLASSNAME (romClass);294J9ClassLoadingConstraint* constraint = findClassLoadingConstraint (vmThread, loader, J9UTF8_DATA(name), J9UTF8_LENGTH(name));295296if (constraint) {297if ((NULL != constraint->clazz) && (constraint->clazz != clazz)) {298return constraint->clazz;299} else {300J9ClassLoadingConstraint* root = constraint;301U_8 *nameToFree = constraint->freeName ? constraint->name : NULL;302constrainList (constraint, clazz);303J9_LINKED_LIST_REMOVE(root, constraint);304hashTableRemove (vmThread->javaVM->classLoadingConstraints, constraint);305if (NULL != nameToFree) {306PORT_ACCESS_FROM_VMC(vmThread);307j9mem_free_memory(nameToFree);308}309}310}311return NULL;312}313314315/*316* Called when class loaders are being unloaded. This function removes all of the dying loaders'317* constraints from linked lists of constraints, and removes references to classes defined by318* the dying loaders.319* The current thread should have exclusive VM access320*/321322void323unlinkClassLoadingConstraints (J9JavaVM* jvm)324{325J9HashTableState walkState;326J9ClassLoadingConstraint* constraint;327328Trc_RTV_unlinkClassLoadingConstraints_Entry();329330if (jvm->classLoadingConstraints != NULL) {331PORT_ACCESS_FROM_JAVAVM(jvm);332constraint = hashTableStartDo(jvm->classLoadingConstraints, &walkState);333while (constraint != NULL) {334U_8 *nameToFree = constraint->freeName ? constraint->name : NULL;335if ((NULL == constraint->clazz) && (constraint->linkNext == constraint)) { /* no point in having a single empty element */336hashTableDoRemove(&walkState);337if (NULL != nameToFree) {338j9mem_free_memory(nameToFree);339}340} else if (J9_GC_CLASS_LOADER_DEAD == (constraint->classLoader->gcFlags & J9_GC_CLASS_LOADER_DEAD) ) {341/* this class loader is being unloaded. Remove the constraint from the linked list */342J9ClassLoadingConstraint* root = constraint;343J9_LINKED_LIST_REMOVE(root, constraint);344hashTableDoRemove(&walkState);345if (NULL != nameToFree) {346j9mem_free_memory(nameToFree);347}348} else {349/* mark the constraint as unsatisfied if it refers to a dying loader */350J9Class* clazz = constraint->clazz;351352if ((NULL != clazz) && (J9AccClassDying == (J9CLASS_FLAGS(clazz) & J9AccClassDying))) {353constraint->clazz = NULL;354}355}356357constraint = hashTableNextDo(&walkState);358}359}360361Trc_RTV_unlinkClassLoadingConstraints_Exit();362}363364365/*366* Set the 'clazz' field of every J9ClassLoadingConstraint in the circular list to be clazz.367*/368static void369constrainList (J9ClassLoadingConstraint* constraint, J9Class* clazz)370{371J9ClassLoadingConstraint* cursor = constraint;372373Assert_RTV_true(J9_ARE_NO_BITS_SET(clazz->classFlags, J9ClassIsAnonymous));374while (NULL != cursor) {375cursor->clazz = clazz;376cursor = J9_LINKED_LIST_NEXT_DO(constraint, cursor);377}378}379380static UDATA381constraintHashFn(void *key, void *userData)382{383J9ClassLoadingConstraint *k = key;384J9JavaVM *vm = userData;385UDATA utf8Hash = J9_VM_FUNCTION_VIA_JAVAVM(vm, computeHashForUTF8)(k->name, k->nameLength);386387return (UDATA)k->classLoader ^ utf8Hash;388}389390391static UDATA392constraintHashEqualFn(void *leftKey, void *rightKey, void *userData)393{394J9ClassLoadingConstraint *left_k = leftKey;395J9ClassLoadingConstraint *right_k = rightKey;396J9JavaVM *vm = userData;397398return399(left_k->classLoader == right_k->classLoader)400&& (J9UTF8_DATA_EQUALS(left_k->name, left_k->nameLength, right_k->name, right_k->nameLength));401}402403404405