update for HEAD-2003091401
[reactos.git] / ntoskrnl / mm / ppool.c
index e06829b..52d7160 100644 (file)
 #include <internal/pool.h>
 #include <internal/mm.h>
 
+#define NDEBUG
 #include <internal/debug.h>
 
 /* GLOBALS *******************************************************************/
 
+/* Enable strict checking of the paged pool on every allocation */
+#define ENABLE_VALIDATE_POOL
+
+#undef assert
+#define assert(x) if (!(x)) {DbgPrint("Assertion "#x" failed at %s:%d\n", __FILE__,__LINE__); KeBugCheck(0); }
+#define ASSERT_SIZE(n) assert ( (n) <= MmPagedPoolSize && (n) >= 0 )
+#define ASSERT_PTR(p) assert ( ((size_t)(p)) >= ((size_t)MmPagedPoolBase) && ((size_t)(p)) < ((size_t)(MmPagedPoolBase+MmPagedPoolSize)) )
+
+// to disable buffer over/under-run detection, set the following macro to 0
+#define MM_PPOOL_REDZONE_BYTES 4
+#define MM_PPOOL_REDZONE_VALUE 0xCD
+
 typedef struct _MM_PPOOL_FREE_BLOCK_HEADER
 {
   ULONG Size;
@@ -28,15 +41,37 @@ typedef struct _MM_PPOOL_FREE_BLOCK_HEADER
 typedef struct _MM_PPOOL_USED_BLOCK_HEADER
 {
   ULONG Size;
+#if MM_PPOOL_REDZONE_BYTES
+  ULONG UserSize; // how many bytes the user actually asked for...
+  struct _MM_PPOOL_USED_BLOCK_HEADER* NextUsed;
+#endif//MM_PPOOL_REDZONE_BYTES
 } MM_PPOOL_USED_BLOCK_HEADER, *PMM_PPOOL_USED_BLOCK_HEADER;
 
 PVOID MmPagedPoolBase;
 ULONG MmPagedPoolSize;
 static FAST_MUTEX MmPagedPoolLock;
 static PMM_PPOOL_FREE_BLOCK_HEADER MmPagedPoolFirstFreeBlock;
+#if MM_PPOOL_REDZONE_BYTES
+static PMM_PPOOL_USED_BLOCK_HEADER MmPagedPoolFirstUsedBlock;
+#endif//MM_PPOOL_REDZONE_BYTES
 
 /* FUNCTIONS *****************************************************************/
 
+inline static void* block_to_address ( PVOID blk )
+/*
+ * FUNCTION: Translate a block header address to the corresponding block
+ * address (internal)
+ */
+{
+  return ( (void *) ((char*)blk + sizeof(MM_PPOOL_USED_BLOCK_HEADER) + MM_PPOOL_REDZONE_BYTES) );
+}
+
+inline static PMM_PPOOL_USED_BLOCK_HEADER address_to_block(PVOID addr)
+{
+  return (PMM_PPOOL_USED_BLOCK_HEADER)
+         ( ((char*)addr) - sizeof(MM_PPOOL_USED_BLOCK_HEADER) - MM_PPOOL_REDZONE_BYTES );
+}
+
 VOID MmInitializePagedPool(VOID)
 {
   MmPagedPoolFirstFreeBlock = (PMM_PPOOL_FREE_BLOCK_HEADER)MmPagedPoolBase;
@@ -48,9 +83,67 @@ VOID MmInitializePagedPool(VOID)
   MmPagedPoolFirstFreeBlock->Size = MmPagedPoolSize;
   MmPagedPoolFirstFreeBlock->NextFree = NULL;
 
+#if MM_PPOOL_REDZONE_BYTES
+  MmPagedPoolFirstUsedBlock = NULL;
+#endif//MM_PPOOL_REDZONE_BYTES
+
   ExInitializeFastMutex(&MmPagedPoolLock);
 }
 
+#ifdef ENABLE_VALIDATE_POOL
+static void VerifyPagedPool ( int line )
+{
+  PMM_PPOOL_FREE_BLOCK_HEADER p = MmPagedPoolFirstFreeBlock;
+  int count = 0;
+  DPRINT ( "VerifyPagedPool(%i):\n", line );
+  while ( p )
+  {
+    DPRINT ( "  0x%x: %lu bytes (next 0x%x)\n", p, p->Size, p->NextFree );
+    ASSERT_PTR(p);
+    ASSERT_SIZE(p->Size);
+    count++;
+    p = p->NextFree;
+  }
+  DPRINT ( "VerifyPagedPool(%i): (%lu blocks)\n", line, count );
+}
+#define VerifyPagedPool() VerifyPagedPool(__LINE__)
+#else
+#define VerifyPagedPool()
+#endif
+
+VOID STDCALL
+MmDbgPagedPoolRedZoneCheck ( const char* file, int line )
+{
+#if MM_PPOOL_REDZONE_BYTES
+  PMM_PPOOL_USED_BLOCK_HEADER pUsed = MmPagedPoolFirstUsedBlock;
+  int i;
+  BOOL bLow = TRUE;
+  BOOL bHigh = TRUE;
+
+  while ( pUsed )
+  {
+    PUCHAR Addr = (PUCHAR)block_to_address(pUsed);
+    for ( i = 0; i < MM_PPOOL_REDZONE_BYTES; i++ )
+    {
+      bLow = bLow && ( *(Addr-i-1) == MM_PPOOL_REDZONE_VALUE );
+      bHigh = bHigh && ( *(Addr+pUsed->UserSize+i) == MM_PPOOL_REDZONE_VALUE );
+    }
+    if ( !bLow || !bHigh )
+    {
+      const char* violation = "High and Low-side";
+      if ( bHigh ) // high is okay, so it was just low failed
+       violation = "Low-side";
+      else if ( bLow ) // low side is okay, so it was just high failed
+       violation = "High-side";
+      DbgPrint("%s(%i): %s redzone violation detected for paged pool address 0x%x\n",
+       file, line, violation, Addr );
+      KEBUGCHECK(0);
+    }
+    pUsed = pUsed->NextUsed;
+  }
+#endif//MM_PPOOL_REDZONE_BYTES
+}
+
 /**********************************************************************
  * NAME                                                        INTERNAL
  *     ExAllocatePagedPoolWithTag@12
@@ -74,38 +167,132 @@ ExAllocatePagedPoolWithTag (IN    POOL_TYPE       PoolType,
   PMM_PPOOL_FREE_BLOCK_HEADER PreviousBlock;
   PMM_PPOOL_FREE_BLOCK_HEADER BestPreviousBlock;
   PVOID BlockAddress;
+  ULONG Alignment;
+
+  ExAcquireFastMutex(&MmPagedPoolLock);
 
   /*
    * Don't bother allocating anything for a zero-byte block.
    */
   if (NumberOfBytes == 0)
     {
+      MmDbgPagedPoolRedZoneCheck(__FILE__,__LINE__);
+      ExReleaseFastMutex(&MmPagedPoolLock);
       return(NULL);
     }
 
+  DPRINT ( "ExAllocatePagedPoolWithTag(%i,%lu,%lu)\n", PoolType, NumberOfBytes, Tag );
+  VerifyPagedPool();
+
+  if (NumberOfBytes >= PAGE_SIZE)
+    {
+      Alignment = PAGE_SIZE;
+    }
+  else if (PoolType == PagedPoolCacheAligned)
+    {
+      Alignment = MM_CACHE_LINE_SIZE;
+    }
+  else
+    {
+      Alignment = 0;
+    }
+
   /*
    * Calculate the total number of bytes we will need.
    */
-  BlockSize = NumberOfBytes + sizeof(MM_PPOOL_USED_BLOCK_HEADER);
+  BlockSize = NumberOfBytes + sizeof(MM_PPOOL_USED_BLOCK_HEADER) + 2*MM_PPOOL_REDZONE_BYTES;
   if (BlockSize < sizeof(MM_PPOOL_FREE_BLOCK_HEADER))
   {
     /* At least we need the size of the free block header. */
     BlockSize = sizeof(MM_PPOOL_FREE_BLOCK_HEADER);
   }
 
-  ExAcquireFastMutex(&MmPagedPoolLock);
 
   /*
-   * Find the best fitting block.
+   * Find the best-fitting block.
    */
   PreviousBlock = NULL;
   BestPreviousBlock = BestBlock = NULL;
   CurrentBlock = MmPagedPoolFirstFreeBlock;
-  while (CurrentBlock != NULL)
+  if ( Alignment > 0 )
     {
-      if (CurrentBlock->Size >= BlockSize &&
-         (BestBlock == NULL || 
-          (BestBlock->Size - BlockSize) > (CurrentBlock->Size - BlockSize)))
+      PVOID BestAlignedAddr = NULL;
+      while ( CurrentBlock != NULL )
+       {
+         PVOID Addr = block_to_address(CurrentBlock);
+         PVOID CurrentBlockEnd = (PVOID)CurrentBlock + CurrentBlock->Size;
+         /* calculate last size-aligned address available within this block */
+         PVOID AlignedAddr = MM_ROUND_DOWN(CurrentBlockEnd-NumberOfBytes-MM_PPOOL_REDZONE_BYTES, Alignment);
+         assert ( AlignedAddr+NumberOfBytes+MM_PPOOL_REDZONE_BYTES <= CurrentBlockEnd );
+
+         /* special case, this address is already size-aligned, and the right size */
+         if ( Addr == AlignedAddr )
+           {
+             BestAlignedAddr = AlignedAddr;
+             BestPreviousBlock = PreviousBlock;
+             BestBlock = CurrentBlock;
+             break;
+           }
+         else if ( Addr < (PVOID)address_to_block(AlignedAddr) )
+           {
+             /*
+              * there's enough room to allocate our size-aligned memory out
+              * of this block, see if it's a better choice than any previous
+              * finds
+              */
+             if ( BestBlock == NULL || BestBlock->Size > CurrentBlock->Size )
+               {
+                 BestAlignedAddr = AlignedAddr;
+                 BestPreviousBlock = PreviousBlock;
+                 BestBlock = CurrentBlock;
+               }
+           }
+
+         PreviousBlock = CurrentBlock;
+         CurrentBlock = CurrentBlock->NextFree;
+       }
+
+      /*
+       * we found a best block can/should we chop a few bytes off the beginning
+       * into a separate memory block?
+       */
+      if ( BestBlock != NULL )
+       {
+         PVOID Addr = block_to_address(BestBlock);
+         if ( BestAlignedAddr != Addr )
+           {
+             PMM_PPOOL_FREE_BLOCK_HEADER NewFreeBlock =
+               (PMM_PPOOL_FREE_BLOCK_HEADER)address_to_block(BestAlignedAddr);
+             assert ( BestAlignedAddr > Addr );
+             NewFreeBlock->Size = Addr + BestBlock->Size - BestAlignedAddr;
+             ASSERT_SIZE(NewFreeBlock->Size);
+             BestBlock->Size = (size_t)NewFreeBlock - (size_t)Addr;
+             ASSERT_SIZE(BestBlock->Size);
+
+             DPRINT ( "breaking off preceding bytes into their own block...\n" );
+             DPRINT ( "NewFreeBlock 0x%x Size %lu (Old Block's new size %lu) NextFree 0x%x\n",
+               NewFreeBlock, NewFreeBlock->Size, BestBlock->Size, BestBlock->NextFree );
+
+             /* insert the new block into the chain */
+             NewFreeBlock->NextFree = BestBlock->NextFree;
+             BestBlock->NextFree = NewFreeBlock;
+
+             /* we want the following code to use our size-aligned block */
+             BestPreviousBlock = BestBlock;
+             BestBlock = NewFreeBlock;
+
+             //VerifyPagedPool();
+           }
+       }
+    }
+  /*
+   * non-size-aligned block search
+   */
+  else while ( CurrentBlock != NULL )
+    {
+      if (    CurrentBlock->Size >= BlockSize
+          && ( BestBlock == NULL || BestBlock->Size > CurrentBlock->Size )
+        )
        {
          BestPreviousBlock = PreviousBlock;
          BestBlock = CurrentBlock;
@@ -120,41 +307,66 @@ ExAllocatePagedPoolWithTag (IN    POOL_TYPE       PoolType,
    */
   if (BestBlock == NULL)
     {
+      DPRINT("ExAllocatePagedPoolWithTag() - nothing suitable found, returning NULL\n" );
       ExReleaseFastMutex(&MmPagedPoolLock);
       return(NULL);
     }
 
+  DPRINT("BestBlock 0x%x NextFree 0x%x\n", BestBlock, BestBlock->NextFree );
+
+  //VerifyPagedPool();
+
   /*
    * Is there enough space to create a second block from the unused portion.
    */
-  if ((BestBlock->Size - BlockSize) > sizeof(MM_PPOOL_FREE_BLOCK_HEADER))
+  if ( BestBlock->Size > BlockSize
+    && (BestBlock->Size - BlockSize) > sizeof(MM_PPOOL_FREE_BLOCK_HEADER)
+    )
     {
       ULONG NewSize = BestBlock->Size - BlockSize;
+      ASSERT_SIZE ( NewSize );
+
+      //DPRINT("creating 2nd block from unused portion\n");
+      DPRINT("BestBlock 0x%x Size 0x%x BlockSize 0x%x NewSize 0x%x\n",
+       BestBlock, BestBlock->Size, BlockSize, NewSize );
 
       /*
        * Create the new free block.
        */
-      NextBlock = (PMM_PPOOL_FREE_BLOCK_HEADER)((PVOID)BestBlock + BlockSize);
+      //DPRINT("creating the new free block");
+      NextBlock = (PMM_PPOOL_FREE_BLOCK_HEADER)((char*)BestBlock + BlockSize);
+      //DPRINT(".");
       NextBlock->Size = NewSize;
+      ASSERT_SIZE ( NextBlock->Size );
+      //DPRINT(".");
       NextBlock->NextFree = BestBlock->NextFree;
+      //DPRINT(".\n");
 
       /*
        * Replace the old free block with it.
        */
+      //DPRINT("replacing old free block with it");
       if (BestPreviousBlock == NULL)
        {
+         //DPRINT("(from beginning)");
          MmPagedPoolFirstFreeBlock = NextBlock;
        }
       else
        {
+         //DPRINT("(from previous)");
          BestPreviousBlock->NextFree = NextBlock;
        }
+      //DPRINT(".\n");
 
       /*
        * Create the new used block header.
        */
+      //DPRINT("create new used block header");
       NewBlock = (PMM_PPOOL_USED_BLOCK_HEADER)BestBlock;
+      //DPRINT(".");
       NewBlock->Size = BlockSize;
+      ASSERT_SIZE ( NewBlock->Size );
+      //DPRINT(".\n");
     }
   else
     {
@@ -163,6 +375,7 @@ ExAllocatePagedPoolWithTag (IN      POOL_TYPE       PoolType,
       /*
        * Remove the selected block from the list of free blocks.
        */
+      //DPRINT ( "Removing selected block from free block list\n" );
       if (BestPreviousBlock == NULL)
        {
          MmPagedPoolFirstFreeBlock = BestBlock->NextFree;
@@ -177,14 +390,45 @@ ExAllocatePagedPoolWithTag (IN    POOL_TYPE       PoolType,
        */
       NewBlock = (PMM_PPOOL_USED_BLOCK_HEADER)BestBlock;
       NewBlock->Size = NewSize;
+      ASSERT_SIZE ( NewBlock->Size );
     }
 
+#if MM_PPOOL_REDZONE_BYTES
+  // now add the block to the used block list
+  NewBlock->NextUsed = MmPagedPoolFirstUsedBlock;
+  MmPagedPoolFirstUsedBlock = NewBlock;
+#endif//MM_PPOOL_REDZONE_BYTES
+
+  VerifyPagedPool();
+
   ExReleaseFastMutex(&MmPagedPoolLock);
 
-  BlockAddress = (PVOID)NewBlock + sizeof(MM_PPOOL_USED_BLOCK_HEADER);
+  BlockAddress = block_to_address ( NewBlock );
 
   memset(BlockAddress, 0, NumberOfBytes);
 
+#if MM_PPOOL_REDZONE_BYTES
+  NewBlock->UserSize = NumberOfBytes;
+  // write out buffer-overrun detection bytes
+  {
+    PUCHAR Addr = (PUCHAR)BlockAddress;
+    //DbgPrint ( "writing buffer-overrun detection bytes" );
+    memset ( Addr - MM_PPOOL_REDZONE_BYTES,
+      MM_PPOOL_REDZONE_VALUE, MM_PPOOL_REDZONE_BYTES );
+    memset ( Addr + NewBlock->UserSize, MM_PPOOL_REDZONE_VALUE,
+      MM_PPOOL_REDZONE_BYTES );
+    /*for ( i = 0; i < MM_PPOOL_REDZONE_BYTES; i++ )
+    {
+      //DbgPrint(".");
+      *(Addr-i-1) = 0xCD;
+      //DbgPrint("o");
+      *(Addr+NewBlock->UserSize+i) = 0xCD;
+    }*/
+    //DbgPrint ( "done!\n" );
+  }
+
+#endif//MM_PPOOL_REDZONE_BYTES
+
   return(BlockAddress);
 }
 
@@ -192,20 +436,56 @@ VOID STDCALL
 ExFreePagedPool(IN PVOID Block)
 {
   PMM_PPOOL_FREE_BLOCK_HEADER PreviousBlock;
-  PMM_PPOOL_USED_BLOCK_HEADER UsedBlock = 
-    (PMM_PPOOL_USED_BLOCK_HEADER)(Block - sizeof(MM_PPOOL_USED_BLOCK_HEADER));
+  PMM_PPOOL_USED_BLOCK_HEADER UsedBlock = address_to_block(Block);
   ULONG UsedSize = UsedBlock->Size;
   PMM_PPOOL_FREE_BLOCK_HEADER FreeBlock = 
     (PMM_PPOOL_FREE_BLOCK_HEADER)UsedBlock;
   PMM_PPOOL_FREE_BLOCK_HEADER NextBlock;
   PMM_PPOOL_FREE_BLOCK_HEADER NextNextBlock;
 
+#if MM_PPOOL_REDZONE_BYTES
+  // write out buffer-overrun detection bytes
+  {
+    int i;
+    PUCHAR Addr = (PUCHAR)Block;
+    //DbgPrint ( "checking buffer-overrun detection bytes..." );
+    for ( i = 0; i < MM_PPOOL_REDZONE_BYTES; i++ )
+    {
+      assert ( *(Addr-i-1) == MM_PPOOL_REDZONE_VALUE );
+      assert ( *(Addr+UsedBlock->UserSize+i) == MM_PPOOL_REDZONE_VALUE );
+    }
+    //DbgPrint ( "done!\n" );
+  }
+#endif//MM_PPOOL_REDZONE_BYTES
+
   ExAcquireFastMutex(&MmPagedPoolLock);
 
+#if MM_PPOOL_REDZONE_BYTES
+  // remove from used list...
+  {
+    PMM_PPOOL_USED_BLOCK_HEADER pPrev = MmPagedPoolFirstUsedBlock;
+    if ( pPrev == UsedBlock )
+    {
+      // special-case, our freeing block is first in list...
+      MmPagedPoolFirstUsedBlock = pPrev->NextUsed;
+    }
+    else
+    {
+      while ( pPrev && pPrev->NextUsed != UsedBlock )
+       pPrev = pPrev->NextUsed;
+      // if this assert fails - memory has been corrupted
+      // ( or I have a logic error...! )
+      assert ( pPrev->NextUsed == UsedBlock );
+      pPrev->NextUsed = UsedBlock->NextUsed;
+    }
+  }
+#endif//MM_PPOOL_REDZONE_BYTES
+
   /*
    * Begin setting up the newly freed block's header.
    */
   FreeBlock->Size = UsedSize;
+  ASSERT_SIZE ( FreeBlock->Size );
 
   /*
    * Find the blocks immediately before and after the newly freed block on the free list.
@@ -237,9 +517,10 @@ ExFreePagedPool(IN PVOID Block)
    * merge them.
    */
   if (NextBlock != NULL && 
-      ((PVOID)FreeBlock + FreeBlock->Size) == (PVOID)NextBlock)
+      ((char*)FreeBlock + FreeBlock->Size) == (char*)NextBlock)
     {
       FreeBlock->Size = FreeBlock->Size + NextBlock->Size;
+      ASSERT_SIZE ( FreeBlock->Size );
       FreeBlock->NextFree = NextBlock->NextFree;
       NextNextBlock = NextBlock->NextFree;
     }
@@ -253,12 +534,15 @@ ExFreePagedPool(IN PVOID Block)
    * merge them.
    */
   if (PreviousBlock != NULL && 
-      ((PVOID)PreviousBlock + PreviousBlock->Size) == (PVOID)FreeBlock)
+      ((char*)PreviousBlock + PreviousBlock->Size) == (char*)FreeBlock)
     {
       PreviousBlock->Size = PreviousBlock->Size + FreeBlock->Size;
+      ASSERT_SIZE ( PreviousBlock->Size );
       PreviousBlock->NextFree = NextNextBlock;
     }
 
+  VerifyPagedPool();
+
   ExReleaseFastMutex(&MmPagedPoolLock);
 }