--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/heap/t_heapcheck.cpp Tue Aug 31 16:34:26 2010 +0300
@@ -0,0 +1,652 @@
+// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+// e32test\debug\t_heapcorruption.cpp
+// This is a test application that will cause heap corruption
+// to generate BTrace events (EHeapCorruption).
+//
+//
+
+// Include Files
+#include <e32test.h>
+#include <e32base.h>
+#include <e32panic.h>
+#include <e32cmn.h>
+#include "dla.h"
+#include "slab.h"
+#include "page_alloc.h"
+#include "heap_hybrid.h"
+
+LOCAL_D RTest test(_L("T_HEAPCHECK"));
+
+TUint32 gSeed = 0xb504f334;
+
+_LIT(KLitHeapCheck,"Heap Check");
+
+
+TUint32 Random()
+{
+ gSeed *= 69069;
+ gSeed += 41;
+ return gSeed;
+}
+
+TInt RandomNumber(TInt aMin, TInt aMax)
+{
+ TInt y = aMax - aMin;
+ if ( y <= 0 )
+ return aMax;
+ TUint32 x = Random() & 0xff;
+ TInt s = 0;
+ while ( y > (0x100 << s) )
+ {
+ s++;
+ }
+ return (aMin + (x << s) % y);
+}
+
+
+/**
+Friend class of RHeapHybrid to access to hybrid heap metadata
+*/
+class TestHybridHeap
+{
+ public:
+ TBool Init();
+ TBool Check();
+ TUint8* Alloc(TInt aLth);
+ TUint8* ReAlloc(TAny* aBfr, TInt aLth, TInt aMode);
+ void Free(TAny* aBfr);
+ TInt AllocLen(TAny* aBfr);
+ TInt AllocSize(TInt& aTotalAllocSize);
+ TBool SlabAllocatorExists();
+ TBool PageAllocatorExists();
+ TBool SlabsCreated();
+ TBool CorruptSmallBin();
+ TBool CorruptTreeBin();
+ TBool ConfigurePageAllocator();
+ TInt CopyPageBitmap(TUint8* aBitmap, TInt aLth);
+ TBool RestorePageBitmap(TUint8* aBitmap, TInt aLth);
+ void AllocateSomeBuffers(TUint8** aBfrs, TInt aMinLth, TInt MaxLth, TInt aCount);
+ TBool PrintHeapInitData();
+
+ private:
+ RHybridHeap* iHybridHeap;
+};
+
+
+
+TBool TestHybridHeap::Init()
+{
+ RHybridHeap::STestCommand cmd;
+ cmd.iCommand = RHybridHeap::EHeapMetaData;
+ RAllocator& heap = User::Allocator();
+ TInt ret = heap.DebugFunction(RHeap::EHybridHeap, &cmd, 0);
+ if (ret != KErrNone)
+ return EFalse;
+ iHybridHeap = (RHybridHeap*) cmd.iData;
+
+ return ETrue;
+}
+
+TBool TestHybridHeap::Check()
+{
+ if ( iHybridHeap )
+ {
+ iHybridHeap->Check();
+ }
+
+ return EFalse;
+}
+
+TUint8* TestHybridHeap::Alloc(TInt aLth)
+{
+ if ( iHybridHeap )
+ {
+ return (TUint8*)iHybridHeap->Alloc(aLth);
+ }
+
+ return NULL;
+}
+
+TUint8* TestHybridHeap::ReAlloc(TAny* aBfr, TInt aLth, TInt aMode)
+{
+ if ( iHybridHeap )
+ {
+ return (TUint8*)iHybridHeap->ReAlloc(aBfr, aLth, aMode);
+ }
+
+ return NULL;
+}
+
+void TestHybridHeap::Free(TAny* aBfr)
+{
+ if ( iHybridHeap )
+ {
+ iHybridHeap->Free(aBfr);
+ }
+}
+
+TInt TestHybridHeap::AllocLen(TAny* aBfr)
+{
+ if ( iHybridHeap )
+ {
+ return iHybridHeap->AllocLen(aBfr);
+ }
+ return 0;
+}
+
+TInt TestHybridHeap::AllocSize(TInt& aTotalAllocSize)
+{
+ aTotalAllocSize = 0;
+ if ( iHybridHeap )
+ {
+ return iHybridHeap->AllocSize(aTotalAllocSize);
+ }
+ return 0;
+}
+
+TBool TestHybridHeap::SlabAllocatorExists()
+{
+ TBool status = EFalse;
+ if ( iHybridHeap )
+ {
+ status = !iHybridHeap->iDLOnly;
+ }
+
+ return status;
+}
+
+TBool TestHybridHeap::PageAllocatorExists()
+{
+ TBool status = EFalse;
+ if ( iHybridHeap )
+ {
+ status = (!iHybridHeap->iDLOnly && (iHybridHeap->iPageThreshold < 31));
+ }
+
+ return status;
+}
+
+TBool TestHybridHeap::SlabsCreated()
+{
+ TBool status = EFalse;
+ if ( iHybridHeap )
+ {
+ status = (iHybridHeap->iSlabThreshold != 0);
+ }
+
+ return status;
+}
+
+TBool TestHybridHeap::ConfigurePageAllocator()
+{
+ TBool status = EFalse;
+ if ( iHybridHeap )
+ {
+ RHybridHeap::STestCommand conf;
+ conf.iCommand = RHybridHeap::ESetConfig;
+ conf.iConfig.iPagePower = 14; // 16 Kb
+ if ( iHybridHeap->DebugFunction(RHeap::EHybridHeap, (TAny*)&conf ) == KErrNone )
+ status = ETrue;
+ }
+
+ return status;
+}
+
+
+TBool TestHybridHeap::CorruptTreeBin()
+{
+ TBool status = EFalse;
+ if ( iHybridHeap )
+ {
+ TUint i;
+ for (i = 0; i < NTREEBINS; ++i)
+ {
+ tbinptr* tb = TREEBIN_AT(&iHybridHeap->iGlobalMallocState, i);
+ tchunkptr t = *tb;
+ if ( t )
+ {
+ // Corrupt tree bin by writing erroneous index value
+ t->iIndex ++;
+ return ETrue;
+ }
+ }
+ }
+
+ return status;
+}
+
+TBool TestHybridHeap::CorruptSmallBin()
+{
+ TBool status = EFalse;
+ if ( iHybridHeap )
+ {
+ TUint i;
+ for (i = 0; i < NSMALLBINS; ++i)
+ {
+ sbinptr b = SMALLBIN_AT(&iHybridHeap->iGlobalMallocState, i);
+ mchunkptr p = b->iBk;
+ if ( p != b )
+ {
+ b->iBk = b;
+ status = ETrue;
+ }
+ }
+ }
+
+ return status;
+}
+
+TInt TestHybridHeap::CopyPageBitmap(TUint8* aBitmap, TInt aLth)
+{
+ TInt lth = 0;
+ if ( iHybridHeap && (aLth > (TInt) sizeof(iHybridHeap->iBitMapBuffer)) )
+ {// Dirty version
+ memcpy(aBitmap, &iHybridHeap->iBitMapBuffer[0], sizeof(iHybridHeap->iBitMapBuffer));
+ lth = sizeof(iHybridHeap->iBitMapBuffer) << 3;
+ }
+
+ return lth;
+}
+
+TBool TestHybridHeap::RestorePageBitmap(TUint8* aBitmap, TInt aLth)
+{
+ TBool status = EFalse;
+ if ( iHybridHeap && ((aLth >> 3) <= (TInt) sizeof(iHybridHeap->iBitMapBuffer)) )
+ {// Dirty version
+ memcpy(&iHybridHeap->iBitMapBuffer[0], aBitmap, (aLth >> 3));
+ status = ETrue;
+ }
+
+ return status;
+}
+
+void TestHybridHeap::AllocateSomeBuffers(TUint8** aBfrs, TInt aMinLth, TInt MaxLth, TInt aCount )
+{
+
+ TInt loop = RandomNumber(2, 8);
+
+ while ( loop )
+ {
+ // allocate all buffers
+ TInt i;
+ for (i=0; i<aCount; ++i)
+ {
+ if (!aBfrs[i])
+ {
+ aBfrs[i] = (TUint8*)Alloc(RandomNumber(aMinLth, MaxLth));
+ }
+ }
+
+ // free some cells
+ TInt n = RandomNumber(2, aCount);
+ while (--n)
+ {
+ i = RandomNumber(2, aCount);
+ if (aBfrs[i])
+ {
+ Free(aBfrs[i]);
+ aBfrs[i] = NULL;
+ }
+ }
+
+ // realloc some cells
+ n = RandomNumber(2, aCount);
+ while (--n)
+ {
+ TInt new_len = RandomNumber(aMinLth, MaxLth);
+ if (aBfrs[i])
+ {
+ TUint8* p = (TUint8*)ReAlloc(aBfrs[i], new_len, Random());
+ if (p)
+ {
+ aBfrs[i] = p;
+ }
+ }
+ }
+
+ loop --;
+ }
+
+}
+
+TBool TestHybridHeap::PrintHeapInitData()
+{
+ TInt total;
+ TInt count = AllocSize(total);
+ RDebug::Printf("Heap initialised for test, alloc count: %d , alloc size: %d\n", count, total);
+ if ( iHybridHeap )
+ RDebug::Printf("Heap initialised for test, iCellCount: %d , iTotalAllocSize: %d\n", iHybridHeap->iCellCount, iHybridHeap->iTotalAllocSize);
+ return (count != 0);
+}
+
+
+// Local Functions
+LOCAL_D TInt HeapCheckTestThread(TAny* param)
+{
+ TInt t = *((TInt*)param);
+ TUint8* bfrs[256];
+ Mem::FillZ(bfrs, sizeof(bfrs));
+ TestHybridHeap heap;
+ test(heap.Init());
+
+ switch( t )
+ {
+ case 1:
+ {
+ // Overwrite Doug Lea buffer and check()
+ heap.AllocateSomeBuffers(bfrs, 0x40, 0xfff0, 256);
+ test(heap.PrintHeapInitData());
+ TUint8 *p = heap.Alloc(64);
+ test( p != NULL );
+ Mem::FillZ(p, 80); // Heap corrupted
+ heap.Check(); // This should cause panic
+ break;
+ }
+
+ case 2:
+ // Corrupt a smallbin and check
+ {
+ TInt i = 0;
+ TBool smallbin_corrupted = EFalse;
+ while ( !smallbin_corrupted )
+ {
+ heap.AllocateSomeBuffers(bfrs, 0x4, 0xff, 256);
+ smallbin_corrupted = heap.CorruptSmallBin();
+ i ++;
+ if ( i > 9 )
+ break;
+ }
+ test(smallbin_corrupted);
+ test(heap.PrintHeapInitData());
+ heap.Check(); // This should cause panic
+ }
+ break;
+
+ case 3:
+ // Corrupt a treebin and check
+ {
+ TInt i = 0;
+ TBool treebin_corrupted = EFalse;
+ while ( !treebin_corrupted )
+ {
+ heap.AllocateSomeBuffers(bfrs, 0x100, 0x4000, 256);
+ treebin_corrupted = heap.CorruptTreeBin();
+ i ++;
+ if ( i > 9 )
+ break;
+ }
+ test(treebin_corrupted);
+ test(heap.PrintHeapInitData());
+ heap.Check(); // This should cause panic
+ break;
+ }
+
+ case 10:
+ // Overwrite slab buffer and check
+ {
+ TInt i = 0;
+ TBool slabs_created = EFalse;
+ if ( !heap.SlabAllocatorExists() )
+ {
+ User::Panic(KLitHeapCheck, ETHeapDebugUnmatchedCallToCheckHeap);
+ }
+
+ while ( !slabs_created )
+ {
+ // Allocate enough buffers to cause slab allocator to be
+ // initialised
+ heap.AllocateSomeBuffers(bfrs, 0x4, 0x2000, 256);
+ slabs_created = heap.SlabsCreated();
+ i ++;
+ if ( i > 9 )
+ break;
+ }
+ test(slabs_created);
+ test(heap.PrintHeapInitData());
+ i = 0;
+ TUint8* p[10];
+ while ( i < 10 )
+ {
+ p[i] = heap.Alloc(24);
+ test( p[i] != NULL );
+ i ++;
+ }
+ i = 0;
+ while ( i < 10 )
+ {
+ heap.Free(p[i]);
+ i +=2;
+ }
+ p[0] = heap.Alloc(24);
+ test( p[0] != NULL );
+ memset((TUint8*)(Floor(p[0], SLABSIZE) + sizeof(slabhdr)), 0xee, KMaxSlabPayload); // Heap corrupted
+ heap.Check(); // This should cause panic
+ break;
+ }
+
+ case 11:
+ // Corrupt slab header
+ {
+ TInt i = 0;
+ TBool slabs_created = EFalse;
+ if ( !heap.SlabAllocatorExists() )
+ {
+ User::Panic(KLitHeapCheck, ETHeapDebugUnmatchedCallToCheckHeap);
+ }
+
+ while ( !slabs_created )
+ {
+ // Allocate enough buffers to cause slab allocator to be
+ // initialised
+ heap.AllocateSomeBuffers(bfrs, 0x4, 0x2000, 256);
+ slabs_created = heap.SlabsCreated();
+ i ++;
+ if ( i > 9 )
+ break;
+ }
+ test(slabs_created);
+ test(heap.PrintHeapInitData());
+ TUint8* p = heap.Alloc(28);
+ test(p != NULL);
+ p = Floor(p, SLABSIZE);
+ *(TUint32*)p = 0xffeeddcc;
+ heap.Check(); // This should cause panic
+ break;
+ }
+
+ case 20:
+ // Corrupt page bitmap data and check
+ {
+ if ( !heap.PageAllocatorExists() )
+ {
+ User::Panic(KLitHeapCheck, ETHeapDebugUnmatchedCallToCheckHeap);
+ }
+ test(heap.ConfigurePageAllocator());
+ // Allocate some buffers to cause slab allocator to be
+ // initialised
+ heap.AllocateSomeBuffers(bfrs, 0x4000, 0x10000, 16);
+ test(heap.PrintHeapInitData());
+ TUint8* bitmap = heap.Alloc(128); // For saved bitmap
+ test(bitmap != NULL);
+ TInt bit_lth = heap.CopyPageBitmap(bitmap, 128);
+ test(bit_lth != 0);
+ memset(bitmap, 0xee, (bit_lth>>3)); // corrupt bitmap data
+ heap.RestorePageBitmap(bitmap, bit_lth);
+ heap.Check(); // This should cause panic
+ break;
+ }
+
+ case 21:
+ // Corrupt page bitmap with a earlier freed "ghost" buffer info
+ {
+ if ( !heap.PageAllocatorExists() )
+ {
+ User::Panic(KLitHeapCheck, ETHeapDebugUnmatchedCallToCheckHeap);
+ }
+ test(heap.ConfigurePageAllocator());
+ // Allocate some buffers to cause slab allocator to be
+ // initialised
+ heap.AllocateSomeBuffers(bfrs, 0x4000, 0x10000, 16);
+ test(heap.PrintHeapInitData());
+ TUint8* bitmap = heap.Alloc(128); // For saved bitmap
+ test(bitmap != NULL);
+ TUint8* p = heap.Alloc(0x8000); // One more page buffer
+ TInt bit_lth = heap.CopyPageBitmap(bitmap, 128);
+ test(bit_lth != 0);
+ heap.Free(p);
+ heap.RestorePageBitmap(bitmap, bit_lth);
+ heap.Check(); // This should cause panic
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ User::Invariant(); // Should not reach here
+ return 0;
+}
+
+
+class TestHeapCheck
+{
+ public:
+ void TestCheck(void);
+ TInt TestThreadExit(RThread& aThread, TExitType aExitType, TInt aExitReason);
+};
+
+
+TInt TestHeapCheck::TestThreadExit(RThread& aThread, TExitType aExitType, TInt aExitReason)
+{
+ // Disable JIT debugging.
+ TBool justInTime=User::JustInTime();
+ User::SetJustInTime(EFalse);
+
+ TRequestStatus status;
+ aThread.Logon(status);
+ aThread.Resume();
+ User::WaitForRequest(status);
+ if (aExitType != aThread.ExitType())
+ return KErrGeneral;
+
+ if ( (status.Int() == ETHeapDebugUnmatchedCallToCheckHeap) && (aThread.ExitReason() == ETHeapDebugUnmatchedCallToCheckHeap))
+ {
+ CLOSE_AND_WAIT(aThread);
+ // Put JIT debugging back to previous status.
+ User::SetJustInTime(justInTime);
+ return KErrNotSupported;
+ }
+
+ if ( status.Int() == ERTestFailed )
+ return KErrGeneral;
+
+ if ( aExitReason > 0 )
+ {
+ if (aExitReason != status.Int())
+ return KErrGeneral;
+
+ if (aExitReason != aThread.ExitReason())
+ return KErrGeneral;
+ }
+
+ CLOSE_AND_WAIT(aThread);
+
+ // Put JIT debugging back to previous status.
+ User::SetJustInTime(justInTime);
+ return KErrNone;
+
+}
+
+void TestHeapCheck::TestCheck()
+{
+ TInt type;
+ TInt r;
+
+ test.Next(_L("Testing Doug Lea allocator check"));
+ {
+ type = 1;
+ RThread thread;
+ test(thread.Create(_L("Check UserHeap"),HeapCheckTestThread, KDefaultStackSize, 0x1000, 0x400000, (TAny*) &type)== KErrNone);
+ test(TestThreadExit(thread, EExitPanic, ETHeapBadCellAddress)==KErrNone);
+
+ type = 2;
+ test(thread.Create(_L("Check UserHeap"),HeapCheckTestThread, KDefaultStackSize, 0x1000, 0x400000, (TAny*) &type)==KErrNone);
+ test(TestThreadExit(thread, EExitPanic, ETHeapBadCellAddress)==KErrNone);
+
+ type = 3;
+ test(thread.Create(_L("Check UserHeap"),HeapCheckTestThread, KDefaultStackSize, 0x1000, 0x400000, (TAny*) &type)==KErrNone);
+ test(TestThreadExit(thread, EExitPanic, ETHeapBadCellAddress)==KErrNone);
+
+ }
+
+ test.Next(_L("Testing Slab allocator check"));
+ {
+ type = 10;
+ RThread thread;
+ test(thread.Create(_L("Check UserHeap"),HeapCheckTestThread, KDefaultStackSize, 0x1000, 0x400000, (TAny*) &type)==KErrNone);
+ r = TestThreadExit(thread, EExitPanic, ETHeapBadCellAddress);
+ if ( r != KErrNotSupported )
+ {
+ test(r==KErrNone);
+
+ type = 11;
+ RThread thread;
+ test(thread.Create(_L("Check UserHeap"),HeapCheckTestThread, KDefaultStackSize, 0x1000, 0x400000, (TAny*) &type)==KErrNone);
+ test(TestThreadExit(thread, EExitPanic, ETHeapBadCellAddress)==KErrNone);
+ }
+ else test.Printf(_L("Slab allocator does not exist, testes bypassed\n"));
+ }
+
+ test.Next(_L("Testing Page allocator check"));
+ {
+ type = 20;
+ RThread thread;
+ test(thread.Create(_L("Check UserHeap"),HeapCheckTestThread, KDefaultStackSize, 0x1000, 0x800000, (TAny*) &type)==KErrNone);
+ r = TestThreadExit(thread, EExitPanic, KErrNone); // Accept any panic reason here
+ if ( r != KErrNotSupported )
+ {
+ test(r==KErrNone);
+
+ type = 21;
+ RThread thread;
+ test(thread.Create(_L("Check UserHeap"),HeapCheckTestThread, KDefaultStackSize, 0x1000, 0x800000, (TAny*) &type)==KErrNone);
+ test(TestThreadExit(thread, EExitPanic, KErrNone)==KErrNone); // Accept any panic reason here
+ }
+ else test.Printf(_L("Page allocator does not exist, testes bypassed\n"));
+ }
+
+}
+
+
+
+
+// Global Functions
+
+GLDEF_C TInt E32Main(void)
+ {
+
+ test.Title();
+
+ test.Start(_L("Testing Heap Check function"));
+
+ TestHeapCheck T;
+
+ T.TestCheck();
+
+ test.End();
+
+ return(0);
+ }
+