[jdk11u-dev] RFR: 8315135: Memory leak in the native implementation of Pack200.Unpacker.unpack() [v2]
Volker Simonis
simonis at openjdk.org
Tue Aug 29 18:43:21 UTC 2023
On Tue, 29 Aug 2023 14:44:24 GMT, Thomas Stuefe <stuefe at openjdk.org> wrote:
>> Volker Simonis has updated the pull request incrementally with one additional commit since the last revision:
>>
>> Fixed indentation in test
>
> src/java.base/share/classes/com/sun/java/util/jar/pack/UnpackerImpl.java line 123:
>
>> 121: // Free up native memory and JNI handles to prevent leaks
>> 122: ((NativeUnpack) _nunp).finish();
>> 123: }
>
> What happens if `NativeUnpack` already called `finish()`? In case an error happened in the window after it calling `finish()` and it returning? Would that be harmless?
The normal control flow if no error happens is that `NativeUnpack::run()` calls `NativeUnpack::finish()` before returning to its caller `UnpackerImpl::unpack()`. You are right that with these changes `NativeUnpack::finish()` will now be called two times in the case of successful unpacking. That's not a problem, because `NativeUnpack::finish()` is implemented as follows:
JNIEXPORT jlong JNICALL
Java_com_sun_java_util_jar_pack_NativeUnpack_finish(JNIEnv *env, jobject pObj) {
unpacker* uPtr = get_unpacker(env, pObj, false);
CHECK_EXCEPTION_RETURN_VALUE(uPtr, 0);
size_t consumed = uPtr->input_consumed();
free_unpacker(env, pObj, uPtr);
return consumed;
}
and `get_unpacker(env, pObj, false)` will always return a valid pointer to a `unpacker` object (or throw an exception). That's controlled by the third argument, which basically instructs `get_unpacker()` to create a new `unpacker` object if the current one is NULL. `free_unpacker(env, pObj, uPtr)` in a subsequent line will than release all the `unpacker`'s native memory and set the unpacker field (i.e. `NativeUnpack::unpackerPtr`) to NULL.
So while calling `NativeUnpack::finish()` repeatedly is correct, it is unnecessarily expensive because we may create a new native `unpacker` object in `Java_com_sun_java_util_jar_pack_NativeUnpack_finish()` just to delete it right afterwards. I've therefore changed `Java_com_sun_java_util_jar_pack_NativeUnpack_finish()` as follows:
JNIEXPORT jlong JNICALL
Java_com_sun_java_util_jar_pack_NativeUnpack_finish(JNIEnv *env, jobject pObj) {
// There's no need to create a new unpacker here if we don't already have one
// just to immediatly free it afterwards.
unpacker* uPtr = get_unpacker(env, pObj, /* noCreate= */ true);
CHECK_EXCEPTION_RETURN_VALUE(uPtr, 0);
size_t consumed = uPtr->input_consumed();
// free_unpacker() will set the unpacker field on 'pObj' to null
free_unpacker(env, pObj, uPtr);
return consumed;
}
Now, if we call `get_unpacker()` with the third argument set to `true`, `get_unpacker()` will not create a new `unpacker` object any more if the `NativeUnpack::unpackerPtr` is already NULL. The subsequent `CHECK_EXCEPTION_RETURN_VALUE` will check for a NULL return value and simple return from `Java_com_sun_java_util_jar_pack_NativeUnpack_finish()`.
PS: the third, boolean argument `noCreate` of `get_unpacker()` is a default argument with the default value being `false`. All other call sites of `get_unpacker()` use this default, only the call from `Java_com_sun_java_util_jar_pack_NativeUnpack_finish()` explicitly adds the third argument as `false` which is already the default anyway. I strongly suspect that should have been `true` from the very beginning, because it makes no sense to create a new `unpacker` object just to delete it again a few lines later.
-------------
PR Review Comment: https://git.openjdk.org/jdk11u-dev/pull/2100#discussion_r1309215728
More information about the jdk-updates-dev
mailing list