Questions about the trivial downcall

Jorn Vernee jorn.vernee at oracle.com
Thu Jul 27 14:52:53 UTC 2023


So, you would only see a crash for the JNI upcall case only when using a 
debug build, but would see a crash always for the Panama upcall case.

Jorn

On 27/07/2023 16:50, Jorn Vernee wrote:
>
> We have a guarantee in UpcallLinker::on_entry to check that we are 
> coming from the native thread state: 
> https://github.com/openjdk/jdk/blob/8650026ff16e5c5eff897f9fd39c0c35fd8b7367/src/hotspot/share/prims/upcallLinker.cpp#L78 
> (I believe we have a test for that as well).
>
> I think for JNI there's just an assert when we call from native code 
> into the JNI API and transition to the VM thread state from native: 
> https://github.com/openjdk/jdk/blob/8650026ff16e5c5eff897f9fd39c0c35fd8b7367/src/hotspot/share/runtime/interfaceSupport.inline.hpp#L98
>
> Jorn
>
> On 27/07/2023 15:44, Maurizio Cimadamore wrote:
>>
>> The behavior is undefined. It might crash, or not.
>>
>> While the description says that the downcall should not upcall, 
>> please note that the Javadoc doesn't say anywhere that ill-behaved 
>> downcalls will produce new errors. Moreover, the javadoc says this:
>>
>>> Using this linker option when linking non trivial functions is 
>>> likely to have adverse effects, such as loss of performance, or JVM 
>>> crashes.
>>>
>> Basically, you are in undefined behavior territory. It seems to me 
>> that you are inferring too much from the javadoc.
>>
>> While I agree it might be desirable to detect this and log some kind 
>> of error (see email I sent yesterday), I don't think that, as thing 
>> stands, the javadoc is incorrect, and/or lacking information.
>>
>> Maurizio
>>
>>
>> On 27/07/2023 14:40, Cheng Jin wrote:
>>>
>>> But I tried a simple example to indirectly trigger a JNI upcall 
>>> within a trivial FFM downcall by saving a global JNIEnv within a JNI 
>>> downcall at first which works good as follows. Does it mean that a 
>>> FFM trivial downcall literally doesn’t stop a JNI upcall which seems 
>>> inconsistent with the description of 
>>> https://download.java.net/java/early_access/jdk21/docs/api/java.base/java/lang/foreign/Linker.Option.html#isTrivial() 
>>> <https://urldefense.com/v3/__https://download.java.net/java/early_access/jdk21/docs/api/java.base/java/lang/foreign/Linker.Option.html*isTrivial()__;Iw!!ACWV5N9M2RV99hQ!KpMq9yrQInVdYsiAcZWCDTeOZd_GLJwrDN4n06JvbKQ194M_eabqlUaxzgDuSjbnoxQLukS4Uu8wT7q__xolFW5e$>
>>>
>>> or OpenJDK doesn’t support such behavior mixed with JNI & FFM?
>>>
>>> *[1] JniTest.java*
>>>
>>> public class JniTest {
>>>
>>> private static Linker linker = Linker.nativeLinker();
>>>
>>> static {
>>>
>>> System.loadLibrary("jnitest");
>>>
>>>    }
>>>
>>> private static final SymbolLookup nativeLibLookup = 
>>> SymbolLookup.loaderLookup();
>>>
>>> private native int addJNI2Ints(int arg1, int arg2);
>>>
>>> public int *addJNI2Ints_Upcall*(int arg1, int arg2) {
>>>
>>> int sum = arg1 + arg2;
>>>
>>> System.out.println("trivial: addJNI2Ints_Upcall: sum = " + sum);
>>>
>>> return sum;
>>>
>>>     }
>>>
>>> public void test_add2Ints() throws Throwable {
>>>
>>> FunctionDescriptor fd = FunctionDescriptor.of(JAVA_INT, JAVA_INT, 
>>> JAVA_INT);
>>>
>>> MemorySegment functionSymbol = nativeLibLookup.find("add2Ints").get();
>>>
>>> MethodHandle mh = linker.downcallHandle(functionSymbol, fd, 
>>> *Linker.Option.isTrivial()*);
>>>
>>> int result = (int)mh.invokeExact(112, 123);
>>>
>>> System.out.println("test_add2Ints result = " + result);
>>>
>>> }
>>>
>>> public static void main(String[] args) throws Throwable {
>>>
>>> JniTest test = new JniTest();
>>>
>>> int sum = test.*addJNI2Ints*(112, 123); ß-- call the JNI native at 
>>> first to save the global JNIEnv & jobject
>>>
>>> System.out.println("addJNI2Ints sum = " + sum);
>>>
>>> test.test_*add2Ints*(); ß-- use saved the global JNIEnv & jobject to 
>>> trigger a JNI upcall in native.
>>>
>>>    }
>>>
>>> }
>>>
>>> *[2] JniTest.c*
>>>
>>> #include <jni.h>
>>>
>>> #include "JniTest.h"
>>>
>>> JNIEnv *globalEnv;
>>>
>>> jobject globalObj;
>>>
>>> JNIEXPORT int JNICALL Java_JniTest_addJNI2Ints(JNIEnv *env, jobject 
>>> thisObj, jint arg1, jint arg2) {
>>>
>>>     jint intSum = arg1 + arg2;
>>>
>>> globalEnv = env;
>>>
>>> globalObj = thisObj;
>>>
>>> printf("\nJava_JniTest_addJNI2Ints: env = %p, globalObj = %p\n", 
>>> globalEnv, globalObj);
>>>
>>> return intSum;
>>>
>>> }
>>>
>>> int
>>>
>>> add2Ints(int intArg1, int intArg2)
>>>
>>> {
>>>
>>> printf("\nadd2Ints: globalEnv = %p, globalObj = %p\n", globalEnv, 
>>> globalObj);
>>>
>>> jclass clazz = (*globalEnv)->GetObjectClass(globalEnv, globalObj);
>>>
>>> jmethodID method = (*globalEnv)->GetMethodID(globalEnv, clazz, 
>>> "addJNI2Ints_Upcall", "(II)I");
>>>
>>> return (*globalEnv)->CallIntMethod(globalEnv, globalObj, method, 
>>> intArg1, intArg2);
>>>
>>> }
>>>
>>> [3]Commands:
>>>
>>> ./jdk21_hotspot_x86_64/bin/javac --enable-preview --source 21  -h . 
>>> JniTest.java
>>>
>>> gcc -fPIC -I"./jdk21_hotspot_x86_64/include" 
>>> -I"./jdk21_hotspot_x86_64/include/linux" -shared -g  -o 
>>> libjnitest.so  JniTest.c
>>>
>>> ./jdk21_hotspot_x86_64/bin/java --enable-preview  
>>> -Djava.library.path=./jnitests --enable-native-access=ALL-UNNAMED 
>>> -Dforeign.restricted=permit   JniTest
>>>
>>> [4] Output:
>>>
>>> Java_JniTest_addJNI2Ints: env = 0x7f81f8028a08, globalObj = 
>>> 0x7f81ff379998
>>>
>>> addJNI2Ints sum = 235
>>>
>>> add2Ints: intSum = 235
>>>
>>> add2Ints: globalEnv = 0x7f81f8028a08, globalObj = 0x7f81ff379998
>>>
>>> *trivial: addJNI2Ints_Upcall: sum = 235 **ß***
>>>
>>> test_add2Ints result = 235
>>>
>>> Best Regards
>>>
>>> Cheng Jin
>>>
>>> *From:*Maurizio Cimadamore <maurizio.cimadamore at oracle.com>
>>> *Sent:* Wednesday, July 26, 2023 8:45 PM
>>> *To:* Cheng Jin <jincheng at ca.ibm.com>; panama-dev at openjdk.org
>>> *Subject:* [EXTERNAL] Re: Questions about the trivial downcall
>>>
>>> On 27/07/2023 01: 40, Cheng Jin wrote: >> Right, JNI has not much to 
>>> do with this. There's different ways to upcall back into Java (JNI 
>>> and FFM upcall stubs). If so, my understanding is that both JNI and 
>>> FFM upcall should crash the VM
>>>
>>> ZjQcmQRYFpfptBannerStart
>>>
>>> *This Message Is From an External Sender *
>>>
>>> This message came from outside your organization.
>>>
>>> *  Report Suspicious * 
>>> <https://us-phishalarm-ewt.proofpoint.com/EWT/v1/PjiDSg!2c-hYpT1IJMQ-iYRkuvEjWPesZVtAje5jWUBYoL2pAM1wRGP31iKedgOBvqFHgP6rIVsAh-wdcVItzgnQJ8qkuKPFDFOpo-3zg$>  ‌ 
>>>
>>>
>>> ZjQcmQRYFpfptBannerEnd
>>>
>>> On 27/07/2023 01:40, Cheng Jin wrote:
>>>
>>>     >> Right, JNI has not much to do with this. There's different
>>>     ways to upcall back into Java (JNI and FFM upcall stubs).
>>>
>>>     If so, my understanding is that both JNI and FFM upcall should
>>>     crash the VM in a trivial FFM downcall, correct?
>>>
>>> I believe so. Jorn will confirm.
>>>
>>> Maurizio
>>>
>>>     Best Regards
>>>
>>>     Cheng Jin
>>>
>>>     *From:*Maurizio Cimadamore <maurizio.cimadamore at oracle.com>
>>>     <mailto:maurizio.cimadamore at oracle.com>
>>>     *Sent:* Wednesday, July 26, 2023 8:29 PM
>>>     *To:* Cheng Jin <jincheng at ca.ibm.com>
>>>     <mailto:jincheng at ca.ibm.com>; panama-dev at openjdk.org
>>>     <mailto:panama-dev at openjdk.org>
>>>     *Subject:* [EXTERNAL] Re: Questions about the trivial downcall
>>>
>>>     and does not call back into Java (e. g. using an upcall stub).
>>>     ß------------------------- it occurs to me that any kind of
>>>     upcall should be disallowed in such case whether it is JNI or
>>>     FFM, am I correct? Right, JNI has not much to do with this. 
>>>
>>>     ZjQcmQRYFpfptBannerStart
>>>
>>>     *This Message Is From an External Sender *
>>>
>>>     This message came from outside your organization.
>>>
>>>     *  Report Suspicious *
>>>     <https://us-phishalarm-ewt.proofpoint.com/EWT/v1/PjiDSg!2c-gyJS_6lP6sKYb2IHEjb2TaeBqM7xJQ9RStijLaImfla_LrFEK8T2PB5_umxDwUuL8X7BGAHosOeXRLCglJfzdYnJaiHF6gQ$>  ‌
>>>
>>>
>>>     ZjQcmQRYFpfptBannerEnd
>>>
>>>         and does not call back into Java (e.g. using an upcall
>>>         stub). ß------------------------- it occurs to me that any
>>>         kind of upcall should be disallowed in such case whether it
>>>         is JNI or FFM, am I correct?
>>>
>>>     Right, JNI has not much to do with this. There's different ways
>>>     to upcall back into Java (JNI and FFM upcall stubs).
>>>
>>>     Your question is: what happens if we do call back when we are in
>>>     trivial mode.
>>>
>>>     I believe the answer is "we crash" (but not in a "nice way"),
>>>     and I'm not too sure we can do much to prevent that. I believe
>>>     Jorn knows more on that topic.
>>>
>>>     Maurizio
>>>
>>>         Best Regards
>>>
>>>         Cheng Jin
>>>
>>>         *From:*Maurizio Cimadamore <maurizio.cimadamore at oracle.com>
>>>         <mailto:maurizio.cimadamore at oracle.com>
>>>         *Sent:* Wednesday, July 26, 2023 7:44 PM
>>>         *To:* Cheng Jin <jincheng at ca.ibm.com>
>>>         <mailto:jincheng at ca.ibm.com>; panama-dev at openjdk.org
>>>         <mailto:panama-dev at openjdk.org>
>>>         *Subject:* [EXTERNAL] Re: Questions about the trivial downcall
>>>
>>>         Hi, a trivial downcall is supposed to target a native
>>>         function that (a) terminates quickly (so as not to block the
>>>         GC for too long) and (b) does not upcall into Java. So, no,
>>>         a trivial downcall cannot trigger upcalls (I don't think we
>>>         detect
>>>
>>>         ZjQcmQRYFpfptBannerStart
>>>
>>>         *This Message Is From an External Sender *
>>>
>>>         This message came from outside your organization.
>>>
>>>         *  Report Suspicious *
>>>         <https://us-phishalarm-ewt.proofpoint.com/EWT/v1/PjiDSg!2c-r455VanMQ2qYReaGl7Z4QRx46ATMxAjK9nbXsj13QlLeimn0PtcIAb-U80cJ-A34Q8dYfoQ6JN5nMYOpBz-Hn3cuAGTZLJQ$>  ‌
>>>
>>>
>>>         ZjQcmQRYFpfptBannerEnd
>>>
>>>         Hi,
>>>         a trivial downcall is supposed to target a native function
>>>         that (a) terminates quickly (so as not to block the GC for
>>>         too long) and (b) does not upcall into Java.
>>>
>>>         So, no, a trivial downcall cannot trigger upcalls (I don't
>>>         think we detect this, I believe the JVM just crashes if you do).
>>>
>>>         Also I notice that you speak of "JNI" upcall. Is that what
>>>         you really mean - e.g. a trivial FFM downcall making an
>>>         upcall using JNI? In general, I think it's a bit hard for
>>>         downcalls to do anything that has to do with JNI because the
>>>         invoked native function is not passed a JNIEnv - FFM really
>>>         just bridges the Java code with the native library function
>>>         you want to call, nothing more. (I suppose that function
>>>         could get its hands on the VM using the JNI attach API, but
>>>         I feel that's a different question from what you were asking?).
>>>
>>>         Maurizio
>>>
>>>         On 26/07/2023 22:23, Cheng Jin wrote:
>>>
>>>             Hi there,
>>>
>>>             I’ve got a couple of questions about the behavior of a
>>>             trivial downcall (with *isTrivial* specified).
>>>
>>>             Is a FFI downcall able to trigger a JNI upcall ?
>>>
>>>             If so, should a JNI upcall should be captured in this
>>>             trivial downcall?
>>>
>>>             Best Regards
>>>
>>>             Cheng Jin
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/panama-dev/attachments/20230727/d7f6bbb0/attachment-0001.htm>


More information about the panama-dev mailing list