diff -r 000000000000 -r a41df078684a kerneltest/e32test/secure/t_ipcsafety.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/kerneltest/e32test/secure/t_ipcsafety.cpp Mon Oct 19 15:55:17 2009 +0100 @@ -0,0 +1,237 @@ +// Copyright (c) 2007-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\secure\t_ipcsafety.cpp +// Overview: +// Test if it's possible for a thread in a server process to access the IPC alias +// region outside the control of the kernel. +// API Information: +// RMessage2 +// Details: +// - Create a server which will take a long time IPCing any client request. +// - Create a high priority thread which will attempt to write to a given +// location in the IPC region, with an exception handler to retry if it fails. +// - Create a client process which connects to the server and offers a +// stack-based descriptor for IPC, as well as the address of another stack +// variable that should not be able to be accessed. +// - The bad writer will attempt to jump in and overwrite the variable, +// causing the client to return a detectable error. +// - Verify that this does not happen. +// Platforms/Drives/Compatibility: +// ARM with multiple memory model only. +// Assumptions/Requirement/Pre-requisites: +// Failures and causes: +// Base Port information: +// +// + +#define __E32TEST_EXTENSION__ +#include +#include +#include +#include +#include "mmudetect.h" + +LOCAL_D RTest test(_L("T_IPCSAFETY")); + +void GoodExitWithError(); + +TInt* DataToSplat; +RSemaphore BadSemaphore; + +// Server stuff + +_LIT(KBadServerName,"BadServer"); + +class CBadSession : public CSession2 + { + virtual void ServiceL(const RMessage2& aMessage); + }; + +class CBadServer : public CServer2 + { +public: + CBadServer(CActive::TPriority aPriority) : CServer2(aPriority) + {} + virtual CBadSession* NewSessionL(const TVersion& aVersion, const RMessage2& aMessage) const + { + return new (ELeave) CBadSession(); + } + }; + +void CBadSession::ServiceL(const RMessage2& aMessage) + { + TBuf16<1024> buf; + DataToSplat = (TInt*)aMessage.Ptr1(); + BadSemaphore.Signal(); + // Read the buffer lots of times to widen the time window + for (TInt i=0; i<1024; i++) + aMessage.Read(0, buf, 0); + CActiveScheduler::Stop(); + aMessage.Complete(KErrNone); + } + +TInt BadServerThread(TAny*) + { + CTrapCleanup* cleanup=CTrapCleanup::New(); + if (!cleanup) + return KErrNoMemory; + CActiveScheduler* scheduler = new CActiveScheduler(); + if (!scheduler) + return KErrNoMemory; + CActiveScheduler::Install(scheduler); + CBadServer* server = new CBadServer(CActive::EPriorityStandard); + if (!server) + return KErrNoMemory; + TInt r = server->Start(KBadServerName); + if (r != KErrNone) + return r; + RThread::Rendezvous(KErrNone); + CActiveScheduler::Start(); + delete server; + delete scheduler; + delete cleanup; + return KErrNone; + } + +class RBadSession : public RSessionBase + { +public: + TInt Connect() + { + return CreateSession(KBadServerName, TVersion(0,0,0)); + } + void AccessMe(TDesC* aBuf, TInt* aValue); + }; + +void RBadSession::AccessMe(TDesC* aBuf, TInt* aValue) + { + SendReceive(0, TIpcArgs(aBuf, aValue)); + }; + +// Bad writer thread + +TInt * const KAliasRegion = (TInt*)0x00200000; +const TUint KAliasMask = 0x000fffff; + +void BadExceptionHandler(TExcType, TInt, TInt, TInt, TUint aStackArgument) + { + // just retry the instruction after a delay + User::AfterHighRes(0); + return; + } + +TInt BadWriterThread(TAny*) + { + // set the exception handler so that we don't die when touching the ipc region + // as it won't be mapped until an unpredictable time + User::SetExceptionHandler((TExceptionHandler)BadExceptionHandler, KExceptionFault); + // wait for the server to tell us where to overwrite + BadSemaphore.Wait(); + + TInt* target = (TInt*)(((TUint)DataToSplat&KAliasMask)|(TUint)KAliasRegion); + *target = KErrGeneral; + + return KErrNone; + } + +// The server process + +TInt BadServerProcess() + { + test.Title(); + test.Start(_L("Test bad server overwriting good client memory")); + + BadSemaphore.CreateLocal(0); + + test.Next(_L("Setup bad server")); + RThread serverThread; + TRequestStatus serverStatus, serverRendezvous; + test_KErrNone(serverThread.Create(_L("BadServer"), BadServerThread, KDefaultStackSize, NULL, NULL)); + serverThread.Logon(serverStatus); + serverThread.Rendezvous(serverRendezvous); + serverThread.Resume(); + User::WaitForRequest(serverRendezvous); + + test.Next(_L("Start bad writer thread")); + RThread writerThread; + TRequestStatus writerStatus; + test_KErrNone(writerThread.Create(_L("BadWriter"), BadWriterThread, KDefaultStackSize, NULL, NULL)); + writerThread.Logon(writerStatus); + writerThread.SetPriority(EPriorityMore); + writerThread.Resume(); + + test.Next(_L("Run the good client")); + RProcess goodProcess; + TRequestStatus goodStatus; + test_KErrNone(goodProcess.Create(_L("T_IPCSAFETY"), _L("client"))); + goodProcess.Logon(goodStatus); + goodProcess.Resume(); + + test.Next(_L("Wait for server to die")); + User::WaitForRequest(serverStatus); + test_Equal(EExitKill, serverThread.ExitType()); + test_KErrNone(serverThread.ExitReason()); + + test.Next(_L("Check if client had memory overwritten")); + User::WaitForRequest(goodStatus); + test_Equal(EExitKill, goodProcess.ExitType()); + test_KErrNone(goodProcess.ExitReason()); + + test.Next(_L("Kill off writer thread")); + writerThread.Kill(KErrNone); + User::WaitForRequest(writerStatus); + test_Equal(EExitKill, writerThread.ExitType()); + test_KErrNone(writerThread.ExitReason()); + + test.End(); + return KErrNone; + } + +// The client process + +TInt GoodClientProcess() + { + RBadSession bad; + TBuf16<1024> buf; + TInt r = KErrNone; + buf.SetLength(1024); + // just keep trying to connect if the server isn't talkative yet + while (bad.Connect() != KErrNone) + User::After(1); + bad.AccessMe(&buf, &r); + // Returns r, which logically should be KErrNone as servers aren't + // supposed to be able to modify + return r; + } + +// Main + +GLDEF_C TInt E32Main() + { + TBuf16<512> cmd; + User::CommandLine(cmd); + + // this test hardcodes various multiple memory model parameters + // and the moving model's aliasing technique is not susceptible to + // the problem in the first place + TUint32 memmodel = MemModelAttributes(); + if ((memmodel & EMemModelTypeMask) != EMemModelTypeMultiple) + return KErrNone; + + if(cmd.Length()) + return GoodClientProcess(); + else + return BadServerProcess(); + } +