JDK-8193234 : When using -Xcheck:jni an internally allocated buffer can leak
  • Type: Bug
  • Component: hotspot
  • Sub-Component: runtime
  • Affected Version: 8,9,10,11
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2017-12-08
  • Updated: 2020-12-11
  • Resolved: 2019-02-09
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11 JDK 13 Other
11.0.5Fixed 13 b08Fixed openjdk8u272Fixed
Related Reports
Relates :  
Description
From discussion in:

http://mail.openjdk.java.net/pipermail/hotspot-dev/2017-December/029523.html

The checked version of getPrimitiveArrayCritical is implemented as follows:

JNI_ENTRY_CHECKED(void *,
  checked_jni_GetPrimitiveArrayCritical(JNIEnv *env,
                                        jarray array,
                                        jboolean *isCopy))
    functionEnterCritical(thr);
    IN_VM(
      check_is_primitive_array(thr, array);
    )
    void *result = UNCHECKED()->GetPrimitiveArrayCritical(env, array, isCopy);
    if (result != NULL) {
      result = check_jni_wrap_copy_array(thr, array, result);
    }
    functionExit(thr);
    return result;
JNI_END

This function always creates a copy of the array - check_jni_wrap_copy - keeps that fact private: *isCopy is not set to true (the real function always sets it to false).

The paired release function is implemented as follows:

JNI_ENTRY_CHECKED(void,
  checked_jni_ReleasePrimitiveArrayCritical(JNIEnv *env,
                                            jarray array,
                                            void *carray,
                                            jint mode))
    functionEnterCriticalExceptionAllowed(thr);
    IN_VM(
      check_is_primitive_array(thr, array);
    )
    // Check the element array...
    void* orig_result = check_wrapped_array_release(thr, "ReleasePrimitiveArrayCritical", array, carray, mode);
    UNCHECKED()->ReleasePrimitiveArrayCritical(env, array, orig_result, mode);
    functionExit(thr);
JNI_END

Note that "mode" is passed through to check_wrapped_array_release, which in turn does:

static void* check_wrapped_array_release(JavaThread* thr, const char* fn_name,
    void* obj, void* carray, jint mode) {
  size_t sz;
  void* orig_result = check_wrapped_array(thr, fn_name, obj, carray, &sz);
  switch (mode) {
  case 0:
    memcpy(orig_result, carray, sz);
    GuardedMemory::free_copy(carray);
    break;
  case JNI_COMMIT:
    memcpy(orig_result, carray, sz);
    break;
  case JNI_ABORT:
    GuardedMemory::free_copy(carray);
    break;
  default:

this mirrors how the real release operation handles the "mode" flag, but in this case we are not dealing with a "real copy" but the internal copy made just for checking purposes. If JNI_COMMIT is passed then we never free this internal copy and it leaks.

The same is true for release<type>ArrayElements functions.

Either check_wrapped_array_release should always be passed a mode of 0; or it should not do any mode processing at all and always free the known copy.
Comments
Fix Request (8u) This resolves the memory leak when JNI verification is enabled. Patch applies to 8u with reshuffling, passes tier1 tests. Plus, used David's test to observe the leak before the patch, and the absence of it after the patch.
19-08-2020

Fix Request (11u) This resolves the memory leak when JNI verification is enabled. Patch applies cleanly to 11u, passes tier1 and tier2 tests. Plus, used David's test to observe the leak before the patch, and the absence of it after the patch. Risk is low.
25-06-2019

I wrote a test that can be used to demonstrate the memory leak using top to see the VIRT and RES memory use on linux. That test can be seen here: http://cr.openjdk.java.net/~dholmes/8193234/webrev.withtest/ but the test is not a regression test and so is not committed.
07-02-2019

On closer inspection all that is needed is to always call GuardedMemory::free_copy(carray); regardless of the mode. Testing will be difficult, probably not able to create a regression tests as such.
06-02-2019

I'm not at all clear how the checked array processing is intended to work.
08-12-2017