Need help about IoBuildPartialMdl

I’m writing a lower filter for cdrom in order to make a crypted disc,
you might think it’s a bad idea, but anyway that’s not the point.

when i start filtering SCSIO_READ(10) and made a try to split a read operation into several stages, i was confused by IoBuildPartialMdl.

the following is what i have done. sorry the codes were not in my hand so i wrote the following by memory.

It’s direct IO and run at IRQL_DISPATCH_LEVEL.

when it read more than 1 block, i tried to split into serval operation.it’s seemed to be a paging I/O. srb->DataBuffer was NULL, and MdlFlags was MDL_MAPPED_TO_SYSTEM_VA | MDL_PAGES_LOCKED | MDL_IO_PAGE_READ.

the current process is explorer, which was trying to read a PE file to display its icon.

I allocated a MDL and build it.

Mdl = IoAllocateMdl (irp->MdlAddress,
Mdl,
MmGetMdlVirtualAddress(irp->MdlAddress), // 0
MmGetMdlByteCount(irp->MdlAddree), // 0x1000
NULL);

IoBuildPartialMdl (irp->MdlAddress,
Mdl,
MmGetMdlVirtualAddress(irp->MdlAddress),
devExt->blockSize // 0x800
);

then save irp->MdlAddress and replace it with the partial mdl, modify srb->DataTransferLength and srb->Cdb to read only 1 block, passed it to the lower device.

In completion, i found it worked (by checking the result with MmGetSystemAddressForMdlSafe),
and then :

MmPrepareMdlForReuse (devExt->Mdl); //same as the current irp->MdlAddress, the partial one

IoBuildPartialMdl (devExt->originalMdl, // if use current irp->MdlAddress, i got same result
devExt->Mdl,
(PUCHAR)MmGetMdlVirtualAddress(devExt->originalMdl) + counter * devExt->blockSize,
devExt->blockSize
);

i modified srb->Cdb to increment LBA and call lower driver again (return
STATUS_MORE_PROCESSING_REQUIRED). if it’s the last block i replace irp->MdlAddress
back with the original one and return STATUS_CONTINUE_COMPLETION.

Now this is my problem.

the read operation of the 2nd block seemed to succeeded since status is ok, but this time the data is read out and copied into the last same address where the 1st block of data resided (mean it occupied the 1st block of data), the buffer+0x800 held nothing!

IoBuildPartialMdl didn’t work? if call MmGetSystemAddressForMdlSafe(irp->MdlAddress) here, it did point to buffer+0x800.

there’s something unknown force it to always copy data into the beginning of buffer?

i made anther try, allocate a NonPagedPool, MmBuildMdlForNonPagedPool and replaced
irp->MdlAddress directly to test it, this time i got BSOD (even the break point in ompletion entry not hit), IRQL_NOT_LESS_OR_EQUAL with a invalid address, it seemed to have something with DMA (some function name like Scatter/Gather). I know nothing about DMA.:frowning:

What should i do if i hope to decrypt data block by block in completion? maybe i can process all the data only once and never split the read operation?

thanks for any help.

Try not using MmPrepareMdlForReuse. Just allocate a new MDL every time and free it.

Also, if you plan to use system mapping (MmGetSystemAddressForMdlSafe), use it through the original MDL, not through your new partial MDL.

DO NOT read/write/modify/compare memory using MmGetMdlVirtualAddress.

I remeber i have tried the 1st method, allocate a new mdl every time and build a new partial mdl (but not very sure). the 2nd, yes i did use the partial one to read memory.

If i understand correctly, what you mean is building a new partial mdl assigned to irp->MdlAddress each time, and aways read memory (by MmGetSystemAddressForMdlSafe) using the original mdl?

I read memory using MmGetSystemAddressForMdlSafe, not MmGetMdlVirtualAddress.

I suspected it might be caued by DMA. I allocated NonPagedPool and replace irp->MdlAddress to see if i could get the corrent data, it always bsod, strange? I remember there’s
something like HalBuildScatterGatherList in call stack.

Anyway i’ll give another try in a few days, and post more detail. thanks for your help Alex.

still no luck, here is my code. btw, i’m testing it in VMWare, win xpsp2

snippet of IRP_MJ_SCSI dispatch routine:

case SCSIOP_READ:
fltExt->NumberOfBlocks = GetTransferLength (cdb);

if (fltExt->NumberOfBlocks > 1) {
fltExt->Counter = 0;

/* the original irp->MdlAddress
1: kd> dt nt!_MDL 0x865d0920
+0x000 Next : (null)
+0x004 Size : 0n32
+0x006 MdlFlags : 0n67
+0x008 Process : 0x05000000 _EPROCESS
+0x00c MappedSystemVa : 0xf7cd6000 Void
+0x010 StartVa : (null)
+0x014 ByteCount : 0x1000
+0x018 ByteOffset : 0
*/

Mdl = IoAllocateMdl (MmGetMdlVirtualAddress (irp->MdlAddress),
fltExt->BytesPerBlock,
FALSE,
FALSE,
NULL);

if (!Mdl) {
status = STATUS_INSUFFICIENT_RESOURCES;

irp->IoStatus.Information = 0L;
irp->IoStatus.Status = status;
IoCompleteRequest (irp, IO_NO_INCREMENT);
return status;
}

fltExt->OriginalMdl = irp->MdlAddress;

/* the 1st partial mdl
1: kd> dt nt!_MDL 0x866104d8
+0x000 Next : (null)
+0x004 Size : 0n32
+0x006 MdlFlags : 0n89
+0x008 Process : 0x05000000 _EPROCESS
+0x00c MappedSystemVa : 0xf7cd6000 Void
+0x010 StartVa : (null)
+0x014 ByteCount : 0x800
+0x018 ByteOffset : 0
*/

IoBuildPartialMdl (irp->MdlAddress,
Mdl,
MmGetMdlVirtualAddress(irp->MdlAddress),
fltExt->BytesPerBlock);

irp->MdlAddress = Mdl;

srb->DataTransferLength = fltExt->BytesPerBlock;
SetTransferLength (cdb, 1);

IoMarkIrpPending(irp);

IoCopyCurrentIrpStackLocationToNext (irp);
IoSetCompletionRoutine (irp,
ReadCompletion,
fltExt,
TRUE,
TRUE,
TRUE);

IoCallDriver (fltExt->LowerDo, irp);

return STATUS_PENDING;

} else {
// only 1 block, set completion & pass down…
}

And the snippet of ReadCompletion:

if (fltExt->NumberOfBlocks > 1) {

DbgBreakPoint();

/* the 1st block:
0: kd> db 0xf7cd6000
f7cd6000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00 MZ…
f7cd6010 b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00 …@…
f7cd6020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …
f7cd6030 00 00 00 00 00 00 00 00-00 00 00 00 c8 00 00 00 …
f7cd6040 0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68 …!..L.!Th
f7cd6050 69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f is program canno
f7cd6060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in DOS
f7cd6070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00 mode…$…

the 2nd block, correct data, wrong address
0: kd> db 0xf7cd6000
f7cd6000 68 30 75 00 00 ff 35 6c-36 42 00 ff 15 30 71 40 h0u…5l6B…0q@
f7cd6010 00 50 68 02 04 00 00 ff-74 24 18 ff 15 44 72 40 .Ph…t$…Dr@
f7cd6020 00 eb 8e b8 ff ff ff 7f-eb 02 33 c0 5e c2 08 00 …3.^…
f7cd6030 8b 44 24 04 8b 0d 90 3e-42 00 6a 00 ff 74 81 6c .D$…>B.j…t.l
f7cd6040 e8 67 ff ff ff c2 04 00-68 64 9f 40 00 ff 74 24 .g…hd.@…t$
f7cd6050 08 e8 55 3b 00 00 c2 04-00 55 8b ec 81 ec a4 01 …U;…U…
f7cd6060 00 00 a1 88 3e 42 00 53-56 8b 75 08 57 6a 07 59 …>B.SV.u.Wj.Y
f7cd6070 8d 7d d8 89 45 cc 33 db-f3 a5 8b 45 dc 8b 55 e0 .}…E.3…E…U.

why not copied here ???
0: kd> db 0xf7cd6000+0x800
f7cd6800 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …
f7cd6810 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …
f7cd6820 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …
f7cd6830 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …
f7cd6840 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …
f7cd6850 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …
f7cd6860 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …
f7cd6870 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …
*/

buffer = MmGetSystemAddressForMdlSafe (fltExt->OriginalMdl, HighPagePriority);

fltExt->Counter += 1;
if (fltExt->Counter != fltExt->NumberOfBlocks) {

IoFreeMdl (irp->MdlAddress);

irp->MdlAddress = IoAllocateMdl (MmGetMdlVirtualAddress (fltExt->OriginalMdl),
fltExt->BytesPerBlock,
FALSE,
FALSE,
NULL);

if (!irp->MdlAddress) {
DbgBreakPoint();
//…
}

/* the 2nd partial mdl
0: kd> dt nt!_MDL 0x866104d8
+0x000 Next : (null)
+0x004 Size : 0n32
+0x006 MdlFlags : 0n89
+0x008 Process : 0x05000000 _EPROCESS
+0x00c MappedSystemVa : 0xf7cd6800 Void
+0x010 StartVa : (null)
+0x014 ByteCount : 0x800
+0x018 ByteOffset : 0x800
*/

IoBuildPartialMdl(
fltExt->OriginalMdl,
irp->MdlAddress,
(PUCHAR)MmGetMdlVirtualAddress (fltExt->OriginalMdl) + (ptrdiff_t)(fltExt->BytesPerBlock * fltExt->Counter),
fltExt->BytesPerBlock
);

SetLBA (cdb, GetLBA(cdb) + fltExt->Counter);

// some fields of srb changed, we can allocate our own srb to do the work
//srb->SrbStatus = SRB_STATUS_PENDING;
//srb->TargetId = 0;
//srb->SrbExtension = NULL;

IoCopyCurrentIrpStackLocationToNext (irp);
IoSetCompletionRoutine (irp,
ReadCompletion,
fltExt,
TRUE,
TRUE,
TRUE);

IoCallDriver (fltExt->LowerDo, irp);

return STATUS_MORE_PROCESSING_REQUIRED;

} else {
// the last block
IoFreeMdl (irp->MdlAddress);

srb->DataTransferLength = fltExt->NumberOfBlocks * fltExt->BytesPerBlock;

irp->IoStatus.Information = fltExt->BytesPerBlock * fltExt->Counter;
irp->MdlAddress = fltExt->OriginalMdl;

return STATUS_CONTINUE_COMPLETION;
}
}

Remember that you can have more than 1 IRP in progress. Don’t use your fltExt to keep any status of the current IRP. Also, if you build partial buffers and use different MDL, you better use separate IRPs, not just substitute the MDL in the original IRP.

One potential problem I see here is that you replace the irp->MdlAddress
with the address of your own MDL.

I haven’t looked at all the details, but I have seen any number of
disasters caused by mucking with “read-only” fields of an IRP.
Essentially, any field that is not documented as something you can write
to must not be written to in any IRP that comes to you, because the
creator of that IRP makes a lot of assumptions that it is going to get
back what it set.

Even though you save the original MdlAddress and appear to restore it
correctly, I get very suspicious when I see code like this. I would think
it better to create a new IRP to send down. I may get slammed for this,
because it may be a perfectly good pattern that is used, but I’ve seen
disasters caused by other fields being modified. One “clever programmer”
decided to use the BufferAddress field as the buffer pointer, and
incremented it during the read operation, and of course disaster quickly
followed. That was pretty obvious. But I’ve seen other fields being
manipulated, and particularly those in unions having particularly nasty
effects (the programmer assumed that because the field was documented as
writeable, it was writeable under all conditions, but the IRP was already
using that space for some other purpose.

If the experts say this trick is a valid pattern, then the problem is
elsewhere. I’ll have to go back and read the earlier messages in this
thread.
joe
*****

still no luck, here is my code. btw, i’m testing it in VMWare, win xpsp2

snippet of IRP_MJ_SCSI dispatch routine:

case SCSIOP_READ:
fltExt->NumberOfBlocks = GetTransferLength (cdb);

if (fltExt->NumberOfBlocks > 1) {
fltExt->Counter = 0;

/* the original irp->MdlAddress
1: kd> dt nt!_MDL 0x865d0920
+0x000 Next : (null)
+0x004 Size : 0n32
+0x006 MdlFlags : 0n67
+0x008 Process : 0x05000000 _EPROCESS
+0x00c MappedSystemVa : 0xf7cd6000 Void
+0x010 StartVa : (null)
+0x014 ByteCount : 0x1000
+0x018 ByteOffset : 0
*/

Mdl = IoAllocateMdl (MmGetMdlVirtualAddress (irp->MdlAddress),
fltExt->BytesPerBlock,
FALSE,
FALSE,
NULL);

if (!Mdl) {
status = STATUS_INSUFFICIENT_RESOURCES;

irp->IoStatus.Information = 0L;
irp->IoStatus.Status = status;
IoCompleteRequest (irp, IO_NO_INCREMENT);
return status;
}

fltExt->OriginalMdl = irp->MdlAddress;

/* the 1st partial mdl
1: kd> dt nt!_MDL 0x866104d8
+0x000 Next : (null)
+0x004 Size : 0n32
+0x006 MdlFlags : 0n89
+0x008 Process : 0x05000000 _EPROCESS
+0x00c MappedSystemVa : 0xf7cd6000 Void
+0x010 StartVa : (null)
+0x014 ByteCount : 0x800
+0x018 ByteOffset : 0
*/

IoBuildPartialMdl (irp->MdlAddress,
Mdl,
MmGetMdlVirtualAddress(irp->MdlAddress),
fltExt->BytesPerBlock);

irp->MdlAddress = Mdl;

srb->DataTransferLength = fltExt->BytesPerBlock;
SetTransferLength (cdb, 1);

IoMarkIrpPending(irp);

IoCopyCurrentIrpStackLocationToNext (irp);
IoSetCompletionRoutine (irp,
ReadCompletion,
fltExt,
TRUE,
TRUE,
TRUE);

IoCallDriver (fltExt->LowerDo, irp);

return STATUS_PENDING;

} else {
// only 1 block, set completion & pass down…
}

And the snippet of ReadCompletion:

if (fltExt->NumberOfBlocks > 1) {

DbgBreakPoint();

/* the 1st block:
0: kd> db 0xf7cd6000
f7cd6000 4d 5a 90 00 03 00 00 00-04 00 00 00 ff ff 00 00
MZ…
f7cd6010 b8 00 00 00 00 00 00 00-40 00 00 00 00 00 00 00
…@…
f7cd6020 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

f7cd6030 00 00 00 00 00 00 00 00-00 00 00 00 c8 00 00 00

f7cd6040 0e 1f ba 0e 00 b4 09 cd-21 b8 01 4c cd 21 54 68
…!..L.!Th
f7cd6050 69 73 20 70 72 6f 67 72-61 6d 20 63 61 6e 6e 6f is program
canno
f7cd6060 74 20 62 65 20 72 75 6e-20 69 6e 20 44 4f 53 20 t be run in
DOS
f7cd6070 6d 6f 64 65 2e 0d 0d 0a-24 00 00 00 00 00 00 00
mode…$…

the 2nd block, correct data, wrong address
0: kd> db 0xf7cd6000
f7cd6000 68 30 75 00 00 ff 35 6c-36 42 00 ff 15 30 71 40
h0u…5l6B…0q@
f7cd6010 00 50 68 02 04 00 00 ff-74 24 18 ff 15 44 72 40
.Ph…t$…Dr@
f7cd6020 00 eb 8e b8 ff ff ff 7f-eb 02 33 c0 5e c2 08 00
…3.^…
f7cd6030 8b 44 24 04 8b 0d 90 3e-42 00 6a 00 ff 74 81 6c
.D$…>B.j…t.l
f7cd6040 e8 67 ff ff ff c2 04 00-68 64 9f 40 00 ff 74 24
.g…hd.@…t$
f7cd6050 08 e8 55 3b 00 00 c2 04-00 55 8b ec 81 ec a4 01
…U;…U…
f7cd6060 00 00 a1 88 3e 42 00 53-56 8b 75 08 57 6a 07 59
…>B.SV.u.Wj.Y
f7cd6070 8d 7d d8 89 45 cc 33 db-f3 a5 8b 45 dc 8b 55 e0
.}…E.3…E…U.

why not copied here ???
0: kd> db 0xf7cd6000+0x800
f7cd6800 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

f7cd6810 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

f7cd6820 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

f7cd6830 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

f7cd6840 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

f7cd6850 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

f7cd6860 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

f7cd6870 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00

*/

buffer = MmGetSystemAddressForMdlSafe (fltExt->OriginalMdl,
HighPagePriority);

fltExt->Counter += 1;
if (fltExt->Counter != fltExt->NumberOfBlocks) {

IoFreeMdl (irp->MdlAddress);

irp->MdlAddress = IoAllocateMdl (MmGetMdlVirtualAddress
(fltExt->OriginalMdl),
fltExt->BytesPerBlock,
FALSE,
FALSE,
NULL);

if (!irp->MdlAddress) {
DbgBreakPoint();
//…
}

/* the 2nd partial mdl
0: kd> dt nt!_MDL 0x866104d8
+0x000 Next : (null)
+0x004 Size : 0n32
+0x006 MdlFlags : 0n89
+0x008 Process : 0x05000000 _EPROCESS
+0x00c MappedSystemVa : 0xf7cd6800 Void
+0x010 StartVa : (null)
+0x014 ByteCount : 0x800
+0x018 ByteOffset : 0x800
*/

IoBuildPartialMdl(
fltExt->OriginalMdl,
irp->MdlAddress,
(PUCHAR)MmGetMdlVirtualAddress (fltExt->OriginalMdl) +
(ptrdiff_t)(fltExt->BytesPerBlock * fltExt->Counter),
fltExt->BytesPerBlock
);

SetLBA (cdb, GetLBA(cdb) + fltExt->Counter);

// some fields of srb changed, we can allocate our own srb to do
the work
//srb->SrbStatus = SRB_STATUS_PENDING;
//srb->TargetId = 0;
//srb->SrbExtension = NULL;

IoCopyCurrentIrpStackLocationToNext (irp);
IoSetCompletionRoutine (irp,
ReadCompletion,
fltExt,
TRUE,
TRUE,
TRUE);

IoCallDriver (fltExt->LowerDo, irp);

return STATUS_MORE_PROCESSING_REQUIRED;

} else {
// the last block
IoFreeMdl (irp->MdlAddress);

srb->DataTransferLength = fltExt->NumberOfBlocks *
fltExt->BytesPerBlock;

irp->IoStatus.Information = fltExt->BytesPerBlock *
fltExt->Counter;
irp->MdlAddress = fltExt->OriginalMdl;

return STATUS_CONTINUE_COMPLETION;
}
}


NTDEV is sponsored by OSR

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

I should have read ahead. Note that if you need to save something on a
per-IRP basis, one way is to allocate a structure that holds the context
(such as the original MDL or other things you might be storing in the
context) and use the pointer to that structure as the argument to be
passed to the completion routine. Thus the completion routine has
everything it needs to do *for that IRP*, and there is no shared state in
the device extension to trip you up.
joe

Remember that you can have more than 1 IRP in progress. Don’t use your
fltExt to keep any status of the current IRP. Also, if you build partial
buffers and use different MDL, you better use separate IRPs, not just
substitute the MDL in the original IRP.


NTDEV is sponsored by OSR

OSR is HIRING!! See http://www.osr.com/careers

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at
http://www.osronline.com/page.cfm?name=ListServer

Thank you Alex & Joseph.

yes i have found one bug today, i should not save everything in device extension, it became obvious when Nero installed (many spti requests), there’re concurrent requests making something messy, so i’ll allocate a context for each request and check if it works.

if not, i’ll try to allocate my own irps. Anyway i’ll keep trying to split the operation, since it’s a common method. the bad thing is i need completing the request of make a encrypted disc quickly since it’s my job, this problem has spent too much time since it’s safe to decrypt all data once without spliting the irp.

if i find anything (or not :wink: i’ll keeping posting.

Well I have made more tries.

First, i moved data on a per-IRP basis (such as originalMdl, numberOfBlocks, counter of blocks read…)
into a CONTEXT, it still remained the same, the 2nd read put data into wrong address.

Then, i tried to “roll my own”, using my irp / srb / senseInfoBuffer, it all failed no matter i
build a partial mdl for the orginal one, or allocate a NonPagedPoolCacheAligned and call
MmBuildMdlForNonPagedPool for it.

the codes were copied from WDK’s cdrom, so no need to posted here.

here is a crash dump, sometimes it might be some diffrent, but always failed.

0: kd> !analyze -v
ERROR: FindPlugIns 8007007b
*******************************************************************************
* *
* Bugcheck Analysis *
* *
*******************************************************************************

IRQL_NOT_LESS_OR_EQUAL (a)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high. This is usually
caused by drivers using improper addresses.
If a kernel debugger is available get the stack backtrace.
Arguments:
Arg1: 8696fec0, memory referenced
Arg2: 00000002, IRQL
Arg3: 00000000, bitfield :
bit 0 : value 0 = read operation, 1 = write operation
bit 3 : value 0 = not an execute operation, 1 = execute operation (only on chips which support this level of status)
Arg4: 806e7b8b, address which referenced memory

Debugging Details:

READ_ADDRESS: 8696fec0

CURRENT_IRQL: 2

FAULTING_IP:
hal!HalBuildScatterGatherList+cd
806e7b8b 8b03 mov eax,dword ptr [ebx]

DEFAULT_BUCKET_ID: DRIVER_FAULT

BUGCHECK_STR: 0xA

PROCESS_NAME: smss.exe

TRAP_FRAME: f7a8c5b0 – (.trap 0xfffffffff7a8c5b0)
ErrCode = 00000000
eax=867892c0 ebx=8696fec0 ecx=86623e90 edx=00000000 esi=00000800 edi=867892c0
eip=806e7b8b esp=f7a8c624 ebp=f7a8c630 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010206
hal!HalBuildScatterGatherList+0xcd:
806e7b8b 8b03 mov eax,dword ptr [ebx] ds:0023:8696fec0=???
Resetting default scope

LAST_CONTROL_TRANSFER: from 804f9afd to 8052b5d8

STACK_TEXT:
f7a8c164 804f9afd 00000003 f7a8c4c0 00000000 nt!RtlpBreakWithStatusInstruction
f7a8c1b0 804fa6e8 00000003 8696fec0 806e7b8b nt!KiBugCheckDebugBreak+0x19
f7a8c590 805446d0 0000000a 8696fec0 00000002 nt!KeBugCheck2+0x574
f7a8c590 806e7b8b 0000000a 8696fec0 00000002 nt!KiTrap0E+0x238
f7a8c630 806e7e14 00000000 8678a640 867892c0 hal!HalBuildScatterGatherList+0xcd
f7a8c660 f786dddb 867ab248 8678a640 867892c0 hal!HalGetScatterGatherList+0x26
WARNING: Stack unwind information not available. Following frames may be wrong.
f7a8c694 f742d8b1 8678a6f8 00000000 00000800 PCIIDEX!PciIdeXSetBusData+0x4a7
f7a8c6cc 804f0f2f 8678d030 8641f620 86752bb8 atapi!IdePortStartIo+0xeb
f7a8c6ec f742cc9a 8678d030 8641f620 00000000 nt!IoStartPacket+0x7d
f7a8c718 804efeb1 8678d030 0041f620 8641f620 atapi!IdePortDispatch+0x4e6
f7a8c728 f76be289 8641f620 86671a48 866bc6f0 nt!IopfCallDriver+0x31
f7a8c73c f76bf070 00000000 8641f620 866badf8 imapi+0x1289
f7a8c760 804efeb1 86671990 28000000 866ab7e0 imapi+0x2070
f7a8c770 f76cf584 00c5f53c 866badf8 866ab7e0 nt!IopfCallDriver+0x31
f7a8c7c4 f76cf825 86671550 f76ce9b0 867e6b28 CdProtect!FltScratchRead+0x2c4 [e:\cdprotect\cdromflt.c @ 1610]
f7a8c814 804efeb1 86671550 866bacf8 866aad78 CdProtect!FltDispatchScsi+0x1d5 [e:\cdprotect\cdromflt.c @ 1701]
f7a8c824 f762e061 86507000 864ed620 866aad78 nt!IopfCallDriver+0x31
f7a8c838 f762dd58 866aad78 864ed620 86670030 CLASSPNP!ClassCompleteRequest+0x402
f7a8c868 f7633203 00000800 00000800 866703b0 CLASSPNP!ClassCompleteRequest+0xf9
f7a8c87c f76e0485 86670030 864ed620 00020000 CLASSPNP!ClassSplitRequest+0x34
f7a8c8c0 f762e540 86670030 864ed620 86670030 cdrom!CdRomStartIo+0x1477
f7a8c8d4 804f0f2f 86670030 864ed620 866700e8 CLASSPNP!ClassIoComplete+0x2c9
f7a8c8f4 f763112a 86670030 864ed620 00000000 nt!IoStartPacket+0x7d
f7a8c920 804efeb1 86670030 00000000 866ab030 CLASSPNP!ClassFindModePage+0x912
f7a8c9f0 805818b9 00000000 86670030 806e5a4c nt!IopfCallDriver+0x31
f7a8ca40 804f6106 c0000001 f7a8cb00 00000000 nt!IopMountVolume+0x1b9
f7a8ca70 80582830 866a7b48 86670030 f7a8cba4 nt!IopCheckVpbMounted+0x5e
f7a8cb60 805bed10 86670030 00000000 8660b6f0 nt!IopParseDevice+0x3c6
f7a8cbd8 805bb398 00000000 f7a8cc18 00000040 nt!ObpLookupObjectName+0x53c
f7a8cc2c 80575e4d 00000000 00000000 00000001 nt!ObOpenObjectByName+0xea
f7a8cca8 805767c4 0015fe18 00100180 0015fddc nt!IopCreateFile+0x407
f7a8cd04 80579f83 0015fe18 00100180 0015fddc nt!IoCreateFile+0x8e
f7a8cd44 8054160c 0015fe18 00100180 0015fddc nt!NtOpenFile+0x27
f7a8cd44 7c92eb94 0015fe18 00100180 0015fddc nt!KiFastCallEntry+0xfc
0015fe30 485840c8 4858c3b8 4858c3b8 7c92ee18 ntdll!RtlAnsiStringToUnicodeString+0xa9
0015fe58 48588ceb 00000000 7c9212d6 00000000 smss+0x40c8
0015fecc 48588f27 0015ff6c 00000005 00000000 smss+0x8ceb
0015ff18 48589bfc 0015ff6c 0015ff64 00000005 smss+0x8f27
0015ffa8 4858ad97 00000001 00162340 00162348 smss+0x9bfc
0015fff4 00000000 7ffdd000 000000c8 00000167 smss+0xad97

STACK_COMMAND: kb

FOLLOWUP_IP:
PCIIDEX!PciIdeXSetBusData+4a7
f786dddb 5f pop edi

SYMBOL_STACK_INDEX: 6

SYMBOL_NAME: PCIIDEX!PciIdeXSetBusData+4a7

FOLLOWUP_NAME: MachineOwner

MODULE_NAME: PCIIDEX

IMAGE_NAME: PCIIDEX.SYS

DEBUG_FLR_IMAGE_TIMESTAMP: 41107b4c

FAILURE_BUCKET_ID: 0xA_PCIIDEX!PciIdeXSetBusData+4a7

BUCKET_ID: 0xA_PCIIDEX!PciIdeXSetBusData+4a7

Followup: MachineOwner

Damn!, i googled a shareware doing the same thing, reverse engineered it’s driver :wink:
It give me some hint what’s the real problem.

It installed it’s driver as a lower filter for device like imapi, not the class lower filter
as i did, but i don’t think it’s the point.

It didn’t split the read request, just decrypt all data one time in completion. Something interesting
is how it processed the IOCTL_SCSI_PASS_THROUGH_DIRECT.

In completion for reading, it check if the current IRQL is PASSSIVE_LEVEL, if true it decrypted
data directly, otherwise it queued a workitem to do the work.

If it’s a writing operation, it allocate a irp this time, replaced the control code with
IOCTL_SCSI_PASS_THROUGH, set IRP.Flags to IRP_BUFFERED_IO | IRP_INPUT_OPERATION, meaning it
alway useing IOCTL_SCSI_PASS_THROUGH instead of IOCTL_SCSI_PASS_THROUGH_DIRECT.

I wondered why it acted like this, and noticed some explaination from WDK:

The SCSI_PASS_THROUGH structure is used with IOCTL_SCSI_PASS_THROUGH, which is
a buffered device control request. To bypass buffering in system memory, callers
should use IOCTL_SCSI_PASS_THROUGH_DIRECT. When handling an IOCTL_SCSI_PASS_THROUGH_DIRECT
request, the system locks down the buffer in user memory and the device accesses
this memory directly.

.
I think that’s exactly my probelem. when i splited a irp, no matter building a partial mdl or
allocating NonPagedPool and build a new mdl, the result never has MDL_PAGES_LOCKED set.
and more, the memory might has a incorret aligment. I noticed WDK’s spti does query
the aligment by GetAlignmentMaskForDevice.

That’s all, might be incorrect. When i have time maybe i’ll take more tries, but now i have
to finish the work first :frowning: