Path: blob/master/runtime/bcverify/classrelationships.c
5986 views
/*******************************************************************************1* Copyright (c) 2019, 2019 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of thse 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 "j9protos.h"23#include "j9consts.h"24#include "ut_j9bcverify.h"25#include "omrlinkedlist.h"2627static VMINLINE J9ClassRelationshipNode *allocateParentNode(J9VMThread *vmThread, U_8 *className, UDATA classNameLength);28static VMINLINE J9ClassRelationship *findClassRelationship(J9VMThread *vmThread, J9ClassLoader *classLoader, U_8 *className, UDATA classNameLength);29static void freeClassRelationshipParentNodes(J9VMThread *vmThread, J9ClassLoader *classLoader, J9ClassRelationship *relationship);30static UDATA relationshipHashFn(void *key, void *userData);31static UDATA relationshipHashEqualFn(void *leftKey, void *rightKey, void *userData);3233/**34* Record a class relationship in the class relationships table.35*36* Returns TRUE if successful and FALSE if an out of memory error occurs.37*/38IDATA39j9bcv_recordClassRelationship(J9VMThread *vmThread, J9ClassLoader *classLoader, U_8 *childName, UDATA childNameLength, U_8 *parentName, UDATA parentNameLength, IDATA *reasonCode)40{41PORT_ACCESS_FROM_VMC(vmThread);42J9JavaVM *vm = vmThread->javaVM;43J9ClassRelationship *childEntry = NULL;44J9ClassRelationshipNode *parentNode = NULL;45J9ClassRelationship child = {0};46IDATA recordResult = FALSE;47*reasonCode = BCV_ERR_INSUFFICIENT_MEMORY;4849Trc_RTV_recordClassRelationship_Entry(vmThread, childNameLength, childName, parentNameLength, parentName);5051Assert_RTV_true((NULL != childName) && (NULL != parentName));5253/* Locate existing childEntry or add new entry to the hashtable */54childEntry = findClassRelationship(vmThread, classLoader, childName, childNameLength);5556if (NULL == childEntry) {57child.className = (U_8 *) j9mem_allocate_memory(childNameLength + 1, J9MEM_CATEGORY_CLASSES);5859/* className for child successfully allocated, continue initialization of child entry */60if (NULL != child.className) {61memcpy(child.className, childName, childNameLength);62child.className[childNameLength] = '\0';63child.classNameLength = childNameLength;64child.flags = 0;6566childEntry = hashTableAdd(classLoader->classRelationshipsHashTable, &child);6768if (NULL == childEntry) {69Trc_RTV_recordClassRelationship_EntryAllocationFailedChild(vmThread);70j9mem_free_memory(child.className);71goto recordDone;72}73} else {74Trc_RTV_recordClassRelationship_EntryAllocationFailedChild(vmThread);75goto recordDone;76}77}7879/* If the parent is java/lang/Throwable, set a flag instead of allocating a node */80if (J9UTF8_DATA_EQUALS(J9RELATIONSHIP_JAVA_LANG_THROWABLE_STRING, J9RELATIONSHIP_JAVA_LANG_THROWABLE_STRING_LENGTH, parentName, parentNameLength)) {81if (!J9_ARE_ANY_BITS_SET(childEntry->flags, J9RELATIONSHIP_PARENT_IS_THROWABLE)) {82childEntry->flags |= J9RELATIONSHIP_PARENT_IS_THROWABLE;83}84} else {85/* Add a parentNode to the child's linked list of parents */86if (J9_LINKED_LIST_IS_EMPTY(childEntry->root)) {87parentNode = allocateParentNode(vmThread, parentName, parentNameLength);88if (parentNode == NULL) {89/* Allocation failure */90Trc_RTV_classRelationships_AllocationFailedParent(vmThread);91goto recordDone;92}93Trc_RTV_recordClassRelationship_AllocatedEntry(vmThread, childEntry->classNameLength, childEntry->className, childEntry, parentNode->classNameLength, parentNode->className, parentNode);94J9_LINKED_LIST_ADD_LAST(childEntry->root, parentNode);95} else {96BOOLEAN alreadyPresent = FALSE;97BOOLEAN addBefore = FALSE;98J9ClassRelationshipNode *walk = J9_LINKED_LIST_START_DO(childEntry->root);99/**100* Keep the list of parent nodes ordered by class name length so it's a faster traversal101* and duplicates can be avoided102*/103while (NULL != walk) {104if (walk->classNameLength > parentNameLength) {105addBefore = TRUE;106break;107} else if (J9UTF8_DATA_EQUALS(walk->className, walk->classNameLength, parentName, parentNameLength)) {108/* Already present, skip */109alreadyPresent = TRUE;110break;111} else {112/* walk->className is shorter or equal length but different data; keep looking */113}114walk = J9_LINKED_LIST_NEXT_DO(childEntry->root, walk);115}116if (!alreadyPresent) {117parentNode = allocateParentNode(vmThread, parentName, parentNameLength);118if (parentNode == NULL) {119/* Allocation failure */120Trc_RTV_classRelationships_AllocationFailedParent(vmThread);121goto recordDone;122}123Trc_RTV_recordClassRelationship_AllocatedEntry(vmThread, childEntry->classNameLength, childEntry->className, childEntry, parentNode->classNameLength, parentNode->className, parentNode);124if (addBefore) {125J9_LINKED_LIST_ADD_BEFORE(childEntry->root, walk, parentNode);126} else {127/* If got through the whole list of shorter or equal length names, add it here */128J9_LINKED_LIST_ADD_LAST(childEntry->root, parentNode);129}130}131}132}133134recordResult = TRUE;135*reasonCode = 0;136137recordDone:138Trc_RTV_recordClassRelationship_Exit(vmThread, recordResult);139return recordResult;140}141142/**143* Validate each recorded relationship for a class.144*145* Returns failedClass, which is NULL if successful, or the class that fails validation if unsuccessful.146*/147J9Class *148j9bcv_validateClassRelationships(J9VMThread *vmThread, J9ClassLoader *classLoader, U_8 *childName, UDATA childNameLength, J9Class *childClass)149{150PORT_ACCESS_FROM_VMC(vmThread);151J9Class *parentClass = NULL;152J9Class *failedClass = NULL;153J9ClassRelationship *childEntry = NULL;154J9ClassRelationshipNode *parentNode = NULL;155156Trc_RTV_validateClassRelationships_Entry(vmThread, childNameLength, childName);157Assert_RTV_true(NULL != childName);158childEntry = findClassRelationship(vmThread, classLoader, childName, childNameLength);159160/* No relationships were recorded for the class (in this class loader), or its relationships have already been verified */161if (NULL == childEntry) {162goto validateDone;163}164165/* The class is invalid if it has been marked as an interface, but it actually isn't */166if (J9_ARE_ANY_BITS_SET(childEntry->flags, J9RELATIONSHIP_MUST_BE_INTERFACE)) {167Trc_RTV_validateClassRelationships_FlaggedAsInterface(vmThread, childNameLength, childName);168if (!J9ROMCLASS_IS_INTERFACE(childClass->romClass)) {169Trc_RTV_validateClassRelationships_ShouldBeInterface(vmThread, childNameLength, childName);170failedClass = childClass;171goto validateDone;172}173}174175/* If J9RELATIONSHIP_PARENT_IS_THROWABLE is set, check that the relationship holds */176if (J9_ARE_ANY_BITS_SET(childEntry->flags, J9RELATIONSHIP_PARENT_IS_THROWABLE)) {177/* Throwable will already be loaded since it is a required class J9VMCONSTANTPOOL_JAVALANGTHROWABLE */178parentClass = J9VMJAVALANGTHROWABLE_OR_NULL(vmThread->javaVM);179Assert_RTV_true(NULL != parentClass);180if (isSameOrSuperClassOf(parentClass, childClass)) {181Trc_RTV_validateClassRelationships_ParentIsSuperClass(vmThread, J9RELATIONSHIP_JAVA_LANG_THROWABLE_STRING_LENGTH, J9RELATIONSHIP_JAVA_LANG_THROWABLE_STRING, NULL);182} else {183/* The class is invalid since it doesn't hold the expected relationship with java/lang/Throwable */184Trc_RTV_validateClassRelationships_InvalidRelationship(vmThread, J9RELATIONSHIP_JAVA_LANG_THROWABLE_STRING_LENGTH, J9RELATIONSHIP_JAVA_LANG_THROWABLE_STRING);185failedClass = parentClass;186goto validateDone;187}188}189190parentNode = J9_LINKED_LIST_START_DO(childEntry->root);191192while (NULL != parentNode) {193/* Find the parent class in the loaded classes table */194parentClass = J9_VM_FUNCTION(vmThread, hashClassTableAt)(classLoader, parentNode->className, parentNode->classNameLength);195196/* If the parent class has not been loaded, then it has to be an interface since the child is already loaded */197if (NULL == parentClass) {198/* Add a new relationship to the table if one doesn't already exist and flag the parentClass as J9RELATIONSHIP_MUST_BE_INTERFACE */199J9ClassRelationship *parentEntry = findClassRelationship(vmThread, classLoader, parentNode->className, parentNode->classNameLength);200201Trc_RTV_validateClassRelationships_ParentNotLoaded(vmThread, parentNode->classNameLength, parentNode->className, parentNode);202203if (NULL == parentEntry) {204J9ClassRelationship parent = {0};205PORT_ACCESS_FROM_VMC(vmThread);206parent.className = (U_8 *) j9mem_allocate_memory(parentNode->classNameLength + 1, J9MEM_CATEGORY_CLASSES);207208/* className for parent successfully allocated, continue initialization of parent entry */209if (NULL != parent.className) {210Trc_RTV_validateClassRelationships_AllocatingParent(vmThread);211memcpy(parent.className, parentNode->className, parentNode->classNameLength);212parent.className[parentNode->classNameLength] = '\0';213parent.classNameLength = parentNode->classNameLength;214parent.flags = J9RELATIONSHIP_MUST_BE_INTERFACE;215216parentEntry = hashTableAdd(classLoader->classRelationshipsHashTable, &parent);217218if (NULL == parentEntry) {219Trc_RTV_classRelationships_AllocationFailedParent(vmThread);220j9mem_free_memory(parent.className);221failedClass = childClass;222goto validateDone;223}224Trc_RTV_validateClassRelationships_AllocatedParentEntry(vmThread);225} else {226Trc_RTV_classRelationships_AllocationFailedParent(vmThread);227failedClass = childClass;228goto validateDone;229}230} else {231parentEntry->flags |= J9RELATIONSHIP_MUST_BE_INTERFACE;232}233} else {234/* The already loaded parentClass should either be an interface, or is the same or superclass of the childClass */235if (J9ROMCLASS_IS_INTERFACE(parentClass->romClass)) {236/* If the target is an interface, be permissive as per the verifier type checking rules */237Trc_RTV_validateClassRelationships_ParentIsInterface(vmThread, parentNode->classNameLength, parentNode->className, parentNode);238} else if (isSameOrSuperClassOf(parentClass, childClass)) {239Trc_RTV_validateClassRelationships_ParentIsSuperClass(vmThread, parentNode->classNameLength, parentNode->className, parentNode);240} else {241/* The child and parent have an invalid relationship */242Trc_RTV_validateClassRelationships_InvalidRelationship(vmThread, parentNode->classNameLength, parentNode->className);243failedClass = parentClass;244goto validateDone;245}246}247parentNode = J9_LINKED_LIST_NEXT_DO(childEntry->root, parentNode);248}249250/* Successful validation; free memory for childEntry */251freeClassRelationshipParentNodes(vmThread, classLoader, childEntry);252j9mem_free_memory(childEntry->className);253hashTableRemove(classLoader->classRelationshipsHashTable, childEntry);254255validateDone:256Trc_RTV_validateClassRelationships_Exit(vmThread, failedClass);257return failedClass;258}259260/**261* Add a parentNode to a child entry's linked list of parents.262*263* Return the allocated J9ClassRelationshipNode.264*/265static VMINLINE J9ClassRelationshipNode *266allocateParentNode(J9VMThread *vmThread, U_8 *className, UDATA classNameLength)267{268PORT_ACCESS_FROM_VMC(vmThread);269J9ClassRelationshipNode *parentNode = (J9ClassRelationshipNode *) j9mem_allocate_memory(sizeof(J9ClassRelationshipNode), J9MEM_CATEGORY_CLASSES);270271if (NULL != parentNode) {272parentNode->className = (U_8 *) j9mem_allocate_memory(classNameLength + 1, J9MEM_CATEGORY_CLASSES);273274if (NULL != parentNode->className) {275memcpy(parentNode->className, className, classNameLength);276parentNode->className[classNameLength] = '\0';277parentNode->classNameLength = classNameLength;278} else {279j9mem_free_memory(parentNode);280parentNode = NULL;281}282}283284return parentNode;285}286287/**288* Find the class relationship table entry for a particular class.289*290* Returns the found J9ClassRelationship, or NULL if it is not found.291*/292static VMINLINE J9ClassRelationship *293findClassRelationship(J9VMThread *vmThread, J9ClassLoader *classLoader, U_8 *className, UDATA classNameLength)294{295J9ClassRelationship *classEntry = NULL;296J9JavaVM *vm = vmThread->javaVM;297298Trc_RTV_findClassRelationship_Entry(vmThread, classNameLength, className);299300if (NULL != classLoader->classRelationshipsHashTable) {301J9ClassRelationship exemplar = {0};302exemplar.className = className;303exemplar.classNameLength = classNameLength;304classEntry = hashTableFind(classLoader->classRelationshipsHashTable, &exemplar);305}306307Trc_RTV_findClassRelationship_Exit(vmThread, classEntry);308return classEntry;309}310311/**312* Free allocated memory for each parent class node of a class relationship table entry.313*/314static void315freeClassRelationshipParentNodes(J9VMThread *vmThread, J9ClassLoader *classLoader, J9ClassRelationship *relationship)316{317PORT_ACCESS_FROM_VMC(vmThread);318J9ClassRelationshipNode *parentNode = NULL;319320Trc_RTV_freeClassRelationshipParentNodes_Entry(vmThread, relationship->classNameLength, relationship->className);321322while (NULL != relationship->root) {323parentNode = relationship->root;324Trc_RTV_freeClassRelationshipParentNodes_Parent(vmThread, parentNode->classNameLength, parentNode->className);325J9_LINKED_LIST_REMOVE(relationship->root, parentNode);326j9mem_free_memory(parentNode->className);327j9mem_free_memory(parentNode);328}329330Trc_RTV_freeClassRelationshipParentNodes_Exit(vmThread);331return;332}333334/**335* Allocates new hash table to store class relationship entries.336*337* Returns 0 if successful, and 1 otherwise.338*/339UDATA340j9bcv_hashClassRelationshipTableNew(J9ClassLoader *classLoader, J9JavaVM *vm)341{342UDATA result = 0;343344/* Allocate classRelationshipsHashTable if -XX:+ClassRelationshipVerifier is used */345if (J9_ARE_ANY_BITS_SET(vm->extendedRuntimeFlags2, J9_EXTENDED_RUNTIME2_ENABLE_CLASS_RELATIONSHIP_VERIFIER)) {346classLoader->classRelationshipsHashTable = hashTableNew(OMRPORT_FROM_J9PORT(vm->portLibrary), J9_GET_CALLSITE(), 256, sizeof(J9ClassRelationship), sizeof(char *), 0, J9MEM_CATEGORY_CLASSES, relationshipHashFn, relationshipHashEqualFn, NULL, vm);347348if (NULL == classLoader->classRelationshipsHashTable) {349result = 1;350}351}352353return result;354}355356/**357* Frees memory for each J9ClassRelationship table entry, J9ClassRelationshipNode,358* and the classRelationships hash table itself.359*/360void361j9bcv_hashClassRelationshipTableFree(J9VMThread *vmThread, J9ClassLoader *classLoader, J9JavaVM *vm)362{363/* Free the class relationships hash table if -XX:+ClassRelationshipVerifier is used */364if (J9_ARE_ANY_BITS_SET(vm->extendedRuntimeFlags2, J9_EXTENDED_RUNTIME2_ENABLE_CLASS_RELATIONSHIP_VERIFIER)) {365PORT_ACCESS_FROM_VMC(vmThread);366J9HashTableState hashTableState = {0};367J9HashTable *classRelationshipsHashTable = classLoader->classRelationshipsHashTable;368J9ClassRelationship *relationshipEntryStart = (J9ClassRelationship *) hashTableStartDo(classRelationshipsHashTable, &hashTableState);369J9ClassRelationship *relationshipEntry = relationshipEntryStart;370371/* Free all parent nodes of a relationship entry and then the entry itself */372while (NULL != relationshipEntry) {373UDATA result = 0;374freeClassRelationshipParentNodes(vmThread, classLoader, relationshipEntry);375j9mem_free_memory(relationshipEntry->className);376result = hashTableDoRemove(&hashTableState);377Assert_RTV_true(0 == result);378relationshipEntry = (J9ClassRelationship *) hashTableNextDo(&hashTableState);379}380}381382return;383}384385static UDATA386relationshipHashFn(void *key, void *userData)387{388J9ClassRelationship *relationshipKey = key;389J9JavaVM *vm = userData;390391UDATA utf8HashClass = J9_VM_FUNCTION_VIA_JAVAVM(vm, computeHashForUTF8)(relationshipKey->className, relationshipKey->classNameLength);392393return utf8HashClass;394}395396static UDATA397relationshipHashEqualFn(void *leftKey, void *rightKey, void *userData)398{399J9ClassRelationship *left_relationshipKey = leftKey;400J9ClassRelationship *right_relationshipKey = rightKey;401402UDATA classNameEqual = J9UTF8_DATA_EQUALS(left_relationshipKey->className, left_relationshipKey->classNameLength, right_relationshipKey->className, right_relationshipKey->classNameLength);403404return classNameEqual;405}406407408