On Tue, 22 Nov 2022 09:11:44 GMT, Per Minborg <[email protected]> wrote:
>> This PR proposes the introduction of **guarding** of the use of
>> `DirectBuffer::address` within the JDK. With the introduction of the Foreign
>> Function and Memory access, it is possible to derive Buffer instances that
>> are backed by native memory that, in turn, can be closed asynchronously by
>> the user (and not only via a `Cleaner` when there is no other reference to
>> the `Buffer` object). If another thread is invoking `MemorySession::close`
>> while a thread is doing work using raw addresses, the outcome is undefined.
>> This means the JVM might crash or even worse, silent modification of
>> unrelated memory might occur.
>>
>> Design choices in this PR:
>>
>> There is already a method `MemorySession::whileAlive` that takes a runnable
>> and that will perform the provided action while acquiring the underlying`
>> MemorySession` (if any). However, using this method entailed relatively
>> large changes while converting larger/nested code segments into lambdas.
>> This would also risk introducing lambda capturing. So, instead, a
>> try-with-resources friendly access method was added. This made is more easy
>> to add guarding and did not risk lambda capturing. Also, introducing lambdas
>> in certain fundamental JDK classes might incur bootstrap problems.
>>
>> The aforementioned TwR is using a "session acquisition" that is not used
>> explicitly in the try block itself. This raises a warning that is suppressed
>> using `@SuppressWarnings("try")`. In the future, we might be able to remove
>> these suppressions by using the reserved variable name `_`. Another
>> alternative was evaluated where a non-autocloseable resource was used.
>> However, it became relatively complicated to guarantee that the session was
>> always released and, at the same time, the existing 'final` block was always
>> executed properly (as they both might throw exceptions). In the end, the
>> proposed solution was much simpler and robust despite requiring a
>> non-referenced TwR variable.
>>
>> In some cases, where is is guaranteed that the backing memory session is
>> non-closeable, we do not have to guard the use of `DirectBuffer::address`.
>> ~~These cases have been documented in the code.~~
>>
>> On some occasions, a plurality of acquisitions are made. This would never
>> lead to deadlocks as acquisitions are fundamentally concurrent counters and
>> not resources that only one thread can "own".
>>
>> I have added comments (and not javadocs) before the declaration of the
>> non-public-api `DirectBuffer::address` method, that the use of the returned
>> address needs to be guarded. It can be discussed if this is preferable or
>> not.
>>
>> This PR spawns several areas of responsibility and so, I expect more than
>> one reviewer before promoting the PR.
>
> Per Minborg has updated the pull request incrementally with one additional
> commit since the last revision:
>
> Rework Acquisition
src/java.base/share/classes/com/sun/crypto/provider/GaloisCounterMode.java line
914:
> 912: * If so, make a copy to put the dst data in.
> 913: */
> 914: @SuppressWarnings("try")
After looking at the implementation some more, I'm not sure this need fixing?
E.g. this method is just using the address to compute some overlap - and return
a buffer sliced accordingly. There's no access to the buffer data (except for
the last part which does a `put`). The access will fail if the session is
closed from underneath. I don't think this can crash the VM (in fact this code
did not have a reachability fence to begin with).
src/java.base/share/classes/java/nio/Buffer.java line 827:
> 825:
> 826: @Override
> 827: public Runnable acquireSessionOrNull(Buffer buffer,
> boolean async) {
If allocation/performance is an issue, a relatively straightforward way to
adjust the code would be to let go of the TWR entirely. E.g. we have code that
does this:
Buffer b = ...
try {
// use buffer.address();
} finally {
Reference.reachabilityFence(b);
}
We could transform to this:
Buffer b = ...
acquire(b); // calls MemorySessionImpl.acquire0 if session is not null
try {
// use buffer.address();
} finally {
release(b); // calls MemorySessionImpl.release0 if session is not null (and
maybe adds a reachability fence, just in case)
}
This leads to a simpler patch that is probably easier to validate. The
remaining IOUtil code will be using a different idiom, and perhaps we can, at
some point, go back and make that consistent too.
src/jdk.sctp/unix/classes/sun/nio/ch/sctp/SctpMultiChannelImpl.java line 590:
> 588: int pos)
> 589: throws IOException {
> 590: try (var guard = NIO_ACCESS.acquireScope(bb)) {
Why was the old code not using reachability fences? Bug or feature?
-------------
PR: https://git.openjdk.org/jdk/pull/11260