// Copyright (c) 2006-2010 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:
// Provides the debug security server session implementation.
//
//
/**
@file
@internalTechnology
@released
*/
// Needed so we get the text strings for capabilities
#define __INCLUDE_CAPABILITY_NAMES__
#include <e32std.h>
#include <e32std_private.h>
#include <e32btrace.h>
#include <d32btrace.h>
#include <e32base.h>
#include <e32base_private.h>
#include <e32uid.h>
#include <f32file.h>
#include <e32capability.h>
#include <rm_debug_api.h>
// required for direct parsing of e32image/tromimage headers
#include <f32image.h>
#include <e32rom.h>
//added for direct access to media driver
#include <partitions.h>
#include <ftlcontrolio.h>
#include "c_security_svr_session.h"
#include "c_security_svr_server.h"
#include "c_security_svr_async.h"
#include "rm_debug_logging.h"
#ifdef _DEBUG
#include "low_mem_requests.h"
#endif
using namespace Debug;
CSecuritySvrSession::CSecuritySvrSession(const TProcessId& aDebugAgentProcessId)
: iDebugAgentProcessId(aDebugAgentProcessId),
iServerNotified(EFalse),
iCrashConnected(EFalse)
{
// Ensure that this debug agent has no target capability override
// by default
iOEMDebugCapabilities.SetEmpty();
}
void CSecuritySvrSession::ServiceError(const RMessage2 &aMessage, TInt aError)
{
LOG_MSG2("CSecuritySvrSession::ServiceError(), aError: %d\n", aError);
//insert ending heap markers
HeapWatcher(aMessage.Function(), EFalse);
aMessage.Complete(aError);
}
/**
Called by the client/server framework as part of session creation.
Notifies the server that a session is being created
*/
void CSecuritySvrSession::CreateL()
{
LOG_MSG("CSecuritySvrSession::CreateL()\n");
//notify the server that the session has been opened
Server().SessionOpened();
iServerNotified = ETrue;
}
/**
Returns a reference to the DSS
@return a reference to the DSS
*/
CSecuritySvrServer& CSecuritySvrSession::Server() const
{
return *static_cast<CSecuritySvrServer*>(const_cast<CServer2*>(CSession2::Server()));
}
/**
Session destructor. Performs necessary cleanup and notifies the server that the
session is being closed
*/
CSecuritySvrSession::~CSecuritySvrSession()
{
LOG_MSG("CSecuritySvrSession::~CSecuritySvrSession!()\n");
// Cancel any outstanding async objects.
iAsyncHandlers.ResetAndDestroy();
// Inform the device driver of the agent detach.
Server().iKernelDriver.DetachAgent(iDebugAgentProcessId.Id());
LOG_MSG( "CSecuritySvrSession::~CSecuritySvrSession() : -> securityServer.DetachAllProcesses()\n" );
Server().DetachAllProcesses(iDebugAgentProcessId);
//notify the server that the session has closed
if(iServerNotified)
{
Server().SessionClosed();
}
}
void CSecuritySvrSession::ConstructL()
{
// nothing to do
}
/**
Used to insert heap checking markers.
@param aFunction The function that heap markers should be added for
@param aEntry if ETrue indicates that heap checking is starting, if EFalse
that heap checking is ending.
*/
void CSecuritySvrSession::HeapWatcher(const TUint32 aFunction, const TBool aEntry) const
{
switch(aFunction)
{
case EDebugServAttachExecutable:
return;
case EDebugServDetachExecutable:
return;
case EDebugServSuspendThread:
return;
case EDebugServResumeThread:
return;
case EDebugServAttachAll:
return;
case EDebugServDetachAll:
return;
// used for out-of-memory testing in debug mode
#ifdef _DEBUG
// start heap marking in on entry, do nothing on exit
case EDebugServMarkHeap:
{
if(aEntry)
{
__UHEAP_MARK;
}
return;
}
// stop heap marking on exit, do nothing on entry
case EDebugServMarkEnd:
{
if(!aEntry)
{
__UHEAP_MARKEND;
}
return;
}
#endif
default:
if(aEntry)
{
__UHEAP_MARK;
}
else
{
__UHEAP_MARKEND;
}
return;
}
}
void CSecuritySvrSession::ServiceL(const RMessage2& aMessage)
//
// Session service handler
//
{
//insert starting heap markers
HeapWatcher(aMessage.Function(), ETrue);
switch(aMessage.Function())
{
case EDebugServResumeThread:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServResumeThread\n" );
ResumeThreadL(aMessage);
break;
case EDebugServSuspendThread:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServSuspendThread\n" );
SuspendThreadL(aMessage);
break;
case EDebugServReadMemory:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServReadMemory\n" );
ReadMemoryL(aMessage);
break;
case EDebugServWriteMemory:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServWriteMemory\n" );
WriteMemoryL(aMessage);
break;
case EDebugServSetBreak:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServSetBreak\n" );
SetBreakL(aMessage);
break;
case EDebugServClearBreak:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServClearBreak\n" );
ClearBreakL(aMessage);
break;
case EDebugServModifyBreak:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServModifyBreak\n" );
ModifyBreakL(aMessage);
break;
case EDebugServModifyProcessBreak:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServModifyProcessBreak\n" );
ModifyProcessBreakL(aMessage);
break;
case EDebugServBreakInfo:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServBreakInfo\n" );
BreakInfoL(aMessage);
break;
case EDebugServReadRegisters:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServReadRegisters\n" );
ReadRegistersL(aMessage);
break;
case EDebugServWriteRegisters:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServWriteRegisters\n" );
WriteRegistersL(aMessage);
break;
case EDebugServGetEvent:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServGetEvent\n" );
GetEventL(aMessage);
break;
case EDebugServCancelGetEvent:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServCancelGetEvent\n" );
CancelGetEventL(aMessage);
break;
case EDebugServAttachExecutable:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServAttachExecutable\n" );
AttachProcessL(aMessage);
break;
case EDebugServDetachExecutable:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServDetachExecutable\n" );
DetachProcessL(aMessage);
break;
case EDebugServAttachAll:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServAttachAll\n" );
AttachAllL(aMessage);
break;
case EDebugServDetachAll:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServDetachAll\n" );
DetachAllL(aMessage);
break;
case EDebugServGetDebugFunctionalityBufSize:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServGetDebugFunctionalityBufSize\n" );
GetDebugFunctionalityBufSizeL(aMessage);
break;
case EDebugServGetDebugFunctionality:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServGetDebugFunctionality\n" );
GetDebugFunctionalityL(aMessage);
break;
case EDebugServSetEventAction:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServSetEventAction\n" );
SetEventActionL(aMessage);
break;
case EDebugServGetList:
LOG_MSG( "CSecuritySvrSession::ServiceL() EDebugServGetList\n" );
GetListL(aMessage);
break;
case EDebugServStep:
LOG_MSG("CSecuritySvrSession::ServiceL() EDebugServStep\n");
StepL(aMessage);
break;
case EDebugServSetProcessBreak:
LOG_MSG("CSecuritySvrSession::ServiceL() EDebugServSetProcessBreak\n");
SetProcessBreakL(aMessage);
break;
case EDebugServProcessBreakInfo:
LOG_MSG("CSecuritySvrSession::ServiceL() EDebugServProcessBreakInfo\n");
ProcessBreakInfoL(aMessage);
break;
case EDebugServKillProcess:
LOG_MSG("CSecuritySvrSession::ServiceL() EDebugServKillProcess\n");
KillProcessL(aMessage);
break;
#ifdef _DEBUG
case EDebugServMarkHeap:
LOG_MSG("CSecuritySvrSession::ServiceL() EDebugServMarkHeap\n");
// all taken care of in HeapWatcher
aMessage.Complete(KErrNone);
break;
case EDebugServMarkEnd:
LOG_MSG("CSecuritySvrSession::ServiceL() EDebugServMarkEnd\n");
// all taken care of in HeapWatcher
aMessage.Complete(KErrNone);
break;
case EDebugServFailAlloc:
LOG_MSG("CSecuritySvrSession::ServiceL() EDebugServFailAlloc\n");
DoFailAlloc(aMessage);
break;
#endif
case EDebugServReadCrashFlash:
ReadCrashLogL(aMessage);
break;
case EDebugServWriteCrashFlash:
LOG_MSG("CSecuritySvrSession::ServiceL() EDebugServWriteCrashFlash\n");
WriteCrashConfigL(aMessage);
break;
case EDebugServEraseCrashFlash:
LOG_MSG("CSecuritySvrSession::ServiceL() EDebugServEraseCrashFlash\n");
EraseCrashLogL(aMessage);
break;
case EDebugServEraseEntireCrashFlash:
LOG_MSG("CSecuritySvrSession::ServiceL() EDebugServEraseEntireCrashFlash\n");
EraseEntireCrashLogL(aMessage);
break;
default:
LOG_MSG( "CSecuritySvrSession::ServiceL() Unknown request, calling User::Leave(KErrNotSupported);\n" );
User::Leave(KErrNotSupported);
break;
}
//insert ending heap markers
HeapWatcher(aMessage.Function(), EFalse);
LOG_EXIT();
}
#ifdef _DEBUG
/**
Used to control heap failure in debug mode.
@param aMessage If aMessage.Int0 is non-zero then heap will be set to fail on that allocation.
If aMessage.Int0 is zero then the heap failure count is reset
*/
void CSecuritySvrSession::DoFailAlloc(const RMessage2& aMessage)
{
TInt count = aMessage.Int0();
if(count == 0)
{
__UHEAP_RESET;
}
else
{
__UHEAP_FAILNEXT(count);
}
aMessage.Complete(KErrNone);
}
#endif
/**
Suspends execution of the specified thread.
@param aMessage contains an integer representation of the target thread's
thread ID at offset 0.
@leave KErrPermissionDenied if security check fails or KErrArgument if the
thread does not exist
*/
void CSecuritySvrSession::SuspendThreadL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::SuspendThreadL()\n" );
//get thread ID
TThreadId threadId = ReadTThreadIdL(aMessage, 0);
//check attached
CheckAttachedL(threadId, aMessage, EFalse);
//security check passed so can perform actions
User::LeaveIfError(Server().iKernelDriver.SuspendThread(threadId));
aMessage.Complete(KErrNone);
}
/**
Resumes execution of the specified thread.
@param aMessage contains an integer representation of the target thread's
thread ID at offset 0.
@leave KErrPermissionDenied if security check fails or KErrArgument if the
thread does not exist
*/
void CSecuritySvrSession::ResumeThreadL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::ResumeThreadL()\n" );
//get thread ID
TThreadId threadId = ReadTThreadIdL(aMessage, 0);
//check attached
CheckAttachedL(threadId, aMessage, EFalse);
//security check passed so can perform actions
TInt err = Server().iKernelDriver.ResumeThread(threadId);
aMessage.Complete(err);
}
void CSecuritySvrSession::GetDebugFunctionalityBufSizeL(const RMessage2& aMessage)
//
// Retrieve size of functionality data buffer in bytes which must be allocated
// by the client
//
{
LOG_MSG( "CSecuritySvrSession::GetDebugFunctionalityBufSizeL()\n" );
TUint32 result = 0;
// Get Buffer size from the kernel driver
User::LeaveIfError(Server().iKernelDriver.GetDebugFunctionalityBufSize(result));
TPtr8 stuff((TUint8*)&result,4, 4);
aMessage.WriteL(0,stuff);
aMessage.Complete(KErrNone);
}
void CSecuritySvrSession::GetDebugFunctionalityL(const RMessage2& aMessage)
//
// Retrieve the functionality data and place it in a buffer
// allocated by the client.
//
{
LOG_MSG( "CSecuritySvrSession::GetDebugFunctionalityL()\n" );
TUint32 dfsize = 0;
// Get Buffer size from the kernel driver
User::LeaveIfError(Server().iKernelDriver.GetDebugFunctionalityBufSize(dfsize));
// Allocate space for the functionality data
HBufC8* dftext = HBufC8::NewLC(dfsize);
const TPtr8& dfPtr = dftext->Des();
// Extract said data from the device driver
User::LeaveIfError(Server().iKernelDriver.GetDebugFunctionality((TDes8&)dfPtr));
// Return data to client
aMessage.WriteL(0,dfPtr);
// Free buffer
CleanupStack::PopAndDestroy(dftext);
aMessage.Complete(KErrNone);
}
/**
Reads memory from a specified thread using the passed parameters. The user
should ensure that the TPtr8 that is passed in has size greater than or equal
to the size of the memory that is trying to be read.
@param aMessage The RMessage2 object should be constructed as follows:
* aMessage.Int0() is the thread ID of the target debug app
* aMessage.Ptr1() is a TMemoryInfo object which contains the following:
* the address of the memory to be read from the target debug thread
* the size of the memory block to be read from the target debug thread
* the access size to use
* the endianess to interpret the data as
* aMessage.Ptr2() is the address of the buffer in the debug agent thread
that the data from the target debug app should be written into
@leave KErrPermissionDenied if client is not attached to the target
thread's process,
KErrNoMemory if memory could not be allocated,
KErrArgument if there are problems with the aMessage object,
KErrBadHandle if the thread represented by aMessage.Ptr0() is invalid,
an error value from CSecuritySvrSession::ValidateMemoryInfo if checking
the memory attributes failed,
or another of the system wide error codes
*/
void CSecuritySvrSession::ReadMemoryL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::ReadMemoryL()\n" );
//get debug app thread ID
TThreadId threadId = ReadTThreadIdL(aMessage, 0);
CheckAttachedL(threadId, aMessage, ETrue);
//create and initialise the memory info object
TMemoryInfo targetMemory;
TPtr8 targetMemoryPtr( (TUint8 *)&targetMemory, sizeof(TMemoryInfo) );
aMessage.ReadL(1,targetMemoryPtr);
//check memory info is acceptable
ValidateMemoryInfoL(threadId, targetMemory, ETrue);
RBuf8 data;
data.CreateL(targetMemory.iSize);
data.CleanupClosePushL();
//fill buffer with data from target debug thread
User::LeaveIfError(Server().iKernelDriver.ReadMemory(threadId, targetMemory.iAddress, targetMemory.iSize, data));
//attempt to write the data from the target debug thread back to the agent
aMessage.WriteL(2, data);
//delete temporary buffer
CleanupStack::PopAndDestroy(&data);
aMessage.Complete(KErrNone);
}
/**
Writes memory to a specified thread using the passed parameters.
@param aMessage The RMessage2 object should be constructed as follows:
* aMessage.Ptr0() is the thread ID of the target debug app
* aMessage.Ptr1() is a TMemoryInfo object which contains the following:
* the address of the memory to be written to the target debug thread
* the size of the memory block to be written to the target debug thread
* the access size to use
* the endianess to interpret the data as
* aMessage.Ptr2() is the address of the buffer in the debug agent thread
that the data to write to the target debug app should be read from
@leave KErrPermissionDenied if client is not attached (actively) to the target
thread's process,
KErrNoMemory if memory could not be allocated,
KErrArgument if there are problems with the aMessage object,
KErrBadHandle if the thread represented by aMessage.Ptr0() is invalid,
an error value from CSecuritySvrSession::ValidateMemoryInfo if checking
the memory attributes failed,
or another of the system wide error codes
*/
void CSecuritySvrSession::WriteMemoryL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::WriteMemoryL()\n" );
//get debug app thread ID
TThreadId threadId = ReadTThreadIdL(aMessage, 0);
CheckAttachedL(threadId, aMessage, EFalse);
//create and initialise the memory info object
TMemoryInfo targetMemory;
TPtr8 targetMemoryPtr( (TUint8 *)&targetMemory, sizeof(TMemoryInfo) );
aMessage.ReadL(1,targetMemoryPtr);
//check memory info is acceptable
ValidateMemoryInfoL(threadId, targetMemory, EFalse);
//create temporary buffer and read data from client
RBuf8 data;
data.CreateL(targetMemory.iSize);
data.CleanupClosePushL();
aMessage.ReadL(2, data);
// what about telling the driver about endianess/access size?
User::LeaveIfError(Server().iKernelDriver.WriteMemory(threadId, targetMemory.iAddress, targetMemory.iSize, data));
//free temporary buffer
CleanupStack::PopAndDestroy(&data);
aMessage.Complete(KErrNone);
}
/**
@internalTechnology
Notes: This call is used to set a thread specific breakpoint. Its input arguments
are the thread id, address and architecture type of the breakpoint. It returns success
or failure, and if successful, it sets the TBreakId in the Debug Agent to the
breakpoint id by which it can be referenced in future calls to ModifyBreak,ClearBreak and
BreakInfo.
@param aMessage.Ptr0() - aThreadId is thread id of the target debug process
@param aMessage.Ptr1() - Address of a TBreakInfo in the Debug Agent
@param aMessage.Ptr2() - Address of a TBreakId in the Debug Agent
@leave KErrPermissionDenied if the security check fails.
KErrAlreadyExists if there is a breakpoint overlapping the desired address.
KErrNotSupported if the architecture type is unrecognised.
KErrNoMemory if there is no more memory to complete the operation.
KErrArgument if the breakpoint address alignment is unsuitable for the requested
breakpoint.
KErrOverflow if there are too many breakpoints set.
*/
void CSecuritySvrSession::SetBreakL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::SetBreakL!()\n" );
//get debug app thread ID
TThreadId threadId = ReadTThreadIdL(aMessage, 0);
//check that the agent has attached to the target process
CheckAttachedL(threadId, aMessage, EFalse);
//create and initialise the break info object
TBreakInfo breakInfo;
TPtr8 breakInfoPtr( (TUint8 *)&breakInfo, sizeof(TBreakInfo) );
aMessage.ReadL(1,breakInfoPtr);
LOG_MSG4( "CSecuritySvrSession::SetBreakL, threadId=0x%lx, addr=0x%x, mode=%d",
threadId.Id(), breakInfo.iAddress, breakInfo.iArchitectureMode);
//set break in target app
TBreakId breakId = 0;
User::LeaveIfError(Server().iKernelDriver.SetBreak(breakId, threadId, breakInfo.iAddress, breakInfo.iArchitectureMode));
//attempt to write the break id back to the debug agent
WriteDataL(aMessage, 2, &breakId, sizeof(breakId));
aMessage.Complete(KErrNone);
}
/**
Clears a breakpoint previously set by a SetBreak() call.
@param aMessage.Int0() - TBreakId of the breakpoint to be removed.
*/
void CSecuritySvrSession::ClearBreakL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::ClearBreakL()\n" );
const TInt breakId = aMessage.Int0();
// Check that the breakpoint exists
TUint64 objectId;
TUint32 address;
TArchitectureMode mode;
TBool threadSpecific = EFalse;
User::LeaveIfError(Server().iKernelDriver.BreakInfo(breakId,objectId,address,mode,threadSpecific));
if(threadSpecific)
{
// Check that the debug agent is attached to the thread for which the
// breakpoint is currently set.
LOG_MSG4( "CSecuritySvrSession::ClearBreakL( brkId=%d), addr=0x%x, tId=0x%x",
breakId, address, I64LOW(objectId) );
CheckAttachedL(TThreadId(objectId), aMessage, EFalse);
}
else
{
// Check that the debug agent is attached to the process for which the
// breakpoint is currently set.
LOG_MSG4( "CSecuritySvrSession::ClearBreakL( brkId=%d), addr=0x%x, pId=0x%x",
breakId, address, I64LOW(objectId) );
CheckAttachedL(TProcessId(objectId), aMessage, EFalse);
}
// Finally clear the breakpoint
User::LeaveIfError(Server().iKernelDriver.ClearBreak(breakId));
aMessage.Complete(KErrNone);
}
/**
@param aMessage.Int0() - Breakpoint Id of interest
@param aMessage.Ptr1() - Address in Debug Agent to place threadId of the breakpoint
@param aMessage.Ptr2() - Address in Debug Agent to place address of the breakpoint
@param aMessage.Ptr3() - Address in Debug Agent to place the architecture mode of the breakpoint
@leave Any error which may be returned by RSessionBase::SendReceive()
*/
void CSecuritySvrSession::BreakInfoL(const RMessage2& aMessage)
{
const TBreakId breakId = (TBreakId)aMessage.Int0();
TThreadId threadId;
TUint32 address;
TArchitectureMode mode;
TBool threadSpecific = ETrue;
TUint64 threadIdData;
User::LeaveIfError(Server().iKernelDriver.BreakInfo(breakId,threadIdData,address,mode,threadSpecific));
if(!threadSpecific)
{
User::Leave(KErrNotFound);
}
threadId = TThreadId(threadIdData);
//check that the agent has attached to the target process
CheckAttachedL(threadId, aMessage, EFalse);
// return the threadId
WriteDataL(aMessage, 1, &threadId, sizeof(threadId));
// return the address
WriteDataL(aMessage, 2, &address, sizeof(address));
// return the mode
WriteDataL(aMessage, 3, &mode, sizeof(mode));
aMessage.Complete(KErrNone);
}
/**
@internalTechnology
Modify a previously set breakpoint.
@param aMessage.Int0() - The breakpoint id of the breakpoint to modify
@param aMessage.Ptr1() - The new Thread Id for the breakpoint
@param aMessage.Int2() - The new virtual memory address for the breakpoint
@param aMessage.Int3() - The new architecture mode for the breakpoint
@return KErrNone if succesful. KErrPermissionDenied if the security check fails.
KErrAlreadyExists if there is a breakpoint overlapping the desired address.
KErrNotSupported if the architecture type is unrecognised.
KErrNoMemory if there is no more memory to complete the operation.
KErrArgument if the breakpoint address alignment is unsuitable for the requested
breakpoint.
KErrOverflow if there are too many breakpoints set.
*/
void CSecuritySvrSession::ModifyBreakL(const RMessage2& aMessage)
{
const TBreakId breakId = (TBreakId)aMessage.Int0();
const TThreadId threadId = ReadTThreadIdL(aMessage, 1);
const TUint32 address = aMessage.Int2();
const TArchitectureMode mode = (TArchitectureMode)aMessage.Int3();
// Get information on the breakpoint to check the security status
TUint64 checkThreadId;
TUint32 checkAddress;
TArchitectureMode checkMode;
TBool threadSpecific;
User::LeaveIfError(Server().iKernelDriver.BreakInfo(breakId,checkThreadId,checkAddress,checkMode,threadSpecific));
LOG_MSG4( "CSecuritySvrSession::ModifyBreakL(brkId=%d) tId=0x%x, addr=0x%x",
breakId, I64LOW(threadId.Id()), address );
// Security check that the thread Id is associated with the debug agent
//check that the agent has attached to the target process
CheckAttachedL(TThreadId(checkThreadId), aMessage, EFalse);
// now check that the thread Id which is being set is permitted
//check that the agent has attached to the target process
CheckAttachedL(threadId, aMessage, EFalse);
User::LeaveIfError(Server().iKernelDriver.ModifyBreak(breakId,threadId,address,mode));
aMessage.Complete(KErrNone);
}
/**
@internalTechnology
Notes: This call is used to set a process wide breakpoint. Its input arguments
are the process id, address and architecture type of the breakpoint. It returns success
or failure, and if successful, it sets the TBreakId in the Debug Agent to the
breakpoint id by which it can be referenced in future calls to ModifyBreak,ClearBreak and
BreakInfo.
@param aMessage.Ptr0() - aProcessId is process id of the target debug process
@param aMessage.Ptr1() - Address of a TBreakInfo in the Debug Agent
@param aMessage.Ptr2() - Address of a TBreakId in the Debug Agent
@leave KErrPermissionDenied if the security check fails.
KErrAlreadyExists if there is a breakpoint overlapping the desired address.
KErrNotSupported if the architecture type is unrecognised.
KErrNoMemory if there is no more memory to complete the operation.
KErrArgument if the breakpoint address alignment is unsuitable for the requested
breakpoint.
KErrOverflow if there are too many breakpoints set.
*/
void CSecuritySvrSession::SetProcessBreakL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::SetProcessBreakL()\n" );
//get debug app thread ID
TProcessId procId = ReadTProcessIdL(aMessage, 0);
//check that the agent has attached to the target process
CheckAttachedL(procId, aMessage, EFalse);
//create and initialise the memory info object
TBreakInfo breakInfo;
TPtr8 breakInfoPtr( (TUint8 *)&breakInfo, sizeof(TBreakInfo) );
aMessage.ReadL(1,breakInfoPtr);
LOG_MSG4( "CSecuritySvrSession::SetProcessBreakL pId=0x%x, addr=0x%x, mode=%d ",
I64LOW(procId.Id()), breakInfo.iAddress, breakInfo.iArchitectureMode );
//set break in target app
TBreakId breakId = 0;
User::LeaveIfError(Server().iKernelDriver.SetProcessBreak(breakId, procId, breakInfo.iAddress, breakInfo.iArchitectureMode));
//attempt to write the break id back to the debug agent
WriteDataL(aMessage, 2, &breakId, sizeof(breakId));
aMessage.Complete(KErrNone);
}
/**
@internalTechnology
Modify a previously set process breakpoint.
@param aMessage.Int0() - The breakpoint id of the breakpoint to modify
@param aMessage.Ptr1() - The new Process Id for the breakpoint
@param aMessage.Int2() - The new virtual memory address for the breakpoint
@param aMessage.Int3() - The new architecture mode for the breakpoint
@return KErrNone if succesful. KErrPermissionDenied if the security check fails.
KErrAlreadyExists if there is a breakpoint overlapping the desired address.
KErrNotSupported if the architecture type is unrecognised.
KErrNoMemory if there is no more memory to complete the operation.
KErrArgument if the breakpoint address alignment is unsuitable for the requested
breakpoint.
KErrOverflow if there are too many breakpoints set.
*/
void CSecuritySvrSession::ModifyProcessBreakL(const RMessage2& aMessage)
{
const TBreakId breakId = (TBreakId)aMessage.Int0();
const TProcessId processId = ReadTProcessIdL(aMessage, 1);
const TUint32 address = aMessage.Int2();
const TArchitectureMode mode = (TArchitectureMode)aMessage.Int3();
// Get information on the breakpoint to check the security status
TUint64 checkProcessId;
TUint32 checkAddress;
TArchitectureMode checkMode;
TBool threadSpecific;
User::LeaveIfError(Server().iKernelDriver.BreakInfo(breakId,checkProcessId,checkAddress,checkMode,threadSpecific));
LOG_MSG4( "CSecuritySvrSession::ModifyProcessBreakL(brkId=%d) pId=0x%x, addr=0x%x",
breakId, I64LOW(processId.Id()), address );
// Security check that the process Id is associated with the debug agent
//check that the agent has attached to the target process
CheckAttachedL(TProcessId(checkProcessId), aMessage, EFalse);
// now check that the process Id which is being set is permitted
//check that the agent has attached to the target process
CheckAttachedL(processId, aMessage, EFalse);
User::LeaveIfError(Server().iKernelDriver.ModifyProcessBreak(breakId,processId,address,mode));
aMessage.Complete(KErrNone);
}
/**
@param aMessage.Int0() - Breakpoint Id of interest
@param aMessage.Ptr1() - Address in Debug Agent to place process Id of the breakpoint
@param aMessage.Ptr2() - Address in Debug Agent to place address of the breakpoint
@param aMessage.Ptr3() - Address in Debug Agent to place the architecture mode of the breakpoint
@leave Any error which may be returned by RSessionBase::SendReceive()
*/
void CSecuritySvrSession::ProcessBreakInfoL(const RMessage2& aMessage)
{
const TBreakId breakId = (TBreakId)aMessage.Int0();
TProcessId procId;
TUint32 address;
TArchitectureMode mode;
TBool threadSpecific;
TUint64 procIdData;
User::LeaveIfError(Server().iKernelDriver.BreakInfo(breakId,procIdData,address,mode,threadSpecific));
if(threadSpecific)
{
User::Leave(KErrNotFound);
}
procId = TProcessId(procIdData);
//check that the agent has attached to the target process
CheckAttachedL(procId, aMessage, EFalse);
// return the processId
WriteDataL(aMessage, 1, &procId, sizeof(procId));
// return the address
WriteDataL(aMessage, 2, &address, sizeof(address));
// return the mode
WriteDataL(aMessage, 3, &mode, sizeof(mode));
aMessage.Complete(KErrNone);
}
/**
Read register values.
@param aMessage should contain:
* at offset 0 a pointer to the thread ID of the target thread
* at offset 1 a descriptor representing an array of TRegisterInfo
register IDs
* at offset 2 a descriptor representing an array into which TRegister
register values will be written
* at offset 3 a descriptor representing an array into which TUint8
register flags will be written
@leave KErrArgument if the max length of the array at offset 1 is not a
multiple of sizeof(TRegisterInfo), if the max length of the array
at offset 2 is not a multiple of sizeof(TRegister), if the max
length of the array at offset 3 is not a multiple of sizeof(TUint8), if
any of the descriptors have max length of 0, or if the three
descriptors do not represent the same number of registers,
KErrNoMemory if there is insufficient memory,
KErrDied, if the thread with thread ID aThreadId is dead
*/
void CSecuritySvrSession::ReadRegistersL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::ReadRegistersL()\n" );
const TThreadId threadId = ReadTThreadIdL(aMessage, 0);
//check the agent is attached to the thread
CheckAttachedL(threadId, aMessage, ETrue);
//number of registers being requested
TUint32 numberOfRegisters;
//check length of descriptors is acceptable
ValidateRegisterBuffersL(aMessage, numberOfRegisters);
// Passed data will be saved in this descriptor.
RBuf8 ids;
ids.CreateL(numberOfRegisters * sizeof(TRegisterInfo));
// Do the right cleanup if anything subsequently goes wrong
ids.CleanupClosePushL();
//read the data from the client thread
aMessage.ReadL(1, ids);
//create buffer to fill with data from target debug thread
HBufC8 *data = HBufC8::NewLC(aMessage.GetDesMaxLength(2));
TPtr8 values(data->Des());
HBufC8 *flagsData = HBufC8::NewLC(numberOfRegisters * sizeof(TUint8));
TPtr8 flags(flagsData->Des());
//get register info and return relevant parts back to agent
User::LeaveIfError(Server().iKernelDriver.ReadRegisters(threadId, ids, values, flags));
aMessage.WriteL(2, values);
aMessage.WriteL(3, flags);
//delete temporary buffers and return status
CleanupStack::PopAndDestroy(flagsData);
CleanupStack::PopAndDestroy(data);
CleanupStack::PopAndDestroy(&ids);
aMessage.Complete(KErrNone);
}
/**
Write register values.
@param aMessage should contain:
* at offset 0 a pointer to the thread ID of the target thread
* at offset 1 a descriptor representing an array of TRegisterInfo
register IDs
* at offset 2 a descriptor representing an array of TRegister register
values
* at offset 3 a descriptor representing an array into which TUint8
register flags will be written
@leave KErrArgument if the max length of the array at offset 1 is not a
multiple of sizeof(TRegisterInfo), if the max length of the array
at offset 2 is not a multiple of sizeof(TRegister), if the max
length of the array at offset 3 is not a multiple of sizeof(TUint8), if
any of the descriptors have max length of 0, or if the three
descriptors do not represent the same number of registers,
KErrNoMemory if there is insufficient memory,
KErrDied, if the thread with thread ID aThreadId is dead
*/
void CSecuritySvrSession::WriteRegistersL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::WriteRegistersL()\n" );
const TThreadId threadId = ReadTThreadIdL(aMessage, 0);
CheckAttachedL(threadId, aMessage, EFalse);
//number of registers attempting to set
TUint32 numberOfRegisters;
//check length of descriptors is acceptable
ValidateRegisterBuffersL(aMessage, numberOfRegisters);
// Passed register ids will be saved in this descriptor.
RBuf8 ids;
//allocate buffer
ids.CreateL(numberOfRegisters * sizeof(TRegisterInfo));
// Do the right cleanup if anything subsequently goes wrong
ids.CleanupClosePushL();
//read the data from the client thread
aMessage.ReadL(1, ids);
// Passed register values will be saved in this descriptor.
RBuf8 values;
//allocate buffer
values.CreateL(aMessage.GetDesMaxLength(2));
// Do the right cleanup if anything subsequently goes wrong
values.CleanupClosePushL();
//read the data from the client thread
aMessage.ReadL(2,values);
HBufC8 *flagsData = HBufC8::NewLC(numberOfRegisters*sizeof(TUint8));
TPtr8 flags(flagsData->Des());
//get register info and return relevant parts back to agent
User::LeaveIfError(Server().iKernelDriver.WriteRegisters(threadId, ids, values, flags));
//write flags data back
aMessage.WriteL(3, flags);
CleanupStack::PopAndDestroy(flagsData);
CleanupStack::PopAndDestroy(&values);
CleanupStack::PopAndDestroy(&ids);
aMessage.Complete(KErrNone);
}
/**
Processes an attach request from a debug agent. Gets the target debug
processes' original FileName as an argument. The method sets completion
status of the aMessage argument to KErrNone if successfully attached and to
another of the system wide error codes if there were problems.
@param aMessage contains:
* a boolean at offset 0 which indicates whether the agent wishes to
attach passively
* a buffer at offset 1 which contains the FileName
of the target debug process.
*/
void CSecuritySvrSession::AttachProcessL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::AttachProcessL()\n" );
const TBool passive = aMessage.Int0();
TInt deslen = aMessage.GetDesLengthL(1);
// Passed data will be saved in this descriptor.
RBuf processName;
// Max length set to the value of "deslen", but current length is zero
processName.CreateL(deslen);
// Do the right cleanup if anything subsequently goes wrong
processName.CleanupClosePushL();
// Copy the client's descriptor data into our buffer.
aMessage.ReadL(1,processName);
//
// Security Check
//
// It is not permitted to debug the debug security server!
//
// get the secure id of the executable
TUid secureId(TUid::Null());
GetSecureIdL(processName, secureId);
if (KUidDebugSecurityServer.iUid == secureId.iUid)
{
// The debug agent has requested to debug the Debug Security Server
// This is either an error, or an attempt to breach security. We
// therefore refuse to agree to this request, and return KErrPermissionDenied
LOG_MSG("Debug Agent attempted to debug the Debug Security Server");
User::Leave(KErrPermissionDenied);
}
// Check the OEM Debug token capabilities
GetDebugAgentOEMTokenCapsL();
// Get the Security info via rlibrary::getinfo
RLibrary::TInfo info;
TPckg<RLibrary::TInfo> infoBuf(info);
TInt err = RLibrary::GetInfo(processName, infoBuf);
if (err != KErrNone)
{
LOG_MSG2("Cannot parse the target executable header, err=%d", err);
// Could not read the header for this executable :-(
CleanupStack::PopAndDestroy(&processName);
aMessage.Complete(err);
return;
}
// Special case for AllFiles - OEM Debug tokens MUST have
// AllFiles, as this is what allows them to read contents
// of other executables.
TBool checkDebuggable = ETrue;
// Does an OEM Debug Token permit debug where it would normally not be
// permitted?
if ( Server().OEMTokenPermitsDebugL(iOEMDebugCapabilities, info.iSecurityInfo.iCaps) )
{
// OEM Debug token is valid and has sufficient capabilities
LOG_MSG("CSecuritySvrSession::AttachProcessL() - Debug Agent has sufficient capabilites based on OEM Debug Token");
checkDebuggable = EFalse;
}
if (checkDebuggable)
{
// OEM Debug token (if any), does not confer sufficient capabilities to
// debug the specified target executable. Therefore debugging can only
// be permitted if the target executable itself has been built as 'Debuggable'
LOG_MSG("CSecuritySvrSession::AttachProcessL() - Debug Agent has insufficient capabilites based on OEM Debug Token");
IsDebuggableL(processName);
}
User::LeaveIfError(Server().AttachProcessL(processName, iDebugAgentProcessId, passive));
// Inform the kernel driver about the attachment, so that it
// can track per-agent data about the process.
RBuf8 processName8;
processName8.CreateL(deslen);
processName8.CleanupClosePushL();
processName8.Copy(processName);
User::LeaveIfError(Server().iKernelDriver.AttachProcess(processName8, iDebugAgentProcessId.Id()));
// Create an Active Object to handle asynchronous calls to GetEvent
CSecuritySvrAsync* handler = CSecuritySvrAsync::NewL(this,processName8,iDebugAgentProcessId);
err = iAsyncHandlers.Insert(handler,0);
if (err != KErrNone)
{
// If we don't have an asynchronous handler, we should detach
// the driver as well.
Server().iKernelDriver.DetachProcess(processName8,iDebugAgentProcessId.Id());
Server().DetachProcess(processName, iDebugAgentProcessId);
// The DSS should NEVER panic itself. If the above calls fail, so be it we will attempt to limp on
}
User::LeaveIfError(err);
CleanupStack::PopAndDestroy(&processName8);
CleanupStack::PopAndDestroy(&processName);
aMessage.Complete(KErrNone);
}
/**
Processes an attach request from a debug agent from all processes.
The method sets completion status of the aMessage argument to KErrNone
if successfully attached and to another of the system wide error codes
if there were problems.
@param aMessage contains no data
*/
void CSecuritySvrSession::AttachAllL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::AttachAllL()\n" );
// Run the OEM Debug token, if run ok then sets iOEMDebugCapabilities
GetDebugAgentOEMTokenCapsL();
// Debug token must have All caps to use AttachToAll
TCapabilitySet allCaps;
allCaps.SetAllSupported();
if (!Server().OEMTokenPermitsDebugL(iOEMDebugCapabilities, allCaps))
{
LOG_MSG("Client's OEM token doesn't have capability ALL required for AttachAll()");
User::Leave(KErrPermissionDenied);
}
User::LeaveIfError(Server().AttachProcessL(_L("*"), iDebugAgentProcessId, EFalse));
TBuf8<1> KStar8=_L8("*");
User::LeaveIfError(Server().iKernelDriver.AttachProcess(KStar8, iDebugAgentProcessId.Id()));
// Create an Active Object to handle asynchronous calls to GetAllEvent
CSecuritySvrAsync* handler = CSecuritySvrAsync::NewL(this,KStar8,iDebugAgentProcessId);
TInt err = iAsyncHandlers.Insert(handler,0);
if (err != KErrNone)
{
// If we don't have an asynchronous handler, we should detach
// the driver as well.
Server().iKernelDriver.DetachProcess(KStar8,iDebugAgentProcessId.Id());
Server().DetachProcess(_L("*"), iDebugAgentProcessId);
}
User::LeaveIfError(err);
aMessage.Complete(KErrNone);
}
/**
Reads the OEM Debug Token associated with the debug agent if any. The OEM Debug Token
allows the Debug Agent to debug certain executables which have not been built as
'Debuggable'.
This works as follows: The OEM Debug Token is an executable with a special name
of the form "OEMDebug_<DA_SID>.exe" where <DA_SID> is the Secure ID of the Debug Agent
in hexadecimal. For example: "OEMDebug_F123ABCD.exe" would be a valid name. This token executable
must be signed with 'AllFiles' + X, where X is the set of PlatSec capabilities that are
possessed by the target executable to be debugged.
This function reads the capabilities possessed by the token by creating a process based
on the executable, and reading the TSecurityInfo associated with the process. This ensures
that the loader has validated the token has not been tampered with and that the security
information is valid.
The security information is then stored for future use as member data in iOEMDebugCapabilities.
Leaves if there is an error, otherwise simply fills in the capabilities
in iOEMDebugCapabilities.
It is not an error for the OEM Debug token not to exist. In this case, the function simply returns.
*/
void CSecuritySvrSession::GetDebugAgentOEMTokenCapsL(void)
{
if (iOEMDebugCapabilities.HasCapability(ECapabilityAllFiles))
{
// Then we've already read the caps - wasteful to read them again
return;
}
// Obtain the security info about the debug agent process
//get the debug agent's process
RProcess debugAgentProcess;
CleanupClosePushL(debugAgentProcess);
debugAgentProcess.Open(iDebugAgentProcessId);
// We have now obtained a process handle based on the token executable, so we can check its security properties.
TSecurityInfo secInfo(debugAgentProcess);
// Compute the name of the OEM debug token based on the SID of the debug agent
_LIT(KDSSOEMDebugTokenPrefix,"OEMDebug_");
_LIT(KDSSOEMDebugTokenAppendFmt,"%08X.exe");
RBuf agentTokenName;
agentTokenName.CreateL(KDSSOEMDebugTokenPrefix().Size()+8+1); // allow space for SID+null terminator
agentTokenName.CleanupClosePushL();
agentTokenName.SetLength(0);
// Add OEMDebug_
agentTokenName.Append(KDSSOEMDebugTokenPrefix());
// Add debug agent Secure ID
agentTokenName.AppendFormat(KDSSOEMDebugTokenAppendFmt,secInfo.iSecureId.iId);
// just log the token name for the moment.
RBuf8 agentTokenName8;
agentTokenName8.CreateL(agentTokenName.Length()+1);
agentTokenName8.CleanupClosePushL();
agentTokenName8.Copy(agentTokenName);
agentTokenName8.Append(TChar(0));
LOG_MSG2("CSecuritySvrSession::GetDebugAgentOEMTokenCapsL() - OEM Debug Token Name is %s",agentTokenName8.Ptr());
// Cleanup
CleanupStack::PopAndDestroy(&agentTokenName8);
// Now locate and start the executable...
RProcess agentToken;
TInt err = agentToken.Create(agentTokenName, KNullDesC);
if (KErrNone != err)
{
// Failed to create a process based on the token, just give up
LOG_MSG2("CSecuritySvrSession::GetDebugAgentOEMTokenCapsL() - Could not create process based on token due to err 0x%8x\n",err);
// Cleanup remaining items from the stack
CleanupStack::PopAndDestroy(&agentTokenName);
CleanupStack::PopAndDestroy(&debugAgentProcess);
return;
}
// Synchronise with the process to make sure it hasn't died straight away
TRequestStatus stat;
agentToken.Rendezvous(stat);
if (stat != KRequestPending)
{
// logon failed - agentToken is not yet running, so cannot have terminated
agentToken.Kill(0); // Abort startup
}
// store the OEM Debug Token security data
TSecurityInfo agentSecInfo(agentToken);
// Note capabilities for future use
iOEMDebugCapabilities=agentSecInfo.iCaps;
// resume the token. It _should_ just exit, but we don't really care.
agentToken.Resume();
// Wait to synchronise with agentToken - if it dies in the meantime, it
// also gets completed
User::WaitForRequest(stat);
// Just close the handle to it again.
agentToken.Close();
// Cleanup remaining items from the stack
CleanupStack::PopAndDestroy(&agentTokenName);
CleanupStack::PopAndDestroy(&debugAgentProcess);
}
/**
Checks whether the file passed in as aExecutable is XIP or not
@param aExecutable file to check
@return ETrue if the file is XIP, EFalse otherwise
*/
TBool CSecuritySvrSession::IsExecutableXipL(RFile& aExecutable)
{
TUint atts;
User::LeaveIfError(aExecutable.Att(atts));
return atts & KEntryAttXIP;
}
/**
Gets access to the symbian crash partition for crash access operation.
*/
void CSecuritySvrSession::ConnectCrashPartitionL (void)
{
LOG_MSG("CSecuritySvrSession::ConnectCrashPartitionL()");
TBool changed;
TInt error = KErrNone;
TInt i=0;
//Intialising to EFalse
iCrashConnected = EFalse;
TPckg<TLocalDriveCapsV2> capsBuf(iCaps);
//check for the symbian crash partition
for (i=0; i<KMaxLocalDrives; i++)
{
error = iLocalDrive.Connect (i, changed);
if ( error == KErrNone)
{
error = iLocalDrive.Caps(capsBuf);
if ( error != KErrNone)
{
//continue if not found
continue;
}
if ( iCaps.iPartitionType == (TUint16)KPartitionTypeSymbianCrashLog)
{
LOG_MSG2("Found Symbian crash log partition on drive: %d",i);
iCrashConnected = ETrue;
break;
}
}
}
if ( i == KMaxLocalDrives)
{
LOG_MSG("No crash log partition found with valid crash log signature found. Exiting...");
User::Leave (KErrNotFound);
}
// Nand Flash not currently supported.
if (iCaps.iType == EMediaNANDFlash)
{
LOG_MSG( "CSecuritySvrSession::ConnectCrashPartitionL() Nand Flash not currently supported\n" );
User::Leave (KErrNotSupported);
}
}
/** Checks that aHeaderData contains enough data to cast it to the
appropriate header type.
@param aHeaderData buffer containing header data read from a file
@param aXip boolean indicating whether the header data is for an XIP image
@return ETrue if enough data in buffer, EFalse otherwise
*/
TBool CSecuritySvrSession::CheckSufficientData(const TDesC8& aHeaderData, const TBool aXip) const
{
TUint minimumHeaderSize = aXip ? sizeof(TRomImageHeader) : sizeof(E32ImageHeaderV);
return (aHeaderData.Length() >= minimumHeaderSize);
}
/**
Opens a file handle to aFileName using aFileHandle
@param aFileName file to open handle to
@param aFs file system to use to open the handle
@param aFileHandle file handle to open
@leave one of the system wide error codes
*/
void CSecuritySvrSession::OpenFileHandleL(const TDesC& aFileName, RFs& aFs, RFile& aFileHandle)
{
TInt err = aFileHandle.Open(aFs, aFileName, EFileRead | EFileShareReadersOnly);
if (err != KErrNone)
{
// Could not open the file for reading
LOG_MSG("CSecuritySvrSession::OpenFileHandleL - Failed to open executable\n");
User::Leave(err);
}
}
/**
Checks whether an executable has the debug bit set
@param aHeaderData buffer containing the header of the executable
@param aXip indication of whether the executable is XIP or not
@return ETrue if debug bit is set, EFalse otherwise
*/
TBool CSecuritySvrSession::IsDebugBitSet(const TDesC8& aHeaderData, const TBool aXip)
{
if(!CheckSufficientData(aHeaderData, aXip))
{
return EFalse;
}
if (aXip)
{
TRomImageHeader* hdr = (TRomImageHeader*)aHeaderData.Ptr();
return (hdr->iFlags & KRomImageDebuggable);
}
else
{
// it is an epoc32 image
E32ImageHeaderV* hdr = (E32ImageHeaderV*)aHeaderData.Ptr();
return (hdr->iFlags & KImageDebuggable);
}
}
/**
Determines whether a particular executable is marked as 'debuggable'
Notes:
This function is currently hard coded to understand the format of e32 and
TRomImage file headers. Ideally this will be replaced by a call to RLibrary::GetInfo
which can return the 'debuggable' information. Unfortunately, this call currently
does not provide the information for XIP executables :-(
@leave KErrPermissionDenied if the debug bit is not set, or one of the other
system wide error codes
*/
void CSecuritySvrSession::IsDebuggableL(const TDesC& aFileName)
{
#ifndef IGNORE_DEBUGGABLE_BIT
RFs fs;
User::LeaveIfError(fs.Connect());
CleanupClosePushL(fs);
RFile targetExe;
OpenFileHandleL(aFileName, fs, targetExe);
CleanupClosePushL(targetExe);
// Read in the entire header
RBuf8 e32HdrBuf;
e32HdrBuf.CreateL(RLibrary::KRequiredImageHeaderSize);
e32HdrBuf.CleanupClosePushL();
// Read the entire header as far as possible
TInt err = targetExe.Read(e32HdrBuf);
if (err != KErrNone)
{
// Could not read the file
LOG_MSG("CSecuritySvrSession::IsDebuggableL - Failed to read executable\n");
User::Leave(err);
}
if(!CheckSufficientData(e32HdrBuf, IsExecutableXipL(targetExe)))
{
User::Leave(KErrGeneral);
}
if(! IsDebugBitSet(e32HdrBuf, IsExecutableXipL(targetExe)))
{
User::Leave(KErrPermissionDenied);
}
CleanupStack::PopAndDestroy(3, &fs);
#else
LOG_MSG("CSecuritySvrSession::IsDebuggableL() Debuggable bit temporarily ignored!!!");
#endif
}
/**
Processes a detach request from a debug agent. Gets the target debug
processes' original FileName as an argument. The method sets completion
status of the aMessage argument to KErrNone if successfully detached and to
another of the system wide error codes if there were problems.
@param aMessage contains:
* a buffer at offset 0 which contains the FileName
of the target debug process.
*/
void CSecuritySvrSession::DetachProcessL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::DetachProcessL()\n" );
TInt deslen = aMessage.GetDesLengthL(0);
// Passed data will be saved in this descriptor.
RBuf processName;
// Max length set to the value of "deslen", but current length is zero
processName.CreateL(deslen);
// Do the right cleanup if anything subsequently goes wrong
processName.CleanupClosePushL();
// Copy the client's descriptor data into our buffer.
aMessage.ReadL(0,processName);
User::LeaveIfError(Server().DetachProcess(processName, iDebugAgentProcessId));
// Inform the kernel driver about the detachment, so that
// it can stop tracking per-agent data for the debugged process.
RBuf8 processName8;
processName8.CreateL(deslen);
processName8.CleanupClosePushL();
processName8.Copy(processName);
// Remove the Asynchronous Object associated with this process
for(TInt i=0; i<iAsyncHandlers.Count(); i++)
{
if (processName8.Compare(iAsyncHandlers[i]->ProcessName()) == 0)
{
delete iAsyncHandlers[i];
iAsyncHandlers.Remove(i);
break;
}
}
// Inform the driver that we are no longer attached to this process
User::LeaveIfError(Server().iKernelDriver.DetachProcess(processName8,iDebugAgentProcessId.Id()));
CleanupStack::PopAndDestroy(&processName8);
CleanupStack::PopAndDestroy(&processName);
aMessage.Complete(KErrNone);
}
/**
Processes a DetachAll request from a debug agent.
The method sets completion status of the aMessage argument to
KErrNone if successfully detached and to
another of the system wide error codes if there were problems.
@param aMessage contains:
* a buffer at offset 0 which contains the FileName
of the target debug process.
*/
void CSecuritySvrSession::DetachAllL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::DetachAllL()" );
User::LeaveIfError(Server().DetachProcess(_L("*"), iDebugAgentProcessId));
TBuf8<1> KStar8=_L8("*");
TBool found = EFalse;
// Remove the Asynchronous Object associated with the AttachAll, not the rest
for(TInt i=0; i<iAsyncHandlers.Count(); i++)
{
if (iAsyncHandlers[i]->ProcessName().Compare(KStar8) == 0)
{
delete iAsyncHandlers[i];
iAsyncHandlers.Remove(i);
User::LeaveIfError(Server().iKernelDriver.DetachProcess(KStar8,iDebugAgentProcessId.Id()));
found = ETrue;
break;
}
}
if( !found )
{
LOG_MSG2( "CSecuritySvrSession::DetachAllL() : Did not find the asynch handler for agent 0x%lx",
iDebugAgentProcessId.Id() );
User::Leave(KErrNotFound);
}
aMessage.Complete(KErrNone);
}
/**
@param aMessage The RMessage2 object is expected to contain:
* aMessage.Int0() - TDes8 Containing the process name.
* aMessage.Int1() - Address of TPtr8 containing TEventInfo
*/
void CSecuritySvrSession::GetEventL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::GetEventL()\n" );
// Local descriptor to contain target process name
TInt deslen = aMessage.GetDesLengthL(0);
RBuf processName;
processName.CreateL(deslen);
processName.CleanupClosePushL();
// Read the target process name into processName
aMessage.ReadL(0,processName);
// Check if debug agent is attached to process
if(!Server().CheckAttachedProcess(processName, aMessage, EFalse))
{
LOG_MSG("CSecuritySvrSession::GetEventL() - Not attached to this process\n");
// Debug Agent is not attached at all to the requested process
User::Leave(KErrPermissionDenied);
}
// Identify which process is being debugged, so that
// we can locate the appropriate active object handler.
RBuf8 processName8;
processName8.CreateL(processName.Length());
processName8.CleanupClosePushL();
processName8.Copy(processName);
// Find the Asynchronous Object associated with this process,
// as it is permissible to have an outstanding GetEvent call
// for each attached process.
TBool foundHandler = EFalse;
for(TInt i=0; i<iAsyncHandlers.Count(); i++)
{
if (processName8.Compare(iAsyncHandlers[i]->ProcessName()) == 0)
{
iAsyncHandlers[i]->GetEvent(aMessage);
foundHandler = ETrue;
break;
}
}
if (foundHandler == EFalse)
{
// could not find an async handler object. Report the problem.
LOG_MSG("CSecuritySvrSessionL - Could not find a handler object\n");
User::Leave(KErrNotFound);
}
// Actually make the driver call, passing in the agent Id
// so that the driver knows which per-agent event queue
// to interrogate to retrieve the latest event.
CleanupStack::PopAndDestroy(&processName8);
CleanupStack::PopAndDestroy(&processName);
}
/**
Cancels a pre-issued GetEvent call for a specific debugged process.
@param aMessage.Int0() - TDes8 containing aProcessName
*/
void CSecuritySvrSession::CancelGetEventL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::CancelGetEventL()\n" );
// Local descriptor to contain target process name
TInt deslen = aMessage.GetDesLengthL(0);
RBuf processName;
processName.CreateL(deslen);
processName.CleanupClosePushL();
// Read the target process name into processName
aMessage.ReadL(0,processName,0);
// Debug Agent is not an active debugger. Check if the DA is passively attached
if(!Server().CheckAttachedProcess(processName, aMessage, EFalse))
{
// Debug Agent is not attached at all to the requested process
User::Leave(KErrPermissionDenied);
}
// Identify the appropriate active object associate
// with this process.
RBuf8 processName8;
processName8.CreateL(processName.Length());
processName8.CleanupClosePushL();
processName8.Copy(processName);
// Find the Asynchronous Object associated with this process
TBool foundHandler = EFalse;
for(TInt i=0; i<iAsyncHandlers.Count(); i++)
{
if (processName8.Compare(iAsyncHandlers[i]->ProcessName()) == 0)
{
// Found the AO handler, so cancel the outstanding getevent call.
iAsyncHandlers[i]->Cancel();
foundHandler = ETrue;
break;
}
}
if(!foundHandler)
{
// We could not found a handler, so report the problem to the debug agent
User::Leave(KErrNotFound);
}
//do cleanup
CleanupStack::PopAndDestroy(&processName8);
CleanupStack::PopAndDestroy(&processName);
aMessage.Complete(KErrNone);
}
/*
Purpose: Sets the required event action to be taken for a specific
process and event combination
@param aMessage The RMessage2 object is expected to contain:
* aMessage.Int0() - TDes8 Containing the process name.
* aMessage.Int1() - TEventType
* aMessage.Int2() - TKernelEventAction
*
*/
void CSecuritySvrSession::SetEventActionL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::SetEventActionL()\n" );
// Local descriptor to contain target process name
TInt deslen = aMessage.GetDesLengthL(0);
RBuf processName;
processName.CreateL(deslen);
processName.CleanupClosePushL();
// Read the target process name into processName
aMessage.ReadL(0,processName);
//check that the agent has attached to the target process
if(!Server().CheckAttachedProcess(processName, aMessage, EFalse))
{
// Debug Agent is not attached at all to the requested process
User::Leave(KErrPermissionDenied);
}
// Extract and validate the arguments from aMessage
TUint32 event = aMessage.Int1();
if (event >= EEventsLast)
{
// Supplied event Id was not recognised
User::Leave(KErrArgument);
}
TUint32 action = aMessage.Int2();
if(action >= EActionLast)
{
// Supplied event action was not recognised
User::Leave(KErrArgument);
}
RBuf8 processName8;
processName8.CreateL(processName.Length());
processName8.CleanupClosePushL();
processName8.Copy(processName);
// Make the call to the device driver
TInt err = Server().iKernelDriver.SetEventAction(processName8, \
(TEventType)event,\
(TKernelEventAction)action,\
iDebugAgentProcessId.Id());
User::LeaveIfError(err);
CleanupStack::PopAndDestroy(&processName8);
CleanupStack::PopAndDestroy(&processName);
aMessage.Complete(KErrNone);
}
/**
Purpose: Single-step a thread for a specified number of instructions
@param aMessage.Ptr0() - Thread Id of the thread to be stepped
@param aMessage.Int1() - Number of instructions to step.
@leave one of the system wide error codes
*/
void CSecuritySvrSession::StepL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::StepL()\n" );
const TThreadId threadId = ReadTThreadIdL(aMessage, 0);
const TInt32 numSteps = aMessage.Int1();
CheckAttachedL(threadId, aMessage, EFalse);
User::LeaveIfError(Server().iKernelDriver.Step( threadId, numSteps ));
aMessage.Complete(KErrNone);
}
/**
* This checks whether or not the agent is permitted access to the flash partition
* @return KErrNone if allowed, otherwise one of the system wide error codes
* @leave one of the system wide error codes
*/
TInt CSecuritySvrSession::CheckFlashAccessPermissionL(const RThread& aClientThread)
{
// Read the OEM Debug token capabilities (if any)
GetDebugAgentOEMTokenCapsL();
if(Server().OEMTokenPermitsFlashAccessL((iOEMDebugCapabilities)))
{
return KErrNone;
}
return KErrPermissionDenied;
}
/**
Purpose: Read the crash log from the crash flash partition
@param aMessage.Int0() - Position to read from.
@param aMessage.Ptr1() - Buffer to hold the data retrieved
@param aMessage.Int2() - Size of the data to read.
@leave one of the system wide error codes
*/
void CSecuritySvrSession::ReadCrashLogL (const RMessage2& aMessage)
{
//get the debug agent's thread and push handle onto clean up stack
RThread clientThread;
User::LeaveIfError(aMessage.Client(clientThread));
CleanupClosePushL(clientThread);
TInt err = CheckFlashAccessPermissionL(clientThread);
CleanupStack::PopAndDestroy(&clientThread);
if(KErrNone != err)
{
LOG_MSG2( "CSecuritySvrSession::ReadCrashLogL() Access Not Granted - [%d]\n", err );
aMessage.Complete(err);
return;
}
//Check whether drive connected.
if(!iCrashConnected)
ConnectCrashPartitionL();
TInt readPosition = aMessage.Int0(); //read position
TInt readSize = aMessage.Int2(); //read size
RBuf8 readBuf;
readBuf.CreateL(readSize);
readBuf.CleanupClosePushL();
err = iLocalDrive.Read (readPosition, readSize, readBuf);
//write the list data back
aMessage.WriteL (1, readBuf);
CleanupStack::PopAndDestroy (&readBuf);
//Complete message
aMessage.Complete(err);
}
/**
Purpose: Function to write the crash config to the crash flash partition
@param aMessage.Int0() - write position in bytes from start position in flash partition.
@param aMessage.Ptr1() - Buffer containing the data to be written onto the flash.
The size could be 0 if only flash partition size is needed.
@param aMessage.Int2() - returns the size of the flash partition.
@leave one of the system wide error codes
*/
void CSecuritySvrSession::WriteCrashConfigL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::WriteCrashConfigL()\n" );
//get the debug agent's thread and push handle onto clean up stack
RThread clientThread;
User::LeaveIfError(aMessage.Client(clientThread));
CleanupClosePushL(clientThread);
TInt err = CheckFlashAccessPermissionL(clientThread);
CleanupStack::PopAndDestroy(&clientThread);
if(KErrNone != err)
{
LOG_MSG2( "CSecuritySvrSession::WriteCrashConfigL() Access Not Granted - [%d]\n", err );
aMessage.Complete(err);
return;
}
//Check whether drive connected.
if(!iCrashConnected)
ConnectCrashPartitionL();
// Get the length of the buffer
TInt deslen = aMessage.GetDesLengthL(1);
RBuf8 dataBuf;
dataBuf.CreateL(deslen);
dataBuf.CleanupClosePushL();
// data to be written to flash
aMessage.ReadL(1,dataBuf);
TUint32 position = aMessage.Int0(); //position to start from
err = iLocalDrive.Write(position,(const TDesC8&)dataBuf);
TPtr8 dataSize((TUint8*)&deslen,4, 4);
//write the size of the data written back
aMessage.WriteL(2,dataSize);
//destroy buffer
CleanupStack::PopAndDestroy(&dataBuf);
aMessage.Complete(err);
}
/**
Purpose: Method to erase the crash flash block
@param aMessage.Int0() - write position in bytes from start position in flash partition.
@param aMessage.Int2() - Number of blocks to erase.
@leave one of the system wide error codes
*/
void CSecuritySvrSession::EraseCrashLogL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::EraseCrashLogL()\n" );
//get the debug agent's thread and push handle onto clean up stack
RThread clientThread;
User::LeaveIfError(aMessage.Client(clientThread));
CleanupClosePushL(clientThread);
TInt err = CheckFlashAccessPermissionL(clientThread);
CleanupStack::PopAndDestroy(&clientThread);
if(KErrNone != err)
{
LOG_MSG2( "CSecuritySvrSession::EraseCrashLogL() Access Not Granted - [%d]\n", err );
aMessage.Complete(err);
return;
}
//Check whether drive connected.
if(!iCrashConnected)
ConnectCrashPartitionL();
TInt64 position = aMessage.Int0();
TInt size = aMessage.Int1();
//Format drive
err = iLocalDrive.Format(position,size*iCaps.iEraseBlockSize);
aMessage.Complete(err);
}
/**
Purpose: Method to erase the entire crash flash block
@leave one of the system wide error codes
*/
void CSecuritySvrSession::EraseEntireCrashLogL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::EraseEntireCrashLogL()\n" );
//get the debug agent's thread and push handle onto clean up stack
RThread clientThread;
User::LeaveIfError(aMessage.Client(clientThread));
CleanupClosePushL(clientThread);
TInt err = CheckFlashAccessPermissionL(clientThread);
CleanupStack::PopAndDestroy(&clientThread);
if(KErrNone != err)
{
LOG_MSG2( "CSecuritySvrSession::EraseEntireCrashLogL() Access Not Granted - [%d]\n", err );
aMessage.Complete(err);
return;
}
//Check whether drive connected.
if(!iCrashConnected)
ConnectCrashPartitionL();
TUint numberBlocks = iCaps.iSize /iCaps.iEraseBlockSize;
//Format drive
for(TInt i = 0; i < numberBlocks; i++)
{
err = iLocalDrive.Format(i*iCaps.iEraseBlockSize,iCaps.iEraseBlockSize);
if(KErrNone != err)
{
RDebug::Printf("err = %d", err);
aMessage.Complete(err);
return;
}
}
aMessage.Complete(err);
}
/**
Purpose: Kill a specified process
@param aMessage.Ptr0() - Process Id of the thread to be stepped
@param aMessage.Int1() - Reason code to supply when killing the process.
@leave one of the system wide error codes
*/
void CSecuritySvrSession::KillProcessL(const RMessage2& aMessage)
{
LOG_MSG( "CSecuritySvrSession::KillProcessL()\n" );
const TProcessId processId = ReadTProcessIdL(aMessage, 0);
const TInt32 reason = aMessage.Int1();
CheckAttachedL(processId, aMessage, EFalse);
User::LeaveIfError(Server().iKernelDriver.KillProcess( processId, reason ));
aMessage.Complete(KErrNone);
}
/** Gets the secure id of aFileName
@param aFileName file name of executable to get SID for
@param aSecureId on return will contain the SID of aFileName
@leave one of the system wide error codes
*/
void CSecuritySvrSession::GetSecureIdL(const TDesC& aFileName, TUid& aSecureId)
{
RFs fs;
User::LeaveIfError(fs.Connect());
CleanupClosePushL(fs);
RFile targetExe;
OpenFileHandleL(aFileName, fs, targetExe);
CleanupClosePushL(targetExe);
// Read in the entire header
RBuf8 e32HdrBuf;
e32HdrBuf.CreateL(RLibrary::KRequiredImageHeaderSize);
e32HdrBuf.CleanupClosePushL();
// Read the entire header as far as possible
TInt err = targetExe.Read(e32HdrBuf);
if (err != KErrNone)
{
// Could not read the file
LOG_MSG("CSecuritySvrSession::GetSecureIdL - Failed to read executable\n");
User::Leave(err);
}
if(!CheckSufficientData(e32HdrBuf, IsExecutableXipL(targetExe)))
{
User::Leave(KErrGeneral);
}
aSecureId = GetSecureIdL(e32HdrBuf, IsExecutableXipL(targetExe));
CleanupStack::PopAndDestroy(3, &fs);
}
/** Get the secure id from aHeaderData
@param aHeaderData an executable's header data to read SID from
@param aXip indication of whether the header data is from an XIP file
@return secure ID from aHeaderData
*/
TUid CSecuritySvrSession::GetSecureIdL(const TDesC8& aHeaderData, TBool aXip)
{
if(!CheckSufficientData(aHeaderData, aXip))
{
User::Leave(KErrGeneral);
}
if (aXip)
{
TRomImageHeader* hdr = (TRomImageHeader*)aHeaderData.Ptr();
return TUid::Uid(hdr->iS.iSecureId);
}
else
{
// it is an epoc32 image
E32ImageHeaderV* hdr = (E32ImageHeaderV*)aHeaderData.Ptr();
return TUid::Uid(hdr->iS.iSecureId);
}
}
/**
@param aMessage contains:
* aMessage.Ptr0() a TListDetails object
* aMessage.Ptr1() a client supplied TDes8 for the driver to return data in
* aMessage.Ptr2() a TUint32 for the driver to return the size of the requested listing's data in
@leave KErrTooBig if the buffer passed as argument 1 of aMessage is too
small to contain the requested data,
KErrNoMemory if a temporary buffer could not be allocated,
or one of the other system wide error codes
*/
void CSecuritySvrSession::GetListL(const RMessage2& aMessage)
{
LOG_MSG("CSecuritySvrSession::GetListL()");
// buffer to write list data into before copying back to agent
RBuf8 listDetailsBuf;
//allocate buffer
listDetailsBuf.CreateL(sizeof(TListDetails));
// Do the right cleanup if anything subsequently goes wrong
listDetailsBuf.CleanupClosePushL();
//read the data from the client thread
aMessage.ReadL(0, listDetailsBuf);
TListDetails* listDetails = (TListDetails*)listDetailsBuf.Ptr();
//get the type of list requested
TListId type = (TListId)aMessage.Int0();
//create a buffer to store the data in
RBuf8 buffer;
buffer.CreateL(aMessage.GetDesMaxLength(1));
buffer.CleanupClosePushL();
//create a temporary variable to potentially store data length in
TUint32 size = 0;
TInt err = KErrNone;
// the executables list is generated in the DSS rather than in the driver
// so is treated separately
if(listDetails->iListId == EExecutables)
{
if(listDetails->iListScope != EScopeGlobal)
{
User::Leave(KErrArgument);
}
if(listDetails->iTargetId != 0)
{
User::Leave(KErrArgument);
}
err = GetExecutablesListL(buffer, size);
}
else
{
err = Server().iKernelDriver.GetList(listDetails->iListId, listDetails->iListScope, listDetails->iTargetId, iDebugAgentProcessId, buffer, size);
}
if(err == KErrNone)
{
//write the list data back
aMessage.WriteL(1, buffer);
}
TPtr8 sizePtr((TUint8*)&size, sizeof(TUint32), sizeof(TUint32));
//write size back to agent
aMessage.WriteL(2, sizePtr);
CleanupStack::PopAndDestroy(&buffer);
CleanupStack::PopAndDestroy(&listDetailsBuf);
aMessage.Complete(err);
}
/**
Gets the executables list and returns it in aBuffer if it's big enough
@param aBuffer caller supplied buffer to write data into
@param aSize on return contains the size of the data in the buffer, or the
size that the buffer would need to be to contain the data
@return KErrNone on success, or KErrTooBig if the requested data will not fit in aBuffer
@leave one of the system wide error codes
*/
TInt CSecuritySvrSession::GetExecutablesListL(TDes8& aBuffer, TUint32& aSize) const
{
LOG_MSG("CSecuritySvrSession::GetExecutablesList()");
//initialise values and connect to file system
aSize = 0;
aBuffer.SetLength(0);
RFs fs;
User::LeaveIfError(fs.Connect());
CleanupClosePushL(fs);
// uids corresponding to executable image
TUidType uids(KExecutableImageUid, KNullUid, KNullUid);
//create a string containing the directory name. The drive letter is represented
//by X but will be replaced by the appropriate drive letter for each drive
_LIT(KColonSysBin,":\\sys\\bin\\");
//create a modifiable copy of KColonSysBin, preceeded by an empty space for the drive letter
RBuf dirName;
dirName.CreateL(1 + KColonSysBin().Length());
dirName.CleanupClosePushL();
//set the length to 1 (to later fill with the drive letter) and then append KColonSysBin
dirName.SetLength(1);
dirName.Append(KColonSysBin());
//get the list of valid drives for the device
TDriveList driveList;
User::LeaveIfError(fs.DriveList(driveList));
//check each valid sys/bin directory for executables
for(TInt i=0; i<KMaxDrives; i++)
{
//if the drive is not valid then skip this drive
if(!driveList[i])
{
//skip processing this drive
continue;
}
//get the drive letter and insert it as the drive letter for dirName
TChar driveLetter;
User::LeaveIfError(fs.DriveToChar(i, driveLetter));
dirName[0] = (TUint)driveLetter;
//get a list of the exes in this drive's sys/bin directory
CDir* localDir = NULL;
TInt err = fs.GetDir(dirName, uids, ESortByName, localDir);
if(KErrNoMemory == err)
{
User::Leave(err);
}
if(!localDir)
{
//skip processing this drive
continue;
}
//push onto cleanup stack in case we leave
CleanupStack::PushL(localDir);
//iterate through the files
for(TInt j=0; j<localDir->Count(); j++)
{
//will store x:\sys\bin\<file-name> type string
RBuf fullPathName;
TUint16 nameLength = dirName.Length() + (*localDir)[j].iName.Length();
fullPathName.CreateL(nameLength);
fullPathName.CleanupClosePushL();
fullPathName.Copy(dirName);
fullPathName.Append((*localDir)[j].iName);
//add the data to the buffer
AppendExecutableData(aBuffer, aSize, fullPathName);
//do cleanup
CleanupStack::PopAndDestroy(&fullPathName);
}
//do cleanup
CleanupStack::PopAndDestroy(localDir);
}
//do cleanup
CleanupStack::PopAndDestroy(2, &fs);
//return appropriate value as to whether the kernel's data was too big
return (aSize <= aBuffer.MaxLength()) ? KErrNone : KErrTooBig;
}
/**
Append data to aBuffer and update size of aSize if the data will fit. If it will
not fit then just puts the nee size in aSize.
@param aBuffer buffer to append the data to
@param aSize on return contains the new size of the buffer if the data could be
appended, otherwise aSize is updated to reflect the size the buffer would have if
the data had fitted.
@param aEntryName file name of the entry to add to the buffer
*/
void CSecuritySvrSession::AppendExecutableData(TDes8& aBuffer, TUint32& aSize, const TDesC& aEntryName) const
{
//update aSize to include the size of the data for this entry
aSize = Align4(aSize + sizeof(TExecutablesListEntry) + (2*aEntryName.Length()) - sizeof(TUint16));
//if the data will fit, and we haven't already stopped putting data in, then append the data,
//if we've stopped putting data in then aSize will be bigger than aBuffer.MaxLength()
if(aSize <= aBuffer.MaxLength())
{
TExecutablesListEntry& entry = *(TExecutablesListEntry*)(aBuffer.Ptr() + aBuffer.Length());
//check whether an agent has registered to actively debug fullPathName
TBool activelyDebugged = IsDebugged(aEntryName, EFalse);
entry.iIsActivelyDebugged = activelyDebugged ? 1 : 0;
//check whether any agents have registered to passively debug fullPathName
TBool passivelyDebugged = IsDebugged(aEntryName, ETrue);
entry.iIsPassivelyDebugged = passivelyDebugged ? 1 : 0;
entry.iNameLength = aEntryName.Length();
TPtr name(&(entry.iName[0]), aEntryName.Length(), aEntryName.Length());
name = aEntryName;
//pad the buffer to a four byte boundary
aBuffer.SetLength(aSize);
}
}
/**
Helper function
Write data back to the thread that owns aMessage
@param aMessage the message which is passed between processes
@param aIndex the message slot which the data will be passed back in
@param aPtr pointer to data in this thread to be written into aMessage
@param aPtrSize size in bytes of the data to be written
@leave one of the system wide error codes
*/
void CSecuritySvrSession::WriteDataL(const RMessage2& aMessage, const TInt aIndex, const TAny* aPtr, const TUint32 aPtrSize) const
{
TPtr8 dataPtr((TUint8*)aPtr, aPtrSize, aPtrSize);
aMessage.WriteL(aIndex, dataPtr);
}
/**
Helper function.
Checks whether the debug agent (the owner of the aMessage) is attached to the
thread with thread id of aThreadId.
@param aThreadId thread ID of target debug thread
@param aMessage message owned by the debug agent
@param aPassive indicates whether to check if attached passively or actively
@leave KErrPermissionDenied if the agent is not attached to the process,
KErrNoMemory if the security server could not be accessed
*/
void CSecuritySvrSession::CheckAttachedL(const TThreadId aThreadId, const RMessage2& aMessage, const TBool aPassive) const
{
//check that the agent has attached to the target process
if(! Server().CheckAttached(aThreadId, aMessage, aPassive))
{
LOG_MSG("CSecuritySvrSession::CheckAttachedL() failed");
User::Leave(KErrPermissionDenied);
}
}
/**
Helper function.
Checks whether the debug agent (the owner of the aMessage) is attached to the
process with process id of aProcessId.
@param aProcessId process ID of target debug thread
@param aMessage message owned by the debug agent
@param aPassive indicates whether to check if attached passively or actively
@leave KErrPermissionDenied if the agent is not attached to the process,
KErrNoMemory if the security server could not be accessed
*/
void CSecuritySvrSession::CheckAttachedL(const TProcessId aProcessId, const RMessage2& aMessage, const TBool aPassive) const
{
//check that the agent has attached to the target process
if(! Server().CheckAttached(aProcessId, aMessage, aPassive))
{
LOG_MSG("CSecuritySvrSession::CheckAttachedL() (process) failed");
User::Leave(KErrPermissionDenied);
}
}
/**
Check whether the debug agent is permitted to attach to the target process.
Note that this function does not actually attach the agent to the process, it
simply tests whether an attach call would potentially be successful.
Currently this method returns ETrue in all cases but will be updated once
the security checking framework is in place.
@param aDebugAgentProcessId process id of the debug agent
@param aTargetProcessName original file name of the target process
@return ETrue if the debug agent would be allowed to attch to the target process,
EFalse otherwise
*/
TBool CSecuritySvrSession::PermitDebugL(const TProcessId aDebugAgentProcessId, const TDesC& aTargetProcessName) const
{
return ETrue;
}
/**
Helper function
Validates that the memory info passed in meets the debug driver's requirements
@param aMemoryInfo memory info passed in from client
@leave KErrArgument if:
* size is zero
* size is greater than the max block size
* size + address > 0xffffffff
* address is not access size aligned
* size is not a multiple of the access size
KErrNotSupported if:
* iAccess is not TAccess::EAccess32
* iEndianess is not TEndianess::EEndLE8
KErrUnknown if:
* the max memory block size cannot be determined
or one of the other system wide error codes
*/
void CSecuritySvrSession::ValidateMemoryInfoL(const TThreadId aThreadId, const TMemoryInfo &aMemoryInfo, const TBool aReadOperation)
{
//check size is not 0
if(aMemoryInfo.iSize == 0)
User::Leave(KErrArgument);
//get the max block size supported
TUint32 maxSize = 0;
User::LeaveIfError(Server().iKernelDriver.GetMemoryOperationMaxBlockSize(maxSize));
//check that the block size given is less than the max block size
if(aMemoryInfo.iSize > maxSize)
User::Leave(KErrArgument);
//must ensure that address + size <= 0xffffffff as will attempt to
//read past 0xffffffff, which wouldn't be good
TUint32 maxAddress = (~aMemoryInfo.iSize) + 1;
if(aMemoryInfo.iAddress > maxAddress)
User::Leave(KErrArgument);
//check that arguments are supported
if(aMemoryInfo.iAccess != EAccess32)
User::Leave(KErrNotSupported);
if(aMemoryInfo.iEndianess != EEndLE8)
User::Leave(KErrNotSupported);
//check that address is multiple of access size
TInt addressIndicator = aMemoryInfo.iAddress % aMemoryInfo.iAccess;
if(addressIndicator != 0)
{
User::Leave(KErrArgument);
}
//check that size is multiple of access size
TInt sizeIndicator = aMemoryInfo.iSize % aMemoryInfo.iAccess;
if(sizeIndicator != 0)
User::Leave(KErrArgument);
}
/**
Helper function
Validates that the three buffers relating to reading register data are of
appropriate sizes, and calculates the number of registers being requested.
@param aMessage message which in offsets 1, 2 and 3 contains descriptors
@param aNumberOfRegisters if the function returns with KErrNone this will
contain the number of registers being requested, guaranteed to be non-zero
@leave KErrArgument if descriptors do not represent the same number of
registers, if any of the descriptors have max length of 0, if any of
the descriptors have max lengths which are not multiples of their data
type's size or if any of the descriptors have max lengths greater than
the max block size for memory operations
or one of the other system wide error codes if there were problems
in getting the descriptors' lengths.
*/
void CSecuritySvrSession::ValidateRegisterBuffersL(const RMessage2& aMessage, TUint32& aNumberOfRegisters)
{
//get lengths of buffers, if error occurs returned value will be less then zero
TInt idsBufferLength = aMessage.GetDesMaxLength(1);
if(idsBufferLength < 0)
{
User::Leave(idsBufferLength);
}
TInt valuesBufferLength = aMessage.GetDesMaxLength(2);
if(valuesBufferLength < 0)
{
User::Leave(valuesBufferLength);
}
TInt flagsBufferLength = aMessage.GetDesMaxLength(3);
if(flagsBufferLength < 0)
{
User::Leave(flagsBufferLength);
}
//get the max block size supported
TUint32 maxSize = 0;
User::LeaveIfError(Server().iKernelDriver.GetMemoryOperationMaxBlockSize(maxSize));
//check none of the descriptors have size greater than the max block size
if((idsBufferLength > maxSize) || (valuesBufferLength > maxSize) || (flagsBufferLength > maxSize))
User::Leave(KErrArgument);
//get sizes of the three types of data the buffers represent arrays of
//and validate that the buffer lengths are multiples of the data sizes
TUint idSize = sizeof(TRegisterInfo);
if(idsBufferLength % idSize != 0)
User::Leave(KErrArgument);
TUint flagSize = sizeof(TUint8);
if(flagsBufferLength % flagSize != 0)
User::Leave(KErrArgument);
//perform check on id buffer length
if(idsBufferLength == 0)
User::Leave(KErrArgument);
//calculate number of registers being requested
aNumberOfRegisters = idsBufferLength / idSize;
//check flags buffer is of appropriate size
if(flagsBufferLength != (aNumberOfRegisters * flagSize))
User::Leave(KErrArgument);
}
/**
Establish whether any agents have registered to debug the specified aFileName
@param aFileName originating file name of the target process
@param aPassive indicates whether to check if there has been active attachment,
or passive attachment.
@return ETrue if aFileName is being debugged, EFalse otherwise
*/
TBool CSecuritySvrSession::IsDebugged(const TDesC& aFileName, const TBool aPassive) const
{
//check whether the target process is being debugged
return Server().IsDebugged(aFileName, aPassive);
}
/**
Helper function which reads a TThreadId object from a client
@param aMessage the message object containing the reference to the TThreadId
@param aIndex the message argument containing the reference
@return the TThreadId passed in by the client
@leave KErrArgument if aIndex is outside of the valid range
*/
TThreadId CSecuritySvrSession::ReadTThreadIdL(const RMessagePtr2& aMessage, const TInt aIndex) const
{
//create a temporary TThreadId to read the data into
TThreadId tempThreadId;
TPtr8 threadIdPtr((TUint8*)&tempThreadId, sizeof(TThreadId));
// read the data in from the client
aMessage.ReadL(aIndex, threadIdPtr);
return tempThreadId;
}
/**
Helper function which reads a TProcessId object from a client
@param aMessage the message object containing the reference to the TProcessId
@param aIndex the message argument containing the reference
@return the TProcessId passed in by the client
@leave KErrArgument if aIndex is outside of the valid range
*/
TProcessId CSecuritySvrSession::ReadTProcessIdL(const RMessagePtr2& aMessage, const TInt aIndex) const
{
//create a temporary TProcessId to read the data into
TProcessId tempProcessId;
TPtr8 processIdPtr((TUint8*)&tempProcessId, sizeof(TProcessId));
// read the data in from the client
aMessage.ReadL(aIndex, processIdPtr);
return tempProcessId;
}
// End of file - c_security_svr_session.cpp