The code to init IRP and URB are given below. I am wondering if OpenHCI will
handle data buffers bigger than the max packet size on the pipes. It appears
that it also does not like the buffer (512 bytes) I pass to the interrupt
pipe (with max packet size of 8 bytes). The usbport.sys, which my driver
calls on XP, handles bigger buffers just fine.
Now MS document on UsbBuildInterruptOrBulkTransferRequest does not put size
limit on the data buffer used. However, I am also highly suspicious that
this bug happens under special circustance--a bug like this would have been
spotted long time ago.
// routine to init and send URB
// Note: the call to read the response failed with
STATUS_INVALID_PARAMETER--the input buffer size (
// pReply->BufferSize is 0x120 (while my max packet size for the input pipe
is 64 bytes).
NTSTATUS
PcscSendSmartcardCommand(
PSMARTCARD_EXTENSION SmartcardExtension)
{
NTSTATUS status = STATUS_SUCCESS;
PSMARTCARD_REPLY pReply = &SmartcardExtension->SmartcardReply;
PSMARTCARD_REQUEST pRequest = &SmartcardExtension->SmartcardRequest;
PDEVICE_OBJECT fdo =
(PDEVICE_OBJECT)SmartcardExtension->OsData->DeviceObject;
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;
#ifdef DEBUG
SmartcardDebug(DEBUG_INFO, ("%s!PcscSendSmartcardCommand: cmd = ",
DRIVERNAME));
for (ULONG j = 0; j < pRequest->BufferLength; j++) {
SmartcardDebug(DEBUG_INFO, ("/x%02X", pRequest->Buffer[j]));
}
SmartcardDebug(DEBUG_INFO, ("\n"));
#endif
URB urb;
// init URB to send command via bulk out pipe
UsbBuildInterruptOrBulkTransferRequest(
&urb, // URB to build
sizeof(_URB_BULK_OR_INTERRUPT_TRANSFER), //
pdx->houtpipe, // pipe handle
pRequest->Buffer, // transfer buffer
NULL, // MDL
pRequest->BufferLength, // transfer buffer length
USBD_TRANSFER_DIRECTION_OUT, // transfer flags
NULL); //
// Send URB
status = SendAwaitUrb(fdo, &urb);
if (status == STATUS_SUCCESS) {
// Read response from bulk in pipe
// init URB to read response from bulk in pipe
UsbBuildInterruptOrBulkTransferRequest(
&urb, // URB to build
sizeof(_URB_BULK_OR_INTERRUPT_TRANSFER), //
pdx->hinpipe, // pipe handle
pReply->Buffer, // transfer buffer
NULL, // MDL
pReply->BufferSize, // transfer buffer length
USBD_TRANSFER_DIRECTION_IN
| USBD_SHORT_TRANSFER_OK, // transfer flags
NULL);
//
// Send URB
status = SendAwaitUrb(fdo, &urb);
if (status == STATUS_SUCCESS) {
pReply->BufferLength =
urb.UrbBulkOrInterruptTransfer.TransferBufferLength;
#ifdef DEBUG
SmartcardDebug(DEBUG_INFO, ("%s!PcscSendSmartcardCommand: resp = ",
DRIVERNAME));
for (ULONG j = 0; j < pReply->BufferLength; j++) {
SmartcardDebug(DEBUG_INFO, ("/x%02X", pReply->Buffer[j]));
}
SmartcardDebug(DEBUG_INFO, ("\n"));
#endif
}
else {
SmartcardDebug(DEBUG_ERROR,
("%s!PcscSendSmartcardCommand: error reading resp (%lX)\n",
DRIVERNAME, status));
}
}
else {
SmartcardDebug(DEBUG_ERROR,
("%s!PcscSendSmartcardCommand: error writing cmd (%lX)\n",
DRIVERNAME, status));
}
return (status);
}
// routine to init and send IRP
NTSTATUS SendAwaitUrb(PDEVICE_OBJECT fdo, PURB urb)
{ // SendAwaitUrb
PAGED_CODE();
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IO_STATUS_BLOCK iostatus;
PIRP Irp = IoBuildDeviceIoControlRequest(
IOCTL_INTERNAL_USB_SUBMIT_URB,
pdx->LowerDeviceObject, NULL, 0,
NULL, 0, TRUE, &event, &iostatus);
if (!Irp) {
KdPrint((DRIVERNAME " - Unable to allocate IRP for sending URB\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
// Set up our completion routine
IoSetCompletionRoutine(Irp, SendUrbComplete, (PVOID)&event, TRUE, TRUE,
TRUE);
PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);
stack->Parameters.Others.Argument1 = (PVOID) urb;
// We will wait for 1 min before the reader returns
LARGE_INTEGER timeOut;
// 1 min = 60 sec, in units of 100 nano sec
timeOut.QuadPart = -60 * 1000 * 1000 * 10;
// Send URB to USBD
NTSTATUS status = IoCallDriver(pdx->LowerDeviceObject, Irp);
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject(
&event, Executive, KernelMode, FALSE, &timeOut);
if (status == STATUS_TIMEOUT) {
SmartcardDebug(DEBUG_INFO,
("%s!SendAwaitUrb: cmd time out, cancelling IRP\n", DRIVERNAME));
// We timed out, need to cancel the IRP
IoCancelIrp(Irp);
// Now wait for our completion routine to signal us
KeWaitForSingleObject(
&event, Executive, KernelMode, FALSE, NULL);
}
else {
// Since our completion routine returned STATUS_MORE_PROCESSING_NEEDED
that
// prevents USBD to finish the IRP, we need to do it here
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
}
else {
iostatus.Status = status;
}
status = iostatus.Status;
if (status != STATUS_SUCCESS)
SmartcardDebug(DEBUG_INFO,
("%s!SendAwaitUrb: err 0x%x\n", DRIVERNAME, status));
return status;
} // SendAwaitUrb
I traced the driver stack and found that the parameter validation happens in
openhci.sys. Here a dump from SoftICE:
:u openhci_queuetransfer l200
_OpenHCI_QueueTransfer
0008:EB6C3B3C PUSH EBP
0008:EB6C3B3D MOV EBP,ESP
0008:EB6C3B3F PUSH ECX
0008:EB6C3B40 PUSH ECX
0008:EB6C3B41 MOV EAX,[EBP+08]
0008:EB6C3B44 PUSH EBX
0008:EB6C3B45 PUSH ESI
0008:EB6C3B46 PUSH EDI
0008:EB6C3B47 MOV EDI,[EBP+0C] ; edi = IRP
0008:EB6C3B4A MOV EAX,[EAX+28]
0008:EB6C3B4D MOV DWORD PTR [EBP-04],00000103
0008:EB6C3B54 MOV [EBP-08],EAX
0008:EB6C3B57 MOV ECX,[EDI+60] ; ecx = IO_stack
0008:EB6C3B5A MOV ESI,[ECX+04] ; esi = URB
0008:EB6C3B5D MOV EBX,[ESI+28] ; HCA area, opapue to driver
0008:EB6C3B60 MOV [ESI+2C],EDI
0008:EB6C3B63 PUSH DWORD PTR [EAX+00000174]
0008:EB6C3B69 CALL [__imp__READ_REGISTER_ULONG]
0008:EB6C3B6F CMP EAX,-01
0008:EB6C3B72 JNZ EB6C3B85
0008:EB6C3B74 MOV DWORD PTR [EBP-04],C00000C0 ; STATUS_DEVICE_DOES_NOT_EXIST
0008:EB6C3B7B PUSH 80000500
0008:EB6C3B80 JMP EB6C3C09
0008:EB6C3B85 TEST BYTE PTR [EBX+10],02
0008:EB6C3B89 JZ EB6C3BA9
0008:EB6C3B8B TEST ESI,ESI
0008:EB6C3B8D JZ EB6C3CC5
0008:EB6C3B93 PUSH EBX
0008:EB6C3B94 PUSH ESI
0008:EB6C3B95 PUSH EDI
0008:EB6C3B96 PUSH DWORD PTR [EBP-08]
0008:EB6C3B99 PUSH DWORD PTR [EBP+08]
0008:EB6C3B9C CALL _OpenHCI_RootHubStartXfer
0008:EB6C3BA1 MOV ESI,[ESI+24]
0008:EB6C3BA4 MOV [EBP-04],EAX
0008:EB6C3BA7 JMP EB6C3B8B
0008:EB6C3BA9 CMP BYTE PTR [EBX+30],01
0008:EB6C3BAD JNZ EB6C3BF5
0008:EB6C3BAF TEST BYTE PTR [ESI+14],04
0008:EB6C3BB3 JNZ EB6C3BF5
0008:EB6C3BB5 PUSH DWORD PTR [EBP-08]
0008:EB6C3BB8 CALL _Get32BitFrameNumber
0008:EB6C3BBD MOV ECX,[ESI+48]
0008:EB6C3BC0 PUSH DWORD PTR [EBP-08]
0008:EB6C3BC3 SUB ECX,EAX
0008:EB6C3BC5 TEST ECX,ECX
0008:EB6C3BC7 JLE EB6C3BD5
0008:EB6C3BC9 CALL _Get32BitFrameNumber
0008:EB6C3BCE MOV ECX,[ESI+48]
0008:EB6C3BD1 SUB ECX,EAX
0008:EB6C3BD3 JMP EB6C3BDF
0008:EB6C3BD5 CALL _Get32BitFrameNumber
0008:EB6C3BDA MOV ECX,EAX
0008:EB6C3BDC SUB ECX,[ESI+48]
0008:EB6C3BDF CMP ECX,00000400
0008:EB6C3BE5 JLE EB6C3BF5
0008:EB6C3BE7 MOV DWORD PTR [EBP-04],C000000D ; STATUS_INVALID_PARAMETER
0008:EB6C3BEE PUSH C0000A00
0008:EB6C3BF3 JMP EB6C3C09
0008:EB6C3BF5 MOV EAX,[ESI+18] ;; NOTE: eax = input buffer size = 0x120
0008:EB6C3BF8 CMP EAX,[EBX+4C] ;; [ebx + 4c] = 0x40, ie 64 bytes, possibly
the max packet size for the pipe?
0008:EB6C3BFB JBE EB6C3C14 (NO JUMP)
0008:EB6C3BFD MOV DWORD PTR [EBP-04],C000000D ; STATUS_INVALID_PARAMETER <=
error code returned to IdtSp3Usb
0008:EB6C3C04 PUSH 80000300
0008:EB6C3C09 PUSH ESI
0008:EB6C3C0A CALL _OpenHCI_SetTranferError
0008:EB6C3C0F JMP EB6C3CC5
0008:EB6C3C14 LEA EAX,[EBP+0C]
0008:EB6C3C17 PUSH EAX
0008:EB6C3C18 CALL [__imp__IoAcquireCancelSpinLock]
0008:EB6C3C1E LEA ECX,[EBX+40]
0008:EB6C3C21 CALL [__imp_@KfAcquireSpinLock]
0008:EB6C3C27 CMP BYTE PTR [EDI+24],00
0008:EB6C3C2B MOV [EBP+08],AL
0008:EB6C3C2E JZ EB6C3C39
0008:EB6C3C30 MOV DWORD PTR [EBP-04],C0000120 ; STATUS_CANCELLED
0008:EB6C3C37 JMP EB6C3C46
0008:EB6C3C39 TEST BYTE PTR [EBX+10],01
0008:EB6C3C3D JZ EB6C3C5E
0008:EB6C3C3F MOV DWORD PTR [EBP-04],C000020F ; STATUS_TRANSACTION_ABORTED
0008:EB6C3C46 XOR EDX,EDX
0008:EB6C3C48 LEA ECX,[EDI+38]
0008:EB6C3C4B CALL [__imp_@InterlockedExchange]
0008:EB6C3C51 PUSH 00010000
0008:EB6C3C56 PUSH ESI
0008:EB6C3C57 CALL _OpenHCI_SetTranferError
0008:EB6C3C5C JMP EB6C3CA7
0008:EB6C3C5E TEST ESI,ESI
0008:EB6C3C60 JZ EB6C3C8B
0008:EB6C3C62 LEA ECX,[EBX+18]
0008:EB6C3C65 LEA EAX,[ESI+38]
0008:EB6C3C68 MOV [ESI+3C],EAX
0008:EB6C3C6B MOV [EAX],EAX
0008:EB6C3C6D MOV DWORD PTR [ESI+04],40404003
0008:EB6C3C74 MOV EDX,[ECX+04]
0008:EB6C3C77 LEA EAX,[ESI+30]
0008:EB6C3C7A MOV [ESI+34],EDX
0008:EB6C3C7D MOV [EAX],ECX
0008:EB6C3C7F MOV [EDX],EAX
0008:EB6C3C81 MOV [ECX+04],EAX
0008:EB6C3C84 MOV ESI,[ESI+24]
0008:EB6C3C87 TEST ESI,ESI
0008:EB6C3C89 JNZ EB6C3C65
0008:EB6C3C8B MOV EAX,[EDI+60]
0008:EB6C3C8E MOV DWORD PTR [EDI+18],00000103
0008:EB6C3C95 MOV EDX,_OpenHCI_CancelTransfer
0008:EB6C3C9A LEA ECX,[EDI+38]
0008:EB6C3C9D OR BYTE PTR [EAX+03],01
0008:EB6C3CA1 CALL [__imp_@InterlockedExchange]
0008:EB6C3CA7 MOV DL,[EBP+08]
0008:EB6C3CAA LEA ECX,[EBX+40]
0008:EB6C3CAD CALL [__imp_@KfReleaseSpinLock]
0008:EB6C3CB3 PUSH DWORD PTR [EBP+0C]
0008:EB6C3CB6 CALL [__imp__IoReleaseCancelSpinLock]
0008:EB6C3CBC PUSH EBX
0008:EB6C3CBD PUSH DWORD PTR [EBP-08]
0008:EB6C3CC0 CALL _OpenHCI_ProcessEndpoint
0008:EB6C3CC5 MOV EAX,[EBP-04]
0008:EB6C3CC8 POP EDI
0008:EB6C3CC9 POP ESI
0008:EB6C3CCA POP EBX
0008:EB6C3CCB LEAVE
0008:EB6C3CCC RET 0008
0008:EB6C3CCF INT 3