sl@0: // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // e32test\system\t_prot.cpp sl@0: // Overview: sl@0: // Tests that the kernel panics the user rather than dying in sl@0: // various situations sl@0: // API Information: sl@0: // N/A sl@0: // Details: sl@0: // - Verify that printing a long string does not panic. sl@0: // - Verify that a stack overflow in a thread causes a panic. sl@0: // - Create the thread which panics a dead thread, verify that sl@0: // it isn't panicked for doing this. sl@0: // - Create the thread which panics a bad handle, verify that sl@0: // it isn't panicked for doing this. sl@0: // - Verify that an RSession send from an uninitialised handle sl@0: // causes a panic. sl@0: // - Verify that the thread causing an exception, for a variety sl@0: // of reasons, is panicked. sl@0: // - Verify thread writing to kernel data is panicked. sl@0: // - Verify that RAM disk access either causes a panic or is sl@0: // denied, based on the platform security settings. sl@0: // - Verify that an RThread::RequestComplete() with an invalid sl@0: // address causes a panic. Verify results are as expected. sl@0: // Platforms/Drives/Compatibility: sl@0: // All. sl@0: // Assumptions/Requirement/Pre-requisites: sl@0: // Failures and causes: sl@0: // Base Port information: sl@0: // sl@0: // sl@0: sl@0: #include sl@0: #include "u32std.h" sl@0: #include sl@0: #include "../mmu/mmudetect.h" sl@0: sl@0: void DoUndefinedInstruction(); sl@0: sl@0: const TInt KHeapSize=0x200; sl@0: const TInt KThreadReturnValue=9999; sl@0: sl@0: _LIT(KLitKernExec,"KERN-EXEC"); sl@0: sl@0: class RSessionTest : public RSessionBase sl@0: { sl@0: public: sl@0: void DoSend(); sl@0: }; sl@0: sl@0: sl@0: void RSessionTest::DoSend() sl@0: { sl@0: Send(34,TIpcArgs(78)); sl@0: //Send(34,(TAny*)78); sl@0: }; sl@0: sl@0: LOCAL_D RTest test(_L("T_PROT")); sl@0: LOCAL_D RTest t(_L("T_PROT thread")); sl@0: sl@0: enum TAction sl@0: { sl@0: EDataAbort=0, sl@0: EPrefetchAbort=1, sl@0: EUndefinedInstruction=2, sl@0: }; sl@0: sl@0: typedef void (*PFV)(void); sl@0: sl@0: #ifndef __MARM__ sl@0: void DoUndefinedInstruction() sl@0: { sl@0: #ifdef __GCC32__ sl@0: asm("int 6"); sl@0: #else sl@0: _asm int 6; sl@0: #endif sl@0: } sl@0: #endif sl@0: sl@0: LOCAL_C TInt ExceptionThread(TAny* anAction) sl@0: { sl@0: TAction action=(TAction)((TInt) anAction); sl@0: switch (action) sl@0: { sl@0: case EDataAbort: sl@0: *(TInt*)0=0; sl@0: break; sl@0: case EPrefetchAbort: sl@0: { sl@0: // PFV f=(PFV)NULL; sl@0: PFV f=(PFV)0x80000; // don't use NULL since it is readable on Snowdrop sl@0: (*f)(); sl@0: break; sl@0: } sl@0: case EUndefinedInstruction: sl@0: DoUndefinedInstruction(); sl@0: break; sl@0: }; sl@0: return 0; sl@0: } sl@0: sl@0: LOCAL_C void RunTestThread(TAction anAction) sl@0: { sl@0: RThread t; sl@0: TInt r=t.Create(_L("TestThread"),ExceptionThread,KDefaultStackSize,KHeapSize,KHeapSize,(TAny*)anAction); sl@0: test(r==KErrNone); sl@0: TRequestStatus s; sl@0: t.Logon(s); sl@0: test(s==KRequestPending); sl@0: t.Resume(); sl@0: User::WaitForRequest(s); sl@0: test(t.ExitType()==EExitPanic); sl@0: test(t.ExitCategory()==_L("KERN-EXEC")); sl@0: test(t.ExitReason()==ECausedException); sl@0: CLOSE_AND_WAIT(t); sl@0: } sl@0: sl@0: LOCAL_C TInt UnintThread(TAny* ) sl@0: { sl@0: sl@0: RSessionTest rsb; sl@0: rsb.DoSend(); sl@0: FOREVER sl@0: ; sl@0: } sl@0: sl@0: void testSession() sl@0: // sl@0: // Test 1 sl@0: // sl@0: { sl@0: sl@0: RThread thread; sl@0: TRequestStatus stat; sl@0: sl@0: test.Next(_L("Create UnintThread")); sl@0: TInt r=thread.Create(_L("UnintThread"),UnintThread,KDefaultStackSize,KHeapSize,KHeapSize,0); sl@0: test(r==KErrNone); sl@0: sl@0: thread.Logon(stat); sl@0: test(thread.ExitType()==EExitPending); sl@0: test.Next(_L("Resume UnintThread")); sl@0: thread.Resume(); sl@0: User::WaitForRequest(stat); sl@0: test.Next(_L("Check UnintThread panicked")); sl@0: test(thread.ExitCategory()==_L("KERN-EXEC")); sl@0: // test(thread.ExitReason()==EBadHandle); sl@0: test(thread.ExitType()==EExitPanic); sl@0: CLOSE_AND_WAIT(thread); sl@0: } sl@0: sl@0: TInt dummy(TAny*) sl@0: { sl@0: return(KErrNone); sl@0: } sl@0: sl@0: // Dennis - modified this to panic a dead thread rather than a bad handle sl@0: TInt pdThread(TAny*) sl@0: { sl@0: RThread thread; sl@0: TRequestStatus stat; sl@0: TInt r=thread.Create(_L("dummy"),dummy,KDefaultStackSize,KHeapSize,KHeapSize,0); sl@0: test(r==KErrNone); sl@0: thread.Logon(stat); sl@0: test(thread.ExitType()==EExitPending); sl@0: thread.Resume(); sl@0: User::WaitForRequest(stat); sl@0: test(thread.ExitType()==EExitKill); sl@0: test(stat.Int()==KErrNone); sl@0: thread.Panic(_L("MYPANIC"),0x666); // this shouldn't panic pdThread sl@0: test(thread.ExitType()==EExitKill); sl@0: test(thread.ExitReason()==KErrNone); sl@0: CLOSE_AND_WAIT(thread); sl@0: return(KErrNone); sl@0: } sl@0: sl@0: void testPanicDeadThread() sl@0: // sl@0: // Dennis - modified this to panic a dead thread rather than a bad handle sl@0: // Create the thread which panics a dead thread /*bad handle*/ sl@0: // Check that it isn't panicked for doing this sl@0: // sl@0: { sl@0: RThread thread; sl@0: TRequestStatus stat; sl@0: test.Next(_L("Create PanicDeadThread")); sl@0: TInt r=thread.Create(_L("PanicDeadThread"),pdThread,KDefaultStackSize,KHeapSize,KHeapSize,0); sl@0: test(r==KErrNone); sl@0: thread.Logon(stat); sl@0: test(thread.ExitType()==EExitPending); sl@0: test.Next(_L("Resume PanicDeadThread")); sl@0: thread.Resume(); sl@0: User::WaitForRequest(stat); sl@0: test.Next(_L("Check PanicDeadThread did not panic")); sl@0: test(thread.ExitReason()==KErrNone); sl@0: test(thread.ExitType()==EExitKill); sl@0: test(thread.ExitCategory()==_L("Kill")); sl@0: CLOSE_AND_WAIT(thread); sl@0: } sl@0: sl@0: TInt doDeadThreadStuff(TAny*) sl@0: { sl@0: RThread thread; sl@0: TRequestStatus stat; sl@0: TInt r=thread.Create(_L("dummy2"),dummy,KDefaultStackSize,KHeapSize,KHeapSize,0); sl@0: test(r==KErrNone); sl@0: thread.Logon(stat); sl@0: test(thread.ExitType()==EExitPending); sl@0: thread.Resume(); sl@0: User::WaitForRequest(stat); sl@0: sl@0: thread.SetPriority(EPriorityNormal); sl@0: sl@0: CLOSE_AND_WAIT(thread); sl@0: return(KErrNone); sl@0: } sl@0: sl@0: void testDeadThread() sl@0: // sl@0: // Create the thread which panics a bad handle sl@0: // Check that it isn't panicked for doing this sl@0: // sl@0: { sl@0: RThread thread; sl@0: TRequestStatus stat; sl@0: test.Next(_L("Create doDeadThreadStuff")); sl@0: TInt r=thread.Create(_L("doDeadThreadStuff"),doDeadThreadStuff,KDefaultStackSize,KHeapSize,KHeapSize,0); sl@0: test(r==KErrNone); sl@0: thread.Logon(stat); sl@0: test(thread.ExitType()==EExitPending); sl@0: test.Next(_L("Resume doDeadThreadStuff")); sl@0: thread.Resume(); sl@0: User::WaitForRequest(stat); sl@0: test.Next(_L("Check doDeadThreadStuff did not panic")); sl@0: test(thread.ExitReason()==KErrNone); sl@0: test(thread.ExitType()==EExitKill); sl@0: test(thread.ExitCategory()==_L("Kill")); sl@0: CLOSE_AND_WAIT(thread); sl@0: } sl@0: sl@0: TInt MinimalThread(TAny*) sl@0: // sl@0: // Minimal thread, used in test 5 sl@0: // sl@0: { sl@0: return(KErrNone); sl@0: } sl@0: sl@0: LOCAL_C TInt ExceptThread(TAny* ) sl@0: { sl@0: sl@0: TUint* nullPtr=0; sl@0: *nullPtr=0xdead; // BANG!! sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: void testExcept() sl@0: // sl@0: // Test thread causing exception is panicked sl@0: // sl@0: { sl@0: sl@0: RThread thread; sl@0: TRequestStatus stat; sl@0: sl@0: test.Next(_L("Create ExceptThread")); sl@0: TInt r=thread.Create(_L("ExceptThread"),ExceptThread,KDefaultStackSize,KHeapSize,KHeapSize,0); sl@0: test(r==KErrNone); sl@0: sl@0: thread.Logon(stat); sl@0: test(thread.ExitType()==EExitPending); sl@0: test.Next(_L("Resume ExceptThread")); sl@0: thread.Resume(); sl@0: User::WaitForRequest(stat); sl@0: test.Next(_L("Check ExceptThread panicked")); sl@0: test(thread.ExitCategory()==_L("KERN-EXEC")); sl@0: test(thread.ExitReason()==ECausedException); sl@0: test(thread.ExitType()==EExitPanic); sl@0: CLOSE_AND_WAIT(thread); sl@0: } sl@0: sl@0: LOCAL_C TInt StackThread(TAny* ) sl@0: { sl@0: sl@0: TFileName heresAnotherOne; sl@0: StackThread((TAny*)heresAnotherOne.Ptr()); // go recursive sl@0: return KErrNone; sl@0: } sl@0: sl@0: void testStackOverflow() sl@0: // sl@0: // Thread overflowing its stack is panicked sl@0: // sl@0: { sl@0: sl@0: RThread thread; sl@0: TRequestStatus stat; sl@0: sl@0: test.Next(_L("Create StackThread")); sl@0: TInt r=thread.Create(_L("StackThread"),StackThread,KDefaultStackSize,KHeapSize,KHeapSize,0); sl@0: test(r==KErrNone); sl@0: sl@0: thread.Logon(stat); sl@0: test(thread.ExitType()==EExitPending); sl@0: test.Next(_L("Resume StackThread")); sl@0: thread.Resume(); sl@0: User::WaitForRequest(stat); sl@0: test.Next(_L("Check StackThread panicked")); sl@0: test(thread.ExitCategory()==_L("KERN-EXEC")); sl@0: test(thread.ExitReason()==ECausedException); sl@0: test(thread.ExitType()==EExitPanic); sl@0: CLOSE_AND_WAIT(thread); sl@0: } sl@0: sl@0: LOCAL_C TInt KernWriter(TAny* ) sl@0: { sl@0: sl@0: TUint* kernPtr=(TUint*)KernData(); sl@0: *kernPtr=0xdead; // BANG!! sl@0: return KErrNone; sl@0: } sl@0: sl@0: LOCAL_C TInt RamDiskWriter(TAny* ) sl@0: { sl@0: sl@0: TFindChunk fChunk(_L("*TheRamDriveChunk*")); sl@0: TFullName n; sl@0: TInt r=fChunk.Next(n); sl@0: RChunk ch; sl@0: r=ch.Open(fChunk); sl@0: if(r!=KErrNone) sl@0: return r; sl@0: TUint8* rdPtr=ch.Base(); sl@0: *rdPtr=0xaa; // BANG!! sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: void testKernelWriter() sl@0: // sl@0: // Thread writing to kernel data is panicked sl@0: // sl@0: { sl@0: sl@0: RThread thread; sl@0: TRequestStatus stat; sl@0: sl@0: test.Next(_L("Create KernWriter")); sl@0: TInt r=thread.Create(_L("KernWriter"),KernWriter,KDefaultStackSize,KHeapSize,KHeapSize,0); sl@0: test(r==KErrNone); sl@0: sl@0: thread.Logon(stat); sl@0: test(thread.ExitType()==EExitPending); sl@0: test.Next(_L("Resume KernWriter")); sl@0: thread.Resume(); sl@0: User::WaitForRequest(stat); sl@0: test.Next(_L("Check KernWriter panicked")); sl@0: test(thread.ExitCategory()==_L("KERN-EXEC")); sl@0: test(thread.ExitReason()==ECausedException); sl@0: test(thread.ExitType()==EExitPanic); sl@0: CLOSE_AND_WAIT(thread); sl@0: } sl@0: sl@0: void testRamDiskAccess() sl@0: { sl@0: sl@0: RThread thread; sl@0: TRequestStatus stat; sl@0: sl@0: test.Next(_L("Create RamDiskWriter")); sl@0: TInt r=thread.Create(_L("RamDiskWriter"),RamDiskWriter,KDefaultStackSize,KHeapSize,KHeapSize,0); sl@0: test(r==KErrNone); sl@0: sl@0: thread.Logon(stat); sl@0: test(thread.ExitType()==EExitPending); sl@0: test.Next(_L("Resume RamDiskWriter")); sl@0: thread.Resume(); sl@0: User::WaitForRequest(stat); sl@0: if((!PlatSec::ConfigSetting(PlatSec::EPlatSecProcessIsolation))||(!PlatSec::ConfigSetting(PlatSec::EPlatSecEnforcement))) sl@0: { sl@0: test.Next(_L("Check RamDiskWriter panicked")); sl@0: test(thread.ExitCategory()==_L("KERN-EXEC")); sl@0: test(thread.ExitReason()==ECausedException); sl@0: test(thread.ExitType()==EExitPanic); sl@0: } sl@0: else sl@0: { sl@0: test.Next(_L("Check RamDiskWriter was refused access")); sl@0: test(thread.ExitReason()==KErrPermissionDenied); sl@0: test(thread.ExitType()==EExitKill); sl@0: } sl@0: CLOSE_AND_WAIT(thread); sl@0: } sl@0: sl@0: RThread MainThread; sl@0: sl@0: TInt RequestCompleteThread(TAny* aPtr) sl@0: { sl@0: TRequestStatus** pS=(TRequestStatus**)aPtr; sl@0: MainThread.RequestComplete(*pS,123); sl@0: return 0; sl@0: } sl@0: sl@0: _LIT(KReqCompThreadName,"ReqCompThread"); sl@0: void StartRequestCompleteThread(RThread& aThread, TRequestStatus** aStatus) sl@0: { sl@0: TInt r=aThread.Create(KReqCompThreadName,RequestCompleteThread,0x1000,0x1000,0x10000,aStatus); sl@0: test (r==KErrNone); sl@0: aThread.SetPriority(EPriorityMore); sl@0: TRequestStatus s; sl@0: aThread.Logon(s); sl@0: aThread.Resume(); sl@0: User::WaitForRequest(s); sl@0: } sl@0: sl@0: _LIT(KLitUserCBase,"E32USER-CBase"); sl@0: GLDEF_C TInt E32Main() sl@0: // sl@0: // Main sl@0: // sl@0: { sl@0: sl@0: // don't want just in time debugging as we trap panics sl@0: TBool justInTime=User::JustInTime(); sl@0: User::SetJustInTime(EFalse); sl@0: sl@0: test.Title(); sl@0: test.Start(_L("Test protection & panicking")); sl@0: sl@0: #if defined(__EPOC32__) sl@0: // this next test doesn't work under WINS because the string needs to be converted sl@0: // from UNICODE to ascii for printing to STDOUT, and that requires the string to be sl@0: // zero-terminated which panics because the string is too long. sl@0: test.Next(_L("Printing long string doesn't get me shot")); sl@0: test.Printf(_L("012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789")); sl@0: test.Printf(_L("\n")); sl@0: #endif sl@0: sl@0: if (HaveVirtMem()) sl@0: { sl@0: test.Next(_L("Stack overflow shoots the thread not the kernel")); sl@0: testStackOverflow(); sl@0: } sl@0: sl@0: test.Next(_L("Panicking a closed thread doesn't panic the panicker!!")); sl@0: testPanicDeadThread(); sl@0: sl@0: test.Next(_L("Dead thread tests")); sl@0: testDeadThread(); sl@0: sl@0: test.Next(_L("RSession send from uninitialised handle")); sl@0: testSession(); sl@0: sl@0: test.Next(_L("Thread causing exception is killed")); sl@0: if (HaveVirtMem()) sl@0: { sl@0: testExcept(); sl@0: RunTestThread(EDataAbort); sl@0: RunTestThread(EPrefetchAbort); sl@0: } sl@0: #ifndef __WINS__ sl@0: RunTestThread(EUndefinedInstruction); sl@0: #endif sl@0: sl@0: #if defined(__EPOC32__) sl@0: if (HaveDirectKernProt()) sl@0: { sl@0: test.Next(_L("Thread writing to kernel data is killed")); sl@0: testKernelWriter(); sl@0: } sl@0: sl@0: if (HaveProcessProt()) sl@0: { sl@0: test.Next(_L("Check access to RamDisk is denied")); sl@0: testRamDiskAccess(); sl@0: } sl@0: sl@0: if (HaveMMU()) sl@0: { sl@0: test.Next(_L("RequestComplete() with bad address")); sl@0: TInt rqc=RThread().RequestCount(); sl@0: test(MainThread.Duplicate(RThread())==KErrNone); sl@0: RThread t; sl@0: TRequestStatus s=KRequestPending; sl@0: TRequestStatus* pS=&s; sl@0: StartRequestCompleteThread(t,&pS); sl@0: test(t.ExitType()==EExitKill); sl@0: test(t.ExitReason()==KErrNone); sl@0: CLOSE_AND_WAIT(t); sl@0: test(s==123); sl@0: test(pS==NULL); sl@0: test(RThread().RequestCount()==rqc+1); sl@0: TRequestStatus** bad=(TRequestStatus**)0x80000000; // kernel space sl@0: StartRequestCompleteThread(t,bad); sl@0: test(t.ExitType()==EExitPanic); sl@0: test(t.ExitReason()==ECausedException); sl@0: test(t.ExitCategory()==KLitKernExec); sl@0: CLOSE_AND_WAIT(t); sl@0: test(RThread().RequestCount()==rqc+1); sl@0: pS=(TRequestStatus*)0x80000000; sl@0: StartRequestCompleteThread(t,&pS); sl@0: // Request status poked user side, so we expect a KERN-EXEC 3... sl@0: test(t.ExitType()==EExitPanic); sl@0: test(t.ExitReason()==ECausedException); sl@0: test(t.ExitCategory()==KLitKernExec); sl@0: CLOSE_AND_WAIT(t); sl@0: test(pS==NULL); sl@0: test(RThread().RequestCount()==rqc+1); sl@0: pS=(TRequestStatus*)(((TUint8*)&s)+0x80000); // aligned, within chunk max size but invalid sl@0: StartRequestCompleteThread(t,&pS); sl@0: // Request status poked user side, so we expect a KERN-EXEC 3... sl@0: test(t.ExitType()==EExitPanic); sl@0: test(t.ExitReason()==ECausedException); sl@0: test(t.ExitCategory()==KLitKernExec); sl@0: CLOSE_AND_WAIT(t); sl@0: test(pS==NULL); sl@0: test(RThread().RequestCount()==rqc+1); sl@0: } sl@0: #endif sl@0: sl@0: test.End(); sl@0: sl@0: User::SetJustInTime(justInTime); sl@0: return(KErrNone); sl@0: } sl@0: