Unregister all previous registered native methods if loading of native code fails… (#10719)

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:
This commit is contained in:
Norman Maurer 2020-10-26 14:15:04 +01:00 committed by GitHub
parent 321f0f0e66
commit 03aafb9cff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 157 additions and 63 deletions

View File

@ -35,6 +35,8 @@
#include "netty_unix_socket.h" #include "netty_unix_socket.h"
#include "netty_unix_util.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. // TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile.
#ifndef TCP_FASTOPEN #ifndef TCP_FASTOPEN
#define TCP_FASTOPEN 23 #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, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/epoll/LinuxSocket", LINUXSOCKET_CLASSNAME,
dynamicMethods, dynamicMethods,
dynamicMethodsTableSize()) != 0) { dynamicMethodsTableSize()) != 0) {
goto done; goto done;
@ -788,6 +790,8 @@ done:
return ret; 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_UNLOAD_CLASS(env, peerCredentialsClass);
netty_unix_util_unregister_natives(env, packagePrefix, LINUXSOCKET_CLASSNAME);
} }

View File

@ -21,6 +21,6 @@
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. // 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); 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 #endif

View File

@ -49,6 +49,9 @@
#include "netty_unix_socket.h" #include "netty_unix_socket.h"
#include "netty_unix_util.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. // TCP_FASTOPEN is defined in linux 3.7. We define this here so older kernels can compile.
#ifndef TCP_FASTOPEN #ifndef TCP_FASTOPEN
#define TCP_FASTOPEN 23 #define TCP_FASTOPEN 23
@ -98,6 +101,7 @@ static jfieldID packetScopeIdFieldId = NULL;
static jfieldID packetPortFieldId = NULL; static jfieldID packetPortFieldId = NULL;
static jfieldID packetMemoryAddressFieldId = NULL; static jfieldID packetMemoryAddressFieldId = NULL;
static jfieldID packetCountFieldId = NULL; static jfieldID packetCountFieldId = NULL;
static char* staticPackagePrefix = NULL;
// util methods // util methods
static int getSysctlValue(const char * property, int* returnValue) { 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) { static jint netty_epoll_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
int ret = JNI_ERR; int ret = JNI_ERR;
int staticallyRegistered = 0;
int nativeRegistered = 0;
int limitsOnLoadCalled = 0; int limitsOnLoadCalled = 0;
int errorsOnLoadCalled = 0; int errorsOnLoadCalled = 0;
int filedescriptorOnLoadCalled = 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! // We must register the statically referenced methods first!
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/epoll/NativeStaticallyReferencedJniMethods", STATICALLY_CLASSNAME,
statically_referenced_fixed_method_table, statically_referenced_fixed_method_table,
statically_referenced_fixed_method_table_size) != 0) { statically_referenced_fixed_method_table_size) != 0) {
goto done; goto done;
} }
staticallyRegistered = 1;
// Register the methods which are not referenced by static member variables // Register the methods which are not referenced by static member variables
dynamicMethods = createDynamicMethodsTable(packagePrefix); dynamicMethods = createDynamicMethodsTable(packagePrefix);
if (dynamicMethods == NULL) { 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, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/epoll/Native", NATIVE_CLASSNAME,
dynamicMethods, dynamicMethods,
dynamicMethodsTableSize()) != 0) { dynamicMethodsTableSize()) != 0) {
goto done; goto done;
} }
nativeRegistered = 1;
// Load all c modules that we depend upon // Load all c modules that we depend upon
if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto done; goto done;
@ -672,23 +682,29 @@ done:
free(nettyClassName); free(nettyClassName);
if (ret == JNI_ERR) { 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) { if (limitsOnLoadCalled == 1) {
netty_unix_limits_JNI_OnUnLoad(env); netty_unix_limits_JNI_OnUnLoad(env, packagePrefix);
} }
if (errorsOnLoadCalled == 1) { if (errorsOnLoadCalled == 1) {
netty_unix_errors_JNI_OnUnLoad(env); netty_unix_errors_JNI_OnUnLoad(env, packagePrefix);
} }
if (filedescriptorOnLoadCalled == 1) { if (filedescriptorOnLoadCalled == 1) {
netty_unix_filedescriptor_JNI_OnUnLoad(env); netty_unix_filedescriptor_JNI_OnUnLoad(env, packagePrefix);
} }
if (socketOnLoadCalled == 1) { if (socketOnLoadCalled == 1) {
netty_unix_socket_JNI_OnUnLoad(env); netty_unix_socket_JNI_OnUnLoad(env, packagePrefix);
} }
if (bufferOnLoadCalled == 1) { if (bufferOnLoadCalled == 1) {
netty_unix_buffer_JNI_OnUnLoad(env); netty_unix_buffer_JNI_OnUnLoad(env, packagePrefix);
} }
if (linuxsocketOnLoadCalled == 1) { if (linuxsocketOnLoadCalled == 1) {
netty_epoll_linuxsocket_JNI_OnUnLoad(env); netty_epoll_linuxsocket_JNI_OnUnLoad(env, packagePrefix);
} }
packetAddrFieldId = NULL; packetAddrFieldId = NULL;
packetAddrLenFieldId = NULL; packetAddrLenFieldId = NULL;
@ -700,13 +716,13 @@ done:
return ret; return ret;
} }
static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) { static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env, const char* packagePrefix) {
netty_unix_limits_JNI_OnUnLoad(env); netty_unix_limits_JNI_OnUnLoad(env, packagePrefix);
netty_unix_errors_JNI_OnUnLoad(env); netty_unix_errors_JNI_OnUnLoad(env, packagePrefix);
netty_unix_filedescriptor_JNI_OnUnLoad(env); netty_unix_filedescriptor_JNI_OnUnLoad(env, packagePrefix);
netty_unix_socket_JNI_OnUnLoad(env); netty_unix_socket_JNI_OnUnLoad(env, packagePrefix);
netty_unix_buffer_JNI_OnUnLoad(env); netty_unix_buffer_JNI_OnUnLoad(env, packagePrefix);
netty_epoll_linuxsocket_JNI_OnUnLoad(env); netty_epoll_linuxsocket_JNI_OnUnLoad(env, packagePrefix);
packetAddrFieldId = NULL; packetAddrFieldId = NULL;
packetAddrLenFieldId = NULL; packetAddrLenFieldId = NULL;
@ -714,6 +730,9 @@ static void netty_epoll_native_JNI_OnUnLoad(JNIEnv* env) {
packetPortFieldId = NULL; packetPortFieldId = NULL;
packetMemoryAddressFieldId = NULL; packetMemoryAddressFieldId = NULL;
packetCountFieldId = 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 // 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 */ #endif /* NETTY_BUILD_STATIC */
jint ret = netty_epoll_native_JNI_OnLoad(env, packagePrefix); jint ret = netty_epoll_native_JNI_OnLoad(env, packagePrefix);
if (ret == JNI_ERR) {
free(packagePrefix); free(packagePrefix);
staticPackagePrefix = NULL;
} else {
staticPackagePrefix = packagePrefix;
}
return ret; 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 :( // Something is wrong but nothing we can do about this :(
return; 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 // We build with -fvisibility=hidden so ensure we mark everything that needs to be visible with JNIEXPORT

View File

@ -32,6 +32,8 @@
#include "netty_unix_socket.h" #include "netty_unix_socket.h"
#include "netty_unix_util.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 // Those are initialized in the init(...) method and cached for performance reasons
static jclass stringClass = NULL; static jclass stringClass = NULL;
static jclass peerCredentialsClass = 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, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/kqueue/BsdSocket", BSDSOCKET_CLASSNAME,
dynamicMethods, dynamicMethods,
dynamicMethodsTableSize()) != 0) { dynamicMethodsTableSize()) != 0) {
goto done; goto done;
@ -284,7 +286,9 @@ done:
return ret; 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, peerCredentialsClass);
NETTY_UNLOAD_CLASS(env, stringClass); NETTY_UNLOAD_CLASS(env, stringClass);
netty_unix_util_unregister_natives(env, packagePrefix, BSDSOCKET_CLASSNAME);
} }

View File

@ -20,6 +20,6 @@
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. // 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); 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_ */ #endif /* NETTY_KQUEUE_BSDSOCKET_H_ */

View File

@ -24,6 +24,8 @@
#include "netty_unix_jni.h" #include "netty_unix_jni.h"
#include "netty_unix_util.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) { 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); 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) { jint netty_kqueue_eventarray_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/kqueue/KQueueEventArray", EVENT_ARRAY_CLASSNAME,
fixed_method_table, fixed_method_table,
fixed_method_table_size) != 0) { fixed_method_table_size) != 0) {
return JNI_ERR; return JNI_ERR;
@ -47,5 +49,6 @@ jint netty_kqueue_eventarray_JNI_OnLoad(JNIEnv* env, const char* packagePrefix)
return NETTY_JNI_VERSION; 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);
} }

View File

@ -20,6 +20,6 @@
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. // 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); 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_ */ #endif /* NETTY_KQUEUE_EVENTARRAY_H_ */

View File

@ -36,6 +36,9 @@
#include "netty_unix_socket.h" #include "netty_unix_socket.h"
#include "netty_unix_util.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. // 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. // To make compiling easier we redefine the values here if they are not present.
#ifdef __APPLE__ #ifdef __APPLE__
@ -68,6 +71,8 @@
static clockid_t waitClockId = 0; // initialized by netty_unix_util_initialize_wait_clock 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) { static jint netty_kqueue_native_kqueueCreate(JNIEnv* env, jclass clazz) {
jint kq = kqueue(); jint kq = kqueue();
if (kq < 0) { if (kq < 0) {
@ -265,6 +270,8 @@ static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(
// JNI Method Registration Table End // JNI Method Registration Table End
static jint netty_kqueue_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) { static jint netty_kqueue_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
int staticallyRegistered = 0;
int nativeRegistered = 0;
int limitsOnLoadCalled = 0; int limitsOnLoadCalled = 0;
int errorsOnLoadCalled = 0; int errorsOnLoadCalled = 0;
int filedescriptorOnLoadCalled = 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! // We must register the statically referenced methods first!
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/kqueue/KQueueStaticallyReferencedJniMethods", STATICALLY_CLASSNAME,
statically_referenced_fixed_method_table, statically_referenced_fixed_method_table,
statically_referenced_fixed_method_table_size) != 0) { statically_referenced_fixed_method_table_size) != 0) {
goto error; goto error;
} }
staticallyRegistered = 1;
// Register the methods which are not referenced by static member variables // 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; goto error;
} }
nativeRegistered = 1;
// Load all c modules that we depend upon // Load all c modules that we depend upon
if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) { if (netty_unix_limits_JNI_OnLoad(env, packagePrefix) == JNI_ERR) {
goto error; goto error;
@ -330,38 +341,44 @@ static jint netty_kqueue_native_JNI_OnLoad(JNIEnv* env, const char* packagePrefi
return NETTY_JNI_VERSION; return NETTY_JNI_VERSION;
error: 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) { if (limitsOnLoadCalled == 1) {
netty_unix_limits_JNI_OnUnLoad(env); netty_unix_limits_JNI_OnUnLoad(env, packagePrefix);
} }
if (errorsOnLoadCalled == 1) { if (errorsOnLoadCalled == 1) {
netty_unix_errors_JNI_OnUnLoad(env); netty_unix_errors_JNI_OnUnLoad(env, packagePrefix);
} }
if (filedescriptorOnLoadCalled == 1) { if (filedescriptorOnLoadCalled == 1) {
netty_unix_filedescriptor_JNI_OnUnLoad(env); netty_unix_filedescriptor_JNI_OnUnLoad(env, packagePrefix);
} }
if (socketOnLoadCalled == 1) { if (socketOnLoadCalled == 1) {
netty_unix_socket_JNI_OnUnLoad(env); netty_unix_socket_JNI_OnUnLoad(env, packagePrefix);
} }
if (bufferOnLoadCalled == 1) { if (bufferOnLoadCalled == 1) {
netty_unix_buffer_JNI_OnUnLoad(env); netty_unix_buffer_JNI_OnUnLoad(env, packagePrefix);
} }
if (bsdsocketOnLoadCalled == 1) { if (bsdsocketOnLoadCalled == 1) {
netty_kqueue_bsdsocket_JNI_OnUnLoad(env); netty_kqueue_bsdsocket_JNI_OnUnLoad(env, packagePrefix);
} }
if (eventarrayOnLoadCalled == 1) { if (eventarrayOnLoadCalled == 1) {
netty_kqueue_eventarray_JNI_OnUnLoad(env); netty_kqueue_eventarray_JNI_OnUnLoad(env, packagePrefix);
} }
return JNI_ERR; return JNI_ERR;
} }
static void netty_kqueue_native_JNI_OnUnLoad(JNIEnv* env) { static void netty_kqueue_native_JNI_OnUnLoad(JNIEnv* env, const char* staticPackagePrefix) {
netty_unix_limits_JNI_OnUnLoad(env); netty_unix_limits_JNI_OnUnLoad(env, staticPackagePrefix);
netty_unix_errors_JNI_OnUnLoad(env); netty_unix_errors_JNI_OnUnLoad(env, staticPackagePrefix);
netty_unix_filedescriptor_JNI_OnUnLoad(env); netty_unix_filedescriptor_JNI_OnUnLoad(env, staticPackagePrefix);
netty_unix_socket_JNI_OnUnLoad(env); netty_unix_socket_JNI_OnUnLoad(env, staticPackagePrefix);
netty_unix_buffer_JNI_OnUnLoad(env); netty_unix_buffer_JNI_OnUnLoad(env, staticPackagePrefix);
netty_kqueue_bsdsocket_JNI_OnUnLoad(env); netty_kqueue_bsdsocket_JNI_OnUnLoad(env, staticPackagePrefix);
netty_kqueue_eventarray_JNI_OnUnLoad(env); netty_kqueue_eventarray_JNI_OnUnLoad(env, staticPackagePrefix);
} }
static jint JNI_OnLoad_netty_transport_native_kqueue0(JavaVM* vm, void* reserved) { 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 */ #endif /* NETTY_BUILD_STATIC */
jint ret = netty_kqueue_native_JNI_OnLoad(env, packagePrefix); jint ret = netty_kqueue_native_JNI_OnLoad(env, packagePrefix);
if (ret == JNI_ERR) {
// It's safe to call free(...) with a NULL argument as well.
free(packagePrefix); free(packagePrefix);
staticPackagePrefix = NULL;
} else {
staticPackagePrefix = packagePrefix;
}
return ret; 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 :( // Something is wrong but nothing we can do about this :(
return; 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 // We build with -fvisibility=hidden so ensure we mark everything that needs to be visible with JNIEXPORT

View File

@ -17,6 +17,8 @@
#include "netty_unix_util.h" #include "netty_unix_util.h"
#include "netty_unix_buffer.h" #include "netty_unix_buffer.h"
#define BUFFER_CLASSNAME "io/netty/channel/unix/Buffer"
// JNI Registered Methods Begin // JNI Registered Methods Begin
static jlong netty_unix_buffer_memoryAddress0(JNIEnv* env, jclass clazz, jobject buffer) { static jlong netty_unix_buffer_memoryAddress0(JNIEnv* env, jclass clazz, jobject buffer) {
return (jlong) (*env)->GetDirectBufferAddress(env, 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! // We must register the statically referenced methods first!
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/unix/Buffer", BUFFER_CLASSNAME,
statically_referenced_fixed_method_table, statically_referenced_fixed_method_table,
statically_referenced_fixed_method_table_size) != 0) { statically_referenced_fixed_method_table_size) != 0) {
return JNI_ERR; return JNI_ERR;
@ -49,4 +51,6 @@ jint netty_unix_buffer_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
return NETTY_JNI_VERSION; 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);
}

View File

@ -20,6 +20,6 @@
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. // 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); 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_ */ #endif /* NETTY_UNIX_BUFFER_H_ */

View File

@ -21,6 +21,8 @@
#include "netty_unix_jni.h" #include "netty_unix_jni.h"
#include "netty_unix_util.h" #include "netty_unix_util.h"
#define ERRORS_CLASSNAME "io/netty/channel/unix/ErrorsStaticallyReferencedJniMethods"
static jclass oomErrorClass = NULL; static jclass oomErrorClass = NULL;
static jclass runtimeExceptionClass = NULL; static jclass runtimeExceptionClass = NULL;
static jclass channelExceptionClass = 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! // We must register the statically referenced methods first!
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/unix/ErrorsStaticallyReferencedJniMethods", ERRORS_CLASSNAME,
statically_referenced_fixed_method_table, statically_referenced_fixed_method_table,
statically_referenced_fixed_method_table_size) != 0) { statically_referenced_fixed_method_table_size) != 0) {
return JNI_ERR; return JNI_ERR;
@ -241,7 +243,7 @@ error:
return JNI_ERR; 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 // delete global references so the GC can collect them
NETTY_UNLOAD_CLASS(env, oomErrorClass); NETTY_UNLOAD_CLASS(env, oomErrorClass);
NETTY_UNLOAD_CLASS(env, runtimeExceptionClass); 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, ioExceptionClass);
NETTY_UNLOAD_CLASS(env, portUnreachableExceptionClass); NETTY_UNLOAD_CLASS(env, portUnreachableExceptionClass);
NETTY_UNLOAD_CLASS(env, closedChannelExceptionClass); NETTY_UNLOAD_CLASS(env, closedChannelExceptionClass);
netty_unix_util_unregister_natives(env, packagePrefix, ERRORS_CLASSNAME);
} }

View File

@ -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. // 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); 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_ */ #endif /* NETTY_UNIX_ERRORS_H_ */

View File

@ -26,6 +26,8 @@
#include "netty_unix_jni.h" #include "netty_unix_jni.h"
#include "netty_unix_util.h" #include "netty_unix_util.h"
#define FILEDESCRIPTOR_CLASSNAME "io/netty/channel/unix/FileDescriptor"
static jmethodID posId = NULL; static jmethodID posId = NULL;
static jmethodID limitId = NULL; static jmethodID limitId = NULL;
static jfieldID posFieldId = 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) { jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
int ret = JNI_ERR; int ret = JNI_ERR;
void* mem = NULL; 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; goto done;
} }
if ((mem = malloc(1)) == NULL) { if ((mem = malloc(1)) == NULL) {
@ -313,4 +315,6 @@ done:
return ret; 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);
}

View File

@ -20,6 +20,6 @@
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. // 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); 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_ */ #endif /* NETTY_UNIX_FILEDESCRIPTOR_H_ */

View File

@ -21,6 +21,8 @@
#include "netty_unix_limits.h" #include "netty_unix_limits.h"
#include "netty_unix_util.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 // Define IOV_MAX if not found to limit the iov size on writev calls
// See https://github.com/netty/netty/issues/2647 // See https://github.com/netty/netty/issues/2647
#ifndef IOV_MAX #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! // We must register the statically referenced methods first!
if (netty_unix_util_register_natives(env, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/unix/LimitsStaticallyReferencedJniMethods", LIMITS_CLASSNAME,
statically_referenced_fixed_method_table, statically_referenced_fixed_method_table,
statically_referenced_fixed_method_table_size) != 0) { statically_referenced_fixed_method_table_size) != 0) {
return JNI_ERR; return JNI_ERR;
@ -79,4 +81,6 @@ jint netty_unix_limits_JNI_OnLoad(JNIEnv* env, const char* packagePrefix) {
return NETTY_JNI_VERSION; 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);
}

View File

@ -20,6 +20,6 @@
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods. // 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); 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_ */ #endif /* NETTY_UNIX_LIMITS_H_ */

View File

@ -31,6 +31,7 @@
#include "netty_unix_socket.h" #include "netty_unix_socket.h"
#include "netty_unix_util.h" #include "netty_unix_util.h"
#define SOCKET_CLASSNAME "io/netty/channel/unix/Socket"
// Define SO_REUSEPORT if not found to fix build issues. // Define SO_REUSEPORT if not found to fix build issues.
// See https://github.com/netty/netty/issues/2558 // See https://github.com/netty/netty/issues/2558
#ifndef SO_REUSEPORT #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, if (netty_unix_util_register_natives(env,
packagePrefix, packagePrefix,
"io/netty/channel/unix/Socket", SOCKET_CLASSNAME,
dynamicMethods, dynamicMethods,
dynamicMethodsTableSize()) != 0) { dynamicMethodsTableSize()) != 0) {
goto done; goto done;
@ -1099,7 +1100,9 @@ done:
return ret; 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, datagramSocketAddressClass);
NETTY_UNLOAD_CLASS(env, inetSocketAddressClass); NETTY_UNLOAD_CLASS(env, inetSocketAddressClass);
netty_unix_util_unregister_natives(env, packagePrefix, SOCKET_CLASSNAME);
} }

View File

@ -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. // 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); 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_ */ #endif /* NETTY_UNIX_SOCKET_H_ */

View File

@ -223,6 +223,22 @@ done:
return ret; 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) { void netty_unix_util_free_dynamic_methods_table(JNINativeMethod* dynamicMethods, jint fixedMethodTableSize, jint fullMethodTableSize) {
if (dynamicMethods != NULL) { if (dynamicMethods != NULL) {
jint i = fixedMethodTableSize; jint i = fixedMethodTableSize;

View File

@ -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. * 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_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_methods_table(JNINativeMethod* dynamicMethods, jint fixedMethodTableSize, jint fullMethodTableSize);
void netty_unix_util_free_dynamic_name(char** dynamicName); void netty_unix_util_free_dynamic_name(char** dynamicName);