Skip to content

Commit 5e69a13

Browse files
authored
Cleanup JNI code to always correctly free memory when loading fails and also correctly respect out of memory in all cases (#9596)
Motivation: At the moment we not consistently (and also not correctly) free allocated native memory in all cases during loading the JNI library. This can lead to native memory leaks in the unlikely case of failure while trying to load the library. Beside this we also not always correctly handle the case when a new java object can not be created in native code because of out of memory. Modification: - Copy some macros from netty-tcnative to be able to handle errors in a more easy fashion - Correctly account for New* functions to return NULL - Share code Result: More robust and clean JNI code
1 parent eb3c4bd commit 5e69a13

File tree

9 files changed

+408
-504
lines changed

9 files changed

+408
-504
lines changed

transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c

Lines changed: 50 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -711,113 +711,83 @@ static jint dynamicMethodsTableSize() {
711711
}
712712

713713
static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) {
714-
JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize());
714+
char* dynamicTypeName = NULL;
715+
size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize();
716+
JNINativeMethod* dynamicMethods = malloc(size);
717+
if (dynamicMethods == NULL) {
718+
return NULL;
719+
}
720+
memset(dynamicMethods, 0, size);
715721
memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table));
722+
716723
JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size];
717-
char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials;");
724+
NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials;", dynamicTypeName, error);
725+
NETTY_PREPEND("(I)L", dynamicTypeName, dynamicMethod->signature, error);
718726
dynamicMethod->name = "getPeerCredentials";
719-
dynamicMethod->signature = netty_unix_util_prepend("(I)L", dynamicTypeName);
720727
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_getPeerCredentials;
721-
free(dynamicTypeName);
728+
netty_unix_util_free_dynamic_name(&dynamicTypeName);
722729

723730
++dynamicMethod;
724-
dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J");
731+
NETTY_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J", dynamicTypeName, error);
732+
NETTY_PREPEND("(IL", dynamicTypeName, dynamicMethod->signature, error);
725733
dynamicMethod->name = "sendFile";
726-
dynamicMethod->signature = netty_unix_util_prepend("(IL", dynamicTypeName);
727734
dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_sendFile;
728-
free(dynamicTypeName);
735+
netty_unix_util_free_dynamic_name(&dynamicTypeName);
729736
return dynamicMethods;
737+
error:
738+
free(dynamicTypeName);
739+
netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
740+
return NULL;
730741
}
731742

732-
static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) {
733-
jint fullMethodTableSize = dynamicMethodsTableSize();
734-
jint i = fixed_method_table_size;
735-
for (; i < fullMethodTableSize; ++i) {
736-
free(dynamicMethods[i].signature);
737-
}
738-
free(dynamicMethods);
739-
}
740743
// JNI Method Registration Table End
741744

742745
jint netty_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
746+
int ret = JNI_ERR;
747+
char* nettyClassName = NULL;
748+
jclass fileRegionCls = NULL;
749+
jclass fileChannelCls = NULL;
750+
jclass fileDescriptorCls = NULL;
751+
// Register the methods which are not referenced by static member variables
743752
JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix);
753+
if (dynamicMethods == NULL) {
754+
goto done;
755+
}
744756
if (netty_unix_util_register_natives(env,
745757
packagePrefix,
746758
"io/netty/channel/epoll/LinuxSocket",
747759
dynamicMethods,
748760
dynamicMethodsTableSize()) != 0) {
749-
freeDynamicMethodsTable(dynamicMethods);
750-
return JNI_ERR;
761+
goto done;
751762
}
752-
freeDynamicMethodsTable(dynamicMethods);
753-
dynamicMethods = NULL;
754763

755-
char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials");
756-
jclass localPeerCredsClass = (*env)->FindClass(env, nettyClassName);
757-
free(nettyClassName);
758-
nettyClassName = NULL;
759-
if (localPeerCredsClass == NULL) {
760-
// pending exception...
761-
return JNI_ERR;
762-
}
763-
peerCredentialsClass = (jclass) (*env)->NewGlobalRef(env, localPeerCredsClass);
764-
if (peerCredentialsClass == NULL) {
765-
// out-of-memory!
766-
netty_unix_errors_throwOutOfMemoryError(env);
767-
return JNI_ERR;
768-
}
769-
peerCredentialsMethodId = (*env)->GetMethodID(env, peerCredentialsClass, "<init>", "(II[I)V");
770-
if (peerCredentialsMethodId == NULL) {
771-
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: PeerCredentials.<init>(int, int, int[])");
772-
return JNI_ERR;
773-
}
764+
NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials", nettyClassName, done);
765+
NETTY_LOAD_CLASS(env, peerCredentialsClass, nettyClassName, done);
766+
netty_unix_util_free_dynamic_name(&nettyClassName);
774767

775-
nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion");
776-
jclass fileRegionCls = (*env)->FindClass(env, nettyClassName);
777-
free(nettyClassName);
778-
nettyClassName = NULL;
779-
if (fileRegionCls == NULL) {
780-
return JNI_ERR;
781-
}
782-
fileChannelFieldId = (*env)->GetFieldID(env, fileRegionCls, "file", "Ljava/nio/channels/FileChannel;");
783-
if (fileChannelFieldId == NULL) {
784-
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.file");
785-
return JNI_ERR;
786-
}
787-
transferredFieldId = (*env)->GetFieldID(env, fileRegionCls, "transferred", "J");
788-
if (transferredFieldId == NULL) {
789-
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.transferred");
790-
return JNI_ERR;
791-
}
768+
NETTY_GET_METHOD(env, peerCredentialsClass, peerCredentialsMethodId, "<init>", "(II[I)V", done);
792769

793-
jclass fileChannelCls = (*env)->FindClass(env, "sun/nio/ch/FileChannelImpl");
794-
if (fileChannelCls == NULL) {
795-
// pending exception...
796-
return JNI_ERR;
797-
}
798-
fileDescriptorFieldId = (*env)->GetFieldID(env, fileChannelCls, "fd", "Ljava/io/FileDescriptor;");
799-
if (fileDescriptorFieldId == NULL) {
800-
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileChannelImpl.fd");
801-
return JNI_ERR;
802-
}
770+
NETTY_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion", nettyClassName, done);
771+
NETTY_FIND_CLASS(env, fileRegionCls, nettyClassName, done);
772+
netty_unix_util_free_dynamic_name(&nettyClassName);
803773

804-
jclass fileDescriptorCls = (*env)->FindClass(env, "java/io/FileDescriptor");
805-
if (fileDescriptorCls == NULL) {
806-
// pending exception...
807-
return JNI_ERR;
808-
}
809-
fdFieldId = (*env)->GetFieldID(env, fileDescriptorCls, "fd", "I");
810-
if (fdFieldId == NULL) {
811-
netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileDescriptor.fd");
812-
return JNI_ERR;
813-
}
774+
NETTY_GET_FIELD(env, fileRegionCls, fileChannelFieldId, "file", "Ljava/nio/channels/FileChannel;", done);
775+
NETTY_GET_FIELD(env, fileRegionCls, transferredFieldId, "transferred", "J", done);
776+
777+
NETTY_FIND_CLASS(env, fileChannelCls, "sun/nio/ch/FileChannelImpl", done);
778+
NETTY_GET_FIELD(env, fileChannelCls, fileDescriptorFieldId, "fd", "Ljava/io/FileDescriptor;", done);
779+
780+
NETTY_FIND_CLASS(env, fileDescriptorCls, "java/io/FileDescriptor", done);
781+
NETTY_GET_FIELD(env, fileDescriptorCls, fdFieldId, "fd", "I", done);
814782

815-
return NETTY_JNI_VERSION;
783+
ret = NETTY_JNI_VERSION;
784+
done:
785+
netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize());
786+
free(nettyClassName);
787+
788+
return ret;
816789
}
817790

818791
void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env) {
819-
if (peerCredentialsClass != NULL) {
820-
(*env)->DeleteGlobalRef(env, peerCredentialsClass);
821-
peerCredentialsClass = NULL;
822-
}
792+
NETTY_UNLOAD_CLASS(env, peerCredentialsClass);
823793
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy