wdf remote target ioctl status success. But not expected data in the output buffer

I am trying to create & send IOCTL request to remote target synchronously. I am getting the status as success but the output buffer is empty.

Please find the attached code that I am using for the same. I have been struck with this issues for last 3-4 days and I dont know what I am missing. Any help or direction to debug would be great.

Notes:

  1. I am opening the target in PnP callback routine
  2. I am new to Windows driver development

##Opening the target

    devExt = GetDeviceExtension(Device);
	DbgPrint("Opened %wZ\n", devExt->symbolicName);

	WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, TARGET_DEVICE_INFO);
	status = WdfIoTargetCreate(devExt->WdfDevice, &attributes, &ioTarget);
	if (!NT_SUCCESS(status)) {
		DbgPrint("WdfIoTargetCreate failed 0x%x\n", status);
		return status;
	}

	targetDeviceInfo = GetTargetDeviceInfo(ioTarget);
	targetDeviceInfo->DeviceExtension = devExt;

	WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME( &openParams,
		SymbolicLink,
		FILE_WRITE_ACCESS | FILE_READ_ACCESS);

	openParams.ShareAccess = FILE_SHARE_WRITE | FILE_SHARE_READ;
	openParams.EvtIoTargetQueryRemove = EvtIoTargetQueryRemove;
	openParams.EvtIoTargetRemoveCanceled = EvtIoTargetRemoveCanceled;
	openParams.EvtIoTargetRemoveComplete = EvtIoTargetRemoveComplete;

	status = WdfIoTargetOpen(ioTarget, &openParams);
	if (!NT_SUCCESS(status)) {
		WdfObjectDelete(ioTarget);
		DbgPrint("SST Target open failed\n");
		return status;
	}

	*Target = ioTarget;
	devExt->TargetDevice = ioTarget;
	DbgPrint("Target Device 0x%p, PDO 0x%p, Fileobject 0x%p, Filehandle 0x%p\n",
		WdfIoTargetWdmGetTargetDeviceObject(ioTarget),
		WdfIoTargetWdmGetTargetPhysicalDevice(ioTarget),
		WdfIoTargetWdmGetTargetFileObject(ioTarget),
	WdfIoTargetWdmGetTargetFileHandle(ioTarget));

    WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = ioTarget;

status = WdfRequestCreate(&attributes,
	ioTarget,
	&targetDeviceInfo->IOCTLRequest);

if (!NT_SUCCESS(status)) {
	WdfObjectDelete(ioTarget);
	return status;
}

Sending the request

   WDF_OBJECT_ATTRIBUTES attributes;
   TARGET_DEVICE_INFO *targetInfo = GetTargetDeviceInfo(devExt->TargetDevice);
   WDFMEMORY ipMemory, opMemory;
   PVOID ipAddr, opAddr;
   WDF_MEMORY_DESCRIPTOR ipMemDescp, opMemDescp;
   status = WdfMemoryCreate(&attributes, NonPagedPoolNx, '4LeM',
	sizeof(hdr) + sizeof(hsk), &ipMemory, &ipAddr);
status = WdfMemoryCreate(&attributes, NonPagedPoolNx, '5LeM',
	sizeof(hdr) + sizeof(hsk), &opMemory, &opAddr);

status |= WdfMemoryCopyFromBuffer(ipMemory, 0, &hdr, sizeof(hdr)); //Filling the input data
status |= WdfMemoryCopyFromBuffer(ipMemory, sizeof(hdr), &hsk, sizeof(hsk));//Filling the input data
if (!NT_SUCCESS(status)) {
	DbgPrint("Mem copy failed\n");
	WdfObjectDelete(ipMemory);
	WdfObjectDelete(opMemory);
	return status;
}
WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&ipMemDescp, ipMemory, 0);
WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&opMemDescp, opMemory, 0);
WDF_REQUEST_SEND_OPTIONS options;
WDF_REQUEST_SEND_OPTIONS_INIT(&options, WDF_REQUEST_SEND_OPTION_SYNCHRONOUS);
WDF_REQUEST_SEND_OPTIONS_SET_TIMEOUT(&options, WDF_REL_TIMEOUT_IN_MS(1000));

status = WdfIoTargetFormatRequestForIoctl(devExt->TargetDevice, targetInfo->IOCTLRequest, IOCTL_CODE, ipMemory,
	NULL, opMemory, NULL);
DbgPrint("Format status 0x%x\n", status);
if (NT_SUCCESS(status)) {
	WdfRequestSetCompletionRoutine(targetInfo->IOCTLRequest,
		IoctlRequestCompletionRoutine,
		targetInfo);
	BOOLEAN ret = WdfRequestSend(targetInfo->IOCTLRequest, devExt->TargetDevice,
		&options);
	if (!ret) {
		status = WdfRequestGetStatus(targetInfo->IOCTLRequest);
		DbgPrint("Request status 0x%x\n", status);
	}
}

##Completion Routine

    targetInfo = GetTargetDeviceInfo(Target);
status = WdfRequestGetStatus(Request);
DbgPrint("Get Request status 0x%x\n", status);
DbgPrint("Ioctl buffer 0x%p\n", CompletionParams->Parameters.Ioctl.Output.Buffer);
DbgPrint("Control code 0x%x\n", CompletionParams->Parameters.Ioctl.IoControlCode);
DbgPrint("Ioctl buffer length 0x%d\n", CompletionParams->Parameters.Ioctl.Output.Length);

Are you sending this to a driver that you created? How did you create the IOCTL_CODE? One potential cause is that you didn’t realize that ioctl code numbers have fields that define how the memory is transferred.

I am send it to a OEM driver. Not written by me. Here’s the code.

#define IOCTL_CODE CTL_CODE( 0x8000, 0x123, METHOD_BUFFERED, (FILE_READ_ACCESS | FILE_WRITE_ACCESS))

I think you forgot to associate the IOCTL_request with your WDF memory object before formatting the IOCTL. Before calling WdfMemoryCreate(), try attributes.ParentObject = targetInfo->IOCTLRequest.

Loosely the process is:

  • Open the IoTarget
  • Create a WDF request to be sent to the IoTarget
  • Stuff the request in a WDF memory object
  • Format the request for IOCTL to send to the target
  • Optionally set additional request options (synch, timeout, etc.)
  • Send the request

When you say “the output buffer is empty”, do you mean the output buffer pointer is null, or do you mean the buffer contains all zeros? The framework stores the output buffer pointer in the UserBuffer field in the IRP. If your lower-level driver messes with that field, it would cause problems. What driver are you calling, exactly?

Was the code you posted an exact cut-and-paste from your code? You never called WDF_OBJECT_ATTRIBUTES_INIT on your “attributes” structure. And if you don’t need any, you can just pass WDF_NO_OBJECT_ATTRIBUTES.

@Tim_Roberts , I have this piece of code before WDFMemoryCreate

WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = targetInfo->IOCTLRequest;

By Output buffer empty I mean that It contains Junk data (sometimes Zeros) instead of expected data. I am calling a OEM driver for which I dont have source code and have only API documentation. And as per documentation, I am supposed to get some data when I make that IOCTL call which I am not getting.

One doubt, Is there anything like that can allow only certain device can talk to the remote target?

It contains Junk data (sometimes Zeros) instead of expected data.

But a non-zero value for the Information field? Do you check that?

BTW… that’s some pretty ugly code. Do you really do all those functions, including calling WdfMemoryCreate without checking the return status?

Is there anything like that can allow only certain device can talk to the remote target?

Well, sure. You can always apply an ACL to the device. But, if this were the case, the WdfTargetOpen would fail, not the I/O.

Peter

@“Peter_Viscarola_(OSR)” , Sorry about the coding and I will add check at each step. Though I did check the status using WinDBG. I have checked the information field it’s Zero. What else might go wrong?

Thanks for the information about ACL.

I have one more question. I am supposed to get IOCTL call to my driver from the target when I make this IOCTL call to the target. But I am not getting it. Is there any tool which I can look at all the IOCTL calls made to my driver and call made by my driver so that I can verify both the sides?

I’m not closely following this thread, but I believe your previous message said you were using buffered I/O IOCTl’s. From the called driver, are you setting he information field to the number of bytes you want copied back to the caller? You said the information field is zero, which would mean you want zero bytes copied back to the caller.

Jan

From the called driver, are you setting he information field

An awesome, and a common, bug. But the driver he’s calling isn’t his. Unfortunately.

have checked the information field it’s Zero

So… yeah. That means you’re getting back zero bytes in response to your IOCTL.

I am supposed to get IOCTL call to my driver from the target

Is the request succeeding? What does WdfRequestSend return? What’s the returned status in the IOSB? Is your completion routine being called? Please try to provide us with a clear and complete picture of what’s happening. Rememebr, we’re not in your office with you, looking over your shoulder (though it sure would make things easier)!

Peter

@“Peter_Viscarola_(OSR)”
I checked with DriverMon & IRPTrace tool and I can see that the request is being received by the Target device.

Request is getting succeeded. status is 0x00

Completion routine is being called. Please find the debug log (Print value is present after “//”) in completion routine.

targetInfo = GetTargetDeviceInfo(Target);
status = WdfRequestGetStatus(Request);
DbgPrint("Get Request status 0x%x\n", status); //0x00
DbgPrint("Completion info 0x%x\n", CompletionParams->IoStatus.Information);//0x00
DbgPrint("Ioctl buffer 0x%p\n", CompletionParams->Parameters.Ioctl.Output.Buffer);//0x0000197623705BA8
DbgPrint("Control code 0x%x\n", CompletionParams->Parameters.Ioctl.IoControlCode);//IOCTL_CODE passed during request
DbgPrint("Ioctl buffer length 0x%d\n", CompletionParams->Parameters.Ioctl.Output.Length);//0x0

PS: Sorry for the delayed response. Was down with fever.

Gunasekaran wrote:

I checked with DriverMon & IRPTrace tool and I can see that the request is being received by the Target device.

Request is getting succeeded. status is 0x00

Completion routine is being called. Please find the debug log (Print value is present after “//”) in completion routine.

DbgPrint(“Ioctl buffer length 0x%d\n”, CompletionParams->Parameters.Ioctl.Output.Length);//0x0

It’s suspicious that the Output.Length is 0.  That should still be set
to the size of your output buffer.  Forgive the silly question, but are
you quite sure this ioctl is supposed to have both input data and output
data?  Is this a real hardware device (what kind?), or is this a
software device?

@Tim_Roberts. I am sure because when I send the IOCTL with output buffer as NULL or empty, I am getting INVALID_PARAMETER error. It’s a Software driver.