Author: amiloslavskiy
Date: Thu Oct 15 10:15:47 2020
New Revision: 1882522

URL: http://svn.apache.org/viewvc?rev=1882522&view=rev
Log:
JavaHL: Fix crash in TunnelAgent.CloseTunnelCallback after GC

When jobject reference is kept across different JNI calls, a new global
reference must be requested with NewGlobalRef(). Otherwise, GC is free
to remove the object. Even if Java code keeps a reference to the object,
GC can still move the object around, invalidating the kept jobject,
which results in a native crash when trying to access it.

This crash is demonstrated by the following JavaHL test:
'testCrash_RemoteSession_nativeDispose'

[in subversion/bindings/javahl]
* native/OperationContext.cpp
  (callCloseTunnelCallback): Extract function to facilitate changes in
                             further commits.
  (openTunnel):              Add NewGlobalRef() for kept jobject.
  (callCloseTunnelCallback): Add a matching DeleteGlobalRef().

Modified:
    
subversion/branches/javahl-1.14-fixes/subversion/bindings/javahl/native/OperationContext.cpp

Modified: 
subversion/branches/javahl-1.14-fixes/subversion/bindings/javahl/native/OperationContext.cpp
URL: 
http://svn.apache.org/viewvc/subversion/branches/javahl-1.14-fixes/subversion/bindings/javahl/native/OperationContext.cpp?rev=1882522&r1=1882521&r2=1882522&view=diff
==============================================================================
--- 
subversion/branches/javahl-1.14-fixes/subversion/bindings/javahl/native/OperationContext.cpp
 (original)
+++ 
subversion/branches/javahl-1.14-fixes/subversion/bindings/javahl/native/OperationContext.cpp
 Thu Oct 15 10:15:47 2020
@@ -629,23 +629,17 @@ OperationContext::openTunnel(svn_stream_
           jtunnel_name, juser, jhostname, jint(port)),
       SVN_ERR_BASE);
 
+  if (tc->jclosecb)
+    {
+      tc->jclosecb = env->NewGlobalRef(tc->jclosecb);
+      SVN_JNI_CATCH(, SVN_ERR_BASE);
+    }
+
   return SVN_NO_ERROR;
 }
 
-void
-OperationContext::closeTunnel(void *tunnel_context, void *)
+void callCloseTunnelCallback(JNIEnv* env, jobject jclosecb)
 {
-  TunnelContext* tc = static_cast<TunnelContext*>(tunnel_context);
-  jobject jclosecb = tc->jclosecb;
-  delete tc;
-
-  if (!jclosecb)
-    return;
-
-  JNIEnv *env = JNIUtil::getEnv();
-  if (JNIUtil::isJavaExceptionThrown())
-    return;
-
   static jmethodID mid = 0;
   if (0 == mid)
     {
@@ -656,4 +650,20 @@ OperationContext::closeTunnel(void *tunn
       SVN_JNI_CATCH_VOID(mid = env->GetMethodID(cls, "closeTunnel", "()V"));
     }
   SVN_JNI_CATCH_VOID(env->CallVoidMethod(jclosecb, mid));
+  env->DeleteGlobalRef(jclosecb);
+}
+
+void
+OperationContext::closeTunnel(void *tunnel_context, void *)
+{
+  TunnelContext* tc = static_cast<TunnelContext*>(tunnel_context);
+  jobject jclosecb = tc->jclosecb;
+  delete tc;
+
+  JNIEnv *env = JNIUtil::getEnv();
+  if (JNIUtil::isJavaExceptionThrown())
+    return;
+
+  if (jclosecb)
+    callCloseTunnelCallback(env, jclosecb);
 }


Reply via email to