想从Filter发送一个OID请求到Miniport.
case IOCTL_FILTER_CREATE_NEW_MAC: //Add by leyond to request miniport to create a new MAC InputBuffer = OutputBuffer = (PUCHAR)Irp->AssociatedIrp.SystemBuffer; InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength; OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength; InfoLength = sizeof(USHORT); if(InfoLength < InputBufferLength){ InfoLength = InputBufferLength; } pInfo = InputBuffer; DEBUGP(DL_TEST,("The input length is %u, and inputdata is%s ",InputBufferLength,InputBuffer)); FILTER_ACQUIRE_LOCK(&FilterListLock, FALSE); Link = FilterModuleList.Flink; Oid = OID_DOT11_CREATE_MAC;//OID used to create a new MAC /************************************************************************* actually, I don't understand why the following code is a while structure. do we really need to send the request to miniport to crate a new MAC for each each filter? But usually, I think there is only one filter in the FilterModulerLink. And we have a test the number of filter is 3! Warn: Miniport couldn't crate more than 3 MAC entities! *********************************************************************************/ #if DBG _asm int 3 #endif pFilter = CONTAINING_RECORD(Link, MS_FILTER, FilterModuleLink); //pFilter = (PMS_FILTER)IrpSp->FileObject->FsContext; //DEBUGP(DL_TEST, ("IOCTL:FileObject %p, Open %p\n", IrpSp->FileObject, pFilter)); Status = filterDoInternalRequest(pFilter, NdisRequestMethod, Oid, &InformationBuffer, sizeof(InformationBuffer), sizeof(InformationBuffer), MethodId, &BytesProcessed); if(Status == NDIS_STATUS_SUCCESS){ DEBUGP(DL_TEST,("Creae a new mac successfully!\n")); }else{ DEBUGP(DL_TEST,("Creae a new mac Fail!\n")); } FILTER_RELEASE_LOCK(&FilterListLock, FALSE); break; default: break; }
然后是filterDoInternalRequest函数体:
NDIS_STATUS filterDoInternalRequest( IN PMS_FILTER FilterModuleContext, IN NDIS_REQUEST_TYPE RequestType, IN NDIS_OID Oid, IN PVOID InformationBuffer, IN ULONG InformationBufferLength, IN ULONG OutputBufferLength, OPTIONAL IN ULONG MethodId, OPTIONAL OUT PULONG pBytesProcessed ) { FILTER_REQUEST FilterRequest; PNDIS_OID_REQUEST NdisRequest = &FilterRequest.Request; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; DEBUGP(DL_TEST,("==>filterDoInternalRequest\n")); NdisZeroMemory(NdisRequest, sizeof(NDIS_OID_REQUEST)); NdisInitializeEvent(&FilterRequest.ReqEvent); NdisRequest->Header.Type = NDIS_OBJECT_TYPE_OID_REQUEST; NdisRequest->Header.Revision = NDIS_OID_REQUEST_REVISION_1; NdisRequest->Header.Size = sizeof(NDIS_OID_REQUEST); NdisRequest->RequestType = RequestType; switch (RequestType) { case NdisRequestQueryInformation: NdisRequest->DATA.QUERY_INFORMATION.Oid = Oid; NdisRequest->DATA.QUERY_INFORMATION.InformationBuffer = InformationBuffer; NdisRequest->DATA.QUERY_INFORMATION.InformationBufferLength = InformationBufferLength; break; case NdisRequestSetInformation: NdisRequest->DATA.SET_INFORMATION.Oid = Oid; NdisRequest->DATA.SET_INFORMATION.InformationBuffer = InformationBuffer; NdisRequest->DATA.SET_INFORMATION.InformationBufferLength = InformationBufferLength; break; case NdisRequestMethod: NdisRequest->DATA.METHOD_INFORMATION.Oid = Oid; NdisRequest->DATA.METHOD_INFORMATION.MethodId = MethodId; NdisRequest->DATA.METHOD_INFORMATION.InformationBuffer = InformationBuffer; NdisRequest->DATA.METHOD_INFORMATION.InputBufferLength = InformationBufferLength; NdisRequest->DATA.METHOD_INFORMATION.OutputBufferLength = OutputBufferLength; break; default: FILTER_ASSERT(FALSE); break; } DEBUGP(DL_TEST,("Everything is ok before NdisOidRequest!\n ")); DEBUGP(DL_TEST,("FilterModululeContext->FilterHandle is%p\n",FilterModuleContext->FilterHandle)); #if DBG _asm int 3 #endif NdisRequest->RequestId = (PVOID)FILTER_REQUEST_ID; Status = NdisFOidRequest(FilterModuleContext->FilterHandle, NdisRequest); if (Status == NDIS_STATUS_PENDING) { DEBUGP(DL_TEST,("Everything is NDIS_STATUS_PENDING!\n ")); NdisWaitEvent(&FilterRequest.ReqEvent, 0); Status = FilterRequest.Status; } if (Status == NDIS_STATUS_SUCCESS) { if (RequestType == NdisRequestSetInformation) { *pBytesProcessed = NdisRequest->DATA.SET_INFORMATION.BytesRead; } if (RequestType == NdisRequestQueryInformation) { *pBytesProcessed = NdisRequest->DATA.QUERY_INFORMATION.BytesWritten; } if (RequestType == NdisRequestMethod) { *pBytesProcessed = NdisRequest->DATA.METHOD_INFORMATION.BytesWritten; } // The driver below should set the correct value to BytesWritten // or BytesRead. But now, we just truncate the value to InformationBufferLength if (RequestType == NdisRequestMethod) { if (*pBytesProcessed > OutputBufferLength) { *pBytesProcessed = OutputBufferLength; } } else { if (*pBytesProcessed > InformationBufferLength) { *pBytesProcessed = InformationBufferLength; } } } return (Status); }
现在问题来了:
在函数NdisFOidRequest()发送OID到miniport之后,返回的状态为:NDIS_STATUS_PENDING。然后进阶进入执行:NdisWaitEvent(&FilterRequest.ReqEvent, 0); 之后就蓝屏了。下面是调试信息:
FOLLOWUP_IP:
ndislwf!filterDoInternalRequest+1c1
[c:\users\leyond\desktop\filter_leyond_beta\src\filter\filter.c @ 2012]
93b882f1 8b4df4 mov ecx,dword ptr [ebp-0Ch]
FAULTING_SOURCE_CODE:
2008: if (Status == NDIS_STATUS_PENDING)
2009: {
2010:
2011: NdisWaitEvent(&FilterRequest.ReqEvent, 0);//应该着这里返回错误了
> 2012: Status = FilterRequest.Status;
2013: }
2014:
2015:
2016: if (Status == NDIS_STATUS_SUCCESS)
2017: {
接着看:
Debug Mesage:
*** Fatal System Error: 0x0000007c
(0x00000014,0x00000002,0x00000000,0x00000000)
Break instruction exception - code 80000003 (first chance)
A fatal system error has occurred.
Debugger entered on first try; Bugcheck callbacks have not been invoked.
A fatal system error has occurred.
BugCheck 7C, {14, 2, 0, 0}
..BUGCODE_NDIS_DRIVER (7c)
注意那四个参数:
0x14 | The current IRQL value | 0 | 0 | An NDIS driver called NdisWaitEvent at IRQL > PASSIVE_LEVEL. The function must be called at IRQL = PASSIVE_LEVEL. |
这里我们找到了蓝屏的真正理由:是 NdisWaitEvent 的IRQl>期望的PASSIVE_LEVEL. 为什么会这样的呢?我也没有找到理由。
从某位大牛Thomas Divine得到的回复是:
Since this is in a NDIS driver one of the most common mistakes is to make a call to NdisOidRequest (or some other function with a completion routine...) and immediately wait for completion - at raised IRQL. The solution to this is NOT to wait. Instead, do the work in the completion routine when it is called.
MSDN上面说NdisWaitEvent()函数的作用在于puts the caller into a wait state until the given event is set to the Signaled state or the wait times out.如果是因为等待参数为0,而需要提高IRQL。那么改成手动调用FilterOidRequestComplete函数看看了...结果未知。
还有一位大牛Tim Roberts的回复:
This is a relatively common misunderstanding, so I want to make sure
it's clear. A lot of people come up against this restriction and say to
themselves "I need to wait in a DISPATCH_LEVEL function, so I'll just
temporarily change myself to non-dispatch," but that's the wrong way to
think about it. As soon as you drop yourself below dispatch, you lose
the guarantees that you needed to be dispatch in the first place.
Spinlocks and synchronization will break, for example.
The solution is to remember that, in Windows, DISPATCH_LEVEL routines
cannot block, for any reason. If you need to wait for something, you
need to spin off separate code at PASSIVE_LEVEL to do the wait. That
means you have to think about WHY the routine was dispatch in the first
place. Are you holding a spinlock? If so, you'll have to find some
other way to protect the resource.
所以他认为问题在于调用NdisFoidRequest时是否已经拿到spinLock.这下我恍然大悟。 FILTER_ACQUIRE_LOCK(&FilterListLock, FALSE); 就是这一句还有对应的释放lock的一句导致了后面返回 NDIS_STATUS_PENDING 的时候,调用NdisWaitEvent,由于无法及时返回就无法释放锁,导致其他进程无法访问某些资源,迫使增加IRQL值,一边第一时间执行。所以注释掉这两句就没有问题了。 非常感谢 Roberts.
-----------------------------------------------------------------------------
DDK文档中明确指出支持例程的IRQL限定。例如,KeWaitForSingleObject 例程有两个限 定:
上面这两行想要说明的是:如果KeWaitForSingleObject真的被阻塞了指定长的时间(你指定的非0超时),那么你必定运行在低于 DISPATCH_LEVEL的IRQL上,因为只有在这样的IRQL上线程阻塞才是允许的.
-----------------------------------------------------------------------------