]> git.ipfire.org Git - thirdparty/open-vm-tools.git/commitdiff
Fix Guest RPC Channel clean up memory leak on the guest side.
authorOliver Kurth <okurth@vmware.com>
Fri, 26 Oct 2018 17:44:56 +0000 (10:44 -0700)
committerOliver Kurth <okurth@vmware.com>
Fri, 26 Oct 2018 17:44:56 +0000 (10:44 -0700)
The RpcChannel_Destroy() is leaking the memory on the outLock.
Refactored the code so that RpcChannel_Shutdown() matches the
RpcChannel_Setup() if invoked, and RpcChannel_Destroy() just calls
RpcChannel_Shutdown()

open-vm-tools/lib/include/vmware/tools/guestrpc.h
open-vm-tools/lib/rpcChannel/bdoorChannel.c
open-vm-tools/lib/rpcChannel/rpcChannel.c
open-vm-tools/lib/rpcChannel/vsockChannel.c
open-vm-tools/services/vmtoolsd/mainPosix.c

index 768b6ba15980750967dc3225479798b384849ca4..ee34802d549220c9595dc7876461698c0be0ebfb 100644 (file)
@@ -186,9 +186,6 @@ RpcChannel *
 RpcChannel_Create(void);
 
 void
-RpcChannel_Shutdown(RpcChannel *chan);
-
-gboolean
 RpcChannel_Destroy(RpcChannel *chan);
 
 gboolean
index f8886fa3211a3b2ae27b7dd4a54d8e70b0c6ffdc..8856c60b0c9c2ab4f3d6ad5bdc1658e833187cd5 100644 (file)
@@ -91,8 +91,8 @@ BkdoorChannelStop(RpcChannel *chan)
    if (bdoor->out != NULL) {
       if (chan->outStarted) {
          RpcOut_stop(bdoor->out);
+         chan->outStarted = FALSE;
       }
-      chan->outStarted = FALSE;
    } else {
       ASSERT(!chan->outStarted);
    }
index 06d0cec8d1a258007b2098138298c172995c4731..5e7eb568023cee76a9b7ae7aabaa8016bec7378a 100644 (file)
@@ -52,6 +52,7 @@ typedef struct RpcChannelInt {
    RpcChannelResetCb       resetCb;
    gpointer                resetData;
    gboolean                rpcError;
+   guint                   rpcErrorCount;
    guint                   rpcResetErrorCount;  /* channel reset failures */
    /*
     * The rpcFailureCount is a cumulative count of calls made to
@@ -61,6 +62,7 @@ typedef struct RpcChannelInt {
    guint                   rpcFailureCount;  /* cumulative channel failures */
    RpcChannelFailureCb     rpcFailureCb;
    guint                   rpcMaxFailures;
+   gboolean                rpcInInitialized;
 #endif
 } RpcChannelInt;
 
@@ -453,6 +455,8 @@ RpcChannel_Setup(RpcChannel *chan,
    size_t i;
    RpcChannelInt *cdata = (RpcChannelInt *) chan;
 
+   ASSERT(!cdata->rpcInInitialized);
+
    cdata->appName = g_strdup(appName);
    cdata->appCtx = appCtx;
    cdata->mainCtx = g_main_context_ref(mainCtx);
@@ -479,6 +483,64 @@ RpcChannel_Setup(RpcChannel *chan,
       chan->in = RpcIn_Construct(mainCtx, RpcChannel_Dispatch, chan);
       ASSERT(chan->in != NULL);
    }
+
+   cdata->rpcInInitialized = TRUE;
+}
+
+
+/**
+ * Undo the RpcChannel_Setup() function if it was called earlier
+ *
+ * Note the outLock is initialized in the RpcChannel_New() function,
+ * and should only be freed in the corresponding RpcChannel_Destroy() function.
+ *
+ * @param[in]  chan        The RPC channel instance.
+ */
+
+static void
+RpcChannelTeardown(RpcChannel *chan)
+{
+   size_t i;
+   RpcChannelInt *cdata = (RpcChannelInt *) chan;
+
+   if (NULL == cdata || !cdata->rpcInInitialized) {
+      return;
+   }
+
+   RpcChannel_UnregisterCallback(chan, &cdata->resetReg);
+   for (i = 0; i < ARRAYSIZE(gRpcHandlers); i++) {
+      RpcChannel_UnregisterCallback(chan, &gRpcHandlers[i]);
+   }
+
+   if (cdata->rpcs != NULL) {
+      g_hash_table_destroy(cdata->rpcs);
+      cdata->rpcs = NULL;
+   }
+
+   cdata->resetCb = NULL;
+   cdata->resetData = NULL;
+   cdata->appCtx = NULL;
+   cdata->rpcFailureCb = NULL;
+
+   g_free(cdata->appName);
+   cdata->appName = NULL;
+
+   if (chan->mainCtx != NULL) {
+      g_main_context_unref(chan->mainCtx);
+      chan->mainCtx = NULL;
+   }
+
+   if (cdata->mainCtx != NULL) {
+      g_main_context_unref(cdata->mainCtx);
+      cdata->mainCtx = NULL;
+   }
+
+   if (cdata->resetCheck != NULL) {
+      g_source_destroy(cdata->resetCheck);
+      cdata->resetCheck = NULL;
+   }
+
+   cdata->rpcInInitialized = FALSE;
 }
 
 
@@ -613,55 +675,39 @@ RpcChannel_Create(void)
 /**
  * Shuts down an RPC channel and release any held resources.
  *
+ * We use the outLock to protect the chan->inStarted and chan->in as well,
+ * even though a separate lock is more desirable in theory to reduce contention.
+ * See RpcChannel_Stop() and RpcChannelStopNoLock() where it is done same.
+ *
  * @param[in]  chan     The RPC channel.
  *
- * @return  Whether the channel was shut down successfully.
  */
 
-gboolean
+void
 RpcChannel_Destroy(RpcChannel *chan)
 {
-#if defined(NEED_RPCIN)
-   size_t i;
-#endif
-   RpcChannelInt *cdata = (RpcChannelInt *) chan;
-
-   if (cdata->impl.funcs != NULL && cdata->impl.funcs->shutdown != NULL) {
-      cdata->impl.funcs->shutdown(chan);
-   }
-
-#if defined(NEED_RPCIN)
-   RpcChannel_UnregisterCallback(chan, &cdata->resetReg);
-   for (i = 0; i < ARRAYSIZE(gRpcHandlers); i++) {
-      RpcChannel_UnregisterCallback(chan, &gRpcHandlers[i]);
-   }
-
-   if (cdata->rpcs != NULL) {
-      g_hash_table_destroy(cdata->rpcs);
-      cdata->rpcs = NULL;
+   if (NULL == chan) {
+      return;
    }
 
-   cdata->resetCb = NULL;
-   cdata->resetData = NULL;
-   cdata->appCtx = NULL;
-   cdata->rpcFailureCb = NULL;
+   g_static_mutex_lock(&chan->outLock);
 
-   g_free(cdata->appName);
-   cdata->appName = NULL;
+   RpcChannelStopNoLock(chan);
 
-   if (cdata->mainCtx != NULL) {
-      g_main_context_unref(cdata->mainCtx);
-      cdata->mainCtx = NULL;
+   if (chan->funcs != NULL && chan->funcs->shutdown != NULL) {
+      /* backend shutdown function also frees backend resources */
+      chan->funcs->shutdown(chan);
    }
 
-   if (cdata->resetCheck != NULL) {
-      g_source_destroy(cdata->resetCheck);
-      cdata->resetCheck = NULL;
-   }
+#if defined(NEED_RPCIN)
+   RpcChannelTeardown(chan);
 #endif
 
-   g_free(cdata);
-   return TRUE;
+   g_static_mutex_unlock(&chan->outLock);
+
+   g_static_mutex_free(&chan->outLock);
+
+   g_free(chan);
 }
 
 
@@ -756,41 +802,6 @@ RpcChannel_New(void)
 }
 
 
-/**
- * Wrapper for the shutdown function of an RPC channel struct.
- *
- * @param[in]  chan        The RPC channel instance.
- */
-
-void
-RpcChannel_Shutdown(RpcChannel *chan)
-{
-   if (chan != NULL) {
-      g_static_mutex_free(&chan->outLock);
-   }
-
-   if (chan != NULL && chan->funcs != NULL && chan->funcs->shutdown != NULL) {
-#if defined(NEED_RPCIN)
-      if (chan->in != NULL) {
-         if (chan->inStarted) {
-            RpcIn_stop(chan->in);
-         }
-         chan->inStarted = FALSE;
-         RpcIn_Destruct(chan->in);
-         chan->in = NULL;
-      } else {
-         ASSERT(!chan->inStarted);
-      }
-
-      if (chan->mainCtx != NULL) {
-         g_main_context_unref(chan->mainCtx);
-      }
-#endif
-      chan->funcs->shutdown(chan);
-   }
-}
-
-
 /**
  * Start an RPC channel. We may fallback to backdoor channel when other type
  * of channel fails to start.
@@ -865,8 +876,8 @@ RpcChannelStopNoLock(RpcChannel *chan)
    if (chan->in != NULL) {
       if (chan->inStarted) {
          RpcIn_stop(chan->in);
+         chan->inStarted = FALSE;
       }
-      chan->inStarted = FALSE;
    } else {
       ASSERT(!chan->inStarted);
    }
index 6e054b8ba4e4cc6793d097f5203335b14498c25d..e8b80a2426211ded2346e08c03b80667f55cb172 100644 (file)
@@ -316,8 +316,8 @@ VSockChannelOnStartErr(RpcChannel *chan)    // IN
 {
    VSockChannel *vsock = chan->_private;
 
-   /* destroy VSockOut part only */
    VSockOutDestruct(vsock->out);
+   g_free(vsock);
    chan->_private = NULL;
 }
 
@@ -382,8 +382,8 @@ VSockChannelStop(RpcChannel *chan)   // IN
    if (vsock->out != NULL) {
       if (chan->outStarted) {
          VSockOutStop(vsock->out);
+         chan->outStarted = FALSE;
       }
-      chan->outStarted = FALSE;
    } else {
       ASSERT(!chan->outStarted);
    }
index 4a2fa2bcdd7890d62ca97ee6b3c73709e9522d39..c60f0690ee7f7899cae56c6352edb33b32d9135b 100644 (file)
@@ -105,7 +105,7 @@ ToolsCoreSigUsrHandler(const siginfo_t *info,
 
    g_info("Shutting down guestrpc on signal USR1 ...\n");
    if (TOOLS_IS_USER_SERVICE(&gState.ctx)) {
-      RpcChannel_Shutdown(gState.ctx.rpc);
+      RpcChannel_Destroy(gState.ctx.rpc);
       gState.ctx.rpc = NULL;
    }