-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Using Memory#close can throw NPE if cleanable not initialized #1446
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
There can be no race condition as jna/src/com/sun/jna/Memory.java Lines 124 to 126 in 1eec7dd
The code is used by the inner class |
While this is true and I generally agree with the approach to mitigate (explicitly set null and null-check) I avoided using But the situation I encountered was a new instantiation of a The default call to the no-arg constructor would have triggered |
Could you add a logger in |
Well, now I'm even more confused. There's some The LUID appears to share the middle 8 bytes of a 16-byte allocation. But the memory address that causes the NPE is nowhere near those 16 bytes. And how is that conditional called twice?! class CloseableLUID extends LUID implements AutoCloseable {
@Override
public void close() {
System.out.println("Close: " + this.toString());
System.out.println("Freeing: " + this.getPointer().toString());
Util.freeMemory(getPointer());
}
} public static void freeMemory(Pointer p) {
if (p instanceof Memory) {
System.out.println("Free p class: " + p.getClass());
System.out.println("Free p toString: " + ((Memory) p).toString());
((Memory) p).close();
}
} Output:
|
OK, mystery somewhat solved. The extra output is actually from the try (CloseableHANDLEByReference hToken = new CloseableHANDLEByReference()) {
// ...
try (CloseableLUID luid = new CloseableLUID()) {
// ...
} finally {
Kernel32.INSTANCE.CloseHandle(hToken.getValue());
}
} That is somewhat similarly mapped: class CloseableHANDLEByReference extends HANDLEByReference implements AutoCloseable {
@Override
public void close() {
Util.freeMemory(getPointer());
}
} And of course the But the stack trace is associated with the earlier LUID output which is somehow using SharedMemory. But where is it being shared from? |
Ultimately I think you're right and the SharedMemory subclass is the issue. I'm not understanding why I get SharedMemory for the LUID but that's a different topic. |
OK, I finally have it all sorted. (I think.) The original LUID had its own memory but then it was used to create a Mystery solved, and PR ready for review. |
Version of JNA and related jars
5.12.0
Version and vendor of the java virtual machine
OpenJDK 64-Bit Server VM Homebrew (build 18.0.1.1+0, mixed mode, sharing)
Operating system
macOS 12.4 (Monterey)
System architecture (CPU type, bitness of the JVM)
64-bit Intel Core i9
Complete description of the problem
In Remove use of finalizers in JNA and improve concurrency for
Memory
,CallbackReference
andNativeLibrary
#1402, aclose()
method was added toMemory
, which also implementedCloseable
(which extendsAutoCloseable
on newer JDKs). The implementation assumes the private instance variablecleanable
still exists:jna/src/com/sun/jna/Memory.java
Lines 182 to 186 in 1eec7dd
However,
cleanable
is of typeCleanerRef
which extendsPhantomReference
and thus calling it in code does not imply reachability. When theCleaner
does its work, the reference is removed from the queue and the instance variable becomesnull
.Executing
close()
in this situation can result in an NPE.I updated my project to use try-with-resources blocks on all of my
Memory
allocations, and addedAutoCloseable
wrapper classes for (nearly all of) my uses of classes which extendByReference
andStructure
. One such example:and the Util class:
Then, in a method:
Hundreds of similar implementations like this worked well, but this one fails.
Possibly/probably related, it's in an initializer of one of the first classes to be instantiated, and may very well have been the very first reference in the cleaner queue, giving it a significant advantage in a race condition.
My tests seem to be running fine by removing this one mapping, so this is likely an exceedingly rare race condition.
The text was updated successfully, but these errors were encountered: