Why dynamically allocated buffer in user program makes kernel driver crash? - c++

I have a program that allocates a buffer whose pointer is passed to a kernel driver through a custom IOCTL. In the driver I get an Mdl and lock the pages of the user-program buffer with "MmGetSystemAddressForMdlSafe" and then use the Mdl to fill the user program buffer.
If in the user program the buffer was a normal array, the driver always works as it should.
(WORD buffer[256], where word is an unsigned short)
If the user program buffer was instead allocated with the new keyword (WORD *buffer = new WORD[256]) or the malloc keyword (WORD *buffer=(WORD*) malloc(sizeof(*buffer)*256))) from time to time I get a BSOD and the error is "page fault in non paged area".
WHY?
Thanks!
EDIT(additional details):
In the driver I use MmGetSystemAddressForMdlSafe this way:
PVOID p_buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority);
Irp is a PIRP that I receive as the second parameter when I handle the IRP_MJ_DEVICE_CONTROL MajorFunction.
After I've checked that p_buffer is not null, I use that pointer to write user buffer:
READ_PORT_BUFFER_USHORT((PUSHORT)(USHORT)current_port.address, (PUSHORT)p_buffer, 256)
IOCTL definition:
#define IOCTL_TEST_READPORT CTL_CODE(FILE_DEVICE_TEST, \
TEST_IOCTL_INDEX + 0, \
METHOD_OUT_DIRECT, \
FILE_ANY_ACCESS)
Driver function that handles IRP_MJ_DEVICE_CONTROL:
NTSTATUS TESTDispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
PIO_STACK_LOCATION IrpStack;
ULONG input_buffer_size;
ULONG output_buffer_size;
ULONG control_code;
PVOID p_buffer;
NTSTATUS nt_status;
struct port current_port;
UNREFERENCED_PARAMETER(DeviceObject);
PAGED_CODE();
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IrpStack = IoGetCurrentIrpStackLocation(Irp);
switch (IrpStack->MajorFunction)
{
case IRP_MJ_DEVICE_CONTROL:
control_code = IrpStack->Parameters.DeviceIoControl.IoControlCode;
switch (control_code)
{
case IOCTL_TEST_READPORT:
p_buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, HighPagePriority);
input_buffer_size = IrpStack->Parameters.DeviceIoControl.InputBufferLength;
if (!p_buffer)
{
nt_status = STATUS_INSUFFICIENT_RESOURCES;
break;
}
if (input_buffer_size)
{
memcpy (&current_port, Irp->AssociatedIrp.SystemBuffer, input_buffer_size);
switch (current_port.size)
{
case 1:
current_port.value = (ULONG)READ_PORT_UCHAR((PUCHAR)(USHORT)current_port.address);
memcpy (p_buffer, &current_port.value, sizeof(current_port.value));
Irp->IoStatus.Information = sizeof(current_port.value);
break;
case 0xF0:
READ_PORT_BUFFER_USHORT((PUSHORT)(USHORT)current_port.address, (PUSHORT)p_buffer, 256);
Irp->IoStatus.Information = sizeof(current_port.value);
break;
case 2:
current_port.value = (ULONG)READ_PORT_USHORT((PUSHORT)(USHORT)current_port.address);
memcpy (p_buffer, &current_port.value, sizeof(current_port.value));
Irp->IoStatus.Information = sizeof(current_port.value);
break;
}
}
else
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
case IRP_MJ_CREATE:
KdPrint(("IRP_MJ_CREATE"));
break;
case IRP_MJ_CLOSE:
KdPrint(("IRP_MJ_CLOSE"));
break;
default:
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
break;
}
break;
}
nt_status = Irp->IoStatus.Status;
IoCompleteRequest (Irp, IO_NO_INCREMENT);
return nt_status;
}
The relevant case is case 0xF0: inside case IOCTL_TEST_READPORT:

From my understanding you are misunderstanding the purpose of MmGetSystemAddressForMdlSafe. Per this document here, you use this function to get virtual addresses that are described by a MDL (Memory Descriptor List).
. If the driver must use virtual addresses to access the pages described by the MDL, it must map those pages into the system address space using MmGetSystemAddressForMdlSafe
that same document also says this:
To use virtual addresses to access the buffer described by the MDL, the driver calls MmGetSystemAddressForMdlSafe to map the buffer into system space.
MmGetSystemAddressForMdlSafe:
Maps the physical pages described by an MDL into system space and returns a virtual address for the MDL. The returned virtual address can be used at any IRQL and in any process context.
If you look at the MSDN documentation of MmGetSystemAddressForMdlSafe then you will see this following line:
The MmGetSystemAddressForMdlSafe macro returns a nonpaged system-space virtual address for the buffer that the specified MDL describes.
It says that this functions returns a non-paged virtual address for a buffer that is described by an MDL.
The definition of MDL is the following:
A memory descriptor list (MDL) describes a list of pages in physical memory.
This is a description of pages in physical memory, not virtual memory. Your buffer allocated by new will have virtual addresses already, trying to use MmGetSystemAddressForMdlSafe on it, is wrong. You should use that function to get a virtual address from an MDL, not a MDL from a virtual address range.
Now, moving on to an explanation for the page fault in non-paged area:
Now if you think about it, it is likely that your buffer allocated by new or malloc is already in a paged memory area (in fact, seeing that it's in user land, it's extremely likely), meaning that trying to get a virtual address to this buffer (which is already wrong because it's not an MDL), will cause a page fault in non-paged area, because the memory of the buffer is in a paged area, whereas you're mapping it to a non-paged area in the kernel, and non-paged areas can not cause a page fault. (most likely will have to do with wrong IRQL levels)

Related

WinAPI's VirtualQueryEx Shows an Inaccurate Amount of Memory Used by a Process

I have the following function, written in C++ using WinAPI:
The function receives a handle to a process, and should return the size of the memory used by the process.
DWORD GetProcessCommitedMemorySize(const HANDLE hProcess)
{
MEMORY_BASIC_INFORMATION mbi;
DWORD totalMemory = 0;
DWORD currAddr = 0;
//for checking repetion of pages base addresses
std::unordered_set<DWORD> tempAddresses;
while (VirtualQueryEx(hProcess, (LPVOID)currAddr, &mbi, (SIZE_T)sizeof(mbi)) == (SIZE_T)sizeof(mbi))
{
if (tempAddresses.find((DWORD)mbi.BaseAddress) == tempAddresses.end()) // not found
{
currAddr = (DWORD)mbi.BaseAddress + mbi.RegionSize;
tempAddresses.insert((DWORD)mbi.BaseAddress);
if (mbi.State == MEM_COMMIT && mbi.Type == MEM_PRIVATE)
{
totalMemory += mbi.RegionSize;
}
}
else
{
break;
}
}
return totalMemory;
}
The function always returns a number which is bigger than the amount of memory which is shown to be used on the 'details' page of the Task Manager.
for example, 86155 KB as the function's output, and 56924 KB on the Task Manager.
The program still isn't complete so I need to hard-code the PID, so I know I'm looking at the right one.
Does this issue occur because the number I'm seeking with this function and the number on the Task Manager have different meanings, or is my code just not doing what's it supposed to do?
Thanks.

reasons MmMapLockedPagesSpecifyCache throwing exception

I am a total newbie windows drivers so there may be obvious mistakes in the code.
My code allocates user space dma buffer which work fine accept when a lot of
buffers are allocated (not necessarily large buffers) on that case
MmMapLockedPagesSpecifyCache throws an exception.
The code stages that fails is as followed:
1. Use MmMapIoSpace to map physical address to kernel mode virtual address,
driver can access this virtual address, but its not accessible in user mode.
2. Use IoAllocateMdl and MmBuildMdlForNonPagedPool to build an MDL for the
mapped physical address.
3. Use MmMapLockedPagesSpecifyCache to map the physical pages described by MDL
to user mode virtual address.
Since our driver will always be the topmost driver, and run in the context of
the current process, this user mode virtual address is valid to the caller.
code snippet:
phy_buf->kernel_addr = MmMapIoSpace(phy_buf->phy_addr, phy_buf->size, MmNonCached);
phy_buf->user_mdl = IoAllocateMdl(
phy_buf->kernel_addr,
phy_buf->size,
FALSE,
FALSE,
NULL);
if(phy_buf->user_mdl)
{
MmBuildMdlForNonPagedPool(phy_buf->user_mdl);
__try{
phy_buf->user_addr = MmMapLockedPagesSpecifyCache(
phy_buf->user_mdl,
UserMode,
MmNonCached,
NULL,
FALSE,
NormalPagePriority) ;
}__except(EXCEPTION_EXECUTE_HANDLER) {
IoFreeMdl(phy_buf->user_mdl);
phy_buf->user_mdl = NULL;
DbgMessage(FATAL, "MmMapLockedPagesSpecifyCache failed on EXCEPTION_EXECUTE_HANDLER\n");
return NULL;
}
if (phy_buf->user_addr == NULL)
{
IoFreeMdl(phy_buf->user_mdl);
phy_buf->user_mdl = NULL;
DbgMessage(FATAL, "MmMapLockedPagesSpecifyCache failed\n");
}
Also how can I get (or print) exact exception that is thrown.

Shared Memory Between User Mode and Kernel Mode

I am writing some kernel side code for Windows7 to access shared memory created in user mode, as suggested here.
The shared memory is created in user space with name:
"MySharedMem"
Opening the shared memory in user space works.
Opening the same shared memory in kernel mode calling ZwOpenSection fails returning:
#define STATUS_OBJECT_NAME_NOT_FOUND ((NTSTATUS)0xC0000034L)
The kernel code is:
NTSTATUS CModule1::OpenShared()
{
SIZE_T vs = 256;
WCHAR stringBuffer[] = L"\\BaseNamedObjects\\MySharedMem";
UNICODE_STRING sectionName;
RtlInitUnicodeString(&sectionName,stringBuffer);
OBJECT_ATTRIBUTES myAttributes;
InitializeObjectAttributes(&myAttributes,&sectionName,0,NULL,NULL);
NTSTATUS status0 = ZwOpenSection(&sectionHandle_,SECTION_MAP_READ|SECTION_MAP_WRITE,&myAttributes);
NTSTATUS status = ZwMapViewOfSection(&sectionHandle_, ZwCurrentProcess(), (PVOID *)&pSharedData_, 0, 0, NULL, &vs, ViewShare, 0, PAGE_READWRITE);
return status;
}
I tried several names (L"\\MySharedMem" or L"MySharedMem") but I got other errors as STATUS_OBJECT_PATH_INVALID or STATUS_OBJECT_PATH_NOT_FOUND. Also creating the shared memory as "Global\\MySharedMem" does not work.What am I doing wrong?
I tried to create the shared memory in kernel mode, I get success on ZwCreateSection and ZwMapViewOfSection but i get access violation when I access the pSharedData_ pointer to test the buffer:
NTSTATUS CModule1::MapUserSection()
{
typedef struct SHARED_SECTION {DWORD i; };
NTSTATUS status = STATUS_SUCCESS;
ULONG Attributes=OBJ_KERNEL_HANDLE | OBJ_FORCE_ACCESS_CHECK;
OBJECT_ATTRIBUTES objectAttributes;
LARGE_INTEGER MaxSize;
SIZE_T ViewSize=sizeof(SHARED_SECTION);
MaxSize.QuadPart=sizeof(SHARED_SECTION);
WCHAR stringBuffer[] = L"\\MySm2";
UNICODE_STRING sectionName;
RtlInitUnicodeString(&sectionName,stringBuffer);
InitializeObjectAttributes(&objectAttributes,&sectionName,Attributes,NULL,NULL);
status= ZwCreateSection(&sectionHandle_,SECTION_ALL_ACCESS,&objectAttributes,&MaxSize,PAGE_READWRITE,SEC_COMMIT,NULL);
status = ZwMapViewOfSection(sectionHandle_, ZwCurrentProcess(), (PVOID *)&pSharedData_, 0, 0, NULL, &ViewSize, ViewShare, 0, PAGE_READWRITE);
//To test the buffer
RtlFillMemory(pSharedData_, '1',ViewSize);
return status;
}
Everything fails...
Concerning CreateFileMapping:
Creating a file mapping object in the global namespace from a session other than session zero requires the SeCreateGlobalPrivilege privilege.
From KB191840:
[T]he object is always mapped in the user address space (below 0x80000000) of a process (regardless of whether the object is created in kernel mode or user mode) the address is valid only if it is accessed in the context of the process.
The KB continues:
This method is not recommended and is used least by low-level device drivers because, as explained earlier, the scope of the address is limited to the process in which the object is mapped, and it cannot be accessed in a DPC or ISR. [Emphasis Mine]
The fix is either:
Create the file mapping in kernel mode. (Suggested by the KB article.)
Use IOCTL

CreateFileMapping returns ERROR_INVALID_HANDLE

I am trying to use CreateFileMapping for the first time and it is giving me this error when I use GetLastError():
ERROR_INVALID_HANDLE: The handle is invalid.
Here is my code:
// create the name of our file-mapping object
nTry++; // Ensures a unique string is used in case user closes and reopens
wsprintfA(szName, FS6IPC_MSGNAME1 ":%X:%X", GetCurrentProcessId(), nTry);
// stuff the name into a global atom
m_atom = GlobalAddAtomA(szName);
if (m_atom == 0)
{ *pdwResult = ERR_ATOM;
return FALSE;
}
// create the file-mapping object
m_hMap = CreateFileMappingA(
(HANDLE)0xFFFFFFFF, // use system paging file
NULL, // security
PAGE_READWRITE, // protection
0, MAX_SIZE+256, // size
szName); //
EDIT:
The first issue was resolved, but now my program crashes somewhere else.
#define FS6IPC_MESSAGE_SUCCESS 1
#define FS6IPC_MESSAGE_FAILURE 0
// IPC message types
#define FS6IPC_READSTATEDATA_ID 1
#define FS6IPC_WRITESTATEDATA_ID 2
// read request structure
typedef struct tagFS6IPC_READSTATEDATA_HDR
{
DWORD dwId; // FS6IPC_READSTATEDATA_ID
DWORD dwOffset; // state table offset
DWORD nBytes; // number of bytes of state data to read
void* pDest; // destination buffer for data (client use only)
} FS6IPC_READSTATEDATA_HDR;
// write request structure
typedef struct tagFS6IPC_WRITESTATEDATA_HDR
{
DWORD dwId; // FS6IPC_WRITESTATEDATA_ID
DWORD dwOffset; // state table offset
DWORD nBytes; // number of bytes of state data to write
} FS6IPC_WRITESTATEDATA_HDR;
while (*pdw)
{ switch (*pdw)
{ case FS6IPC_READSTATEDATA_ID:
pHdrR = (FS6IPC_READSTATEDATA_HDR *) pdw;
m_pNext += sizeof(FS6IPC_READSTATEDATA_HDR);
if (pHdrR->pDest && pHdrR->nBytes)
CopyMemory(pHdrR->pDest, m_pNext, pHdrR->nBytes);
m_pNext += pHdrR->nBytes; // Debugger says the issue is here
break;
case FS6IPC_WRITESTATEDATA_ID:
// This is a write, so there's no returned data to store
pHdrW = (FS6IPC_WRITESTATEDATA_HDR *) pdw;
m_pNext += sizeof(FS6IPC_WRITESTATEDATA_HDR) + pHdrW->nBytes;
break;
default:
// Error! So terminate the scan
*pdw = 0;
break;
}
pdw = (DWORD *) m_pNext;
}
I'm guessing you're running on a 64-bit system, on which HANDLEs are 64 bits. The OS is quite right—the handle value 0x00000000FFFFFFFF is an invalid handle value for your process.
What exactly are you trying to do? If you want to create a file mapping backed by an actual file, pass in the handle for that file. If you want to a create a file mapping backed by the paging file instead, pass in INVALID_HANDLE_VALUE. INVALID_HANDLE_VALUE happens to be (HANDLE)-1, which is 0xFFFFFFFF on 32-bit systems but 0xFFFFFFFFFFFFFFFF on 64-bit systems, but that doesn't really matter since you should just use the symbolic value INVALID_HANDLE_VALUE in any case.
If your application is crashing when you pass in INVALID_HANDLE_VALUE, it's not because the call to CreateFileMapping is failing, it's for some other reason, and you should debug that.

Driver on 64 bit

I have a driver code which works good on 32 bit. On 64 bit i compiled it and also digitally signed it. The driver loads but fails to work properly. The main functionality of the driver to to register process creation and termination to my program in call back. So i have two IOCTL working. The code is as follows..
NTSTATUS DispatchIoctl(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
NTSTATUS ntStatus = STATUS_UNSUCCESSFUL;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
PPROCESS_CALLBACK_INFO pProcCallbackInfo;
//
// These IOCTL handlers are the set and get interfaces between
// the driver and the user mode app
//
switch(irpStack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_PROCOBSRV_ACTIVATE_MONITORING:
{
ntStatus = ActivateMonitoringHanlder( Irp );
break;
}
case IOCTL_PROCOBSRV_GET_PROCINFO:
{
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(PROCESS_CALLBACK_INFO))
{
pProcCallbackInfo = Irp->AssociatedIrp.SystemBuffer;
pProcCallbackInfo->hParentId = extension->hParentId;
pProcCallbackInfo->hProcessId = extension->hProcessId;
pProcCallbackInfo->bCreate = extension->bCreate;
ntStatus = STATUS_SUCCESS;
}
break;
}
default:
break;
}
Irp->IoStatus.Status = ntStatus;
//
// Set number of bytes to copy back to user-mode
//
if(ntStatus == STATUS_SUCCESS)
Irp->IoStatus.Information = irpStack->Parameters.DeviceIoControl.OutputBufferLength;
else
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
when i call
bReturnCode = ::DeviceIoControl(
hDriverFile,
IOCTL_PROCOBSRV_ACTIVATE_MONITORING,
&activateInfo,
sizeof(activateInfo),
NULL,
0,
&dwBytesReturned,
NULL
);
The code succeeds but when i call
bReturnCode = ::DeviceIoControl(
m_hDriverFile,
IOCTL_PROCOBSRV_GET_PROCINFO,
0,
0,
&callbackInfo, sizeof(callbackInfo),
&dwBytesReturned,
&ov
);
getLastError returns 31. Can anyone help me with this. Is it a problem of IOCTL structure with 64 bit? Please help me find a solution to this. Thanks..
If you don't have driver debugging experience, try to diagnose this problem using trace. Add KdPrint lines to your code in all places where it is necessary, for example:
case IOCTL_PROCOBSRV_GET_PROCINFO:
{
KdPrint(("IOCTL_PROCOBSRV_GET_PROCINFO. OutputBufferLength = %d\n", irpStack->Parameters.DeviceIoControl.OutputBufferLength));
KdPrint(("sizeof(PROCESS_CALLBACK_INFO) = %d\n", sizeof(PROCESS_CALLBACK_INFO)));
if (irpStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(PROCESS_CALLBACK_INFO))
{
pProcCallbackInfo = Irp->AssociatedIrp.SystemBuffer;
pProcCallbackInfo->hParentId = extension->hParentId;
pProcCallbackInfo->hProcessId = extension->hProcessId;
pProcCallbackInfo->bCreate = extension->bCreate;
KdPrint(("IOCTL_PROCOBSRV_GET_PROCINFO. STATUS_SUCCESS\n"));
ntStatus = STATUS_SUCCESS;
}
break;
}
This is just a sample, add KdPrint lines to all places to understand what happens. Build the driver in checked configuration, install it and run your program. See KdPrint output in DbgView program, with Caprure Kernel option enabled.
You can use DbgPrint instead of KdPrint, in this case it will work also in free driver configuration.
Edit:
How is PROCESS_CALLBACK_INFO defined? What is client code that calls the driver? Is client compiled as 32 or 64 bit? Passing a structure between client and driver, ensure that it doesn't contain bitness-dependent fields (has the same size when compiled both in 32 and 64 bit), and structure padding is the same.
Not too much information here, but you could always check that the size of your own defined structures are the same in the compiled user-mode client and the driver when running 64-bit. There might be packing/alignment issues that can be fixed by using #pragma pack (or whatever your compiler supports) for your structs.
As a general rule also try to set the status code to a more specific value on detected problems, such as e.g. STATUS_BUFFER_TOO_SMALL if you detect that a passed buffer is too small. I suspect that this isn't the actual issue here when using the IOCTL_PROCOBSRV_GET_PROCINFO IOCTL as you are getting back Win32 error 31, but it helps clients to troubleshoot their problems in general.
Update: As the differences actually seem to mismatch judging from your comments, try to surround the struct definitions with packing and then make sure to recompile both the client and driver. Example if you are using the Visual C++ compiler:
#pragma pack(push, 8) // Save current packing and set to 8-byte
typedef struct _PROCESS_CALLBACK_INFO
{
// Whatever ...
} PROCESS_CALLBACK_INFO;
#pragma pack(pop) // Restore previous packing
I found the answer. Thanks to the debugging. As mention earlier the outputBufferLength was less than the structure length due to which the driver was failing.
The outputBufferLength depands on the size of the struct you pass while calling the following function..
bReturnCode = ::DeviceIoControl(
m_hDriverFile,
IOCTL_PROCOBSRV_GET_PROCINFO,
0,
0,
&callbackInfo, sizeof(callbackInfo),
&dwBytesReturned,
&ov
);
So callbackInfo size was 12 so the outputBufferLength = 12. The structure callbackInfo has DWORD datamember which for 64 bit had to be DWORD64. When i changed the datatype of the member in the structure and then called the DeviceIOControl function , the driver worked great and the outputBufferLength was = 24 as expected. Thanks for all your help.