kerneltest/e32test/system/t_prot.cpp
author Tom Cosgrove <tom.cosgrove@nokia.com>
Fri, 28 May 2010 16:29:07 +0100
changeset 30 8aab599e3476
parent 0 a41df078684a
child 43 c1f20ce4abcf
permissions -rw-r--r--
Fix for bug 2283 (RVCT 4.0 support is missing from PDK 3.0.h) Have multiple extension sections in the bld.inf, one for each version of the compiler. The RVCT version building the tools will build the runtime libraries for its version, but make sure we extract all the other versions from zip archives. Also add the archive for RVCT4.

// Copyright (c) 1996-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\system\t_prot.cpp
// Overview:
// Tests that the kernel panics the user rather than dying in 
// various situations
// API Information:
// N/A
// Details:
// - Verify that printing a long string does not panic.
// - Verify that a stack overflow in a thread causes a panic.
// - Create the thread which panics a dead thread, verify that 
// it isn't panicked for doing this.
// - Create the thread which panics a bad handle, verify that 
// it isn't panicked for doing this.
// - Verify that an RSession send from an uninitialised handle
// causes a panic.
// - Verify that the thread causing an exception, for a variety
// of reasons, is panicked.
// - Verify thread writing to kernel data is panicked.
// - Verify that RAM disk access either causes a panic or is
// denied, based on the platform security settings.
// - Verify that an RThread::RequestComplete() with an invalid
// address causes a panic. Verify results are as expected.
// Platforms/Drives/Compatibility:
// All.
// Assumptions/Requirement/Pre-requisites:
// Failures and causes:
// Base Port information:
// 
//

#include <e32test.h>
#include "u32std.h"
#include <e32panic.h>
#include "../mmu/mmudetect.h"

void DoUndefinedInstruction();

const TInt KHeapSize=0x200;
const TInt KThreadReturnValue=9999;

_LIT(KLitKernExec,"KERN-EXEC");

class RSessionTest : public RSessionBase
	{
public:
	void DoSend();
	};


void RSessionTest::DoSend()
	{
    Send(34,TIpcArgs(78));
	//Send(34,(TAny*)78);
	};

LOCAL_D RTest test(_L("T_PROT"));
LOCAL_D RTest t(_L("T_PROT thread"));

enum TAction
	{
	EDataAbort=0,
	EPrefetchAbort=1,
	EUndefinedInstruction=2,
	};

typedef void (*PFV)(void);

#ifndef __MARM__
void DoUndefinedInstruction()
	{
#ifdef __GCC32__	
	asm("int 6");
#else
	_asm int 6;
#endif
	}
#endif

LOCAL_C TInt ExceptionThread(TAny* anAction)
	{
	TAction action=(TAction)((TInt) anAction);
	switch (action)
		{
		case EDataAbort:
			*(TInt*)0=0;
			break;
		case EPrefetchAbort:
			{
//			PFV f=(PFV)NULL;
			PFV f=(PFV)0x80000;	// don't use NULL since it is readable on Snowdrop
			(*f)();
			break;
			}
		case EUndefinedInstruction:
			DoUndefinedInstruction();
			break;
		};
	return 0;
	}

LOCAL_C void RunTestThread(TAction anAction)
	{
	RThread t;
	TInt r=t.Create(_L("TestThread"),ExceptionThread,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)anAction);
	test(r==KErrNone);
	TRequestStatus s;
	t.Logon(s);
	test(s==KRequestPending);
	t.Resume();
	User::WaitForRequest(s);
	test(t.ExitType()==EExitPanic);
	test(t.ExitCategory()==_L("KERN-EXEC"));
	test(t.ExitReason()==ECausedException);
	CLOSE_AND_WAIT(t);
	}

LOCAL_C TInt UnintThread(TAny* )
	{

	RSessionTest rsb;
	rsb.DoSend();
	FOREVER
		;
	}

void testSession()
//
// Test 1
//
	{
	
	RThread thread;
	TRequestStatus stat;

	test.Next(_L("Create UnintThread"));
	TInt r=thread.Create(_L("UnintThread"),UnintThread,KDefaultStackSize,KHeapSize,KHeapSize,0);
	test(r==KErrNone);

	thread.Logon(stat);
	test(thread.ExitType()==EExitPending);
	test.Next(_L("Resume UnintThread"));
	thread.Resume();
	User::WaitForRequest(stat);
	test.Next(_L("Check UnintThread panicked"));
	test(thread.ExitCategory()==_L("KERN-EXEC"));
//	test(thread.ExitReason()==EBadHandle);
	test(thread.ExitType()==EExitPanic);
	CLOSE_AND_WAIT(thread);
	}

TInt dummy(TAny*)
	{
	return(KErrNone);
	}

// Dennis - modified this to panic a dead thread rather than a bad handle
TInt pdThread(TAny*)
	{
	RThread thread;
	TRequestStatus stat;
	TInt r=thread.Create(_L("dummy"),dummy,KDefaultStackSize,KHeapSize,KHeapSize,0);
	test(r==KErrNone);
	thread.Logon(stat);
	test(thread.ExitType()==EExitPending);
	thread.Resume();
	User::WaitForRequest(stat);
	test(thread.ExitType()==EExitKill);
	test(stat.Int()==KErrNone);
	thread.Panic(_L("MYPANIC"),0x666); // this shouldn't panic pdThread
	test(thread.ExitType()==EExitKill);
	test(thread.ExitReason()==KErrNone);
	CLOSE_AND_WAIT(thread);
	return(KErrNone);
	}

void testPanicDeadThread()
//
// Dennis - modified this to panic a dead thread rather than a bad handle
// Create the thread which panics a dead thread /*bad handle*/
// Check that it isn't panicked for doing this
//
	{
	RThread thread;
	TRequestStatus stat;
	test.Next(_L("Create PanicDeadThread"));
	TInt r=thread.Create(_L("PanicDeadThread"),pdThread,KDefaultStackSize,KHeapSize,KHeapSize,0);
	test(r==KErrNone);
	thread.Logon(stat);
	test(thread.ExitType()==EExitPending);
	test.Next(_L("Resume PanicDeadThread"));
	thread.Resume();
	User::WaitForRequest(stat);
	test.Next(_L("Check PanicDeadThread did not panic"));
	test(thread.ExitReason()==KErrNone);
	test(thread.ExitType()==EExitKill);
	test(thread.ExitCategory()==_L("Kill"));
	CLOSE_AND_WAIT(thread);
	}

TInt doDeadThreadStuff(TAny*)
	{
	RThread thread;
	TRequestStatus stat;
	TInt r=thread.Create(_L("dummy2"),dummy,KDefaultStackSize,KHeapSize,KHeapSize,0);
	test(r==KErrNone);
	thread.Logon(stat);
	test(thread.ExitType()==EExitPending);
	thread.Resume();
	User::WaitForRequest(stat);

	thread.SetPriority(EPriorityNormal);

	CLOSE_AND_WAIT(thread);
	return(KErrNone);
	}

void testDeadThread()
//
// Create the thread which panics a bad handle
// Check that it isn't panicked for doing this
//
	{
	RThread thread;
	TRequestStatus stat;
	test.Next(_L("Create doDeadThreadStuff"));
	TInt r=thread.Create(_L("doDeadThreadStuff"),doDeadThreadStuff,KDefaultStackSize,KHeapSize,KHeapSize,0);
	test(r==KErrNone);
	thread.Logon(stat);
	test(thread.ExitType()==EExitPending);
	test.Next(_L("Resume doDeadThreadStuff"));
	thread.Resume();
	User::WaitForRequest(stat);
	test.Next(_L("Check doDeadThreadStuff did not panic"));
	test(thread.ExitReason()==KErrNone);
	test(thread.ExitType()==EExitKill);
	test(thread.ExitCategory()==_L("Kill"));
	CLOSE_AND_WAIT(thread);
	}

TInt MinimalThread(TAny*)
//
// Minimal thread, used in test 5
//
	{
	return(KErrNone);
	}

LOCAL_C TInt ExceptThread(TAny* )
	{

	TUint* nullPtr=0;
	*nullPtr=0xdead; // BANG!!
	return KErrNone;
	}


void testExcept()
//
// Test thread causing exception is panicked
//
	{
	
	RThread thread;
	TRequestStatus stat;

	test.Next(_L("Create ExceptThread"));
	TInt r=thread.Create(_L("ExceptThread"),ExceptThread,KDefaultStackSize,KHeapSize,KHeapSize,0);
	test(r==KErrNone);

	thread.Logon(stat);
	test(thread.ExitType()==EExitPending);
	test.Next(_L("Resume ExceptThread"));
	thread.Resume();
	User::WaitForRequest(stat);
	test.Next(_L("Check ExceptThread panicked"));
	test(thread.ExitCategory()==_L("KERN-EXEC"));
	test(thread.ExitReason()==ECausedException);
	test(thread.ExitType()==EExitPanic);
	CLOSE_AND_WAIT(thread);
	}

LOCAL_C TInt StackThread(TAny* )
	{

	TFileName heresAnotherOne;
	StackThread((TAny*)heresAnotherOne.Ptr()); // go recursive
	return KErrNone;
	}

void testStackOverflow()
//
// Thread overflowing its stack is panicked
//
	{

	RThread thread;
	TRequestStatus stat;

	test.Next(_L("Create StackThread"));
	TInt r=thread.Create(_L("StackThread"),StackThread,KDefaultStackSize,KHeapSize,KHeapSize,0);
	test(r==KErrNone);

	thread.Logon(stat);
	test(thread.ExitType()==EExitPending);
	test.Next(_L("Resume StackThread"));
	thread.Resume();
	User::WaitForRequest(stat);
	test.Next(_L("Check StackThread panicked"));
	test(thread.ExitCategory()==_L("KERN-EXEC"));
	test(thread.ExitReason()==ECausedException);
	test(thread.ExitType()==EExitPanic);
	CLOSE_AND_WAIT(thread);
	}

LOCAL_C TInt KernWriter(TAny* )
	{

	TUint* kernPtr=(TUint*)KernData();
	*kernPtr=0xdead; // BANG!!
	return KErrNone;
	}

LOCAL_C TInt RamDiskWriter(TAny* )
	{

	TFindChunk fChunk(_L("*TheRamDriveChunk*"));
	TFullName n;
	TInt r=fChunk.Next(n);
	RChunk ch;
	r=ch.Open(fChunk);
	if(r!=KErrNone)
		return r;
	TUint8* rdPtr=ch.Base();
	*rdPtr=0xaa; // BANG!!
	return KErrNone;
	}


void testKernelWriter()
//
// Thread writing to kernel data is panicked
//
	{

	RThread thread;
	TRequestStatus stat;

	test.Next(_L("Create KernWriter"));
	TInt r=thread.Create(_L("KernWriter"),KernWriter,KDefaultStackSize,KHeapSize,KHeapSize,0);
	test(r==KErrNone);

	thread.Logon(stat);
	test(thread.ExitType()==EExitPending);
	test.Next(_L("Resume KernWriter"));
	thread.Resume();
	User::WaitForRequest(stat);
	test.Next(_L("Check KernWriter panicked"));
	test(thread.ExitCategory()==_L("KERN-EXEC"));
	test(thread.ExitReason()==ECausedException);
	test(thread.ExitType()==EExitPanic);
	CLOSE_AND_WAIT(thread);
	}

void testRamDiskAccess()
	{

	RThread thread;
	TRequestStatus stat;

	test.Next(_L("Create RamDiskWriter"));
	TInt r=thread.Create(_L("RamDiskWriter"),RamDiskWriter,KDefaultStackSize,KHeapSize,KHeapSize,0);
	test(r==KErrNone);

	thread.Logon(stat);
	test(thread.ExitType()==EExitPending);
	test.Next(_L("Resume RamDiskWriter"));
	thread.Resume();
	User::WaitForRequest(stat);
	if((!PlatSec::ConfigSetting(PlatSec::EPlatSecProcessIsolation))||(!PlatSec::ConfigSetting(PlatSec::EPlatSecEnforcement)))
		{
		test.Next(_L("Check RamDiskWriter panicked"));
		test(thread.ExitCategory()==_L("KERN-EXEC"));
		test(thread.ExitReason()==ECausedException);
		test(thread.ExitType()==EExitPanic);
		}
	else
		{
		test.Next(_L("Check RamDiskWriter was refused access"));
		test(thread.ExitReason()==KErrPermissionDenied);
		test(thread.ExitType()==EExitKill);
		}
	CLOSE_AND_WAIT(thread);
	}

RThread MainThread;

TInt RequestCompleteThread(TAny* aPtr)
	{
	TRequestStatus** pS=(TRequestStatus**)aPtr;
	MainThread.RequestComplete(*pS,123);
	return 0;
	}

_LIT(KReqCompThreadName,"ReqCompThread");
void StartRequestCompleteThread(RThread& aThread, TRequestStatus** aStatus)
	{
	TInt r=aThread.Create(KReqCompThreadName,RequestCompleteThread,0x1000,0x1000,0x10000,aStatus);
	test (r==KErrNone);
	aThread.SetPriority(EPriorityMore);
	TRequestStatus s;
	aThread.Logon(s);
	aThread.Resume();
	User::WaitForRequest(s);
	}

_LIT(KLitUserCBase,"E32USER-CBase");
GLDEF_C TInt E32Main()
//
// Main
//
	{	

	// don't want just in time debugging as we trap panics
	TBool justInTime=User::JustInTime(); 
	User::SetJustInTime(EFalse); 

	test.Title();
	test.Start(_L("Test protection & panicking"));

#if defined(__EPOC32__)
	// this next test doesn't work under WINS because the string needs to be converted
	// from UNICODE to ascii for printing to STDOUT, and that requires the string to be
	// zero-terminated which panics because the string is too long.
	test.Next(_L("Printing long string doesn't get me shot"));
	test.Printf(_L("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"));
	test.Printf(_L("\n"));
#endif

	if (HaveVirtMem())
		{
		test.Next(_L("Stack overflow shoots the thread not the kernel"));
		testStackOverflow();
		}

	test.Next(_L("Panicking a closed thread doesn't panic the panicker!!"));
	testPanicDeadThread();

	test.Next(_L("Dead thread tests"));
	testDeadThread();

	test.Next(_L("RSession send from uninitialised handle"));
	testSession();

	test.Next(_L("Thread causing exception is killed"));
	if (HaveVirtMem())
		{
		testExcept();
		RunTestThread(EDataAbort);
		RunTestThread(EPrefetchAbort);
		}
#ifndef __WINS__
	RunTestThread(EUndefinedInstruction);
#endif

#if defined(__EPOC32__)
	if (HaveDirectKernProt())
		{
		test.Next(_L("Thread writing to kernel data is killed"));
		testKernelWriter();
		}

	if (HaveProcessProt())
		{
		test.Next(_L("Check access to RamDisk is denied"));
		testRamDiskAccess();
		}

	if (HaveMMU())
		{
		test.Next(_L("RequestComplete() with bad address"));
		TInt rqc=RThread().RequestCount();
		test(MainThread.Duplicate(RThread())==KErrNone);
		RThread t;
		TRequestStatus s=KRequestPending;
		TRequestStatus* pS=&s;
		StartRequestCompleteThread(t,&pS);
		test(t.ExitType()==EExitKill);
		test(t.ExitReason()==KErrNone);
		CLOSE_AND_WAIT(t);
		test(s==123);
		test(pS==NULL);
		test(RThread().RequestCount()==rqc+1);
		TRequestStatus** bad=(TRequestStatus**)0x80000000;	// kernel space
		StartRequestCompleteThread(t,bad);
		test(t.ExitType()==EExitPanic);
		test(t.ExitReason()==ECausedException);
		test(t.ExitCategory()==KLitKernExec);
		CLOSE_AND_WAIT(t);
		test(RThread().RequestCount()==rqc+1);
		pS=(TRequestStatus*)0x80000000;
		StartRequestCompleteThread(t,&pS);
		// Request status poked user side, so we expect a KERN-EXEC 3...
		test(t.ExitType()==EExitPanic);
		test(t.ExitReason()==ECausedException);
		test(t.ExitCategory()==KLitKernExec);
		CLOSE_AND_WAIT(t);
		test(pS==NULL);
		test(RThread().RequestCount()==rqc+1);
		pS=(TRequestStatus*)(((TUint8*)&s)+0x80000);		// aligned, within chunk max size but invalid
		StartRequestCompleteThread(t,&pS);
		// Request status poked user side, so we expect a KERN-EXEC 3...
		test(t.ExitType()==EExitPanic);
		test(t.ExitReason()==ECausedException);
		test(t.ExitCategory()==KLitKernExec);
		CLOSE_AND_WAIT(t);
		test(pS==NULL);
		test(RThread().RequestCount()==rqc+1);
		}
#endif

	test.End();

	User::SetJustInTime(justInTime);
	return(KErrNone);
	}