kerneltest/e32test/heap/t_heapcheck.cpp
changeset 109 b3a1d9898418
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32test/heap/t_heapcheck.cpp	Fri May 14 17:13:29 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);
+	}
+