Index: trunk/src/VBox/Storage/VDI.cpp
===================================================================
--- trunk/src/VBox/Storage/VDI.cpp	(revision 40712)
+++ trunk/src/VBox/Storage/VDI.cpp	(revision 40713)
@@ -487,4 +487,5 @@
          */
         rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, cbTotal);
+        pImage->cbImage = cbTotal;
     }
     else
@@ -492,4 +493,5 @@
         /* Set file size to hold header and blocks array. */
         rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pImage->offStartData);
+        pImage->cbImage = pImage->offStartData;
     }
     if (RT_FAILURE(rc))
@@ -615,4 +617,14 @@
         /* Do NOT signal an appropriate error here, as the VD layer has the
          * choice of retrying the open if it failed. */
+        goto out;
+    }
+
+    /* Get file size. */
+    rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage,
+                              &pImage->cbImage);
+    if (RT_FAILURE(rc))
+    {
+        vdIfError(pImage->pIfError, rc, RT_SRC_POS, N_("VDI: error getting the image size in '%s'"), pImage->pszFilename);
+        rc = VERR_VD_VDI_INVALID_HEADER;
         goto out;
     }
@@ -962,11 +974,7 @@
             break;
 
-        /* Set new file size. */
-        rc = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbImage);
-        if (RT_FAILURE(rc))
-            break;
-
-        LogFlowFunc(("Set new size %llu\n", cbImage - pImage->cbTotalBlockData));
-        rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, cbImage - pImage->cbTotalBlockData);
+        pImage->cbImage -= pImage->cbTotalBlockData;
+        LogFlowFunc(("Set new size %llu\n", pImage->cbImage));
+        rc = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pImage->cbImage);
     } while (0);
 
@@ -1046,11 +1054,7 @@
                 break;
 
-            /* Set new file size. */
-            rc2 = vdIfIoIntFileGetSize(pImage->pIfIo, pImage->pStorage, &cbImage);
-            if (RT_FAILURE(rc2))
-                break;
-
-            LogFlowFunc(("Set new size %llu\n", cbImage - pImage->cbTotalBlockData));
-            rc2 = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, cbImage - pImage->cbTotalBlockData);
+            pImage->cbImage -= pImage->cbTotalBlockData;
+            LogFlowFunc(("Set new size %llu\n", pImage->cbImage));
+            rc2 = vdIfIoIntFileSetSize(pImage->pIfIo, pImage->pStorage, pImage->cbImage);
             if (RT_FAILURE(rc2))
                 rc = rc2;
@@ -1144,4 +1148,6 @@
 
     pbmAllocationBitmap = RTMemAllocZ(cSectors / 8);
+    if (!pbmAllocationBitmap)
+        return NULL;
 
     while (uSectorCur < cSectors)
@@ -1164,4 +1170,37 @@
 }
 
+
+/**
+ * Updates the state of the async cluster allocation.
+ *
+ * @returns VBox status code.
+ * @param   pBackendData    The opaque backend data.
+ * @param   pIoCtx          I/O context associated with this request.
+ * @param   pvUser          Opaque user data passed during a read/write request.
+ * @param   rcReq           Status code for the completed request.
+ */
+static DECLCALLBACK(int) vdiAsyncBlockAllocUpdate(void *pBackendData, PVDIOCTX pIoCtx, void *pvUser, int rcReq)
+{
+    int rc = VINF_SUCCESS;
+    PVDIIMAGEDESC pImage = (PVDIIMAGEDESC)pBackendData;
+    PVDIASYNCBLOCKALLOC pBlockAlloc = (PVDIASYNCBLOCKALLOC)pvUser;
+
+    if (RT_SUCCESS(rcReq))
+    {
+        pImage->cbImage += pImage->cbTotalBlockData;
+        pImage->paBlocks[pBlockAlloc->uBlock] = pBlockAlloc->cBlocksAllocated;
+
+        if (pImage->paBlocksRev)
+            pImage->paBlocksRev[pBlockAlloc->cBlocksAllocated] = pBlockAlloc->uBlock;
+
+        setImageBlocksAllocated(&pImage->Header, pBlockAlloc->cBlocksAllocated + 1);
+        rc = vdiUpdateBlockInfoAsync(pImage, pBlockAlloc->uBlock, pIoCtx,
+                                     true /* fUpdateHdr */);
+    }
+    /* else: I/O error don't update the block table. */
+
+    RTMemFree(pBlockAlloc);
+    return rc;
+}
 
 /** @copydoc VBOXHDDBACKEND::pfnCheckIfValid */
@@ -1265,4 +1304,9 @@
     void *pvUser = NULL;
     PVDINTERFACEPROGRESS pIfProgress = VDIfProgressGet(pVDIfsOperation);
+    if (pIfProgress)
+    {
+        pfnProgress = pIfProgress->pfnProgress;
+        pvUser = pIfProgress->Core.pvUser;
+    }
 
     /* Check the image flags. */
@@ -1441,6 +1485,15 @@
         uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
                            + (pImage->offStartData + pImage->offStartBlockData + offRead);
-        rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
-                                   pvBuf, cbToRead, NULL);
+
+        if (u64Offset + cbToRead <= pImage->cbImage)
+            rc = vdIfIoIntFileReadSync(pImage->pIfIo, pImage->pStorage, u64Offset,
+                                       pvBuf, cbToRead, NULL);
+        else
+        {
+            LogRel(("VDI: Out of range access (%llu) in image %s, image size %llu\n",
+                    u64Offset, pImage->pszFilename, pImage->cbImage));
+            memset(pvBuf, 0, cbToRead);
+            rc = VERR_VD_READ_OUT_OF_RANGE;
+        }
     }
 
@@ -1540,4 +1593,5 @@
                     goto out;
 
+                pImage->cbImage += cbToWrite;
                 *pcbPreRead = 0;
                 *pcbPostRead = 0;
@@ -2243,6 +2297,15 @@
         uint64_t u64Offset = (uint64_t)pImage->paBlocks[uBlock] * pImage->cbTotalBlockData
                            + (pImage->offStartData + pImage->offStartBlockData + offRead);
-        rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
-                                        pIoCtx, cbToRead);
+
+        if (u64Offset + cbToRead <= pImage->cbImage)
+            rc = vdIfIoIntFileReadUserAsync(pImage->pIfIo, pImage->pStorage, u64Offset,
+                                            pIoCtx, cbToRead);
+        else
+        {
+            LogRel(("VDI: Out of range access (%llu) in image %s, image size %llu\n",
+                    u64Offset, pImage->pszFilename, pImage->cbImage));
+            vdIfIoIntIoCtxSet(pImage->pIfIo, pIoCtx, 0, cbToRead);
+            rc = VERR_VD_READ_OUT_OF_RANGE;
+        }
     }
 
@@ -2325,24 +2388,33 @@
                  * Allocate block and write data. */
                 Assert(!offWrite);
+                PVDIASYNCBLOCKALLOC pBlockAlloc = (PVDIASYNCBLOCKALLOC)RTMemAllocZ(sizeof(VDIASYNCBLOCKALLOC));
+                if (!pBlockAlloc)
+                {
+                    rc = VERR_NO_MEMORY;
+                    break;
+                }
+
                 unsigned cBlocksAllocated = getImageBlocksAllocated(&pImage->Header);
                 uint64_t u64Offset = (uint64_t)cBlocksAllocated * pImage->cbTotalBlockData
                                    + (pImage->offStartData + pImage->offStartBlockData);
-                rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
-                                                 u64Offset, pIoCtx, cbToWrite, NULL, NULL);
-                if (RT_UNLIKELY(RT_FAILURE_NP(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS)))
-                    goto out;
-                pImage->paBlocks[uBlock] = cBlocksAllocated;
-
-                if (pImage->paBlocksRev)
-                    pImage->paBlocksRev[cBlocksAllocated] = uBlock;
-
-                setImageBlocksAllocated(&pImage->Header, cBlocksAllocated + 1);
-
-                rc = vdiUpdateBlockInfoAsync(pImage, uBlock, pIoCtx, true /* fUpdateHdr */);
-                if (RT_FAILURE(rc) && (rc != VERR_VD_ASYNC_IO_IN_PROGRESS))
-                    goto out;
+
+                pBlockAlloc->cBlocksAllocated = cBlocksAllocated;
+                pBlockAlloc->uBlock           = uBlock;
 
                 *pcbPreRead = 0;
                 *pcbPostRead = 0;
+
+                rc = vdIfIoIntFileWriteUserAsync(pImage->pIfIo, pImage->pStorage,
+                                                 u64Offset, pIoCtx, cbToWrite,
+                                                 vdiAsyncBlockAllocUpdate, pBlockAlloc);
+                if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
+                    break;
+                else if (RT_FAILURE(rc))
+                {
+                    RTMemFree(pBlockAlloc);
+                    break;
+                }
+
+                rc = vdiAsyncBlockAllocUpdate(pImage, pIoCtx, pBlockAlloc, rc);
             }
             else
Index: trunk/src/VBox/Storage/VDICore.h
===================================================================
--- trunk/src/VBox/Storage/VDICore.h	(revision 40712)
+++ trunk/src/VBox/Storage/VDICore.h	(revision 40713)
@@ -554,4 +554,6 @@
     /** I/O interface. */
     PVDINTERFACEIOINT       pIfIo;
+    /** Current size of the image (used for range validation when reading). */
+    uint64_t                cbImage;
 } VDIIMAGEDESC, *PVDIIMAGEDESC;
 
@@ -592,4 +594,15 @@
 } VDIBLOCKDISCARDASYNC, *PVDIBLOCKDISCARDASYNC;
 
+/**
+ * Async image expansion state.
+ */
+typedef struct VDIASYNCBLOCKALLOC
+{
+    /** Number of blocks allocated. */
+    unsigned                cBlocksAllocated;
+    /** Block index to allocate. */
+    unsigned                uBlock;
+} VDIASYNCBLOCKALLOC, *PVDIASYNCBLOCKALLOC;
+
 #endif
 
