ObReferenceObjectByHandle returns 0xC0000008

Hello,

My application calls:
HANDLE hSignal = CreateEvent(NULL, FALSE, FALSE, “[00] MG Signal”);
hSignal is sent by IOCTL to a WDF device driver.

The IOCTL handler in the driver calls:
*status = ObReferenceObjectByHandle(pMgSettingsRequest->hEvent,
SYNCHRONIZE | EVENT_MODIFY_STATE,
ExEventObjectType,
UserMode,
&devExt->hEvent,
NULL);

ObReferenceObjectByHandle is called from:
** BOOLEAN RequestDispatchToSequentialQueue(
__in WDFDEVICE Device,
__in WDFREQUEST Request,
__in WDF_REQUEST_PARAMETERS RequestParameters
)**
in order to make sure the call is from the right process.

But status returned is 0xC0000008

Can you please tell what is wrong in my code ?

Thank you,
Zvika

HANDLE hSignal = CreateEvent(NULL, FALSE, FALSE, “[00] MG Signal”);

IIRC, you cannot share named events with a driver. If you want to share the event with a driver, the name parameter passed to CreateEvent() has to be NULL

Anton Bassov

Hi Anton, All,

The CreateEvent with name worked well with an old WDM driver that I now have to rewrite in WDF.
Is it a WDF limitation (that I cannot use event name) ?

Thank you,
Zvika

In what callback is RequestDispatchToSequentialQueue running in? The WDFQUEUE contract does not guarantee that the presented request will be in the context of the calling process. To make sure you are in the caller’s context you must register an in caller context callback and refer the handle there, see https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdfdevice/nc-wdfdevice-evt_wdf_io_in_caller_context

Hi Doron, All,

The following code is based on the Plx9x5x sample for PCI card.
The IOCTL case that calls ObReferenceObjectByHandle is called from the application’s process context
(inside RequestDispatchToSequentialQueue)

NTSTATUS
PLxEvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
{

//
// Register the PnP Callbacks…
//
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);

// Register the EvtIoInCallerContext to deal with IOCTLs that need to stay in original context
WdfDeviceInitSetIoInCallerContextCallback(DeviceInit, DeviceEvtIoInCallerContext);


}

VOID DeviceEvtIoInCallerContext(__in WDFDEVICE Device, __in WDFREQUEST Request)
{
WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
WdfRequestGetParameters(Request, &requestParameters);

if (requestParameters.Type == WdfRequestTypeDeviceControl)
    {
             //Requests that should be processed only in the context of the app's process
            if (!processed)
	{
		processed = RequestDispatchToSequentialQueue(Device, Request, requestParameters);
	}
            if (!processed)
	{
		//Forwarding to default IOCTL
		status = WdfDeviceEnqueueRequest(Device, Request);
            }
   }

}

Thank you,
Zvika

The CreateEvent with name worked well with an old WDM driver

Please ignore my post then - it looks like I put a foot in my mouth yet another time…

Anton Bassov

Hello,

Based on the old WDM driver I changed the code to:

PIRP Irp = WdfRequestWdmGetIrp (Request);

status = ObReferenceObjectByHandle(pMgSettingsRequest->hEvent,
SYNCHRONIZE | EVENT_MODIFY_STATE,
*ExEventObjectType,
pIrp->RequestorMode,
&devExt->hEvent,
NULL);

But status is still: 0xC0000008

Thank you,
Zvika

Hi,

To make sure ObReferenceObjectByHandle is called from the right context:

Before calling to:
ObReferenceObjectByHandle
I called to:
KdPrint ((“Proc=0x%x\n”,PsGetCurrentProcessId()));

In the application, before calling to CreateEvent I called to:
GetCurrentProcessId ();

The return value of PsGetCurrentProcessId, GetCurrentProcessId was the same.

Thank you,
Zvika

To make sure ObReferenceObjectByHandle is called from the right context

Do you remember that in KMDF, unlike WDM, ioctls will likely become pending, and you won’t be called in the original process context.
Use EvtPreProcessCallback to handle the ioctl in caller context.

– pa

Perhaps you should show us the rest of the code leading up to the ObReference call.

Hi Pavel,

Can I use the routine : NonPnpEvtDeviceIoInCallerContext in ‘Windows-driver-samples\general\ioctl\kmdf\sys\nonpnp.c’ as a reference code ?

Hi Tim,

What is the right way to show all the code ?
Can I upload zip file that contains all source code ?

Thank you in advance,
Zvika

Yes, you need to use an InCallerContext Event Processing Callback. ALL the EvtIoxxxx Event Processing Callbacks are defined to run (a) at an IRQL <= DISPATCH_LEVEL, and (b) in an arbitrary process and thread context.

Peter

Hi Peter, Tim, and Pavel,

My driver is based on Plx9x5x driver.
I added the marked line in Pci9656.c

What is the right way to share my code here ?
Should I paste it or upload the files ?

Thank you,
Zvika

Paste always. Upload… never. Consider the problem with people uploading potentially virus infected files.

Peter

I would also make sure that the handle is valid and not damaged. What is the value of pMgSettingsRequest->hEvent field just before the call to ObReferenceObjectByHandle? What is the output of !handle pMgSettingsRequest->hEvent command?

I wasn’t really asking for the whole driver. I was asking to see the ioctl handler, leading up to the ObReference call.

Hello All,

Following MichaelK’s advice I checked the values of pMgSettingsRequest->arBoards[0].arGlinks[0…3].hEvent
Those values are identical to the ones in the application.

The first request is IOCTL_MG_GET_INFO which handled by the “regular” handler(not pasted). It returns a number to the application.
The second request is IOCTL_MG_INIT. It’s handler calls to ObReferenceObjectByHandle.

The Main handler is:


VOID DeviceEvtIoInCallerContext(__in WDFDEVICE Device, __in WDFREQUEST Request)
{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION devExt = NULL;
WDF_REQUEST_PARAMETERS requestParameters;
BOOLEAN processed = FALSE;

WdfRequestGetParameters(Request, &requestParameters);

devExt = PLxGetDeviceContext(Device);

// get the request parameters
WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
WdfRequestGetParameters(Request, &requestParameters);

if (requestParameters.Type == WdfRequestTypeDeviceControl)
{
	// 1. Requests that should be processed only in the context of the app's process
	if (!processed)
	{
		processed = RequestDispatchToSequentialQueue(Device, Request, requestParameters);
	}
	if (!processed)
	{
		//KdPrint(("Forwarding to default IOCTL\n"));
		status = WdfDeviceEnqueueRequest(Device, Request);
		if (!NT_SUCCESS(status))
		{
			KdPrint(("WdfDeviceEnqueueRequest failed\n"));
		}
	}
}

}


All requests first go to here:


BOOLEAN
RequestDispatchToSequentialQueue(
__in WDFDEVICE Device,
__in WDFREQUEST Request,
__in WDF_REQUEST_PARAMETERS RequestParameters
)

{
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_EXTENSION devExt = NULL;

size_t        bytesReturned = 0;

void *pInBuffer;
void *pOutBuffer;
int Channel;
size_t Length;

//-->Legacy
MG_SETTING *pMgSettingsRequest;
MG_DESCRIPTOR *pMgDescriptorReply;
//<--Legacy

ULONG	IoControlCode = RequestParameters.Parameters.DeviceIoControl.IoControlCode;
devExt = PLxGetDeviceContext(Device);

switch (IoControlCode)
{
case IOCTL_MG_INIT:
	status = WdfRequestRetrieveInputBuffer(Request, sizeof(MG_SETTING),
		&pInBuffer, &Length);
	pMgSettingsRequest = (MG_SETTING *)pInBuffer;

	status = WdfRequestRetrieveOutputBuffer(Request, sizeof(MG_DESCRIPTOR),
		&pOutBuffer, &Length);
	pMgDescriptorReply = (MG_DESCRIPTOR *)pOutBuffer;

	RtlFillMemory(devExt->KernelCommonBuffer[SHARED_BUFFER_ID], SHARED_BUFFER_SIZE, 0x0);

	//Handle request
	devExt->nFrequency = pMgSettingsRequest->arBoards[0].nFrequency;
	devExt->nEOMode = pMgSettingsRequest->arBoards[0].nEOMode;
	devExt->fSubModeEnable = pMgSettingsRequest->arBoards[0].fSubModeEnable;
	devExt->fEnhancedSimplex = pMgSettingsRequest->arBoards[0].fEnhancedSimplex;
	devExt->fGlinkErrorCheck = pMgSettingsRequest->arBoards[0].fGlinkErrorCheck;
	devExt->fBfcuEnable = pMgSettingsRequest->arBoards[0].fBfcuEnable;
	devExt->cbHeaderSize = pMgSettingsRequest->arBoards[0].cbHeaderSize;

	//Handle reply
	RtlZeroMemory(pMgDescriptorReply, sizeof(MG_DESCRIPTOR));

	pMgDescriptorReply->nDrvVersion = 1;
	pMgDescriptorReply->nNumOfBoards = 1;
	pMgDescriptorReply->arBoards[0].cbGlinkDataSize = DATA_BUFFER_SIZE;
	pMgDescriptorReply->arBoards[0].cbGlinkOffsetSize = HDR_BUFFER_SIZE;
	pMgDescriptorReply->arBoards[0].nBoardId = 0;
	pMgDescriptorReply->arBoards[0].nFPGAVersion = READ_REGISTER_ULONG((PVOID)((UINT64)devExt->Bar1Address + MODULE_NAME_OFFSET));

	pMgDescriptorReply->arBoards[0].pInfo = MmMapLockedPagesSpecifyCache(devExt->CommonBufferMdl[SHARED_BUFFER_ID],
											UserMode,
											MmCached,
											NULL,
											FALSE,
											NormalPagePriority);

	for (Channel = 0; Channel < N_CHANNELS; Channel++)
	{
		pMgDescriptorReply->arBoards[0].arGlinks[Channel].pData = MmMapLockedPagesSpecifyCache(devExt->CommonBufferMdl[Channel],
																								UserMode,
																								MmCached,
																								NULL,
																								FALSE,
																								NormalPagePriority);

		pMgDescriptorReply->arBoards[0].arGlinks[Channel].pOffset = (char*)pMgDescriptorReply->arBoards[0].arGlinks[Channel].pData + DATA_BUFFER_SIZE;

		// Event
		if (devExt->hEvent[Channel])
		{
			ObDereferenceObject(devExt->hEvent[Channel]);

			devExt->hEvent[Channel] = NULL;
		}

		status = ObReferenceObjectByHandle(pMgSettingsRequest->arBoards[0].arGlinks[Channel].hEvent, 
											SYNCHRONIZE | EVENT_MODIFY_STATE, 
											*ExEventObjectType, 
											UserMode, 
											&devExt->hEvent[Channel], 
											NULL);

		if (!NT_SUCCESS(status))
		{
			KdPrint(("ObReferenceObjectByHandle to event %d failed. status=0x%x\n", Channel, status));
		}
	}

	bytesReturned = sizeof(MG_DESCRIPTOR);
	WdfRequestCompleteWithInformation(Request, STATUS_SUCCESS, bytesReturned);
	break;
default:
	return FALSE; //The IOCTL code will be handled in the default IoControl handler
}

return TRUE; //This will mark the request as processed

}


Your help is highly appreciable.

Best regards,
Zvika

OK, seeing your code, I have a pretty good guess. My guess is that IOCTL_MG_INIT is a METHOD_BUFFERED ioctl. In a METHOD_BUFFERED ioctl, the input and the output buffers are the SAME PIECE OF MEMORY. When you zero out and then fill in pMgDescriptorReply, you are overwriting the stuff that came in in pMgDescriptorRequest, so the hEvent contains garbage.

The easy fix, of course, is to save the hEvent in a local before you start trashing the buffer. Did you even consider printing the hEvent value immediately before you used it?

Hi Tim,

You are right !
Tried it on the Echo sample driver (Don’t have the PCI card at home)

Thank you very much,
Zvika