<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<p>Hi,</p>
<p>The section of javadoc that you reference is talking about a case
like this:<br>
<br>
<font face="monospace"> static MemorySegment target() {<br>
try (Arena arena = Arena.openConfined()) {<br>
return arena.allocate(JAVA_INT);<br>
}<br>
}</font><br>
<br>
Note that the memory will be freed before the target function
returns, so if this function is used as the target method of an
upcall stub, it will effectively return a dangling pointer to
native code. If the native code then dereferences the returned
pointer, this can crash the VM.</p>
<p>> I don’t understand how/what clients should do to ensure the
address of the upcall stub is alive during the upcall invocation
within an implication session.</p>
<p>In the case of an implicit scope, the scope of the upcall stub
should be kept reachable (per [1]) until the upcall stub can no
longer be called. The Reference::reachability fence method can be
used for this. But, it might be easier to use an explicitly closed
scope for the upcall stub instead, if you plan on storing it in
native memory and calling it later.<br>
</p>
<p>> Does that mean the implicit session is ALREADY kept alive in
the existing implementation of OpenJDK (which I don’t think so) to
guarantee the upcall stub works during the upcall invocation based
on the explanation in [1]?<br>
<br>
Not quite. The current implementation guarantees that the implicit
session of an upcall stub is kept alive (and the upcall stub
works) during and invocation of a _downcall_ to which the upcall
stub memory segment is passed. i.e. if an upcall stub is passed to
a downcall, the scope it is attached to can not be closed for the
duration of that downcall (and this goes for any MemorySegment
passed to a downcall). This is achieved by calling
ImplicitSession::release [2] after a downcall returns.<br>
</p>
<p>There is no guarantee that an arbitrary upcall stub will be kept
alive until it is invoked. For instance, if I pass an upcall stub
to native code, and store the pointer in some memory there (e.g. a
global variable), then I return back to Java. If I don't have a
strong reference to the upcall stub's scope, the upcall stub might
be freed. If I then later try to invoke the upcall stub again
through the pointer I stored in native code, the VM can crash. In
short: the JVM can not 'see' any pointer to the upcall stub from
native code, so they don't count toward its reachability.<br>
</p>
<p>To illustrate the problematic case, consider this native library
and Java program that calls into it (using the Java 20 API):<br>
<br>
libMylib.c:<br>
<br>
<font face="monospace">typedef void (*callback_t)(void);<br>
</font></p>
<p><font face="monospace">static callback_t GLOBAL_CB; // global
pointer variable</font></p>
<p><font face="monospace">void store(callback_t cb) {<br>
GLOBAL_CB = cb;<br>
}<br>
<br>
void call() {<br>
GLOBAL_CB();<br>
}</font><br>
<br>
Main.java:<br>
<font face="monospace"><br>
import java.lang.foreign.*;<br>
import java.lang.invoke.*;<br>
<br>
import static java.lang.foreign.ValueLayout.*;<br>
<br>
public class Main {<br>
static final Linker LINKER = Linker.nativeLinker();<br>
<br>
static final MethodHandle STORE;<br>
static final MethodHandle CALL;<br>
static final MethodHandle TARGET;<br>
<br>
static {<br>
System.loadLibrary("Mylib");<br>
SymbolLookup lookup = SymbolLookup.loaderLookup();<br>
STORE = LINKER.downcallHandle(lookup.find("store").get(),
FunctionDescriptor.ofVoid(ADDRESS));<br>
CALL = LINKER.downcallHandle(lookup.find("call").get(),
FunctionDescriptor.ofVoid());<br>
try {<br>
TARGET = MethodHandles.lookup().findStatic(Main.class,
"target", MethodType.methodType(void.class));<br>
} catch (ReflectiveOperationException e) {<br>
throw new InternalError(e);<br>
}<br>
}<br>
public static void main(String[] args) throws Throwable {<br>
store();<br>
System.gc(); // invoke gc which can clean up 'stub'<br>
call();<br>
}<br>
<br>
static void store() throws Throwable {<br>
MemorySegment stub = LINKER.upcallStub(TARGET,
FunctionDescriptor.ofVoid(), SegmentScope.auto());<br>
STORE.invokeExact(stub);<br>
// stub is unreachable here<br>
}<br>
<br>
static void call() throws Throwable {<br>
CALL.invokeExact();<br>
}<br>
<br>
static void target() {}<br>
}</font><br>
</p>
<p>On my machine this code reliably crashes the VM, since the upcall
stub is called after it becomes unreachable (and is freed).<br>
</p>
<p>In order to fix this, I would have to keep the stub alive until
after I call the 'call' function from the native library:<br>
<br>
<font face="monospace"> public static void main(String[] args)
throws Throwable {<br>
MemorySegment stub = store(); // <-----<br>
System.gc(); // invoke gc<br>
call();<br>
Reference.reachabilityFence(stub); </font><font face="monospace"><font face="monospace">// <----- keep stub
alive until after I call it</font><br>
}<br>
<br>
static MemorySegment store() throws Throwable {<br>
MemorySegment stub = LINKER.upcallStub(TARGET,
FunctionDescriptor.ofVoid(), SegmentScope.auto());<br>
STORE.invokeExact(stub);<br>
return stub;</font><font face="monospace"><font face="monospace"> // <--- return stub here</font><br>
}</font><br>
</p>
<p>Hope that helps,<br>
Jorn<br>
</p>
<p>[1]:
<a class="moz-txt-link-freetext" href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/ref/package-summary.html#reachability">https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/ref/package-summary.html#reachability</a><br>
[2]:
<a class="moz-txt-link-freetext" href="https://github.com/openjdk/panama-foreign/blob/a943c2eb10ba40c36d5a6e874160d0a747457510/src/java.base/share/classes/jdk/internal/foreign/ImplicitSession.java#L50">https://github.com/openjdk/panama-foreign/blob/a943c2eb10ba40c36d5a6e874160d0a747457510/src/java.base/share/classes/jdk/internal/foreign/ImplicitSession.java#L50</a><br>
</p>
<div class="moz-cite-prefix">On 31/01/2023 21:01, Cheng Jin wrote:<br>
</div>
<blockquote type="cite" cite="mid:MN2PR15MB259204050A96476D15ECA613F5D09@MN2PR15MB2592.namprd15.prod.outlook.com">
<meta name="Generator" content="Microsoft Word 15 (filtered
medium)">
<style>@font-face
{font-family:Wingdings;
panose-1:5 0 0 0 0 0 0 0 0 0;}@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}@font-face
{font-family:DengXian;
panose-1:2 1 6 0 3 1 1 1 1 1;}@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}@font-face
{font-family:"\@DengXian";
panose-1:2 1 6 0 3 1 1 1 1 1;}p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0cm;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}a:link, span.MsoHyperlink
{mso-style-priority:99;
color:blue;
text-decoration:underline;}span.EmailStyle20
{mso-style-type:personal-reply;
font-family:"Calibri",sans-serif;
color:windowtext;}.MsoChpDefault
{mso-style-type:export-only;
font-size:10.0pt;}div.WordSection1
{page:WordSection1;}ol
{margin-bottom:0cm;}ul
{margin-bottom:0cm;}</style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
<div class="WordSection1">
<p class="MsoNormal">Hi Jorn,<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Correct but these bullets are only intended
for the downcall invocation rather than the upcall stub.<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">According to the explanation of upcall stub
in [1] as follows:<o:p></o:p></p>
<p class="MsoNormal">“When creating upcall stubs the linker
runtime....<o:p></o:p></p>
<p class="MsoNormal">Moreover, if the target method handle
associated with an upcall stub returns a memory address,<o:p></o:p></p>
<p class="MsoNormal">clients must ensure that this address
cannot become invalid after the upcall completes.
<span style="font-family:Wingdings">ß</span>-----------------<o:p></o:p></p>
<p class="MsoNormal">This can lead to unspecified behavior, and
even JVM crashes, since an upcall is
<o:p></o:p></p>
<p class="MsoNormal">typically executed in the context of a
downcall method handle invocation.”<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">I don’t understand how/what clients should
do to ensure the address of the upcall stub is alive during
the upcall invocation within an implication session.<o:p></o:p></p>
<p class="MsoNormal">Does that mean the implicit session is
ALREADY kept alive in the existing implementation of OpenJDK
(which I don’t think so) to guarantee the upcall stub works
during the upcall invocation based on the explanation in [1]?<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<p class="MsoNormal">Best Regards<o:p></o:p></p>
<p class="MsoNormal">Cheng Jin<o:p></o:p></p>
<p class="MsoNormal"><o:p> </o:p></p>
<div style="border:none;border-top:solid #E1E1E1
1.0pt;padding:3.0pt 0cm 0cm 0cm">
<p class="MsoNormal"><b><span lang="EN-US">From:</span></b><span lang="EN-US"> Jorn Vernee <a class="moz-txt-link-rfc2396E" href="mailto:jorn.vernee@oracle.com"><jorn.vernee@oracle.com></a>
<br>
<b>Sent:</b> January 31, 2023 2:14 PM<br>
<b>To:</b> Cheng Jin <a class="moz-txt-link-rfc2396E" href="mailto:jincheng@ca.ibm.com"><jincheng@ca.ibm.com></a>;
<a class="moz-txt-link-abbreviated" href="mailto:panama-dev@openjdk.org">panama-dev@openjdk.org</a><br>
<b>Subject:</b> [EXTERNAL] Re: Inappropriate use of the
implicit session in allocating the upcall stub in
SafeFunctionAccessTest.java<o:p></o:p></span></p>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
<div>
<p class="MsoNormal" style="mso-line-height-alt:.75pt"><span style="font-size:1.0pt;color:white">Hi Cheng Jin, Thanks
for the email. In this case it is not a problem since
memory sessions (including upcall stubs) are kept
alive/reachable for the duration of a downcall (see the
third bullet here: [1]) "The memory session of R is kept <o:p>
</o:p></span></p>
</div>
<div>
<p class="MsoNormal" style="mso-line-height-alt:.75pt"><span style="font-size:1.0pt;color:white">ZjQcmQRYFpfptBannerStart<o:p></o:p></span></p>
</div>
<table class="MsoNormalTable" style="width:100.0%;border-radius:4px" width="100%" cellspacing="0" cellpadding="0" border="0">
<tbody>
<tr>
<td style="padding:12.0pt 0cm 12.0pt 0cm">
<table class="MsoNormalTable" style="width:100.0%;background:#D0D8DC;border:none;border-top:solid
#90A4AE 3.0pt" width="100%" cellspacing="0" cellpadding="0" border="1">
<tbody>
<tr>
<td style="border:none;padding:0cm 7.5pt 3.75pt
4.5pt" valign="top">
<table class="MsoNormalTable" cellspacing="0" cellpadding="0" border="0" align="left">
<tbody>
<tr>
<td style="padding:3.0pt 6.0pt 3.0pt
6.0pt">
<p class="MsoNormal"><b><span style="font-size:10.5pt;font-family:"Arial",sans-serif;color:black">This
Message Is From an External Sender
<o:p></o:p></span></b></p>
</td>
</tr>
<tr>
<td style="padding:3.0pt 6.0pt 3.0pt
6.0pt">
<p class="MsoNormal"><span style="font-size:9.0pt;font-family:"Arial",sans-serif;color:black">This
message came from outside your
organization.
<o:p></o:p></span></p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<div>
<p class="MsoNormal" style="mso-line-height-alt:.75pt"><span style="font-size:1.0pt;color:white">ZjQcmQRYFpfptBannerEnd<o:p></o:p></span></p>
</div>
<p>Hi Cheng Jin,<o:p></o:p></p>
<p>Thanks for the email.<br>
<br>
In this case it is not a problem since memory sessions
(including upcall stubs) are kept alive/reachable for the
duration of a downcall (see the third bullet here: [1])<o:p></o:p></p>
<p> "The memory session of R is kept alive (and cannot be
closed) during the invocation."<o:p></o:p></p>
<p>Since we don't store a reference to the upcall stub in native
code until after the downcall returns, this is safe to rely
on.<o:p></o:p></p>
<p>Cheers,<br>
Jorn<o:p></o:p></p>
<p>[1]: <a href="https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/foreign/Linker.html#safety" moz-do-not-send="true" class="moz-txt-link-freetext">https://docs.oracle.com/en/java/javase/19/docs/api/java.base/java/lang/foreign/Linker.html#safety</a><o:p></o:p></p>
<div>
<p class="MsoNormal">On 31/01/2023 18:49, Cheng Jin wrote:<o:p></o:p></p>
</div>
<blockquote style="margin-top:5.0pt;margin-bottom:5.0pt">
<p class="MsoNormal">Hi there, <o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal">I notice there might be an issue with the
allocation of upcall stub under an implicit session at
test/jdk/java/foreign/SafeFunctionAccessTest.java in JDK19+
as follows:<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal">e.g.<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal"> @Test<o:p></o:p></p>
<p class="MsoNormal"> public void
testClosedStructCallback() throws Throwable {<o:p></o:p></p>
<p class="MsoNormal"> MethodHandle handle =
Linker.nativeLinker().downcallHandle(<o:p></o:p></p>
<p class="MsoNormal">
findNativeOrThrow("addr_func_cb"),<o:p></o:p></p>
<p class="MsoNormal">
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER));<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal"> try (MemorySession session =
MemorySession.openConfined()) {<o:p></o:p></p>
<p class="MsoNormal"> MemorySegment segment =
MemorySegment.allocateNative(POINT, session);<o:p></o:p></p>
<p class="MsoNormal"> handle.invoke(segment,
sessionChecker(session));
<span style="font-family:Wingdings">ß</span>---------------
the upcall stub is allocated in sessionChecker()<o:p></o:p></p>
<p class="MsoNormal"> }<o:p></o:p></p>
<p class="MsoNormal"> }<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal"> MemorySegment
sessionChecker(MemorySession session) {<o:p></o:p></p>
<p class="MsoNormal"> try {<o:p></o:p></p>
<p class="MsoNormal"> MethodHandle handle =
MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class,
"checkSession",<o:p></o:p></p>
<p class="MsoNormal">
MethodType.methodType(void.class, MemorySession.class));<o:p></o:p></p>
<p class="MsoNormal"> handle =
handle.bindTo(session);<o:p></o:p></p>
<p class="MsoNormal"> return
Linker.nativeLinker().upcallStub(handle,
FunctionDescriptor.ofVoid(), MemorySession.openImplicit());
<span style="font-family:Wingdings">ß</span>-----the upcall
stub is allocated with an implicit session<o:p></o:p></p>
<p class="MsoNormal"> } catch (Throwable ex) {<o:p></o:p></p>
<p class="MsoNormal"> throw new AssertionError(ex);<o:p></o:p></p>
<p class="MsoNormal"> }<o:p></o:p></p>
<p class="MsoNormal" style="text-indent:9.9pt">}<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal">It is correct to allocate the upcall stub
with a session different from the current session in tests
given these tests intend to close the current session in
upcall to verify the behavior. But it is problematic to
exploit an implicit session backed by GC, in which case the
memory of the upcall stub is more likely to be forced to
release by GC (when implicitly closing the session to free
memory) especially on the memory-restricted machines, which
leads to unexpected behavior in upcall (e.g. crash which was
captured recently). To work around this case, I’d suggest to
replace it with a global session or others so as to keep the
upcall stub alive during the upcall.<o:p></o:p></p>
<p class="MsoNormal"> <o:p></o:p></p>
<p class="MsoNormal">Best Regards<o:p></o:p></p>
<p class="MsoNormal">Cheng Jin<o:p></o:p></p>
</blockquote>
</div>
</blockquote>
</body>
</html>