From 5e69a13c21bcf89bf26dbf1421c5ec8188561a17 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Tue, 24 Sep 2019 07:18:35 +0200 Subject: [PATCH] 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 --- .../src/main/c/netty_epoll_linuxsocket.c | 134 +++++------- .../src/main/c/netty_epoll_native.c | 193 ++++++++---------- .../src/main/c/netty_kqueue_bsdsocket.c | 172 +++++++--------- .../src/main/c/netty_kqueue_native.c | 6 +- .../src/main/c/netty_unix_errors.c | 120 +++-------- .../src/main/c/netty_unix_filedescriptor.c | 60 ++---- .../src/main/c/netty_unix_socket.c | 139 +++++-------- .../src/main/c/netty_unix_util.c | 49 ++++- .../src/main/c/netty_unix_util.h | 69 +++++++ 9 files changed, 423 insertions(+), 519 deletions(-) diff --git a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c index 2d24132be5..3bb7b2010f 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c +++ b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c @@ -711,113 +711,83 @@ static jint dynamicMethodsTableSize() { } static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { - JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize()); + char* dynamicTypeName = NULL; + size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize(); + JNINativeMethod* dynamicMethods = malloc(size); + if (dynamicMethods == NULL) { + return NULL; + } + memset(dynamicMethods, 0, size); memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table)); + JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size]; - char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials;"); + NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials;", dynamicTypeName, error); + NETTY_PREPEND("(I)L", dynamicTypeName, dynamicMethod->signature, error); dynamicMethod->name = "getPeerCredentials"; - dynamicMethod->signature = netty_unix_util_prepend("(I)L", dynamicTypeName); dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_getPeerCredentials; - free(dynamicTypeName); + netty_unix_util_free_dynamic_name(&dynamicTypeName); ++dynamicMethod; - dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J"); + NETTY_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J", dynamicTypeName, error); + NETTY_PREPEND("(IL", dynamicTypeName, dynamicMethod->signature, error); dynamicMethod->name = "sendFile"; - dynamicMethod->signature = netty_unix_util_prepend("(IL", dynamicTypeName); dynamicMethod->fnPtr = (void *) netty_epoll_linuxsocket_sendFile; - free(dynamicTypeName); + netty_unix_util_free_dynamic_name(&dynamicTypeName); return dynamicMethods; +error: + free(dynamicTypeName); + netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize()); + return NULL; } -static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) { - jint fullMethodTableSize = dynamicMethodsTableSize(); - jint i = fixed_method_table_size; - for (; i < fullMethodTableSize; ++i) { - free(dynamicMethods[i].signature); - } - free(dynamicMethods); -} // JNI Method Registration Table End jint netty_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { + int ret = JNI_ERR; + char* nettyClassName = NULL; + jclass fileRegionCls = NULL; + jclass fileChannelCls = NULL; + jclass fileDescriptorCls = NULL; + // Register the methods which are not referenced by static member variables JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); + if (dynamicMethods == NULL) { + goto done; + } if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/epoll/LinuxSocket", dynamicMethods, dynamicMethodsTableSize()) != 0) { - freeDynamicMethodsTable(dynamicMethods); - return JNI_ERR; + goto done; } - freeDynamicMethodsTable(dynamicMethods); - dynamicMethods = NULL; - char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials"); - jclass localPeerCredsClass = (*env)->FindClass(env, nettyClassName); + NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials", nettyClassName, done); + NETTY_LOAD_CLASS(env, peerCredentialsClass, nettyClassName, done); + netty_unix_util_free_dynamic_name(&nettyClassName); + + NETTY_GET_METHOD(env, peerCredentialsClass, peerCredentialsMethodId, "", "(II[I)V", done); + + NETTY_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion", nettyClassName, done); + NETTY_FIND_CLASS(env, fileRegionCls, nettyClassName, done); + netty_unix_util_free_dynamic_name(&nettyClassName); + + NETTY_GET_FIELD(env, fileRegionCls, fileChannelFieldId, "file", "Ljava/nio/channels/FileChannel;", done); + NETTY_GET_FIELD(env, fileRegionCls, transferredFieldId, "transferred", "J", done); + + NETTY_FIND_CLASS(env, fileChannelCls, "sun/nio/ch/FileChannelImpl", done); + NETTY_GET_FIELD(env, fileChannelCls, fileDescriptorFieldId, "fd", "Ljava/io/FileDescriptor;", done); + + NETTY_FIND_CLASS(env, fileDescriptorCls, "java/io/FileDescriptor", done); + NETTY_GET_FIELD(env, fileDescriptorCls, fdFieldId, "fd", "I", done); + + ret = NETTY_JNI_VERSION; +done: + netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize()); free(nettyClassName); - nettyClassName = NULL; - if (localPeerCredsClass == NULL) { - // pending exception... - return JNI_ERR; - } - peerCredentialsClass = (jclass) (*env)->NewGlobalRef(env, localPeerCredsClass); - if (peerCredentialsClass == NULL) { - // out-of-memory! - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; - } - peerCredentialsMethodId = (*env)->GetMethodID(env, peerCredentialsClass, "", "(II[I)V"); - if (peerCredentialsMethodId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get method ID: PeerCredentials.(int, int, int[])"); - return JNI_ERR; - } - nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion"); - jclass fileRegionCls = (*env)->FindClass(env, nettyClassName); - free(nettyClassName); - nettyClassName = NULL; - if (fileRegionCls == NULL) { - return JNI_ERR; - } - fileChannelFieldId = (*env)->GetFieldID(env, fileRegionCls, "file", "Ljava/nio/channels/FileChannel;"); - if (fileChannelFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.file"); - return JNI_ERR; - } - transferredFieldId = (*env)->GetFieldID(env, fileRegionCls, "transferred", "J"); - if (transferredFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.transferred"); - return JNI_ERR; - } - - jclass fileChannelCls = (*env)->FindClass(env, "sun/nio/ch/FileChannelImpl"); - if (fileChannelCls == NULL) { - // pending exception... - return JNI_ERR; - } - fileDescriptorFieldId = (*env)->GetFieldID(env, fileChannelCls, "fd", "Ljava/io/FileDescriptor;"); - if (fileDescriptorFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileChannelImpl.fd"); - return JNI_ERR; - } - - jclass fileDescriptorCls = (*env)->FindClass(env, "java/io/FileDescriptor"); - if (fileDescriptorCls == NULL) { - // pending exception... - return JNI_ERR; - } - fdFieldId = (*env)->GetFieldID(env, fileDescriptorCls, "fd", "I"); - if (fdFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileDescriptor.fd"); - return JNI_ERR; - } - - return NETTY_JNI_VERSION; + return ret; } void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env) { - if (peerCredentialsClass != NULL) { - (*env)->DeleteGlobalRef(env, peerCredentialsClass); - peerCredentialsClass = NULL; - } + NETTY_UNLOAD_CLASS(env, peerCredentialsClass); } diff --git a/transport-native-epoll/src/main/c/netty_epoll_native.c b/transport-native-epoll/src/main/c/netty_epoll_native.c index fda29b5890..697e26a8d0 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_native.c +++ b/transport-native-epoll/src/main/c/netty_epoll_native.c @@ -516,42 +516,48 @@ static jint dynamicMethodsTableSize() { } static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { - JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize()); - memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table)); - - char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I"); - JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size]; - dynamicMethod->name = "sendmmsg0"; - dynamicMethod->signature = netty_unix_util_prepend("(IZ[L", dynamicTypeName); - dynamicMethod->fnPtr = (void *) netty_epoll_native_sendmmsg0; - free(dynamicTypeName); - - dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I"); - dynamicMethod = &dynamicMethods[fixed_method_table_size + 1]; - dynamicMethod->name = "recvmmsg0"; - dynamicMethod->signature = netty_unix_util_prepend("(IZ[L", dynamicTypeName); - dynamicMethod->fnPtr = (void *) netty_epoll_native_recvmmsg0; - free(dynamicTypeName); - return dynamicMethods; -} - -static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) { - jint fullMethodTableSize = dynamicMethodsTableSize(); - jint i = fixed_method_table_size; - for (; i < fullMethodTableSize; ++i) { - free(dynamicMethods[i].signature); + char* dynamicTypeName = NULL; + size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize(); + JNINativeMethod* dynamicMethods = malloc(size); + if (dynamicMethods == NULL) { + return NULL; } - free(dynamicMethods); + memset(dynamicMethods, 0, size); + memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table)); + + JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size]; + NETTY_PREPEND(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I", dynamicTypeName, error); + NETTY_PREPEND("(IZ[L", dynamicTypeName, dynamicMethod->signature, error); + dynamicMethod->name = "sendmmsg0"; + dynamicMethod->fnPtr = (void *) netty_epoll_native_sendmmsg0; + netty_unix_util_free_dynamic_name(&dynamicTypeName); + + ++dynamicMethod; + NETTY_PREPEND(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket;II)I", dynamicTypeName, error); + NETTY_PREPEND("(IZ[L", dynamicTypeName, dynamicMethod->signature, error); + dynamicMethod->name = "recvmmsg0"; + dynamicMethod->fnPtr = (void *) netty_epoll_native_recvmmsg0; + netty_unix_util_free_dynamic_name(&dynamicTypeName); + + return dynamicMethods; +error: + free(dynamicTypeName); + netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize()); + return NULL; } + // JNI Method Registration Table End static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { + int ret = JNI_ERR; int limitsOnLoadCalled = 0; int errorsOnLoadCalled = 0; int filedescriptorOnLoadCalled = 0; int socketOnLoadCalled = 0; int bufferOnLoadCalled = 0; int linuxsocketOnLoadCalled = 0; + char* nettyClassName = NULL; + jclass nativeDatagramPacketCls = NULL; // We must register the statically referenced methods first! if (netty_unix_util_register_natives(env, @@ -559,122 +565,97 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix "io/netty/channel/epoll/NativeStaticallyReferencedJniMethods", statically_referenced_fixed_method_table, statically_referenced_fixed_method_table_size) != 0) { - goto error; + goto done; } // Register the methods which are not referenced by static member variables JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); + if (dynamicMethods == NULL) { + goto done; + } + if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/epoll/Native", dynamicMethods, dynamicMethodsTableSize()) != 0) { - freeDynamicMethodsTable(dynamicMethods); - goto error; + goto done; } - freeDynamicMethodsTable(dynamicMethods); - dynamicMethods = NULL; // Load all c modules that we depend upon if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - goto error; + goto done; } limitsOnLoadCalled = 1; if (netty_unix_errors_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - goto error; + goto done; } errorsOnLoadCalled = 1; if (netty_unix_filedescriptor_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - goto error; + goto done; } filedescriptorOnLoadCalled = 1; if (netty_unix_socket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - goto error; + goto done; } socketOnLoadCalled = 1; if (netty_unix_buffer_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - goto error; + goto done; } bufferOnLoadCalled = 1; if (netty_epoll_linuxsocket_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { - goto error; + goto done; } linuxsocketOnLoadCalled = 1; // Initialize this module - char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket"); - jclass nativeDatagramPacketCls = (*env)->FindClass(env, nettyClassName); + NETTY_PREPEND(packagePrefix, "io/netty/channel/epoll/NativeDatagramPacketArray$NativeDatagramPacket", nettyClassName, done); + NETTY_FIND_CLASS(env, nativeDatagramPacketCls, nettyClassName, done); + netty_unix_util_free_dynamic_name(&nettyClassName); + + NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetAddrFieldId, "addr", "[B", done); + NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetAddrLenFieldId, "addrLen", "I", done); + NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetScopeIdFieldId, "scopeId", "I", done); + NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetPortFieldId, "port", "I", done); + NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetMemoryAddressFieldId, "memoryAddress", "J", done); + NETTY_GET_FIELD(env, nativeDatagramPacketCls, packetCountFieldId, "count", "I", done); + + ret = NETTY_JNI_VERSION; +done: + + netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize()); free(nettyClassName); - nettyClassName = NULL; - if (nativeDatagramPacketCls == NULL) { - // pending exception... - goto error; - } - packetAddrFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "addr", "[B"); - if (packetAddrFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.addr"); - goto error; + if (ret == JNI_ERR) { + if (limitsOnLoadCalled == 1) { + netty_unix_limits_JNI_OnUnLoad(env); + } + if (errorsOnLoadCalled == 1) { + netty_unix_errors_JNI_OnUnLoad(env); + } + if (filedescriptorOnLoadCalled == 1) { + netty_unix_filedescriptor_JNI_OnUnLoad(env); + } + if (socketOnLoadCalled == 1) { + netty_unix_socket_JNI_OnUnLoad(env); + } + if (bufferOnLoadCalled == 1) { + netty_unix_buffer_JNI_OnUnLoad(env); + } + if (linuxsocketOnLoadCalled == 1) { + netty_epoll_linuxsocket_JNI_OnUnLoad(env); + } + packetAddrFieldId = NULL; + packetAddrLenFieldId = NULL; + packetScopeIdFieldId = NULL; + packetPortFieldId = NULL; + packetMemoryAddressFieldId = NULL; + packetCountFieldId = NULL; } - packetAddrLenFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "addrLen", "I"); - if (packetAddrLenFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.addrLen"); - goto error; - } - packetScopeIdFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "scopeId", "I"); - if (packetScopeIdFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.scopeId"); - goto error; - } - packetPortFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "port", "I"); - if (packetPortFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.port"); - goto error; - } - packetMemoryAddressFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "memoryAddress", "J"); - if (packetMemoryAddressFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.memoryAddress"); - goto error; - } - - packetCountFieldId = (*env)->GetFieldID(env, nativeDatagramPacketCls, "count", "I"); - if (packetCountFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: NativeDatagramPacket.count"); - goto error; - } - - return NETTY_JNI_VERSION; - -error: - if (limitsOnLoadCalled == 1) { - netty_unix_limits_JNI_OnUnLoad(env); - } - if (errorsOnLoadCalled == 1) { - netty_unix_errors_JNI_OnUnLoad(env); - } - if (filedescriptorOnLoadCalled == 1) { - netty_unix_filedescriptor_JNI_OnUnLoad(env); - } - if (socketOnLoadCalled == 1) { - netty_unix_socket_JNI_OnUnLoad(env); - } - if (bufferOnLoadCalled == 1) { - netty_unix_buffer_JNI_OnUnLoad(env); - } - if (linuxsocketOnLoadCalled == 1) { - netty_epoll_linuxsocket_JNI_OnUnLoad(env); - } - packetAddrFieldId = NULL; - packetAddrLenFieldId = NULL; - packetScopeIdFieldId = NULL; - packetPortFieldId = NULL; - packetMemoryAddressFieldId = NULL; - packetCountFieldId = NULL; - - return JNI_ERR; + return ret; } static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) { @@ -717,11 +698,7 @@ static jint JNI_OnLoad_netty_transport_native_epoll0(JavaVM* vm, void* reserved) #endif /* NETTY_BUILD_STATIC */ jint ret = netty_epoll_native_JNI_OnLoad(env, packagePrefix); - if (packagePrefix != NULL) { - free(packagePrefix); - packagePrefix = NULL; - } - + free(packagePrefix); return ret; } diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.c b/transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.c index e9e13030c7..53a376901e 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.c @@ -109,8 +109,19 @@ static jobjectArray netty_kqueue_bsdsocket_getAcceptFilter(JNIEnv* env, jclass c return NULL; } jobjectArray resultArray = (*env)->NewObjectArray(env, 2, stringClass, NULL); - (*env)->SetObjectArrayElement(env, resultArray, 0, (*env)->NewStringUTF(env, &af.af_name[0])); - (*env)->SetObjectArrayElement(env, resultArray, 1, (*env)->NewStringUTF(env, &af.af_arg[0])); + if (resultArray == NULL) { + return NULL; + } + jstring name = (*env)->NewStringUTF(env, &af.af_name[0]); + if (name == NULL) { + return NULL; + } + jstring arg = (*env)->NewStringUTF(env, &af.af_arg[0]); + if (arg == NULL) { + return NULL; + } + (*env)->SetObjectArrayElement(env, resultArray, 0, name); + (*env)->SetObjectArrayElement(env, resultArray, 1, arg); return resultArray; #else // No know replacement on MacOS // Don't throw here because this is used when getting a list of all options. @@ -151,11 +162,15 @@ static jobject netty_kqueue_bsdsocket_getPeerCredentials(JNIEnv *env, jclass cla } jintArray gids = NULL; if (credentials.cr_ngroups > 1) { - gids = (*env)->NewIntArray(env, credentials.cr_ngroups); + if ((gids = (*env)->NewIntArray(env, credentials.cr_ngroups)) == NULL) { + return NULL; + } (*env)->SetIntArrayRegion(env, gids, 0, credentials.cr_ngroups, (jint*) credentials.cr_groups); } else { // It has been observed on MacOS that cr_ngroups may not be set, but the cr_gid field is set. - gids = (*env)->NewIntArray(env, 1); + if ((gids = (*env)->NewIntArray(env, 1)) == NULL) { + return NULL; + } (*env)->SetIntArrayRegion(env, gids, 0, 1, (jint*) &credentials.cr_gid); } @@ -189,130 +204,87 @@ static jint dynamicMethodsTableSize() { } static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { - JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize()); + char* dynamicTypeName = NULL; + size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize(); + JNINativeMethod* dynamicMethods = malloc(size); + if (dynamicMethods == NULL) { + return NULL; + } + memset(dynamicMethods, 0, size); memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table)); - char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J"); + JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size]; + NETTY_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion;JJJ)J", dynamicTypeName, error); + NETTY_PREPEND("(IL", dynamicTypeName, dynamicMethod->signature, error); dynamicMethod->name = "sendFile"; - dynamicMethod->signature = netty_unix_util_prepend("(IL", dynamicTypeName); dynamicMethod->fnPtr = (void *) netty_kqueue_bsdsocket_sendFile; - free(dynamicTypeName); + netty_unix_util_free_dynamic_name(&dynamicTypeName); ++dynamicMethod; - dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials;"); + NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials;", dynamicTypeName, error); + NETTY_PREPEND("(I)L", dynamicTypeName, dynamicMethod->signature, error); dynamicMethod->name = "getPeerCredentials"; - dynamicMethod->signature = netty_unix_util_prepend("(I)L", dynamicTypeName); dynamicMethod->fnPtr = (void *) netty_kqueue_bsdsocket_getPeerCredentials; - free(dynamicTypeName); + netty_unix_util_free_dynamic_name(&dynamicTypeName); return dynamicMethods; +error: + free(dynamicTypeName); + netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize()); + return NULL; } -static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) { - jint fullMethodTableSize = dynamicMethodsTableSize(); - jint i = fixed_method_table_size; - for (; i < fullMethodTableSize; ++i) { - free(dynamicMethods[i].signature); - } - free(dynamicMethods); -} // JNI Method Registration Table End jint netty_kqueue_bsdsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { + int ret = JNI_ERR; + char* nettyClassName = NULL; + jclass fileRegionCls = NULL; + jclass fileChannelCls = NULL; + jclass fileDescriptorCls = NULL; // Register the methods which are not referenced by static member variables JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); + if (dynamicMethods == NULL) { + goto done; + } if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/kqueue/BsdSocket", dynamicMethods, dynamicMethodsTableSize()) != 0) { - freeDynamicMethodsTable(dynamicMethods); - return JNI_ERR; + goto done; } - freeDynamicMethodsTable(dynamicMethods); - dynamicMethods = NULL; // Initialize this module - char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/DefaultFileRegion"); - jclass fileRegionCls = (*env)->FindClass(env, nettyClassName); + NETTY_PREPEND(packagePrefix, "io/netty/channel/DefaultFileRegion", nettyClassName, done); + NETTY_FIND_CLASS(env, fileRegionCls, nettyClassName, done); + netty_unix_util_free_dynamic_name(&nettyClassName); + + NETTY_GET_FIELD(env, fileRegionCls, fileChannelFieldId, "file", "Ljava/nio/channels/FileChannel;", done); + NETTY_GET_FIELD(env, fileRegionCls, transferredFieldId, "transferred", "J", done); + + NETTY_FIND_CLASS(env, fileChannelCls, "sun/nio/ch/FileChannelImpl", done); + NETTY_GET_FIELD(env, fileChannelCls, fileDescriptorFieldId, "fd", "Ljava/io/FileDescriptor;", done); + + NETTY_FIND_CLASS(env, fileDescriptorCls, "java/io/FileDescriptor", done); + NETTY_GET_FIELD(env, fileDescriptorCls, fdFieldId, "fd", "I", done); + + NETTY_LOAD_CLASS(env, stringClass, "java/lang/String", done); + + NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/PeerCredentials", nettyClassName, done); + NETTY_LOAD_CLASS(env, peerCredentialsClass, nettyClassName, done); + netty_unix_util_free_dynamic_name(&nettyClassName); + + NETTY_GET_METHOD(env, peerCredentialsClass, peerCredentialsMethodId, "", "(II[I)V", done); + ret = NETTY_JNI_VERSION; +done: + netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize()); free(nettyClassName); - nettyClassName = NULL; - if (fileRegionCls == NULL) { - return JNI_ERR; - } - fileChannelFieldId = (*env)->GetFieldID(env, fileRegionCls, "file", "Ljava/nio/channels/FileChannel;"); - if (fileChannelFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.file"); - return JNI_ERR; - } - transferredFieldId = (*env)->GetFieldID(env, fileRegionCls, "transferred", "J"); - if (transferredFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: DefaultFileRegion.transferred"); - return JNI_ERR; - } - jclass fileChannelCls = (*env)->FindClass(env, "sun/nio/ch/FileChannelImpl"); - if (fileChannelCls == NULL) { - // pending exception... - return JNI_ERR; - } - fileDescriptorFieldId = (*env)->GetFieldID(env, fileChannelCls, "fd", "Ljava/io/FileDescriptor;"); - if (fileDescriptorFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileChannelImpl.fd"); - return JNI_ERR; - } - - jclass fileDescriptorCls = (*env)->FindClass(env, "java/io/FileDescriptor"); - if (fileDescriptorCls == NULL) { - // pending exception... - return JNI_ERR; - } - fdFieldId = (*env)->GetFieldID(env, fileDescriptorCls, "fd", "I"); - if (fdFieldId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get field ID: FileDescriptor.fd"); - return JNI_ERR; - } - jclass stringCls = (*env)->FindClass(env, "java/lang/String"); - if (stringCls == NULL) { - // pending exception... - return JNI_ERR; - } - if ((stringClass = (*env)->NewGlobalRef(env, stringCls)) == NULL) { - // out-of-memory! - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; - } - - nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/PeerCredentials"); - jclass localPeerCredsClass = (*env)->FindClass(env, nettyClassName); - free(nettyClassName); - nettyClassName = NULL; - if (localPeerCredsClass == NULL) { - // pending exception... - return JNI_ERR; - } - peerCredentialsClass = (jclass) (*env)->NewGlobalRef(env, localPeerCredsClass); - if (peerCredentialsClass == NULL) { - // out-of-memory! - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; - } - peerCredentialsMethodId = (*env)->GetMethodID(env, peerCredentialsClass, "", "(II[I)V"); - if (peerCredentialsMethodId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get method ID: PeerCredentials.(int, int, int[])"); - return JNI_ERR; - } - - return NETTY_JNI_VERSION; + return ret; } void netty_kqueue_bsdsocket_JNI_OnUnLoad(JNIEnv* env) { - if (peerCredentialsClass != NULL) { - (*env)->DeleteGlobalRef(env, peerCredentialsClass); - peerCredentialsClass = NULL; - } - if (stringClass != NULL) { - (*env)->DeleteGlobalRef(env, stringClass); - stringClass = NULL; - } + NETTY_UNLOAD_CLASS(env, peerCredentialsClass); + NETTY_UNLOAD_CLASS(env, stringClass); } diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_native.c b/transport-native-kqueue/src/main/c/netty_kqueue_native.c index 86f2d18586..9e995ec913 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_native.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_native.c @@ -388,10 +388,8 @@ static jint JNI_OnLoad_netty_transport_native_kqueue0(JavaVM* vm, void* reserved #endif /* NETTY_BUILD_STATIC */ jint ret = netty_kqueue_native_JNI_OnLoad(env, packagePrefix); - if (packagePrefix != NULL) { - free(packagePrefix); - packagePrefix = NULL; - } + // It's safe to call free(...) with a NULL argument as well. + free(packagePrefix); return ret; } diff --git a/transport-native-unix-common/src/main/c/netty_unix_errors.c b/transport-native-unix-common/src/main/c/netty_unix_errors.c index 8298f910b8..634c99bc4a 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_errors.c +++ b/transport-native-unix-common/src/main/c/netty_unix_errors.c @@ -21,6 +21,7 @@ #include "netty_unix_jni.h" #include "netty_unix_util.h" +static jclass oomErrorClass = NULL; static jclass runtimeExceptionClass = NULL; static jclass channelExceptionClass = NULL; static jclass ioExceptionClass = NULL; @@ -43,12 +44,18 @@ void netty_unix_errors_throwRuntimeException(JNIEnv* env, char* message) { void netty_unix_errors_throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { char* allocatedMessage = exceptionMessage(message, errorNumber); + if (allocatedMessage == NULL) { + return; + } (*env)->ThrowNew(env, runtimeExceptionClass, allocatedMessage); free(allocatedMessage); } void netty_unix_errors_throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { char* allocatedMessage = exceptionMessage(message, errorNumber); + if (allocatedMessage == NULL) { + return; + } (*env)->ThrowNew(env, channelExceptionClass, allocatedMessage); free(allocatedMessage); } @@ -63,18 +70,23 @@ void netty_unix_errors_throwPortUnreachableException(JNIEnv* env, char* message) void netty_unix_errors_throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) { char* allocatedMessage = exceptionMessage(message, errorNumber); + if (allocatedMessage == NULL) { + return; + } (*env)->ThrowNew(env, ioExceptionClass, allocatedMessage); free(allocatedMessage); } void netty_unix_errors_throwClosedChannelException(JNIEnv* env) { jobject exception = (*env)->NewObject(env, closedChannelExceptionClass, closedChannelExceptionMethodId); + if (exception == NULL) { + return; + } (*env)->Throw(env, exception); } void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env) { - jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); - (*env)->ThrowNew(env, exceptionClass, ""); + (*env)->ThrowNew(env, oomErrorClass, ""); } // JNI Registered Methods Begin @@ -151,6 +163,7 @@ static const jint statically_referenced_fixed_method_table_size = sizeof(statica // JNI Method Registration Table End jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { + char* nettyClassName = NULL; // We must register the statically referenced methods first! if (netty_unix_util_register_natives(env, packagePrefix, @@ -160,98 +173,33 @@ jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { return JNI_ERR; } - jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException"); - if (localRuntimeExceptionClass == NULL) { - // pending exception... - return JNI_ERR; - } - runtimeExceptionClass = (jclass) (*env)->NewGlobalRef(env, localRuntimeExceptionClass); - if (runtimeExceptionClass == NULL) { - // out-of-memory! - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; - } + NETTY_LOAD_CLASS(env, oomErrorClass, "java/lang/OutOfMemoryError", error); - char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/ChannelException"); - jclass localChannelExceptionClass = (*env)->FindClass(env, nettyClassName); - free(nettyClassName); - nettyClassName = NULL; - if (localChannelExceptionClass == NULL) { - // pending exception... - return JNI_ERR; - } - channelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localChannelExceptionClass); - if (channelExceptionClass == NULL) { - // out-of-memory! - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; - } + NETTY_LOAD_CLASS(env, runtimeExceptionClass, "java/lang/RuntimeException", error); - // cache classes that are used within other jni methods for performance reasons - jclass localClosedChannelExceptionClass = (*env)->FindClass(env, "java/nio/channels/ClosedChannelException"); - if (localClosedChannelExceptionClass == NULL) { - // pending exception... - return JNI_ERR; - } - closedChannelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localClosedChannelExceptionClass); - if (closedChannelExceptionClass == NULL) { - // out-of-memory! - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; - } - closedChannelExceptionMethodId = (*env)->GetMethodID(env, closedChannelExceptionClass, "", "()V"); - if (closedChannelExceptionMethodId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ClosedChannelException.()"); - return JNI_ERR; - } + NETTY_PREPEND(packagePrefix, "io/netty/channel/ChannelException", nettyClassName, error); + NETTY_LOAD_CLASS(env, channelExceptionClass, nettyClassName, error); + netty_unix_util_free_dynamic_name(&nettyClassName); - jclass localIoExceptionClass = (*env)->FindClass(env, "java/io/IOException"); - if (localIoExceptionClass == NULL) { - // pending exception... - return JNI_ERR; - } - ioExceptionClass = (jclass) (*env)->NewGlobalRef(env, localIoExceptionClass); - if (ioExceptionClass == NULL) { - // out-of-memory! - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; - } + NETTY_LOAD_CLASS(env, closedChannelExceptionClass, "java/nio/channels/ClosedChannelException", error); + NETTY_GET_METHOD(env, closedChannelExceptionClass, closedChannelExceptionMethodId, "", "()V", error); - jclass localPortUnreachableExceptionClass = (*env)->FindClass(env, "java/net/PortUnreachableException"); - if (localPortUnreachableExceptionClass == NULL) { - // pending exception... - return JNI_ERR; - } - portUnreachableExceptionClass = (jclass) (*env)->NewGlobalRef(env, localPortUnreachableExceptionClass); - if (portUnreachableExceptionClass == NULL) { - // out-of-memory! - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; - } + NETTY_LOAD_CLASS(env, ioExceptionClass, "java/io/IOException", error); + + NETTY_LOAD_CLASS(env, portUnreachableExceptionClass, "java/net/PortUnreachableException", error); return NETTY_JNI_VERSION; +error: + free(nettyClassName); + return JNI_ERR; } void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) { // delete global references so the GC can collect them - if (runtimeExceptionClass != NULL) { - (*env)->DeleteGlobalRef(env, runtimeExceptionClass); - runtimeExceptionClass = NULL; - } - if (channelExceptionClass != NULL) { - (*env)->DeleteGlobalRef(env, channelExceptionClass); - channelExceptionClass = NULL; - } - if (ioExceptionClass != NULL) { - (*env)->DeleteGlobalRef(env, ioExceptionClass); - ioExceptionClass = NULL; - } - if (portUnreachableExceptionClass != NULL) { - (*env)->DeleteGlobalRef(env, portUnreachableExceptionClass); - portUnreachableExceptionClass = NULL; - } - if (closedChannelExceptionClass != NULL) { - (*env)->DeleteGlobalRef(env, closedChannelExceptionClass); - closedChannelExceptionClass = NULL; - } + NETTY_UNLOAD_CLASS(env, oomErrorClass); + NETTY_UNLOAD_CLASS(env, runtimeExceptionClass); + NETTY_UNLOAD_CLASS(env, channelExceptionClass); + NETTY_UNLOAD_CLASS(env, ioExceptionClass); + NETTY_UNLOAD_CLASS(env, portUnreachableExceptionClass); + NETTY_UNLOAD_CLASS(env, closedChannelExceptionClass); } diff --git a/transport-native-unix-common/src/main/c/netty_unix_filedescriptor.c b/transport-native-unix-common/src/main/c/netty_unix_filedescriptor.c index d13ffb0d1c..7e08bb5153 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_filedescriptor.c +++ b/transport-native-unix-common/src/main/c/netty_unix_filedescriptor.c @@ -276,65 +276,41 @@ static const jint method_table_size = sizeof(method_table) / sizeof(method_table // JNI Method Registration Table End jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { + int ret = JNI_ERR; + void* mem = NULL; if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/unix/FileDescriptor", method_table, method_table_size) != 0) { - return JNI_ERR; + goto done; } - void* mem = malloc(1); - if (mem == NULL) { - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; + if ((mem = malloc(1)) == NULL) { + goto done; } jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1); if (directBuffer == NULL) { - free(mem); - - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; + goto done; } if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) { - free(mem); - - netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address"); - return JNI_ERR; + goto done; } - jclass cls = (*env)->GetObjectClass(env, directBuffer); - + if (cls == NULL) { + goto done; + } + // Get the method id for Buffer.position() and Buffer.limit(). These are used as fallback if // it is not possible to obtain the position and limit using the fields directly. - posId = (*env)->GetMethodID(env, cls, "position", "()I"); - if (posId == NULL) { - free(mem); + NETTY_GET_METHOD(env, cls, posId, "position", "()I", done); + NETTY_GET_METHOD(env, cls, limitId, "limit", "()I", done); - // position method was not found.. something is wrong so bail out - netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ByteBuffer.position()"); - return JNI_ERR; - } - - limitId = (*env)->GetMethodID(env, cls, "limit", "()I"); - if (limitId == NULL) { - free(mem); - - // limit method was not found.. something is wrong so bail out - netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ByteBuffer.limit()"); - return JNI_ERR; - } // Try to get the ids of the position and limit fields. We later then check if we was able // to find them and if so use them get the position and limit of the buffer. This is // much faster then call back into java via (*env)->CallIntMethod(...). - posFieldId = (*env)->GetFieldID(env, cls, "position", "I"); - if (posFieldId == NULL) { - // this is ok as we can still use the method so just clear the exception - (*env)->ExceptionClear(env); - } - limitFieldId = (*env)->GetFieldID(env, cls, "limit", "I"); - if (limitFieldId == NULL) { - // this is ok as we can still use the method so just clear the exception - (*env)->ExceptionClear(env); - } + NETTY_TRY_GET_FIELD(env, cls, posFieldId, "position", "I"); + NETTY_TRY_GET_FIELD(env, cls, limitFieldId, "limit", "I"); + ret = NETTY_JNI_VERSION; +done: free(mem); - return NETTY_JNI_VERSION; + return ret; } void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env) { } diff --git a/transport-native-unix-common/src/main/c/netty_unix_socket.c b/transport-native-unix-common/src/main/c/netty_unix_socket.c index 2f817159b2..83a507fe60 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_socket.c +++ b/transport-native-unix-common/src/main/c/netty_unix_socket.c @@ -85,7 +85,9 @@ static jobject createDatagramSocketAddress(JNIEnv* env, const struct sockaddr_st int scopeId; int ipLength = netty_unix_socket_ipAddressLength(addr); jbyteArray addressBytes = (*env)->NewByteArray(env, ipLength); - + if (addressBytes == NULL) { + return NULL; + } if (addr->ss_family == AF_INET) { struct sockaddr_in* s = (struct sockaddr_in*) addr; port = ntohs(s->sin_port); @@ -174,7 +176,9 @@ static void initInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storag static jbyteArray createInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr) { jsize len = addressLength(addr); jbyteArray bArray = (*env)->NewByteArray(env, len); - + if (bArray == NULL) { + return NULL; + } initInetSocketAddressArray(env, addr, bArray, 0, len); return bArray; } @@ -1009,128 +1013,87 @@ static jint dynamicMethodsTableSize() { } static JNINativeMethod* createDynamicMethodsTable(const char* packagePrefix) { - JNINativeMethod* dynamicMethods = malloc(sizeof(JNINativeMethod) * dynamicMethodsTableSize()); + char* dynamicTypeName = NULL; + size_t size = sizeof(JNINativeMethod) * dynamicMethodsTableSize(); + JNINativeMethod* dynamicMethods = malloc(size); + if (dynamicMethods == NULL) { + return NULL; + } + memset(dynamicMethods, 0, size); memcpy(dynamicMethods, fixed_method_table, sizeof(fixed_method_table)); - char* dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;"); + JNINativeMethod* dynamicMethod = &dynamicMethods[fixed_method_table_size]; + NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;", dynamicTypeName, error); + NETTY_PREPEND("(ILjava/nio/ByteBuffer;II)L", dynamicTypeName, dynamicMethod->signature, error); dynamicMethod->name = "recvFrom"; - dynamicMethod->signature = netty_unix_util_prepend("(ILjava/nio/ByteBuffer;II)L", dynamicTypeName); dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFrom; - free(dynamicTypeName); + netty_unix_util_free_dynamic_name(&dynamicTypeName); ++dynamicMethod; - dynamicTypeName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;"); + NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress;", dynamicTypeName, error); + NETTY_PREPEND("(IJII)L", dynamicTypeName, dynamicMethod->signature, error); dynamicMethod->name = "recvFromAddress"; - dynamicMethod->signature = netty_unix_util_prepend("(IJII)L", dynamicTypeName); dynamicMethod->fnPtr = (void *) netty_unix_socket_recvFromAddress; - free(dynamicTypeName); + netty_unix_util_free_dynamic_name(&dynamicTypeName); return dynamicMethods; +error: + free(dynamicTypeName); + netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize()); + return NULL; } -static void freeDynamicMethodsTable(JNINativeMethod* dynamicMethods) { - jint fullMethodTableSize = dynamicMethodsTableSize(); - jint i = fixed_method_table_size; - for (; i < fullMethodTableSize; ++i) { - free(dynamicMethods[i].signature); - } - free(dynamicMethods); -} // JNI Method Registration Table End jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { + int ret = JNI_ERR; + char* nettyClassName = NULL; + void* mem = NULL; JNINativeMethod* dynamicMethods = createDynamicMethodsTable(packagePrefix); + if (dynamicMethods == NULL) { + goto done; + } if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/unix/Socket", dynamicMethods, dynamicMethodsTableSize()) != 0) { - freeDynamicMethodsTable(dynamicMethods); - return JNI_ERR; - } - freeDynamicMethodsTable(dynamicMethods); - dynamicMethods = NULL; - char* nettyClassName = netty_unix_util_prepend(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress"); - jclass localDatagramSocketAddressClass = (*env)->FindClass(env, nettyClassName); - if (localDatagramSocketAddressClass == NULL) { - free(nettyClassName); - nettyClassName = NULL; - // pending exception... - return JNI_ERR; - } - datagramSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localDatagramSocketAddressClass); - if (datagramSocketAddressClass == NULL) { - free(nettyClassName); - nettyClassName = NULL; - // out-of-memory! - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; + goto done; } + + NETTY_PREPEND(packagePrefix, "io/netty/channel/unix/DatagramSocketAddress", nettyClassName, done); + NETTY_LOAD_CLASS(env, datagramSocketAddressClass, nettyClassName, done); // Respect shading... char parameters[1024] = {0}; snprintf(parameters, sizeof(parameters), "([BIIIL%s;)V", nettyClassName); + netty_unix_util_free_dynamic_name(&nettyClassName); + NETTY_GET_METHOD(env, datagramSocketAddressClass, datagramSocketAddrMethodId, "", parameters, done); - datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "", parameters); - if (datagramSocketAddrMethodId == NULL) { - char msg[1024] = {0}; - snprintf(msg, sizeof(msg), "failed to get method ID: %s.(byte[], int, int, int, %s)", nettyClassName, nettyClassName); - free(nettyClassName); - nettyClassName = NULL; - netty_unix_errors_throwRuntimeException(env, msg); - return JNI_ERR; + NETTY_LOAD_CLASS(env, inetSocketAddressClass, "java/net/InetSocketAddress", done); + NETTY_GET_METHOD(env, inetSocketAddressClass, inetSocketAddrMethodId, "", "(Ljava/lang/String;I)V", done); + + if ((mem = malloc(1)) == NULL) { + goto done; } - free(nettyClassName); - nettyClassName = NULL; - - jclass localInetSocketAddressClass = (*env)->FindClass(env, "java/net/InetSocketAddress"); - if (localInetSocketAddressClass == NULL) { - // pending exception... - return JNI_ERR; - } - inetSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localInetSocketAddressClass); - if (inetSocketAddressClass == NULL) { - // out-of-memory! - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; - } - inetSocketAddrMethodId = (*env)->GetMethodID(env, inetSocketAddressClass, "", "(Ljava/lang/String;I)V"); - if (inetSocketAddrMethodId == NULL) { - netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.(String, int)"); - return JNI_ERR; - } - - void* mem = malloc(1); - if (mem == NULL) { - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; - } jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1); if (directBuffer == NULL) { - free(mem); - - netty_unix_errors_throwOutOfMemoryError(env); - return JNI_ERR; + goto done; } if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) { - free(mem); - - netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address"); - return JNI_ERR; + goto done; } - free(mem); - return NETTY_JNI_VERSION; + ret = NETTY_JNI_VERSION; +done: + netty_unix_util_free_dynamic_methods_table(dynamicMethods, fixed_method_table_size, dynamicMethodsTableSize()); + free(nettyClassName); + free(mem); + return ret; } void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) { - if (datagramSocketAddressClass != NULL) { - (*env)->DeleteGlobalRef(env, datagramSocketAddressClass); - datagramSocketAddressClass = NULL; - } - if (inetSocketAddressClass != NULL) { - (*env)->DeleteGlobalRef(env, inetSocketAddressClass); - inetSocketAddressClass = NULL; - } + NETTY_UNLOAD_CLASS(env, datagramSocketAddressClass); + NETTY_UNLOAD_CLASS(env, inetSocketAddressClass); } diff --git a/transport-native-unix-common/src/main/c/netty_unix_util.c b/transport-native-unix-common/src/main/c/netty_unix_util.c index 7ff91c5a94..3d7bda871d 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_util.c +++ b/transport-native-unix-common/src/main/c/netty_unix_util.c @@ -29,12 +29,17 @@ static const uint64_t NETTY_BILLION = 1000000000L; #endif /* NETTY_USE_MACH_INSTEAD_OF_CLOCK */ char* netty_unix_util_prepend(const char* prefix, const char* str) { + char* result = NULL; if (prefix == NULL) { - char* result = (char*) malloc(sizeof(char) * (strlen(str) + 1)); + if ((result = (char*) malloc(sizeof(char) * (strlen(str) + 1))) == NULL) { + return NULL; + } strcpy(result, str); return result; } - char* result = (char*) malloc(sizeof(char) * (strlen(prefix) + strlen(str) + 1)); + if ((result = (char*) malloc(sizeof(char) * (strlen(prefix) + strlen(str) + 1))) == NULL) { + return NULL; + } strcpy(result, prefix); strcat(result, str); return result; @@ -82,7 +87,10 @@ char* netty_unix_util_parse_package_prefix(const char* libraryPathName, const ch // packagePrefix length is > 0 // Make a copy so we can modify the value without impacting libraryPathName. size_t packagePrefixLen = packageNameEnd - packagePrefix; - packagePrefix = strndup(packagePrefix, packagePrefixLen); + if ((packagePrefix = strndup(packagePrefix, packagePrefixLen)) == NULL) { + *status = JNI_ERR; + return NULL; + } // Make sure the packagePrefix is in the correct format for the JNI functions it will be used with. char* temp = packagePrefix; packageNameEnd = packagePrefix + packagePrefixLen; @@ -95,7 +103,9 @@ char* netty_unix_util_parse_package_prefix(const char* libraryPathName, const ch // Make sure packagePrefix is terminated with the '/' JNI package separator. if(*(--temp) != '/') { temp = packagePrefix; - packagePrefix = netty_unix_util_prepend(packagePrefix, "/"); + if ((packagePrefix = netty_unix_util_prepend(packagePrefix, "/")) == NULL) { + *status = JNI_ERR; + } free(temp); } return packagePrefix; @@ -185,13 +195,34 @@ jboolean netty_unix_util_initialize_wait_clock(clockid_t* clockId) { } jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods) { - char* nettyClassName = netty_unix_util_prepend(packagePrefix, className); + char* nettyClassName = NULL; + int ret = JNI_ERR; + NETTY_PREPEND(packagePrefix, className, nettyClassName, done); + jclass nativeCls = (*env)->FindClass(env, nettyClassName); - free(nettyClassName); - nettyClassName = NULL; if (nativeCls == NULL) { - return JNI_ERR; + goto done; } - return (*env)->RegisterNatives(env, nativeCls, methods, numMethods); + ret = (*env)->RegisterNatives(env, nativeCls, methods, numMethods); +done: + free(nettyClassName); + return ret; +} + +void netty_unix_util_free_dynamic_methods_table(JNINativeMethod* dynamicMethods, jint fixedMethodTableSize, jint fullMethodTableSize) { + if (dynamicMethods != NULL) { + jint i = fixedMethodTableSize; + for (; i < fullMethodTableSize; ++i) { + free(dynamicMethods[i].signature); + } + free(dynamicMethods); + } +} + +void netty_unix_util_free_dynamic_name(char** dynamicName) { + if (dynamicName != NULL && *dynamicName != NULL) { + free(*dynamicName); + *dynamicName = NULL; + } } diff --git a/transport-native-unix-common/src/main/c/netty_unix_util.h b/transport-native-unix-common/src/main/c/netty_unix_util.h index aba672dc5f..5aa9e7f15e 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_util.h +++ b/transport-native-unix-common/src/main/c/netty_unix_util.h @@ -36,6 +36,72 @@ typedef int clockid_t; #endif /* __MACH__ */ + +#define NETTY_BEGIN_MACRO if (1) { +#define NETTY_END_MACRO } else (void)(0) + +#define NETTY_FIND_CLASS(E, C, N, R) \ + NETTY_BEGIN_MACRO \ + C = (*(E))->FindClass((E), N); \ + if (C == NULL) { \ + (*(E))->ExceptionClear((E)); \ + goto R; \ + } \ + NETTY_END_MACRO + +#define NETTY_LOAD_CLASS(E, C, N, R) \ + NETTY_BEGIN_MACRO \ + jclass _##C = (*(E))->FindClass((E), N); \ + if (_##C == NULL) { \ + (*(E))->ExceptionClear((E)); \ + goto R; \ + } \ + C = (*(E))->NewGlobalRef((E), _##C); \ + (*(E))->DeleteLocalRef((E), _##C); \ + if (C == NULL) { \ + goto R; \ + } \ + NETTY_END_MACRO + +#define NETTY_UNLOAD_CLASS(E, C) \ + NETTY_BEGIN_MACRO \ + if (C != NULL) { \ + (*(E))->DeleteGlobalRef((E), (C)); \ + C = NULL; \ + } \ + NETTY_END_MACRO + + +#define NETTY_GET_METHOD(E, C, M, N, S, R) \ + NETTY_BEGIN_MACRO \ + M = (*(E))->GetMethodID((E), C, N, S); \ + if (M == NULL) { \ + goto R; \ + } \ + NETTY_END_MACRO + +#define NETTY_GET_FIELD(E, C, F, N, S, R) \ + NETTY_BEGIN_MACRO \ + F = (*(E))->GetFieldID((E), C, N, S); \ + if (F == NULL) { \ + goto R; \ + } \ + NETTY_END_MACRO + +#define NETTY_TRY_GET_FIELD(E, C, F, N, S) \ + NETTY_BEGIN_MACRO \ + F = (*(E))->GetFieldID((E), C, N, S); \ + if (F == NULL) { \ + (*(E))->ExceptionClear((E)); \ + } \ + NETTY_END_MACRO + +#define NETTY_PREPEND(P, S, N, R) \ + NETTY_BEGIN_MACRO \ + if ((N = netty_unix_util_prepend(P, S)) == NULL) { \ + goto R; \ + } \ + NETTY_END_MACRO /** * Return a new string (caller must free this string) which is equivalent to
prefix + str
. * @@ -84,4 +150,7 @@ jboolean netty_unix_util_timespec_subtract_ns(struct timespec* ts, uint64_t nano */ jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods); +void netty_unix_util_free_dynamic_methods_table(JNINativeMethod* dynamicMethods, jint fixedMethodTableSize, jint fullMethodTableSize); +void netty_unix_util_free_dynamic_name(char** dynamicName); + #endif /* NETTY_UNIX_UTIL_H_ */