diff -r a232af6b0b1f -r a5496987b1da kerneltest/e32test/heap/t_heapcheck.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/heap/t_heapcheck.cpp Thu Jul 01 17:57:33 2010 +0100 @@ -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 +#include +#include +#include +#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; iiCellCount, 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); + } +