From 03aafb9cff352269a7fc73e4cf0c281770676be9 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Mon, 26 Oct 2020 14:15:04 +0100 Subject: [PATCH] =?UTF-8?q?Unregister=20all=20previous=20registered=20nati?= =?UTF-8?q?ve=20methods=20if=20loading=20of=20native=20code=20fails?= =?UTF-8?q?=E2=80=A6=20(#10719)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Motivation: It's important to unload all previous registered native methods when there is a failure during loading the native lib. Failing to do so may lead to an "invalid state" and so may segfault the JVM when trying to call a native method that was previous loaded. This was observed when two versions of netty-tcnative were on the classpath which had different requirements in terms of linking. Something like this was reported in he hs log: ``` Instructions: (pc=0x0000000116413bf0) 0x0000000116413bd0: [error occurred during error reporting (printing registers, top of stack, instructions near pc), id 0xb] Register to memory mapping: RAX=0x0000000116413bf0 is an unknown value RBX={method} {0x000000011422e708} 'aprMajorVersion' '()I' in 'io/netty/internal/tcnative/Library' RCX=0x000000000000000a is an unknown value RDX=0x000000000000000a is an unknown value ``` Modifications: - Unregister previous registered native methods on failure - Unregister previous registered native methods on on unload of the native lib Result: No more segfault caused by invalid state when loading of the native lib fails in between. In this case the user will receive an error now like: --- .../src/main/c/netty_epoll_linuxsocket.c | 8 ++- .../src/main/c/netty_epoll_linuxsocket.h | 2 +- .../src/main/c/netty_epoll_native.c | 61 ++++++++++++------ .../src/main/c/netty_kqueue_bsdsocket.c | 8 ++- .../src/main/c/netty_kqueue_bsdsocket.h | 2 +- .../src/main/c/netty_kqueue_eventarray.c | 7 +- .../src/main/c/netty_kqueue_eventarray.h | 2 +- .../src/main/c/netty_kqueue_native.c | 64 +++++++++++++------ .../src/main/c/netty_unix_buffer.c | 8 ++- .../src/main/c/netty_unix_buffer.h | 2 +- .../src/main/c/netty_unix_errors.c | 8 ++- .../src/main/c/netty_unix_errors.h | 2 +- .../src/main/c/netty_unix_filedescriptor.c | 8 ++- .../src/main/c/netty_unix_filedescriptor.h | 2 +- .../src/main/c/netty_unix_limits.c | 8 ++- .../src/main/c/netty_unix_limits.h | 2 +- .../src/main/c/netty_unix_socket.c | 7 +- .../src/main/c/netty_unix_socket.h | 2 +- .../src/main/c/netty_unix_util.c | 16 +++++ .../src/main/c/netty_unix_util.h | 1 + 20 files changed, 157 insertions(+), 63 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 119ccef1f9..90ada5ddf3 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c +++ b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.c @@ -35,6 +35,8 @@ #include "netty_unix_socket.h" #include "netty_unix_util.h" +#define LINUXSOCKET_CLASSNAME "io/netty/channel/epoll/LinuxSocket" + // TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile. #ifndef TCP_FASTOPEN #define TCP_FASTOPEN 23 @@ -755,7 +757,7 @@ jint netty_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) } if (netty_unix_util_register_natives(env, packagePrefix, - "io/netty/channel/epoll/LinuxSocket", + LINUXSOCKET_CLASSNAME, dynamicMethods, dynamicMethodsTableSize()) != 0) { goto done; @@ -788,6 +790,8 @@ done: return ret; } -void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env) { +void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) { NETTY_UNLOAD_CLASS(env, peerCredentialsClass); + + netty_unix_util_unregister_natives(env, packagePrefix, LINUXSOCKET_CLASSNAME); } diff --git a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.h b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.h index 050a323c24..b4cb301993 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.h +++ b/transport-native-epoll/src/main/c/netty_epoll_linuxsocket.h @@ -21,6 +21,6 @@ // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. jint netty_epoll_linuxsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix); -void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env); +void netty_epoll_linuxsocket_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix); #endif 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 f4cca723d4..594713e911 100644 --- a/transport-native-epoll/src/main/c/netty_epoll_native.c +++ b/transport-native-epoll/src/main/c/netty_epoll_native.c @@ -49,6 +49,9 @@ #include "netty_unix_socket.h" #include "netty_unix_util.h" +#define STATICALLY_CLASSNAME "io/netty/channel/epoll/NativeStaticallyReferencedJniMethods" +#define NATIVE_CLASSNAME "io/netty/channel/epoll/Native" + // TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile. #ifndef TCP_FASTOPEN #define TCP_FASTOPEN 23 @@ -98,6 +101,7 @@ static jfieldID packetScopeIdFieldId = NULL; static jfieldID packetPortFieldId = NULL; static jfieldID packetMemoryAddressFieldId = NULL; static jfieldID packetCountFieldId = NULL; +static char* staticPackagePrefix = NULL; // util methods static int getSysctlValue(const char * property, int* returnValue) { @@ -591,6 +595,8 @@ error: static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { int ret = JNI_ERR; + int staticallyRegistered = 0; + int nativeRegistered = 0; int limitsOnLoadCalled = 0; int errorsOnLoadCalled = 0; int filedescriptorOnLoadCalled = 0; @@ -604,11 +610,13 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix // We must register the statically referenced methods first! if (netty_unix_util_register_natives(env, packagePrefix, - "io/netty/channel/epoll/NativeStaticallyReferencedJniMethods", + STATICALLY_CLASSNAME, statically_referenced_fixed_method_table, statically_referenced_fixed_method_table_size) != 0) { goto done; } + staticallyRegistered = 1; + // Register the methods which are not referenced by static member variables dynamicMethods = createDynamicMethodsTable(packagePrefix); if (dynamicMethods == NULL) { @@ -617,11 +625,13 @@ static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix if (netty_unix_util_register_natives(env, packagePrefix, - "io/netty/channel/epoll/Native", + NATIVE_CLASSNAME, dynamicMethods, dynamicMethodsTableSize()) != 0) { goto done; } + nativeRegistered = 1; + // Load all c modules that we depend upon if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { goto done; @@ -672,23 +682,29 @@ done: free(nettyClassName); if (ret == JNI_ERR) { + if (staticallyRegistered == 1) { + netty_unix_util_unregister_natives(env, packagePrefix, STATICALLY_CLASSNAME); + } + if (nativeRegistered == 1) { + netty_unix_util_unregister_natives(env, packagePrefix, NATIVE_CLASSNAME); + } if (limitsOnLoadCalled == 1) { - netty_unix_limits_JNI_OnUnLoad(env); + netty_unix_limits_JNI_OnUnLoad(env, packagePrefix); } if (errorsOnLoadCalled == 1) { - netty_unix_errors_JNI_OnUnLoad(env); + netty_unix_errors_JNI_OnUnLoad(env, packagePrefix); } if (filedescriptorOnLoadCalled == 1) { - netty_unix_filedescriptor_JNI_OnUnLoad(env); + netty_unix_filedescriptor_JNI_OnUnLoad(env, packagePrefix); } if (socketOnLoadCalled == 1) { - netty_unix_socket_JNI_OnUnLoad(env); + netty_unix_socket_JNI_OnUnLoad(env, packagePrefix); } if (bufferOnLoadCalled == 1) { - netty_unix_buffer_JNI_OnUnLoad(env); + netty_unix_buffer_JNI_OnUnLoad(env, packagePrefix); } if (linuxsocketOnLoadCalled == 1) { - netty_epoll_linuxsocket_JNI_OnUnLoad(env); + netty_epoll_linuxsocket_JNI_OnUnLoad(env, packagePrefix); } packetAddrFieldId = NULL; packetAddrLenFieldId = NULL; @@ -700,13 +716,13 @@ done: return ret; } -static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) { - netty_unix_limits_JNI_OnUnLoad(env); - netty_unix_errors_JNI_OnUnLoad(env); - netty_unix_filedescriptor_JNI_OnUnLoad(env); - netty_unix_socket_JNI_OnUnLoad(env); - netty_unix_buffer_JNI_OnUnLoad(env); - netty_epoll_linuxsocket_JNI_OnUnLoad(env); +static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) { + netty_unix_limits_JNI_OnUnLoad(env, packagePrefix); + netty_unix_errors_JNI_OnUnLoad(env, packagePrefix); + netty_unix_filedescriptor_JNI_OnUnLoad(env, packagePrefix); + netty_unix_socket_JNI_OnUnLoad(env, packagePrefix); + netty_unix_buffer_JNI_OnUnLoad(env, packagePrefix); + netty_epoll_linuxsocket_JNI_OnUnLoad(env, packagePrefix); packetAddrFieldId = NULL; packetAddrLenFieldId = NULL; @@ -714,6 +730,9 @@ static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) { packetPortFieldId = NULL; packetMemoryAddressFieldId = NULL; packetCountFieldId = NULL; + + netty_unix_util_unregister_natives(env, packagePrefix, STATICALLY_CLASSNAME); + netty_unix_util_unregister_natives(env, packagePrefix, NATIVE_CLASSNAME); } // Invoked by the JVM when statically linked @@ -739,8 +758,12 @@ 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); - - free(packagePrefix); + if (ret == JNI_ERR) { + free(packagePrefix); + staticPackagePrefix = NULL; + } else { + staticPackagePrefix = packagePrefix; + } return ret; } @@ -750,7 +773,9 @@ static void JNI_OnUnload_netty_transport_native_epoll0(JavaVM* vm, void* reserve // Something is wrong but nothing we can do about this :( return; } - netty_epoll_native_JNI_OnUnLoad(env); + netty_epoll_native_JNI_OnUnLoad(env, staticPackagePrefix); + free(staticPackagePrefix); + staticPackagePrefix = NULL; } // We build with -fvisibility=hidden so ensure we mark everything that needs to be visible with JNIEXPORT 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 0972440184..3aa670893a 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.c @@ -32,6 +32,8 @@ #include "netty_unix_socket.h" #include "netty_unix_util.h" +#define BSDSOCKET_CLASSNAME "io/netty/channel/kqueue/BsdSocket" + // Those are initialized in the init(...) method and cached for performance reasons static jclass stringClass = NULL; static jclass peerCredentialsClass = NULL; @@ -249,7 +251,7 @@ jint netty_kqueue_bsdsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { } if (netty_unix_util_register_natives(env, packagePrefix, - "io/netty/channel/kqueue/BsdSocket", + BSDSOCKET_CLASSNAME, dynamicMethods, dynamicMethodsTableSize()) != 0) { goto done; @@ -284,7 +286,9 @@ done: return ret; } -void netty_kqueue_bsdsocket_JNI_OnUnLoad(JNIEnv* env) { +void netty_kqueue_bsdsocket_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) { NETTY_UNLOAD_CLASS(env, peerCredentialsClass); NETTY_UNLOAD_CLASS(env, stringClass); + + netty_unix_util_unregister_natives(env, packagePrefix, BSDSOCKET_CLASSNAME); } diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.h b/transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.h index 4641a9b0a7..adc1313e39 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.h +++ b/transport-native-kqueue/src/main/c/netty_kqueue_bsdsocket.h @@ -20,6 +20,6 @@ // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. jint netty_kqueue_bsdsocket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix); -void netty_kqueue_bsdsocket_JNI_OnUnLoad(JNIEnv* env); +void netty_kqueue_bsdsocket_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix); #endif /* NETTY_KQUEUE_BSDSOCKET_H_ */ diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c b/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c index eec680062b..63010a65f0 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.c @@ -24,6 +24,8 @@ #include "netty_unix_jni.h" #include "netty_unix_util.h" +#define EVENT_ARRAY_CLASSNAME "io/netty/channel/kqueue/KQueueEventArray" + static void netty_kqueue_eventarray_evSet(JNIEnv* env, jclass clzz, jlong keventAddress, jint ident, jshort filter, jshort flags, jint fflags) { EV_SET((struct kevent*) keventAddress, ident, filter, flags, fflags, 0, NULL); } @@ -39,7 +41,7 @@ static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof( jint netty_kqueue_eventarray_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { if (netty_unix_util_register_natives(env, packagePrefix, - "io/netty/channel/kqueue/KQueueEventArray", + EVENT_ARRAY_CLASSNAME, fixed_method_table, fixed_method_table_size) != 0) { return JNI_ERR; @@ -47,5 +49,6 @@ jint netty_kqueue_eventarray_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) return NETTY_JNI_VERSION; } -void netty_kqueue_eventarray_JNI_OnUnLoad(JNIEnv* env) { +void netty_kqueue_eventarray_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) { + netty_unix_util_unregister_natives(env, packagePrefix, EVENT_ARRAY_CLASSNAME); } diff --git a/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.h b/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.h index 04cf22fee0..2d7ae3a5a1 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.h +++ b/transport-native-kqueue/src/main/c/netty_kqueue_eventarray.h @@ -20,6 +20,6 @@ // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. jint netty_kqueue_eventarray_JNI_OnLoad(JNIEnv* env, const char* packagePrefix); -void netty_kqueue_eventarray_JNI_OnUnLoad(JNIEnv* env); +void netty_kqueue_eventarray_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix); #endif /* NETTY_KQUEUE_EVENTARRAY_H_ */ 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 0a04412fc2..f9722148f3 100644 --- a/transport-native-kqueue/src/main/c/netty_kqueue_native.c +++ b/transport-native-kqueue/src/main/c/netty_kqueue_native.c @@ -36,6 +36,9 @@ #include "netty_unix_socket.h" #include "netty_unix_util.h" +#define STATICALLY_CLASSNAME "io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods" +#define NATIVE_CLASSNAME "io/netty/channel/kqueue/Native" + // Currently only macOS supports EVFILT_SOCK, and it is currently only available in internal APIs. // To make compiling easier we redefine the values here if they are not present. #ifdef __APPLE__ @@ -68,6 +71,8 @@ static clockid_t waitClockId = 0; // initialized by netty_unix_util_initialize_wait_clock +static char* staticPackagePrefix; + static jint netty_kqueue_native_kqueueCreate(JNIEnv* env, jclass clazz) { jint kq = kqueue(); if (kq < 0) { @@ -265,6 +270,8 @@ static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof( // JNI Method Registration Table End static jint netty_kqueue_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { + int staticallyRegistered = 0; + int nativeRegistered = 0; int limitsOnLoadCalled = 0; int errorsOnLoadCalled = 0; int filedescriptorOnLoadCalled = 0; @@ -276,15 +283,19 @@ static jint netty_kqueue_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefi // We must register the statically referenced methods first! if (netty_unix_util_register_natives(env, packagePrefix, - "io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods", + STATICALLY_CLASSNAME, statically_referenced_fixed_method_table, statically_referenced_fixed_method_table_size) != 0) { goto error; } + staticallyRegistered = 1; + // Register the methods which are not referenced by static member variables - if (netty_unix_util_register_natives(env, packagePrefix, "io/netty/channel/kqueue/Native", fixed_method_table, fixed_method_table_size) != 0) { + if (netty_unix_util_register_natives(env, packagePrefix, NATIVE_CLASSNAME, fixed_method_table, fixed_method_table_size) != 0) { goto error; } + nativeRegistered = 1; + // Load all c modules that we depend upon if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { goto error; @@ -330,38 +341,44 @@ static jint netty_kqueue_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefi return NETTY_JNI_VERSION; error: + if (staticallyRegistered == 1) { + netty_unix_util_unregister_natives(env, packagePrefix, STATICALLY_CLASSNAME); + } + if (nativeRegistered == 1) { + netty_unix_util_unregister_natives(env, packagePrefix, NATIVE_CLASSNAME); + } if (limitsOnLoadCalled == 1) { - netty_unix_limits_JNI_OnUnLoad(env); + netty_unix_limits_JNI_OnUnLoad(env, packagePrefix); } if (errorsOnLoadCalled == 1) { - netty_unix_errors_JNI_OnUnLoad(env); + netty_unix_errors_JNI_OnUnLoad(env, packagePrefix); } if (filedescriptorOnLoadCalled == 1) { - netty_unix_filedescriptor_JNI_OnUnLoad(env); + netty_unix_filedescriptor_JNI_OnUnLoad(env, packagePrefix); } if (socketOnLoadCalled == 1) { - netty_unix_socket_JNI_OnUnLoad(env); + netty_unix_socket_JNI_OnUnLoad(env, packagePrefix); } if (bufferOnLoadCalled == 1) { - netty_unix_buffer_JNI_OnUnLoad(env); + netty_unix_buffer_JNI_OnUnLoad(env, packagePrefix); } if (bsdsocketOnLoadCalled == 1) { - netty_kqueue_bsdsocket_JNI_OnUnLoad(env); + netty_kqueue_bsdsocket_JNI_OnUnLoad(env, packagePrefix); } if (eventarrayOnLoadCalled == 1) { - netty_kqueue_eventarray_JNI_OnUnLoad(env); + netty_kqueue_eventarray_JNI_OnUnLoad(env, packagePrefix); } return JNI_ERR; } -static void netty_kqueue_native_JNI_OnUnLoad(JNIEnv* env) { - netty_unix_limits_JNI_OnUnLoad(env); - netty_unix_errors_JNI_OnUnLoad(env); - netty_unix_filedescriptor_JNI_OnUnLoad(env); - netty_unix_socket_JNI_OnUnLoad(env); - netty_unix_buffer_JNI_OnUnLoad(env); - netty_kqueue_bsdsocket_JNI_OnUnLoad(env); - netty_kqueue_eventarray_JNI_OnUnLoad(env); +static void netty_kqueue_native_JNI_OnUnLoad(JNIEnv* env, const char* staticPackagePrefix) { + netty_unix_limits_JNI_OnUnLoad(env, staticPackagePrefix); + netty_unix_errors_JNI_OnUnLoad(env, staticPackagePrefix); + netty_unix_filedescriptor_JNI_OnUnLoad(env, staticPackagePrefix); + netty_unix_socket_JNI_OnUnLoad(env, staticPackagePrefix); + netty_unix_buffer_JNI_OnUnLoad(env, staticPackagePrefix); + netty_kqueue_bsdsocket_JNI_OnUnLoad(env, staticPackagePrefix); + netty_kqueue_eventarray_JNI_OnUnLoad(env, staticPackagePrefix); } static jint JNI_OnLoad_netty_transport_native_kqueue0(JavaVM* vm, void* reserved) { @@ -387,9 +404,12 @@ 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); - - // It's safe to call free(...) with a NULL argument as well. - free(packagePrefix); + if (ret == JNI_ERR) { + free(packagePrefix); + staticPackagePrefix = NULL; + } else { + staticPackagePrefix = packagePrefix; + } return ret; } @@ -400,7 +420,9 @@ static void JNI_OnUnload_netty_transport_native_kqueue0(JavaVM* vm, void* reserv // Something is wrong but nothing we can do about this :( return; } - netty_kqueue_native_JNI_OnUnLoad(env); + netty_kqueue_native_JNI_OnUnLoad(env, staticPackagePrefix); + free(staticPackagePrefix); + staticPackagePrefix = NULL; } // We build with -fvisibility=hidden so ensure we mark everything that needs to be visible with JNIEXPORT diff --git a/transport-native-unix-common/src/main/c/netty_unix_buffer.c b/transport-native-unix-common/src/main/c/netty_unix_buffer.c index d5a82d2239..809db9246f 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_buffer.c +++ b/transport-native-unix-common/src/main/c/netty_unix_buffer.c @@ -17,6 +17,8 @@ #include "netty_unix_util.h" #include "netty_unix_buffer.h" +#define BUFFER_CLASSNAME "io/netty/channel/unix/Buffer" + // JNI Registered Methods Begin static jlong netty_unix_buffer_memoryAddress0(JNIEnv* env, jclass clazz, jobject buffer) { return (jlong) (*env)->GetDirectBufferAddress(env, buffer); @@ -40,7 +42,7 @@ jint netty_unix_buffer_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { // We must register the statically referenced methods first! if (netty_unix_util_register_natives(env, packagePrefix, - "io/netty/channel/unix/Buffer", + BUFFER_CLASSNAME, statically_referenced_fixed_method_table, statically_referenced_fixed_method_table_size) != 0) { return JNI_ERR; @@ -49,4 +51,6 @@ jint netty_unix_buffer_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { return NETTY_JNI_VERSION; } -void netty_unix_buffer_JNI_OnUnLoad(JNIEnv* env) { } +void netty_unix_buffer_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) { + netty_unix_util_unregister_natives(env, packagePrefix, BUFFER_CLASSNAME); +} diff --git a/transport-native-unix-common/src/main/c/netty_unix_buffer.h b/transport-native-unix-common/src/main/c/netty_unix_buffer.h index a9e96eb62d..6444f5f590 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_buffer.h +++ b/transport-native-unix-common/src/main/c/netty_unix_buffer.h @@ -20,6 +20,6 @@ // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. jint netty_unix_buffer_JNI_OnLoad(JNIEnv* env, const char* packagePrefix); -void netty_unix_buffer_JNI_OnUnLoad(JNIEnv* env); +void netty_unix_buffer_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix); #endif /* NETTY_UNIX_BUFFER_H_ */ 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 0c181355d4..17f1a5cf18 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,8 @@ #include "netty_unix_jni.h" #include "netty_unix_util.h" +#define ERRORS_CLASSNAME "io/netty/channel/unix/ErrorsStaticallyReferencedJniMethods" + static jclass oomErrorClass = NULL; static jclass runtimeExceptionClass = NULL; static jclass channelExceptionClass = NULL; @@ -214,7 +216,7 @@ jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { // We must register the statically referenced methods first! if (netty_unix_util_register_natives(env, packagePrefix, - "io/netty/channel/unix/ErrorsStaticallyReferencedJniMethods", + ERRORS_CLASSNAME, statically_referenced_fixed_method_table, statically_referenced_fixed_method_table_size) != 0) { return JNI_ERR; @@ -241,7 +243,7 @@ error: return JNI_ERR; } -void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) { +void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) { // delete global references so the GC can collect them NETTY_UNLOAD_CLASS(env, oomErrorClass); NETTY_UNLOAD_CLASS(env, runtimeExceptionClass); @@ -249,4 +251,6 @@ void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) { NETTY_UNLOAD_CLASS(env, ioExceptionClass); NETTY_UNLOAD_CLASS(env, portUnreachableExceptionClass); NETTY_UNLOAD_CLASS(env, closedChannelExceptionClass); + + netty_unix_util_unregister_natives(env, packagePrefix, ERRORS_CLASSNAME); } diff --git a/transport-native-unix-common/src/main/c/netty_unix_errors.h b/transport-native-unix-common/src/main/c/netty_unix_errors.h index e4642bf490..8ea8e4c672 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_errors.h +++ b/transport-native-unix-common/src/main/c/netty_unix_errors.h @@ -29,6 +29,6 @@ void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env); // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. jint netty_unix_errors_JNI_OnLoad(JNIEnv* env, const char* packagePrefix); -void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env); +void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix); #endif /* NETTY_UNIX_ERRORS_H_ */ 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 bafe3c914c..8b505489eb 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 @@ -26,6 +26,8 @@ #include "netty_unix_jni.h" #include "netty_unix_util.h" +#define FILEDESCRIPTOR_CLASSNAME "io/netty/channel/unix/FileDescriptor" + static jmethodID posId = NULL; static jmethodID limitId = NULL; static jfieldID posFieldId = NULL; @@ -278,7 +280,7 @@ static const jint method_table_size = sizeof(method_table) / sizeof(method_table 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) { + if (netty_unix_util_register_natives(env, packagePrefix, FILEDESCRIPTOR_CLASSNAME, method_table, method_table_size) != 0) { goto done; } if ((mem = malloc(1)) == NULL) { @@ -313,4 +315,6 @@ done: return ret; } -void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env) { } +void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) { + netty_unix_util_unregister_natives(env, packagePrefix, FILEDESCRIPTOR_CLASSNAME); +} diff --git a/transport-native-unix-common/src/main/c/netty_unix_filedescriptor.h b/transport-native-unix-common/src/main/c/netty_unix_filedescriptor.h index 502dd373f1..8c83e52b8d 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_filedescriptor.h +++ b/transport-native-unix-common/src/main/c/netty_unix_filedescriptor.h @@ -20,6 +20,6 @@ // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* packagePrefix); -void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env); +void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix); #endif /* NETTY_UNIX_FILEDESCRIPTOR_H_ */ diff --git a/transport-native-unix-common/src/main/c/netty_unix_limits.c b/transport-native-unix-common/src/main/c/netty_unix_limits.c index a4851d08a7..550f766a15 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_limits.c +++ b/transport-native-unix-common/src/main/c/netty_unix_limits.c @@ -21,6 +21,8 @@ #include "netty_unix_limits.h" #include "netty_unix_util.h" +#define LIMITS_CLASSNAME "io/netty/channel/unix/LimitsStaticallyReferencedJniMethods" + // Define IOV_MAX if not found to limit the iov size on writev calls // See https://github.com/netty/netty/issues/2647 #ifndef IOV_MAX @@ -70,7 +72,7 @@ jint netty_unix_limits_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { // We must register the statically referenced methods first! if (netty_unix_util_register_natives(env, packagePrefix, - "io/netty/channel/unix/LimitsStaticallyReferencedJniMethods", + LIMITS_CLASSNAME, statically_referenced_fixed_method_table, statically_referenced_fixed_method_table_size) != 0) { return JNI_ERR; @@ -79,4 +81,6 @@ jint netty_unix_limits_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { return NETTY_JNI_VERSION; } -void netty_unix_limits_JNI_OnUnLoad(JNIEnv* env) { } +void netty_unix_limits_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) { + netty_unix_util_unregister_natives(env, packagePrefix, LIMITS_CLASSNAME); +} diff --git a/transport-native-unix-common/src/main/c/netty_unix_limits.h b/transport-native-unix-common/src/main/c/netty_unix_limits.h index 6b072dff69..5b3d24ee72 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_limits.h +++ b/transport-native-unix-common/src/main/c/netty_unix_limits.h @@ -20,6 +20,6 @@ // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. jint netty_unix_limits_JNI_OnLoad(JNIEnv* env, const char* packagePrefix); -void netty_unix_limits_JNI_OnUnLoad(JNIEnv* env); +void netty_unix_limits_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix); #endif /* NETTY_UNIX_LIMITS_H_ */ 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 bb2e3399eb..3725fb626e 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 @@ -31,6 +31,7 @@ #include "netty_unix_socket.h" #include "netty_unix_util.h" +#define SOCKET_CLASSNAME "io/netty/channel/unix/Socket" // Define SO_REUSEPORT if not found to fix build issues. // See https://github.com/netty/netty/issues/2558 #ifndef SO_REUSEPORT @@ -1061,7 +1062,7 @@ jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { } if (netty_unix_util_register_natives(env, packagePrefix, - "io/netty/channel/unix/Socket", + SOCKET_CLASSNAME, dynamicMethods, dynamicMethodsTableSize()) != 0) { goto done; @@ -1099,7 +1100,9 @@ done: return ret; } -void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) { +void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) { NETTY_UNLOAD_CLASS(env, datagramSocketAddressClass); NETTY_UNLOAD_CLASS(env, inetSocketAddressClass); + + netty_unix_util_unregister_natives(env, packagePrefix, SOCKET_CLASSNAME); } diff --git a/transport-native-unix-common/src/main/c/netty_unix_socket.h b/transport-native-unix-common/src/main/c/netty_unix_socket.h index 5667632cbd..ff7a6c523c 100644 --- a/transport-native-unix-common/src/main/c/netty_unix_socket.h +++ b/transport-native-unix-common/src/main/c/netty_unix_socket.h @@ -34,6 +34,6 @@ void netty_unix_socket_getOptionHandleError(JNIEnv* env, int err); // JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. jint netty_unix_socket_JNI_OnLoad(JNIEnv* env, const char* packagePrefix); -void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env); +void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix); #endif /* NETTY_UNIX_SOCKET_H_ */ 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 f72da6fbc4..fe6f27754a 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 @@ -223,6 +223,22 @@ done: return ret; } +jint netty_unix_util_unregister_natives(JNIEnv* env, const char* packagePrefix, const char* className) { + char* nettyClassName = NULL; + int ret = JNI_ERR; + NETTY_PREPEND(packagePrefix, className, nettyClassName, done); + + jclass nativeCls = (*env)->FindClass(env, nettyClassName); + if (nativeCls == NULL) { + goto done; + } + + ret = (*env)->UnregisterNatives(env, nativeCls); +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; 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 cf7d17a623..1800919d94 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 @@ -149,6 +149,7 @@ jboolean netty_unix_util_timespec_subtract_ns(struct timespec* ts, uint64_t nano * Return type is as defined in https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html#wp5833. */ jint netty_unix_util_register_natives(JNIEnv* env, const char* packagePrefix, const char* className, const JNINativeMethod* methods, jint numMethods); +jint netty_unix_util_unregister_natives(JNIEnv* env, const char* packagePrefix, const char* className); void netty_unix_util_free_dynamic_methods_table(JNINativeMethod* dynamicMethods, jint fixedMethodTableSize, jint fullMethodTableSize); void netty_unix_util_free_dynamic_name(char** dynamicName);