Index: /trunk/src/VBox/Devices/VMMDev/VMMDev.cpp
===================================================================
--- /trunk/src/VBox/Devices/VMMDev/VMMDev.cpp	(revision 90263)
+++ /trunk/src/VBox/Devices/VMMDev/VMMDev.cpp	(revision 90264)
@@ -104,4 +104,5 @@
 #include <iprt/buildconfig.h>
 #include <iprt/string.h>
+#include <iprt/system.h>
 #include <iprt/time.h>
 #ifndef IN_RC
@@ -4413,5 +4414,5 @@
      * Everything HGCM.
      */
-    vmmdevR3HgcmDestroy(pDevIns, pThisCC);
+    vmmdevR3HgcmDestroy(pDevIns, PDMDEVINS_2_DATA(pDevIns, PVMMDEV), pThisCC);
 #endif
 
@@ -4563,5 +4564,14 @@
                                   "TestingEnabled|"
                                   "TestingMMIO|"
-                                  "TestintXmlOutputFile"
+                                  "TestintXmlOutputFile|"
+                                  "HGCMHeapBudgetDefault|"
+                                  "HGCMHeapBudgetLegacy|"
+                                  "HGCMHeapBudgetVBoxGuest|"
+                                  "HGCMHeapBudgetOtherDrv|"
+                                  "HGCMHeapBudgetRoot|"
+                                  "HGCMHeapBudgetSystem|"
+                                  "HGCMHeapBudgetReserved1|"
+                                  "HGCMHeapBudgetUser|"
+                                  "HGCMHeapBudgetGuest"
                                   ,
                                   "");
@@ -4642,4 +4652,68 @@
 #endif
 
+#ifdef VBOX_WITH_HGCM
+    /*
+     * Heap budgets for HGCM requestor categories.  Take the available host
+     * memory as a rough hint of how much we can handle.
+     */
+    /** @todo If we reduced the number of categories here, we could alot more to
+     *        each... */
+    uint64_t cbDefaultBudget = 0;
+    if (RT_FAILURE(RTSystemQueryTotalRam(&cbDefaultBudget)))
+        cbDefaultBudget = 16 * _1G64;
+    LogFunc(("RTSystemQueryTotalRam -> %'RU64 (%RX64)\n", cbDefaultBudget, cbDefaultBudget));
+# if ARCH_BITS == 32
+    cbDefaultBudget  = RT_MIN(cbDefaultBudget, _512M);
+# endif
+    cbDefaultBudget /= 8;                               /* One eighth of physical memory ... */
+    cbDefaultBudget /= RT_ELEMENTS(pThisCC->aHgcmAcc);  /* over 8 accounting categories. (8GiB -> 64MiB) */
+    cbDefaultBudget  = RT_MIN(cbDefaultBudget, _512M);  /* max 512MiB */
+    cbDefaultBudget  = RT_MAX(cbDefaultBudget, _32M);   /* min  32MiB */
+    rc = pHlp->pfnCFGMQueryU64Def(pCfg, "HGCMHeapBudgetDefault", &cbDefaultBudget, cbDefaultBudget);
+    if (RT_FAILURE(rc))
+        return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed querying \"HGCMHeapBudgetDefault\" as a 64-bit unsigned integer"));
+
+    LogRel(("VMMDev: cbDefaultBudget: %'RU64 (%RX64)\n", cbDefaultBudget, cbDefaultBudget));
+    static const struct { const char *pszName; unsigned idx; } s_aCfgHeapBudget[] =
+    {
+        { "HGCMHeapBudgetLegacy",       VMMDEV_REQUESTOR_USR_NOT_GIVEN  },
+        { "HGCMHeapBudgetVBoxGuest",    VMMDEV_REQUESTOR_USR_DRV        },
+        { "HGCMHeapBudgetOtherDrv",     VMMDEV_REQUESTOR_USR_DRV_OTHER  },
+        { "HGCMHeapBudgetRoot",         VMMDEV_REQUESTOR_USR_ROOT       },
+        { "HGCMHeapBudgetSystem",       VMMDEV_REQUESTOR_USR_SYSTEM     },
+        { "HGCMHeapBudgetReserved1",    VMMDEV_REQUESTOR_USR_RESERVED1  },
+        { "HGCMHeapBudgetUser",         VMMDEV_REQUESTOR_USR_USER       },
+        { "HGCMHeapBudgetGuest",        VMMDEV_REQUESTOR_USR_GUEST      },
+    };
+    AssertCompile(RT_ELEMENTS(s_aCfgHeapBudget) == RT_ELEMENTS(pThisCC->aHgcmAcc));
+    for (uintptr_t i = 0; i < RT_ELEMENTS(s_aCfgHeapBudget); i++)
+    {
+        uintptr_t const idx = s_aCfgHeapBudget[i].idx;
+        rc = pHlp->pfnCFGMQueryU64Def(pCfg, s_aCfgHeapBudget[i].pszName,
+                                      &pThisCC->aHgcmAcc[idx].cbHeapBudgetConfig, cbDefaultBudget);
+        if (RT_FAILURE(rc))
+            return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+                                       N_("Configuration error: Failed querying \"%s\" as a 64-bit unsigned integer"),
+                                       s_aCfgHeapBudget[i].pszName);
+        pThisCC->aHgcmAcc[idx].cbHeapBudget = pThisCC->aHgcmAcc[idx].cbHeapBudgetConfig;
+        if (pThisCC->aHgcmAcc[idx].cbHeapBudgetConfig != cbDefaultBudget)
+            LogRel(("VMMDev: %s: %'RU64 (%#RX64)\n", s_aCfgHeapBudget[i].pszName,
+                    pThisCC->aHgcmAcc[idx].cbHeapBudgetConfig, pThisCC->aHgcmAcc[idx].cbHeapBudgetConfig));
+
+        const char * const pszCatName = &s_aCfgHeapBudget[i].pszName[sizeof("HGCMHeapBudget") - 1];
+        PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aHgcmAcc[idx].cbHeapBudget, STAMTYPE_U64, STAMVISIBILITY_ALWAYS,
+                               STAMUNIT_BYTES, "Currently available budget", "HGCM-%s/BudgetAvailable", pszCatName);
+        PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aHgcmAcc[idx].cbHeapBudgetConfig, STAMTYPE_U64, STAMVISIBILITY_ALWAYS,
+                               STAMUNIT_BYTES, "Configured budget",          "HGCM-%s/BudgetConfig", pszCatName);
+        PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aHgcmAcc[idx].cbHeapTotal, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
+                               STAMUNIT_BYTES, "Total heap usage",           "HGCM-%s/cbHeapTotal", pszCatName);
+        PDMDevHlpSTAMRegisterF(pDevIns, &pThisCC->aHgcmAcc[idx].cTotalMessages, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS,
+                               STAMUNIT_COUNT, "Total messages",             "HGCM-%s/cTotalMessages", pszCatName);
+    }
+#endif
+
+    /*
+     * <missing comment>
+     */
     pThis->cbGuestRAM = MMR3PhysGetRamSize(PDMDevHlpGetVM(pDevIns));
 
Index: /trunk/src/VBox/Devices/VMMDev/VMMDevHGCM.cpp
===================================================================
--- /trunk/src/VBox/Devices/VMMDev/VMMDevHGCM.cpp	(revision 90263)
+++ /trunk/src/VBox/Devices/VMMDev/VMMDevHGCM.cpp	(revision 90264)
@@ -182,4 +182,10 @@
     PGMPAGEMAPLOCK      ReqMapLock;
 
+    /** The accounting index (into VMMDEVR3::aHgcmAcc). */
+    uint8_t             idxHeapAcc;
+    uint8_t             abPadding[3];
+    /** The heap cost of this command. */
+    uint32_t            cbHeapCost;
+
     /** The STAM_GET_TS() value when the request arrived. */
     uint64_t            tsArrival;
@@ -231,13 +237,20 @@
 typedef struct VBOXHGCMCMDCACHED
 {
-    VBOXHGCMCMD         Core;           /**< 112 */
+    VBOXHGCMCMD         Core;           /**< 120 */
     VBOXHGCMGUESTPARM   aGuestParms[6]; /**< 40 * 6 = 240 */
     VBOXHGCMSVCPARM     aHostParms[6];  /**< 24 * 6 = 144 */
-} VBOXHGCMCMDCACHED;                    /**< 112+240+144 = 496 */
-AssertCompile(sizeof(VBOXHGCMCMD) <= 112);
+} VBOXHGCMCMDCACHED;                    /**< 120+240+144 = 504 */
+AssertCompile(sizeof(VBOXHGCMCMD) <= 120);
 AssertCompile(sizeof(VBOXHGCMGUESTPARM) <= 40);
 AssertCompile(sizeof(VBOXHGCMSVCPARM) <= 24);
 AssertCompile(sizeof(VBOXHGCMCMDCACHED) <= 512);
 AssertCompile(sizeof(VBOXHGCMCMDCACHED) > sizeof(VBOXHGCMCMD) + sizeof(HGCMServiceLocation));
+
+
+/*********************************************************************************************************************************
+*   Internal Functions                                                                                                           *
+*********************************************************************************************************************************/
+DECLINLINE(void *) vmmdevR3HgcmCallMemAllocZ(PVMMDEVCC pThisCC, PVBOXHGCMCMD pCmd, size_t cbRequested);
+
 
 
@@ -268,4 +281,8 @@
                                          uint32_t cbRequest, uint32_t cParms, uint32_t fRequestor)
 {
+    /* For heap accounting. */
+    uintptr_t const idxHeapAcc = fRequestor != VMMDEV_REQUESTOR_LEGACY
+                               ? fRequestor & VMMDEV_REQUESTOR_USR_MASK
+                               : VMMDEV_REQUESTOR_USR_NOT_GIVEN;
 #if 1
     /*
@@ -276,24 +293,39 @@
     if (cParms <= RT_ELEMENTS(pCmdCached->aGuestParms))
     {
-        int rc = RTMemCacheAllocEx(pThisCC->hHgcmCmdCache, (void **)&pCmdCached);
-        if (RT_SUCCESS(rc))
-        {
-            RT_ZERO(*pCmdCached);
-            pCmdCached->Core.fMemCache  = true;
-            pCmdCached->Core.GCPhys     = GCPhys;
-            pCmdCached->Core.cbRequest  = cbRequest;
-            pCmdCached->Core.enmCmdType = enmCmdType;
-            pCmdCached->Core.fRequestor = fRequestor;
-            if (enmCmdType == VBOXHGCMCMDTYPE_CALL)
-            {
-                pCmdCached->Core.u.call.cParms       = cParms;
-                pCmdCached->Core.u.call.paGuestParms = pCmdCached->aGuestParms;
-                pCmdCached->Core.u.call.paHostParms  = pCmdCached->aHostParms;
-            }
-            else if (enmCmdType == VBOXHGCMCMDTYPE_CONNECT)
-                pCmdCached->Core.u.connect.pLoc = (HGCMServiceLocation *)(&pCmdCached->Core + 1);
-
-            return &pCmdCached->Core;
+        if (sizeof(*pCmdCached) <= pThisCC->aHgcmAcc[idxHeapAcc].cbHeapBudget)
+        {
+            int rc = RTMemCacheAllocEx(pThisCC->hHgcmCmdCache, (void **)&pCmdCached);
+            if (RT_SUCCESS(rc))
+            {
+                RT_ZERO(*pCmdCached);
+                pCmdCached->Core.fMemCache  = true;
+                pCmdCached->Core.GCPhys     = GCPhys;
+                pCmdCached->Core.cbRequest  = cbRequest;
+                pCmdCached->Core.enmCmdType = enmCmdType;
+                pCmdCached->Core.fRequestor = fRequestor;
+                pCmdCached->Core.idxHeapAcc = (uint8_t)idxHeapAcc;
+                pCmdCached->Core.cbHeapCost = sizeof(*pCmdCached);
+                Log5Func(("aHgcmAcc[%zu] %#RX64 -= %#zx (%p)\n",
+                          idxHeapAcc, pThisCC->aHgcmAcc[idxHeapAcc].cbHeapBudget, sizeof(*pCmdCached), &pCmdCached->Core));
+                pThisCC->aHgcmAcc[idxHeapAcc].cbHeapBudget -= sizeof(*pCmdCached);
+
+                if (enmCmdType == VBOXHGCMCMDTYPE_CALL)
+                {
+                    pCmdCached->Core.u.call.cParms       = cParms;
+                    pCmdCached->Core.u.call.paGuestParms = pCmdCached->aGuestParms;
+                    pCmdCached->Core.u.call.paHostParms  = pCmdCached->aHostParms;
+                }
+                else if (enmCmdType == VBOXHGCMCMDTYPE_CONNECT)
+                    pCmdCached->Core.u.connect.pLoc = (HGCMServiceLocation *)(&pCmdCached->Core + 1);
+
+                Assert(!pCmdCached->Core.pvReqLocked);
+
+                Log3Func(("returns %p (enmCmdType=%d GCPhys=%RGp)\n", &pCmdCached->Core, enmCmdType, GCPhys));
+                return &pCmdCached->Core;
+            }
         }
+        else
+            LogFunc(("Heap budget overrun: sizeof(*pCmdCached)=%#zx aHgcmAcc[%zu].cbHeapBudget=%#RX64 - enmCmdType=%d\n",
+                     sizeof(*pCmdCached), idxHeapAcc, pThisCC->aHgcmAcc[idxHeapAcc].cbHeapBudget, enmCmdType));
         return NULL;
     }
@@ -307,28 +339,38 @@
     const uint32_t cbCmd = sizeof(VBOXHGCMCMD) + cParms * (sizeof(VBOXHGCMGUESTPARM) + sizeof(VBOXHGCMSVCPARM))
                          + (enmCmdType == VBOXHGCMCMDTYPE_CONNECT ? sizeof(HGCMServiceLocation) : 0);
-
-    PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ(cbCmd);
-    if (pCmd)
-    {
-        pCmd->enmCmdType = enmCmdType;
-        pCmd->GCPhys     = GCPhys;
-        pCmd->cbRequest  = cbRequest;
-        pCmd->fRequestor = fRequestor;
-
-        if (enmCmdType == VBOXHGCMCMDTYPE_CALL)
-        {
-            pCmd->u.call.cParms = cParms;
-            if (cParms)
-            {
-                pCmd->u.call.paGuestParms = (VBOXHGCMGUESTPARM *)((uint8_t *)pCmd
-                                                                  + sizeof(struct VBOXHGCMCMD));
-                pCmd->u.call.paHostParms = (VBOXHGCMSVCPARM *)((uint8_t *)pCmd->u.call.paGuestParms
-                                                               + cParms * sizeof(VBOXHGCMGUESTPARM));
-            }
+    if (cbCmd <= pThisCC->aHgcmAcc[idxHeapAcc].cbHeapBudget)
+    {
+        PVBOXHGCMCMD pCmd = (PVBOXHGCMCMD)RTMemAllocZ(cbCmd);
+        if (pCmd)
+        {
+            pCmd->enmCmdType = enmCmdType;
+            pCmd->GCPhys     = GCPhys;
+            pCmd->cbRequest  = cbRequest;
+            pCmd->fRequestor = fRequestor;
+            pCmd->idxHeapAcc = (uint8_t)idxHeapAcc;
+            pCmd->cbHeapCost = cbCmd;
+            Log5Func(("aHgcmAcc[%zu] %#RX64 -= %#x (%p)\n", idxHeapAcc, pThisCC->aHgcmAcc[idxHeapAcc].cbHeapBudget, cbCmd, pCmd));
+            pThisCC->aHgcmAcc[idxHeapAcc].cbHeapBudget -= cbCmd;
+
+            if (enmCmdType == VBOXHGCMCMDTYPE_CALL)
+            {
+                pCmd->u.call.cParms = cParms;
+                if (cParms)
+                {
+                    pCmd->u.call.paGuestParms = (VBOXHGCMGUESTPARM *)((uint8_t *)pCmd
+                                                                      + sizeof(struct VBOXHGCMCMD));
+                    pCmd->u.call.paHostParms = (VBOXHGCMSVCPARM *)((uint8_t *)pCmd->u.call.paGuestParms
+                                                                   + cParms * sizeof(VBOXHGCMGUESTPARM));
+                }
+            }
+            else if (enmCmdType == VBOXHGCMCMDTYPE_CONNECT)
+                pCmd->u.connect.pLoc = (HGCMServiceLocation *)(pCmd + 1);
         }
-        else if (enmCmdType == VBOXHGCMCMDTYPE_CONNECT)
-            pCmd->u.connect.pLoc = (HGCMServiceLocation *)(pCmd + 1);
-    }
-    return pCmd;
+        Log3Func(("returns %p (enmCmdType=%d GCPhys=%RGp cbCmd=%#x)\n", pCmd, enmCmdType, GCPhys, cbCmd));
+        return pCmd;
+    }
+    LogFunc(("Heap budget overrun: cbCmd=%#x aHgcmAcc[%zu].cbHeapBudget=%#RX64 - enmCmdType=%d\n",
+             cbCmd, idxHeapAcc, pThisCC->aHgcmAcc[idxHeapAcc].cbHeapBudget, enmCmdType));
+    return NULL;
 }
 
@@ -336,11 +378,16 @@
  *
  * @param   pDevIns         The device instance.
+ * @param   pThis           The VMMDev shared instance data.
  * @param   pThisCC         The VMMDev ring-3 instance data.
  * @param   pCmd            Command to deallocate.
  */
-static void vmmdevR3HgcmCmdFree(PPDMDEVINS pDevIns, PVMMDEVCC pThisCC, PVBOXHGCMCMD pCmd)
+static void vmmdevR3HgcmCmdFree(PPDMDEVINS pDevIns, PVMMDEV pThis, PVMMDEVCC pThisCC, PVBOXHGCMCMD pCmd)
 {
     if (pCmd)
     {
+        Assert(   pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL
+               || pCmd->enmCmdType == VBOXHGCMCMDTYPE_CONNECT
+               || pCmd->enmCmdType == VBOXHGCMCMDTYPE_DISCONNECT
+               || pCmd->enmCmdType == VBOXHGCMCMDTYPE_LOADSTATE);
         if (pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL)
         {
@@ -390,10 +437,33 @@
         }
 
+        pCmd->enmCmdType = UINT8_MAX; /* poison */
+
+        /* Update heap budget.  Need the critsect to do this safely. */
+        Assert(pCmd->cbHeapCost != 0);
+        uintptr_t idx = pCmd->idxHeapAcc;
+        AssertStmt(idx < RT_ELEMENTS(pThisCC->aHgcmAcc), idx %= RT_ELEMENTS(pThisCC->aHgcmAcc));
+
+        PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_IGNORED);
+
+        Log5Func(("aHgcmAcc[%zu] %#RX64 += %#x (%p)\n", idx, pThisCC->aHgcmAcc[idx].cbHeapBudget, pCmd->cbHeapCost, pCmd));
+        pThisCC->aHgcmAcc[idx].cbHeapBudget += pCmd->cbHeapCost;
+        AssertMsg(pThisCC->aHgcmAcc[idx].cbHeapBudget <= pThisCC->aHgcmAcc[idx].cbHeapBudgetConfig,
+                  ("idx=%d (%d) fRequestor=%#x pCmd=%p: %#RX64 vs %#RX64 -> %#RX64\n", idx, pCmd->idxHeapAcc, pCmd->fRequestor, pCmd,
+                   pThisCC->aHgcmAcc[idx].cbHeapBudget,  pThisCC->aHgcmAcc[idx].cbHeapBudgetConfig,
+                   pThisCC->aHgcmAcc[idx].cbHeapBudget - pThisCC->aHgcmAcc[idx].cbHeapBudgetConfig));
+        pCmd->cbHeapCost = 0;
+
 #if 1
         if (pCmd->fMemCache)
+        {
             RTMemCacheFree(pThisCC->hHgcmCmdCache, pCmd);
+            PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect); /* releasing it after just to be on the safe side. */
+        }
         else
 #endif
+        {
+            PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
             RTMemFree(pCmd);
+        }
     }
 }
@@ -415,4 +485,10 @@
 
     RTListPrepend(&pThisCC->listHGCMCmd, &pCmd->node);
+
+    /* stats */
+    uintptr_t idx = pCmd->idxHeapAcc;
+    AssertStmt(idx < RT_ELEMENTS(pThisCC->aHgcmAcc), idx %= RT_ELEMENTS(pThisCC->aHgcmAcc));
+    STAM_REL_COUNTER_ADD(&pThisCC->aHgcmAcc[idx].cbHeapTotal, pCmd->cbHeapCost);
+    STAM_REL_COUNTER_INC(&pThisCC->aHgcmAcc[idx].cTotalMessages);
 
     /* Automatically enable HGCM events, if there are HGCM commands. */
@@ -683,8 +759,9 @@
  * @returns VBox status code that the guest should see.
  * @param   pDevIns         The device instance.
+ * @param   pThisCC         The VMMDev ring-3 instance data.
  * @param   pCmd            Command structure where host parameters needs initialization.
  * @param   pbReq           The request buffer.
  */
-static int vmmdevR3HgcmInitHostParameters(PPDMDEVINS pDevIns, PVBOXHGCMCMD pCmd, uint8_t const *pbReq)
+static int vmmdevR3HgcmInitHostParameters(PPDMDEVINS pDevIns, PVMMDEVCC pThisCC, PVBOXHGCMCMD pCmd, uint8_t const *pbReq)
 {
     AssertReturn(pCmd->enmCmdType == VBOXHGCMCMDTYPE_CALL, VERR_INTERNAL_ERROR);
@@ -728,5 +805,5 @@
                 {
                     /* Zero memory, the buffer content is potentially copied to the guest. */
-                    void *pv = RTMemAllocZ(cbData);
+                    void *pv = vmmdevR3HgcmCallMemAllocZ(pThisCC, pCmd, cbData);
                     AssertReturn(pv, VERR_NO_MEMORY);
                     pHostParm->u.pointer.addr = pv;
@@ -831,4 +908,56 @@
 }
 
+/**
+ * Heap budget wrapper around RTMemAlloc and RTMemAllocZ.
+ */
+static void *vmmdevR3HgcmCallMemAllocEx(PVMMDEVCC pThisCC, PVBOXHGCMCMD pCmd, size_t cbRequested, bool fZero)
+{
+    /* Check against max heap costs for this request. */
+    Assert(pCmd->cbHeapCost <= VMMDEV_MAX_HGCM_DATA_SIZE);
+    if (cbRequested <= VMMDEV_MAX_HGCM_DATA_SIZE - pCmd->cbHeapCost)
+    {
+        /* Check heap budget (we're under lock). */
+        uintptr_t idx = pCmd->idxHeapAcc;
+        AssertStmt(idx < RT_ELEMENTS(pThisCC->aHgcmAcc), idx %= RT_ELEMENTS(pThisCC->aHgcmAcc));
+        if (cbRequested <= pThisCC->aHgcmAcc[idx].cbHeapBudget)
+        {
+            /* Do the actual allocation. */
+            void *pv = fZero ? RTMemAllocZ(cbRequested) : RTMemAlloc(cbRequested);
+            if (pv)
+            {
+                /* Update the request cost and heap budget. */
+                Log5Func(("aHgcmAcc[%zu] %#RX64 += %#x (%p)\n", idx, pThisCC->aHgcmAcc[idx].cbHeapBudget, cbRequested, pCmd));
+                pThisCC->aHgcmAcc[idx].cbHeapBudget -=           cbRequested;
+                pCmd->cbHeapCost                    += (uint32_t)cbRequested;
+                return pv;
+            }
+            LogFunc(("Heap alloc failed: cbRequested=%#zx - enmCmdType=%d\n", cbRequested, pCmd->enmCmdType));
+        }
+        else
+            LogFunc(("Heap budget overrun: cbRequested=%#zx cbHeapCost=%#x aHgcmAcc[%u].cbHeapBudget=%#RX64 - enmCmdType=%d\n",
+                     cbRequested, pCmd->cbHeapCost, pCmd->idxHeapAcc, pThisCC->aHgcmAcc[idx].cbHeapBudget, pCmd->enmCmdType));
+    }
+    else
+        LogFunc(("Request too big: cbRequested=%#zx cbHeapCost=%#x - enmCmdType=%d\n",
+                 cbRequested, pCmd->cbHeapCost, pCmd->enmCmdType));
+    return NULL;
+}
+
+/**
+ * Heap budget wrapper around RTMemAlloc.
+ */
+DECLINLINE(void *) vmmdevR3HgcmCallMemAlloc(PVMMDEVCC pThisCC, PVBOXHGCMCMD pCmd, size_t cbRequested)
+{
+    return vmmdevR3HgcmCallMemAllocEx(pThisCC, pCmd, cbRequested, false /*fZero*/);
+}
+
+/**
+ * Heap budget wrapper around RTMemAllocZ.
+ */
+DECLINLINE(void *) vmmdevR3HgcmCallMemAllocZ(PVMMDEVCC pThisCC, PVBOXHGCMCMD pCmd, size_t cbRequested)
+{
+    return vmmdevR3HgcmCallMemAllocEx(pThisCC, pCmd, cbRequested, true /*fZero*/);
+}
+
 /** Copy VMMDevHGCMCall request data from the guest to VBOXHGCMCMD command.
  *
@@ -864,5 +993,4 @@
     const uint8_t *pu8HGCMParm = (uint8_t *)pHGCMCall + offHGCMParms;
 
-    uint32_t cbTotalData = 0;
     for (uint32_t i = 0; i < cParms; ++i, pu8HGCMParm += cbHGCMParmStruct)
     {
@@ -927,6 +1055,5 @@
                 LogFunc(("LinAddr guest parameter %RGv, cb %u\n", GCPtr, cbData));
 
-                ASSERT_GUEST_RETURN(cbData <= VMMDEV_MAX_HGCM_DATA_SIZE - cbTotalData, VERR_INVALID_PARAMETER);
-                cbTotalData += cbData;
+                ASSERT_GUEST_RETURN(cbData <= VMMDEV_MAX_HGCM_DATA_SIZE, VERR_INVALID_PARAMETER);
 
                 const uint32_t offFirstPage = cbData > 0 ? GCPtr & PAGE_OFFSET_MASK : 0;
@@ -944,5 +1071,7 @@
                     else
                     {
-                        pGuestParm->u.ptr.paPages = (RTGCPHYS *)RTMemAlloc(cPages * sizeof(RTGCPHYS));
+                        /* (Max 262144 bytes with current limits.) */
+                        pGuestParm->u.ptr.paPages = (RTGCPHYS *)vmmdevR3HgcmCallMemAlloc(pThisCC, pCmd,
+                                                                                         cPages * sizeof(RTGCPHYS));
                         AssertReturn(pGuestParm->u.ptr.paPages, VERR_NO_MEMORY);
                     }
@@ -986,6 +1115,5 @@
                 LogFunc(("PageList guest parameter cb %u, offset %u\n", cbData, offPageListInfo));
 
-                ASSERT_GUEST_RETURN(cbData <= VMMDEV_MAX_HGCM_DATA_SIZE - cbTotalData, VERR_INVALID_PARAMETER);
-                cbTotalData += cbData;
+                ASSERT_GUEST_RETURN(cbData <= VMMDEV_MAX_HGCM_DATA_SIZE, VERR_INVALID_PARAMETER);
 
 /** @todo respect zero byte page lists...    */
@@ -1048,6 +1176,7 @@
                     pGuestParm->u.Pages.cPages       = (uint16_t)cPages;
                     pGuestParm->u.Pages.fLocked      = false;
-                    pGuestParm->u.Pages.paPgLocks    = (PPGMPAGEMAPLOCK)RTMemAllocZ(  (sizeof(PGMPAGEMAPLOCK) + sizeof(void *))
-                                                                                    * cPages);
+                    pGuestParm->u.Pages.paPgLocks    = (PPGMPAGEMAPLOCK)vmmdevR3HgcmCallMemAllocZ(pThisCC, pCmd,
+                                                                                                  (  sizeof(PGMPAGEMAPLOCK)
+                                                                                                   + sizeof(void *)) * cPages);
                     AssertReturn(pGuestParm->u.Pages.paPgLocks, VERR_NO_MEMORY);
 
@@ -1090,5 +1219,6 @@
                 else
                 {
-                    pGuestParm->u.ptr.paPages   = (RTGCPHYS *)RTMemAlloc(pPageListInfo->cPages * sizeof(RTGCPHYS));
+                    pGuestParm->u.ptr.paPages   = (RTGCPHYS *)vmmdevR3HgcmCallMemAlloc(pThisCC, pCmd,
+                                                                                       pPageListInfo->cPages * sizeof(RTGCPHYS));
                     AssertReturn(pGuestParm->u.ptr.paPages, VERR_NO_MEMORY);
 
@@ -1113,6 +1243,5 @@
                 LogFunc(("Embedded guest parameter cb %u, offset %u, flags %#x\n", cbData, offData, fFlags));
 
-                ASSERT_GUEST_RETURN(cbData <= VMMDEV_MAX_HGCM_DATA_SIZE - cbTotalData, VERR_INVALID_PARAMETER);
-                cbTotalData += cbData;
+                ASSERT_GUEST_RETURN(cbData <= VMMDEV_MAX_HGCM_DATA_SIZE, VERR_INVALID_PARAMETER);
 
                 /* Check flags and buffer range. */
@@ -1197,5 +1326,5 @@
         {
             /* Copy guest data to host parameters, so HGCM services can use the data. */
-            rc = vmmdevR3HgcmInitHostParameters(pDevIns, pCmd, (uint8_t const *)pHGCMCall);
+            rc = vmmdevR3HgcmInitHostParameters(pDevIns, pThisCC, pCmd, (uint8_t const *)pHGCMCall);
             if (RT_SUCCESS(rc))
             {
@@ -1255,5 +1384,5 @@
             }
         }
-        vmmdevR3HgcmCmdFree(pDevIns, pThisCC, pCmd);
+        vmmdevR3HgcmCmdFree(pDevIns, pThis, pThisCC, pCmd);
     }
     return rc;
@@ -1688,7 +1817,7 @@
 #endif
 
-    /* Deallocate the command memory. */
+    /* Deallocate the command memory. Enter the critsect for proper  */
     VBOXDD_HGCMCALL_COMPLETED_DONE(pCmd, idFunction, idClient, result);
-    vmmdevR3HgcmCmdFree(pDevIns, pThisCC, pCmd);
+    vmmdevR3HgcmCmdFree(pDevIns, pThis, pThisCC, pCmd);
 
 #ifndef VBOX_WITHOUT_RELEASE_STATISTICS
@@ -2004,5 +2133,6 @@
                                 AssertReturn(   pGuestParm->enmType != VMMDevHGCMParmType_Embedded
                                              && pGuestParm->enmType != VMMDevHGCMParmType_ContiguousPageList, VERR_INTERNAL_ERROR_3);
-                                pPtr->paPages = (RTGCPHYS *)RTMemAlloc(pPtr->cPages * sizeof(RTGCPHYS));
+                                pPtr->paPages = (RTGCPHYS *)vmmdevR3HgcmCallMemAlloc(pThisCC, pCmd,
+                                                                                     pPtr->cPages * sizeof(RTGCPHYS));
                                 AssertStmt(pPtr->paPages, rc = VERR_NO_MEMORY);
                             }
@@ -2064,5 +2194,5 @@
                 Log(("vmmdevR3HgcmLoadState: Skipping cancelled request: enmCmdType=%d GCPhys=%#RX32 LB %#x\n",
                      enmCmdType, GCPhys, cbRequest));
-                vmmdevR3HgcmCmdFree(pDevIns, pThisCC, pCmd);
+                vmmdevR3HgcmCmdFree(pDevIns, pThis, pThisCC, pCmd);
             }
         }
@@ -2128,5 +2258,5 @@
                     AssertRCReturn(rc, rc);
 
-                    pPtr->paPages = (RTGCPHYS *)RTMemAlloc(pPtr->cPages * sizeof(RTGCPHYS));
+                    pPtr->paPages = (RTGCPHYS *)vmmdevR3HgcmCallMemAlloc(pThisCC, pCmd, pPtr->cPages * sizeof(RTGCPHYS));
                     AssertReturn(pPtr->paPages, VERR_NO_MEMORY);
 
@@ -2148,5 +2278,5 @@
                 Log(("vmmdevR3HgcmLoadState: Skipping cancelled request: enmCmdType=%d GCPhys=%#RX32 LB %#x\n",
                      enmCmdType, GCPhys, cbRequest));
-                vmmdevR3HgcmCmdFree(pDevIns, pThisCC, pCmd);
+                vmmdevR3HgcmCmdFree(pDevIns, pThis, pThisCC, pCmd);
             }
         }
@@ -2255,4 +2385,5 @@
  * @returns VBox status code that the guest should see.
  * @param   pDevIns         The device instance.
+ * @param   pThis           The VMMDev shared instance data.
  * @param   pThisCC         The VMMDev ring-3 instance data.
  * @param   uSavedStateVersion   The saved state version the command has been loaded from.
@@ -2263,7 +2394,7 @@
  * @param   ppRestoredCmd   Where to store pointer to newly allocated restored command.
  */
-static int vmmdevR3HgcmRestoreCall(PPDMDEVINS pDevIns, PVMMDEVCC pThisCC, uint32_t uSavedStateVersion, const VBOXHGCMCMD *pLoadedCmd,
-                                   VMMDevHGCMCall *pReq, uint32_t cbReq, VMMDevRequestType enmRequestType,
-                                   VBOXHGCMCMD **ppRestoredCmd)
+static int vmmdevR3HgcmRestoreCall(PPDMDEVINS pDevIns, PVMMDEV pThis, PVMMDEVCC pThisCC, uint32_t uSavedStateVersion,
+                                   const VBOXHGCMCMD *pLoadedCmd, VMMDevHGCMCall *pReq, uint32_t cbReq,
+                                   VMMDevRequestType enmRequestType, VBOXHGCMCMD **ppRestoredCmd)
 {
     /* Verify the request.  */
@@ -2315,5 +2446,5 @@
         *ppRestoredCmd = pCmd;
     else
-        vmmdevR3HgcmCmdFree(pDevIns, pThisCC, pCmd);
+        vmmdevR3HgcmCmdFree(pDevIns, pThis, pThisCC, pCmd);
 
     return rc;
@@ -2325,4 +2456,5 @@
  * @returns VBox status code that the guest should see.
  * @param   pDevIns         The device instance.
+ * @param   pThis           The VMMDev shared instance data.
  * @param   pThisCC         The VMMDev ring-3 instance data.
  * @param   uSavedStateVersion   Saved state version.
@@ -2332,5 +2464,5 @@
  * @param   ppRestoredCmd   Where to store pointer to restored command.
  */
-static int vmmdevR3HgcmRestoreCommand(PPDMDEVINS pDevIns, PVMMDEVCC pThisCC, uint32_t uSavedStateVersion,
+static int vmmdevR3HgcmRestoreCommand(PPDMDEVINS pDevIns, PVMMDEV pThis, PVMMDEVCC pThisCC, uint32_t uSavedStateVersion,
                                       const VBOXHGCMCMD *pLoadedCmd, const VMMDevHGCMRequestHeader *pReqHdr, uint32_t cbReq,
                                       VBOXHGCMCMD **ppRestoredCmd)
@@ -2365,5 +2497,6 @@
         {
             VMMDevHGCMCall *pReq = (VMMDevHGCMCall *)pReqHdr;
-            rc = vmmdevR3HgcmRestoreCall(pDevIns, pThisCC, uSavedStateVersion, pLoadedCmd, pReq, cbReq, enmRequestType, ppRestoredCmd);
+            rc = vmmdevR3HgcmRestoreCall(pDevIns, pThis, pThisCC, uSavedStateVersion, pLoadedCmd,
+                                         pReq, cbReq, enmRequestType, ppRestoredCmd);
             break;
         }
@@ -2425,5 +2558,5 @@
          */
         VMMDevHGCMRequestHeader *pReqHdr = (VMMDevHGCMRequestHeader *)RTMemAlloc(pCmd->cbRequest);
-        AssertBreakStmt(pReqHdr, vmmdevR3HgcmCmdFree(pDevIns, pThisCC, pCmd); rcFunc = VERR_NO_MEMORY);
+        AssertBreakStmt(pReqHdr, vmmdevR3HgcmCmdFree(pDevIns, pThis, pThisCC, pCmd); rcFunc = VERR_NO_MEMORY);
 
         PDMDevHlpPhysRead(pDevIns, pCmd->GCPhys, pReqHdr, pCmd->cbRequest);
@@ -2441,10 +2574,10 @@
             {
                 PVBOXHGCMCMD pRestoredCmd = NULL;
-                rcCmd = vmmdevR3HgcmRestoreCommand(pDevIns, pThisCC, pThisCC->uSavedStateVersion, pCmd,
+                rcCmd = vmmdevR3HgcmRestoreCommand(pDevIns, pThis, pThisCC, pThisCC->uSavedStateVersion, pCmd,
                                                    pReqHdr, pCmd->cbRequest, &pRestoredCmd);
                 if (RT_SUCCESS(rcCmd))
                 {
                     Assert(pCmd != pRestoredCmd); /* vmmdevR3HgcmRestoreCommand must allocate restored command. */
-                    vmmdevR3HgcmCmdFree(pDevIns, pThisCC, pCmd);
+                    vmmdevR3HgcmCmdFree(pDevIns, pThis, pThisCC, pCmd);
                     pCmd = pRestoredCmd;
                 }
@@ -2477,5 +2610,5 @@
                     case VBOXHGCMCMDTYPE_CALL:
                     {
-                        rcCmd = vmmdevR3HgcmInitHostParameters(pDevIns, pCmd, (uint8_t const *)pReqHdr);
+                        rcCmd = vmmdevR3HgcmInitHostParameters(pDevIns, pThisCC, pCmd, (uint8_t const *)pReqHdr);
                         if (RT_SUCCESS(rcCmd))
                         {
@@ -2520,5 +2653,5 @@
 
             /* Deallocate the command memory. */
-            vmmdevR3HgcmCmdFree(pDevIns, pThisCC, pCmd);
+            vmmdevR3HgcmCmdFree(pDevIns, pThis, pThisCC, pCmd);
         }
 
@@ -2531,5 +2664,5 @@
         {
             RTListNodeRemove(&pCmd->node);
-            vmmdevR3HgcmCmdFree(pDevIns, pThisCC, pCmd);
+            vmmdevR3HgcmCmdFree(pDevIns, pThis, pThisCC, pCmd);
         }
     }
@@ -2543,7 +2676,8 @@
  *
  * @param   pDevIns         The device instance.
+ * @param   pThis           The VMMDev shared instance data.
  * @param   pThisCC         The VMMDev ring-3 instance data.
  */
-void vmmdevR3HgcmDestroy(PPDMDEVINS pDevIns, PVMMDEVCC pThisCC)
+void vmmdevR3HgcmDestroy(PPDMDEVINS pDevIns, PVMMDEV pThis, PVMMDEVCC pThisCC)
 {
     LogFlowFunc(("\n"));
@@ -2555,5 +2689,5 @@
         {
             vmmdevR3HgcmRemoveCommand(pThisCC, pCmd);
-            vmmdevR3HgcmCmdFree(pDevIns, pThisCC, pCmd);
+            vmmdevR3HgcmCmdFree(pDevIns, pThis, pThisCC, pCmd);
         }
 
Index: /trunk/src/VBox/Devices/VMMDev/VMMDevHGCM.h
===================================================================
--- /trunk/src/VBox/Devices/VMMDev/VMMDevHGCM.h	(revision 90263)
+++ /trunk/src/VBox/Devices/VMMDev/VMMDevHGCM.h	(revision 90264)
@@ -45,5 +45,5 @@
 int  vmmdevR3HgcmLoadStateDone(PPDMDEVINS pDevIns, PVMMDEV pThis, PVMMDEVCC pThisCC);
 
-void vmmdevR3HgcmDestroy(PPDMDEVINS pDevIns, PVMMDEVCC pThisCC);
+void vmmdevR3HgcmDestroy(PPDMDEVINS pDevIns, PVMMDEV pThis, PVMMDEVCC pThisCC);
 int  vmmdevR3HgcmInit(PVMMDEVCC pThisCC);
 RT_C_DECLS_END
Index: /trunk/src/VBox/Devices/VMMDev/VMMDevState.h
===================================================================
--- /trunk/src/VBox/Devices/VMMDev/VMMDevState.h	(revision 90263)
+++ /trunk/src/VBox/Devices/VMMDev/VMMDevState.h	(revision 90264)
@@ -400,4 +400,17 @@
     uint32_t                        uSavedStateVersion;
     RTMEMCACHE                      hHgcmCmdCache;
+    /** Accounting by for each requestor VMMDEV_REQUESTOR_USR_XXX group.
+     * Legacy requests ends up with VMMDEV_REQUESTOR_USR_NOT_GIVEN */
+    struct
+    {
+        /** The configured heap budget. */
+        uint64_t                    cbHeapBudgetConfig;
+        /** The currently available heap budget.   */
+        uint64_t                    cbHeapBudget;
+        /** Total sum of all heap usage.   */
+        STAMCOUNTER                 cbHeapTotal;
+        /** Total number of message. */
+        STAMCOUNTER                 cTotalMessages;
+    } aHgcmAcc[VMMDEV_REQUESTOR_USR_MASK + 1];
     STAMPROFILE                     StatHgcmCmdArrival;
     STAMPROFILE                     StatHgcmCmdCompletion;
