+ All Categories
Home > Documents > IRP Voodoo

IRP Voodoo

Date post: 12-Jan-2016
Category:
Upload: jada
View: 57 times
Download: 1 times
Share this document with a friend
Description:
IRP Voodoo. Adrian J. Oney Kernel Developer Windows Base OS Microsoft Corporation. Overview. Theory Request “Basics” Asynchronous I/O Stacking Building IRPs Miscellaneous. What’s Not Covered As we only have an hour. MDLs Start I/O PnP, Power, WMI Security This is a big one! - PowerPoint PPT Presentation
Popular Tags:
63
IRP Voodoo IRP Voodoo Adrian J. Oney Adrian J. Oney Kernel Developer Kernel Developer Windows Base OS Windows Base OS Microsoft Corporation Microsoft Corporation
Transcript
Page 1: IRP Voodoo

IRP VoodooIRP Voodoo

Adrian J. OneyAdrian J. OneyKernel DeveloperKernel DeveloperWindows Base OSWindows Base OSMicrosoft CorporationMicrosoft Corporation

Page 2: IRP Voodoo

OverviewOverview

TheoryTheory Request “Basics”Request “Basics” Asynchronous I/O Asynchronous I/O StackingStacking Building IRPsBuilding IRPs MiscellaneousMiscellaneous

Page 3: IRP Voodoo

What’s Not CoveredWhat’s Not CoveredAs we only have an hourAs we only have an hour

MDLsMDLs Start I/OStart I/O PnP, Power, WMIPnP, Power, WMI SecuritySecurity

This is a big one!This is a big one! See WinHEC 2002’s “Understanding See WinHEC 2002’s “Understanding

Driver Security” talkDriver Security” talk

Page 4: IRP Voodoo

IRP TheoryIRP Theory

The OS communicates with drivers by The OS communicates with drivers by sending I/O Request Packets (IRPs)sending I/O Request Packets (IRPs)

IRPs are both:IRPs are both: A container for I/O requests, andA container for I/O requests, and A thread-independent call stackA thread-independent call stack

Both definitions will be discussed in detail!Both definitions will be discussed in detail!

Page 5: IRP Voodoo

IRPs as a Request ContainerIRPs as a Request ContainerDefinition #1Definition #1

Most driver requests are presented Most driver requests are presented using IRPsusing IRPs IRPs can be processed asynchronouslyIRPs can be processed asynchronously IRPs can be recalled (i.e. canceled)IRPs can be recalled (i.e. canceled) IRPs are designed for I/O involving multiple IRPs are designed for I/O involving multiple

driversdrivers

The IRP structure packages together the The IRP structure packages together the data needed to respond to a requestdata needed to respond to a request Requests From Kernel ModeRequests From Kernel Mode Requests From User ModeRequests From User Mode

Page 6: IRP Voodoo

IRPs as a Request ContainerIRPs as a Request Container

IRPs are divided into two piecesIRPs are divided into two pieces A global header for the main requestA global header for the main request An array of parameters for sub-requests An array of parameters for sub-requests

(“dispatches”)(“dispatches”)

A driver uses both pieces to handle the A driver uses both pieces to handle the requestrequest

DEVICE_OBJECTDEVICE_OBJECT IR

PIR

P

IRP HeaderIRP Header

Sub-Request ParametersSub-Request Parameters

Sub-Request ParametersSub-Request Parameters

……

Sub-Request ParametersSub-Request Parameters

Page 7: IRP Voodoo

IRP Header ContentsIRP Header Contents

The IRP header containsThe IRP header contains Where to get the inputWhere to get the input Where to write the final resultWhere to write the final result Driver safe locations or copies for each of Driver safe locations or copies for each of

the abovethe above Scratch space for the current driver Scratch space for the current driver

owning the requestowning the request A driver-supplied callback the OS uses to A driver-supplied callback the OS uses to

recall the IRPrecall the IRP

Page 8: IRP Voodoo

IRP ParametersIRP Parameters

The IRP header is followed by an array of The IRP header is followed by an array of sub-requestssub-requests Each of these sub-requests is represented by an Each of these sub-requests is represented by an

IO_STACK_LOCATION structureIO_STACK_LOCATION structure A field in the IRP Header identifies the currently A field in the IRP Header identifies the currently

used stack locationused stack location

Fields in the IO_STACK_LOCATION includeFields in the IO_STACK_LOCATION include Major and Minor CodesMajor and Minor Codes Major/Minor specific argumentsMajor/Minor specific arguments Device ObjectDevice Object File ObjectFile Object

But why But why multiple multiple sub-requests?sub-requests?

Page 9: IRP Voodoo

Thread Independent Call StacksThread Independent Call StacksIRP Definition #2IRP Definition #2

Doing an operation often Doing an operation often requires multiple drivers requires multiple drivers Usually arranged in chains Usually arranged in chains

called Device Stackscalled Device Stacks

IRPs are WDM’s solutionIRPs are WDM’s solutionto asynchronous I/Oto asynchronous I/O Specifically designed for I/O Specifically designed for I/O

involving multiple driversinvolving multiple drivers

DEVICE_OBJECTDEVICE_OBJECT

DEVICE_OBJECTDEVICE_OBJECT

DEVICE_OBJECTDEVICE_OBJECT

DEVICE_OBJECTDEVICE_OBJECT

IRPIRP

Page 10: IRP Voodoo

Asynchronous I/OAsynchronous I/O

Goal: Let applications queue one or more Goal: Let applications queue one or more requests without blockingrequests without blocking Example: Decompressing video frames Example: Decompressing video frames

from a CDfrom a CD

Two dedicated application threads Two dedicated application threads is not an optimal solutionis not an optimal solution Thread switches are expensiveThread switches are expensive

Policy needs to be in the driver stackPolicy needs to be in the driver stack Only it knows whether a given request should be Only it knows whether a given request should be

handled synchronously or asynchronouslyhandled synchronously or asynchronously

Page 11: IRP Voodoo

Synchronous OS designSynchronous OS design

Driver ADriver A

Driver BDriver B

Driver CDriver C

AppApp Param for AParam for AParam for AParam for AReturn address to AppReturn address to App……Param for BParam for BParam for BParam for BReturn address to AReturn address to A……Param for CParam for CParam for CParam for CReturn address to BReturn address to B

Thread StackThread Stack

Stack PointerStack Pointer

Instruction PointerInstruction Pointer

User ModeUser Mode

Kernel ModeKernel Mode

Page 12: IRP Voodoo

I/O Request PacketsI/O Request Packetsas Thread Independent Call Stacksas Thread Independent Call Stacks

Param for AParam for AParam for AParam for AReturn address to AppReturn address to App……Param for BParam for BParam for BParam for BReturn address to AReturn address to A……Param for CParam for CParam for CParam for CReturn address to BReturn address to B

Thread StackThread Stack

IRP HeaderIRP Header(Contains Status and Stack Pointer)(Contains Status and Stack Pointer)

Param AParam A Param AParam A Callback for InitiatorCallback for Initiator

Param BParam B Param BParam B Callback for ACallback for A

Param CParam C Param CParam C Callback for BCallback for B

Page 13: IRP Voodoo

Calling Down The IRP StackCalling Down The IRP StackStep by stepStep by step

1.1. Set up the next location’s parametersSet up the next location’s parameters Use Use IoGetNextIrpStackLocationIoGetNextIrpStackLocation to get a pointer to the to get a pointer to the

locationlocation Fill in the appropriate parametersFill in the appropriate parameters

2.2. Set a completion routine callback for post-Set a completion routine callback for post-processing if neededprocessing if needed

Use Use IoSetCompletionRoutineIoSetCompletionRoutine..

3.3. Call the next driverCall the next driver IoCallDriverIoCallDriver automatically advances the IRP’s stack automatically advances the IRP’s stack

pointerpointer

IRP HeaderIRP Header

Param for AParam for A Param for AParam for A Param for AParam for A Callback for InitiatorCallback for Initiator

Param for BParam for B Param for BParam for B Param for BParam for B Callback for ACallback for A

Page 14: IRP Voodoo

Completing a RequestCompleting a Request

IoCompleteRequestIoCompleteRequest moves the IRP stack pointer moves the IRP stack pointer upwardsupwards

Completion routines get called when the stack Completion routines get called when the stack pointer arrives back at their locationpointer arrives back at their location

The current stack location points to the original parametersThe current stack location points to the original parameters Completion Routines are the IRP equivalent of return Completion Routines are the IRP equivalent of return

addressesaddresses

IRP HeaderIRP HeaderCurrent Stack LocationCurrent Stack Location

Param for AParam for A Param for AParam for A Param for AParam for A Callback for InitiatorCallback for Initiator

Param for BParam for B Param for BParam for B Param for BParam for B Callback for ACallback for A

Param for CParam for C Param for CParam for C Param for CParam for C Callback for BCallback for B

Page 15: IRP Voodoo

Completion RoutinesCompletion Routines

Completion routines can return eitherCompletion routines can return either STATUS_MORE_PROCESSING_REQUIREDSTATUS_MORE_PROCESSING_REQUIRED

This halts the upward movement of the IRP’s stack pointerThis halts the upward movement of the IRP’s stack pointer STATUS_CONTINUE_COMPLETIONSTATUS_CONTINUE_COMPLETION

This continues upward completion of the IRPThis continues upward completion of the IRP Actually, returning any value other than Actually, returning any value other than

STATUS_MORE_PROCESSING_REQUIREDSTATUS_MORE_PROCESSING_REQUIRED does this does this Pseudo-code for some of IoCompleteRequest:Pseudo-code for some of IoCompleteRequest:

while ( Can advance stack location ) {while ( Can advance stack location ) {

status = CompletionRoutine(…);status = CompletionRoutine(…);

if (status == STATUS_MORE_PROCESSING_REQUIRED) {if (status == STATUS_MORE_PROCESSING_REQUIRED) {

return; // return; // Stop upward completion Stop upward completion } }}}

// No more stack locations to complete…// No more stack locations to complete…

Page 16: IRP Voodoo

Completing I/O RequestsCompleting I/O Requests

When every sub-request is complete…When every sub-request is complete… The main request is complete tooThe main request is complete too The status is retrieved from IrpThe status is retrieved from IrpIoStatus.StatusIoStatus.Status The number of bytes transferred is retrieved from The number of bytes transferred is retrieved from

IrpIrpIoStatus.InformationIoStatus.Information More details later…More details later…

IRP HeaderIRP Header

Current Stack LocationCurrent Stack Location

IoStatus.StatusIoStatus.Status

IoStatus.InformationIoStatus.Information

Param for AParam for A Param for AParam for A Param for AParam for A Callback for InitiatorCallback for Initiator

Param for BParam for B Param for BParam for B Param for BParam for B Callback for ACallback for A

Param for CParam for C Param for CParam for C Param for CParam for C Callback for BCallback for B

Page 17: IRP Voodoo

Sub-Request DepthSub-Request Depth

Each IRP is allocated with a Each IRP is allocated with a fixed number of sub-requestsfixed number of sub-requests This typically comes from the This typically comes from the

StackSizeStackSize field of the top device field of the top device object in a stackobject in a stack

Usually the number of device Usually the number of device objects in the stackobjects in the stack

Implication: Driver’s must Implication: Driver’s must allocate a new IRP if they need allocate a new IRP if they need to forward requests to another to forward requests to another stackstack

Device ADevice A

Device BDevice B

Device CDevice C

33

22

11

Page 18: IRP Voodoo

Synchronous RequestsSynchronous Requests

Most application requests are synchronousMost application requests are synchronous But any I/O can respond asynchronouslyBut any I/O can respond asynchronously

Logic for sending a synchronous I/O request could Logic for sending a synchronous I/O request could be designed like this:be designed like this:

// Register something that will set an event// Register something that will set an event// (Not shown)// (Not shown)

// Send the IRP down// Send the IRP downIoCallDriver(nextDevice, Irp);IoCallDriver(nextDevice, Irp);

// Wait on some event that will be signaled// Wait on some event that will be signaledKeWaitForSingleObject( &event, ... );KeWaitForSingleObject( &event, ... );

// Get the final status// Get the final statusstatus = Irpstatus = IrpIoStatus.Status;IoStatus.Status;

Page 19: IRP Voodoo

Design ProblemDesign Problem

KeWaitForSingleObject grabs the KeWaitForSingleObject grabs the Dispatcher LockDispatcher Lock

Hottest lock in the operating systemHottest lock in the operating system This lock protects the signal state of This lock protects the signal state of

events, semaphores, mutexes, etcevents, semaphores, mutexes, etc Also protects the thread schedulerAlso protects the thread scheduler

Except on Win2003Except on Win2003

And the wait usually isn’t neededAnd the wait usually isn’t needed Most I/O responses are synchronous Most I/O responses are synchronous

anyway!anyway!

Page 20: IRP Voodoo

NT Design SolutionNT Design Solution

Make IoCallDriver return a status, eitherMake IoCallDriver return a status, either The IRP’s status when the driver completed it, orThe IRP’s status when the driver completed it, or STATUS_PENDINGSTATUS_PENDING

Visualization trick – draw completion routines Visualization trick – draw completion routines one slot higher in the IRP:one slot higher in the IRP:

Completion Routine Completion Routine for Initiatorfor Initiator

Param for AParam for A Param for AParam for A Param for AParam for A Completion Routine Completion Routine for Afor A

Param for BParam for B Param for BParam for B Param for BParam for B Completion Routine Completion Routine for Bfor B

Param for CParam for C Param for CParam for C Param for CParam for C

Note: Completion routines shown one slot higher than in real IRPNote: Completion routines shown one slot higher than in real IRP

Page 21: IRP Voodoo

NTSTATUS and IoCallDriverNTSTATUS and IoCallDriver

To respond synchronously:To respond synchronously: Completes IRP with statusCompletes IRP with status Then returns that statusThen returns that status

““Here’s what I completed with”Here’s what I completed with”

Caller above now has two ways to get resultCaller above now has two ways to get result From completion routineFrom completion routine From unwind of IoCallDriverFrom unwind of IoCallDriver

Completion Routine Completion Routine for Initiatorfor Initiator

Param for AParam for A Param for AParam for A Param for AParam for A Completion Routine Completion Routine for Afor A

Param for BParam for B Param for BParam for B Param for BParam for B

Note: Completion routines shown one slot higher than in real IRPNote: Completion routines shown one slot higher than in real IRP

Status = IoCallDriver(…)Status = IoCallDriver(…)IrpIrpIoStatus.StatusIoStatus.Status

Page 22: IRP Voodoo

NTSTATUS and IoCallDriverNTSTATUS and IoCallDriver

status = IoCallDriver(…);status = IoCallDriver(…);

Note: Completion routines shown one slot higher than in real IRPNote: Completion routines shown one slot higher than in real IRP

Completion for AppCompletion for App

Param for AParam for A Param for AParam for A Completion for ACompletion for A

Param for BParam for B Param for BParam for B Completion for BCompletion for B

Param for CParam for C Param for CParam for C

status = Irpstatus = IrpIoStatus.StatusIoStatus.Status

STATUS_SUCCESSSTATUS_SUCCESSSTATUS_SUCCESSSTATUS_SUCCESS

STATUS_RETRYSTATUS_RETRYSTATUS_RETRYSTATUS_RETRY

STATUS_ERRORSTATUS_ERRORSTATUS_ERRORSTATUS_ERROR

Synchronous completion: right side, then left sideSynchronous completion: right side, then left side

IrpIrpIoStatus.Status = STATUS_SUCCESS;IoStatus.Status = STATUS_SUCCESS;IoCompleteRequest(Irp, IO_NO_INCREMENT);IoCompleteRequest(Irp, IO_NO_INCREMENT);return STATUS_SUCCESS;return STATUS_SUCCESS;

Driver CDriver C

Page 23: IRP Voodoo

Synchronous RequestsSynchronous Requests

Synchronous case can now avoid the Synchronous case can now avoid the KeWaitForSingleObject callKeWaitForSingleObject call

Only needs to be done if STATUS_PENDING is returnedOnly needs to be done if STATUS_PENDING is returned

// Register something that will set an event (Not shown)// Register something that will set an event (Not shown)

// Send the IRP down// Send the IRP downstatus = IoCallDriver(nextDevice, Irp);status = IoCallDriver(nextDevice, Irp);

if (status == STATUS_PENDING) {if (status == STATUS_PENDING) {

// Wait on some event that will be signaled// Wait on some event that will be signaled KeWaitForSingleObject( &event, ... );KeWaitForSingleObject( &event, ... );

// Get the final status// Get the final status status = Irp status = IrpIoStatus.Status;IoStatus.Status;}}

Page 24: IRP Voodoo

Asynchronous ResponsesAsynchronous Responses

A driver returns STATUS_PENDING when it A driver returns STATUS_PENDING when it responds asynchronously to an I/O Requestresponds asynchronously to an I/O Request

Specifically, STATUS_PENDING is returned ifSpecifically, STATUS_PENDING is returned if The dispatch routine might unwind before the IRP The dispatch routine might unwind before the IRP

completes past the driver’s locationcompletes past the driver’s location The IRP is completed past the driver’s location on The IRP is completed past the driver’s location on

another threadanother thread The dispatch routine doesn’t know what the status The dispatch routine doesn’t know what the status

code will becode will be

Page 25: IRP Voodoo

IoMarkIrpPendingIoMarkIrpPending

A driver must call IoMarkIrpPending before it A driver must call IoMarkIrpPending before it returns returns STATUS_PENDINGSTATUS_PENDING

This sets a bit (This sets a bit (SL_PENDING_RETURNEDSL_PENDING_RETURNED) in the ) in the currentcurrent stack location stack location

Note: Completion routines shown one slot higher than in real IRPNote: Completion routines shown one slot higher than in real IRP

SL_PENDING_RETURNEDSL_PENDING_RETURNED

IoMarkIrpPending(Irp);IoMarkIrpPending(Irp);

Completion RoutineCompletion Routinefor Initiatorfor Initiator

Control for AControl for A Param for AParam for A Param for AParam for A Param for AParam for A Completion RoutineCompletion Routinefor Afor A

Control for BControl for B Param for AParam for A Param for AParam for A Param for AParam for A

Page 26: IRP Voodoo

IoMarkIrpPending (cont)IoMarkIrpPending (cont)

Each time an IRP location is completed…Each time an IRP location is completed… The I/O Manager copies its The I/O Manager copies its SL_PENDING_RETURNED SL_PENDING_RETURNED

bit to the IRP header’s PendingReturned fieldbit to the IRP header’s PendingReturned field

Note: Completion routines shown one slot higher than in real IRPNote: Completion routines shown one slot higher than in real IRP

SL_PENDING_RETURNED

SL_PENDING_RETURNED

IRP HeaderIRP HeaderPendingReturnedPendingReturned

Current Stack LocationCurrent Stack Location

Completion RoutineCompletion Routinefor Initiatorfor Initiator

Control for AControl for A Param for AParam for A Param for AParam for A Param for AParam for A Completion RoutineCompletion Routinefor Afor A

Control for BControl for B Param for BParam for B Param for BParam for B Param for BParam for B

Page 27: IRP Voodoo

Return Result RulesReturn Result Rules

The IrpThe IrpPendingReturned field lets the completion PendingReturned field lets the completion routine know whether the lower driver responded routine know whether the lower driver responded asynchronouslyasynchronously

Case 1: Synchronous responseCase 1: Synchronous response IrpIrpPendingReturned is clearPendingReturned is clear IoCallDriver does not return STATUS_PENDINGIoCallDriver does not return STATUS_PENDING

Returns same value completion routine sees in Returns same value completion routine sees in IrpIrpIoStatus.StatusIoStatus.Status

Note: Completion routines shown one slot higher than in real IRPNote: Completion routines shown one slot higher than in real IRP

Completion Routine Completion Routine for Initiatorfor Initiator

Param for AParam for A Param for AParam for A Param for AParam for A Completion Routine Completion Routine for Afor A

Param for BParam for B Param for BParam for B Param for BParam for B

IoCallDriver returns IoCallDriver returns IrpIrpIoStatus.StatusIoStatus.Status

IrpIrpPendingReturned = PendingReturned =

FALSEFALSE

Page 28: IRP Voodoo

Return Result Rules (cont)Return Result Rules (cont)

Case 2: Asynchronous ResponseCase 2: Asynchronous Response IrpIrpPendingReturned is setPendingReturned is set IoCallDriver must return STATUS_PENDINGIoCallDriver must return STATUS_PENDING

Completion routine gets the final result from Completion routine gets the final result from IrpIrpIoStatus.StatusIoStatus.Status

Note: Completion routines shown one slot higher than in real IRPNote: Completion routines shown one slot higher than in real IRP

Completion Routine Completion Routine for Initiatorfor Initiator

Param for AParam for A Param for AParam for A Param for AParam for A Completion Routine Completion Routine for Afor A

Param for BParam for B Param for BParam for B Param for BParam for B

IoCallDriver returns IoCallDriver returns STATUS_PENDINGSTATUS_PENDING

IrpIrpPendingReturned = PendingReturned = TRUETRUE

Page 29: IRP Voodoo

Pending Bit TricksPending Bit Tricks

Our synchronous request code looked like this:Our synchronous request code looked like this:

KeInitializeEvent(&event, NotificationEvent, FALSE);KeInitializeEvent(&event, NotificationEvent, FALSE);

// Set a completion routine that will catch the IRP// Set a completion routine that will catch the IRPIoSetCompletionRoutine(Irp, CatchIrpRoutine, &event, ...);IoSetCompletionRoutine(Irp, CatchIrpRoutine, &event, ...);

// Send the IRP down// Send the IRP downstatus = IoCallDriver(nextDevice, Irp);status = IoCallDriver(nextDevice, Irp);

if (status == STATUS_PENDING) {if (status == STATUS_PENDING) {

// Wait on some event that will be signaled// Wait on some event that will be signaled KeWaitForSingleObject( &event, ... );KeWaitForSingleObject( &event, ... );

// Get the final status// Get the final status status = Irp status = IrpIoStatus.Status;IoStatus.Status;}}

Page 30: IRP Voodoo

Pending Bit Tricks (cont)Pending Bit Tricks (cont)

Here’s the completion routine:Here’s the completion routine:

NTSTATUSNTSTATUSCatchIrpRoutine( CatchIrpRoutine( IN PDEVICE_OBJECT IN PDEVICE_OBJECT DeviceObject,DeviceObject, IN PIRPIN PIRP Irp, Irp, IN PKEVENTIN PKEVENT EventEvent )){{ if (Irpif (IrpPendingReturned) {PendingReturned) {

// Release waiting thread// Release waiting thread KeSetEvent( Event, IO_NO_INCREMENT, FALSE );KeSetEvent( Event, IO_NO_INCREMENT, FALSE ); }}

return STATUS_MORE_PROCESSING_REQUIRED;return STATUS_MORE_PROCESSING_REQUIRED;}}

Event is set only if caller will see STATUS_PENDINGEvent is set only if caller will see STATUS_PENDING This avoids yet another hit on the Dispatcher Lock!This avoids yet another hit on the Dispatcher Lock!

Page 31: IRP Voodoo

Golden Pending RuleGolden Pending Rule

If a driver returns STATUS_PENDING, it must mark If a driver returns STATUS_PENDING, it must mark the IRP stack location pendingthe IRP stack location pending

Similarly, if a driver marks the IRP stack location Similarly, if a driver marks the IRP stack location pending, it must return STATUS_PENDINGpending, it must return STATUS_PENDING

This is illegal:This is illegal:

IoMarkIrpPending(Irp);IoMarkIrpPending(Irp);

If ( Some error condition ) {If ( Some error condition ) {

IrpIrpIoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;

IoCompleteRequest(Irp, IO_NO_INCREMENT);IoCompleteRequest(Irp, IO_NO_INCREMENT);

return STATUS_INSUFFICIENT_RESOURCES;return STATUS_INSUFFICIENT_RESOURCES;}}

Must return STATUS_PENDINGMust return STATUS_PENDING

Page 32: IRP Voodoo

Forwarding RequestsForwarding Requests

Use IoCopyCurrentIrpStackLocationToNext to Use IoCopyCurrentIrpStackLocationToNext to forward a request to another driverforward a request to another driver The function looks like this:The function looks like this:

RtlCopyMemory( IoGetNextIrpStackLocation(Irp), RtlCopyMemory( IoGetNextIrpStackLocation(Irp), IoGetCurrentIrpStackLocation(Irp), IoGetCurrentIrpStackLocation(Irp), sizeof( sizeof(IO_STACK_LOCATIONIO_STACK_LOCATION));));IoSetCompletionRoutine( Irp, ..., NULL, NULL );IoSetCompletionRoutine( Irp, ..., NULL, NULL );

Code doing this by hand often misses the last stepCode doing this by hand often misses the last step And completion routines get called twice!And completion routines get called twice!

Return result of IoCallDriver to next deviceReturn result of IoCallDriver to next device

IRP HeaderIRP Header

Param for AParam for A Param for AParam for A Param for AParam for A Callback for InitiatorCallback for Initiator

Param for BParam for B Param for BParam for B Param for BParam for B Callback for ACallback for A

Page 33: IRP Voodoo

Migrating the Pending BitMigrating the Pending Bit

Consider what happens if a driver has this Consider what happens if a driver has this code:code:

If we return STATUS_PENDING, we must If we return STATUS_PENDING, we must mark our stack location pendingmark our stack location pending

// Forward request to next driver// Forward request to next driverIoCopyCurrentIrpStackLocationToNext( Irp );IoCopyCurrentIrpStackLocationToNext( Irp );

// Send the IRP down// Send the IRP downstatus = IoCallDriver( nextDevice, Irp );status = IoCallDriver( nextDevice, Irp );

// Return the lower driver’s status// Return the lower driver’s statusreturn status;return status;

Page 34: IRP Voodoo

Migrating the Pending BitMigrating the Pending Bit

This trick doesn’t work thoughThis trick doesn’t work though

IoMarkIrpPending operates on the current IoMarkIrpPending operates on the current stack location, and there isn’t one!stack location, and there isn’t one!

// Forward request to next driver// Forward request to next driverIoCopyCurrentIrpStackLocationToNext( Irp );IoCopyCurrentIrpStackLocationToNext( Irp );

// Send the IRP down// Send the IRP downstatus = IoCallDriver( nextDevice, Irp );status = IoCallDriver( nextDevice, Irp );

If (status == STATUS_PENDING) {If (status == STATUS_PENDING) {

IoMarkIrpPending( Irp );IoMarkIrpPending( Irp );}}

// Return the lower driver’s status// Return the lower driver’s statusreturn status;return status;

Page 35: IRP Voodoo

Migrating the Pending BitMigrating the Pending Bit

This problem can be solved in the completion routineThis problem can be solved in the completion routine

This “boilerplate” code should only be used when This “boilerplate” code should only be used when returning the lower driver’s answerreturning the lower driver’s answer

If no completion routine is specified, the OS does If no completion routine is specified, the OS does this automaticallythis automatically

NTSTATUSNTSTATUSCompletionRoutine( ... )CompletionRoutine( ... ){{ if (Irpif (IrpPendingReturned) {PendingReturned) {

// We’re returning the lower driver’s result. So if the// We’re returning the lower driver’s result. So if the // lower driver marked its stack location pending, so do we.// lower driver marked its stack location pending, so do we. IoMarkIrpPending( Irp ); IoMarkIrpPending( Irp ); }}

return STATUS_CONTINUE_COMPLETION;return STATUS_CONTINUE_COMPLETION;}}

Page 36: IRP Voodoo

Forwarding Requests (cont)Forwarding Requests (cont)

If a driver returns the answer of a driver If a driver returns the answer of a driver beneath it, and sets a completion routine…beneath it, and sets a completion routine…

The completion routine The completion routine mustmust migrate the pending migrate the pending bitbit

The completion routine must The completion routine must notnot change the change the statusstatus The dispatch has committed to the lower driver’s answerThe dispatch has committed to the lower driver’s answer

The completion routine The completion routine mustmust return return STATUS_CONTINUE_COMPLETIONSTATUS_CONTINUE_COMPLETION The IRP must not be completed asynchronously later, asThe IRP must not be completed asynchronously later, as The lower driver’s response may have been The lower driver’s response may have been

synchronoussynchronous

Page 37: IRP Voodoo

Building IRPsBuilding IRPs

There are two types of IRPs that can be There are two types of IRPs that can be builtbuilt ““Threaded” IRPsThreaded” IRPs ““Non-Threaded” IRPsNon-Threaded” IRPs These are sometimes termed synchronous These are sometimes termed synchronous

and asynchronous requestsand asynchronous requests Yes, this is confusingYes, this is confusing

Page 38: IRP Voodoo

Building Threaded IRPsBuilding Threaded IRPs

Threaded IRPs are bound to the current thread at Threaded IRPs are bound to the current thread at build timebuild time

They are automatically canceled when their thread is They are automatically canceled when their thread is terminatedterminated

Caller provides event, buffers for I/O Manager to setCaller provides event, buffers for I/O Manager to set The I/O Manager frees threaded IRP upon final The I/O Manager frees threaded IRP upon final

completioncompletion FunctionsFunctions

IoBuildSynchronousFsdRequestIoBuildSynchronousFsdRequest IoBuildDeviceIoControlRequestIoBuildDeviceIoControlRequest

Page 39: IRP Voodoo

Threaded IRP CompletionThreaded IRP Completion

When every sub-request is complete…When every sub-request is complete… The main request is complete tooThe main request is complete too

The I/O Manager performs post-processingThe I/O Manager performs post-processing Copies kernel data to “user mode” pointersCopies kernel data to “user mode” pointers

Pointers pass to ZwReadFile, IoBuildDeviceIoControl, Pointers pass to ZwReadFile, IoBuildDeviceIoControl, etcetc

Signals supplied event when doneSignals supplied event when done

IrpIrpUserIosb.StatusUserIosb.Status IrpIrpIoStatus.StatusIoStatus.Status

IrpIrpUserIosb.InformationUserIosb.Information IrpIrpIoStatus.InformationIoStatus.Information

User Mode BuffersUser Mode Buffers Kernel Mode SnapshotsKernel Mode Snapshots

User Mode EventUser Mode Event Signal when doneSignal when done

Page 40: IRP Voodoo

Building Non-Threaded IRPsBuilding Non-Threaded IRPs

Non-threaded IRPs are not associated with any threadNon-threaded IRPs are not associated with any thread Initiator of request must catch IRP with a completion Initiator of request must catch IRP with a completion

routine and free itroutine and free it I/O Manager does not handle final completion for these IRPsI/O Manager does not handle final completion for these IRPs

These are really meant for driver to driver These are really meant for driver to driver communicationcommunication

FunctionsFunctions IoBuildAsynchronousFsdRequestIoBuildAsynchronousFsdRequest IoAllocateIrpIoAllocateIrp

Page 41: IRP Voodoo

More Pending Bit TricksMore Pending Bit Tricks

Clever code can use the pending bit to decide where Clever code can use the pending bit to decide where to do post-processing workto do post-processing work

Synchronous completion case: Initiator thread does post-Synchronous completion case: Initiator thread does post-processing processing If IoCallDriver doesn’t return STATUS_PENDINGIf IoCallDriver doesn’t return STATUS_PENDING

Asynchronous completion case: Completion Routine does Asynchronous completion case: Completion Routine does post-processingpost-processing Only if IrpOnly if IrpPendingReturn is TRUEPendingReturn is TRUE

This design is often more efficientThis design is often more efficient Less stack space, better cache localityLess stack space, better cache locality

Windows does this for reads, writes, some IOCTLsWindows does this for reads, writes, some IOCTLs Pending bugs can cause post-processing to occur twice Pending bugs can cause post-processing to occur twice

(crash), or never (hang)(crash), or never (hang)

Page 42: IRP Voodoo

Sub-RequestsSub-Requestsor Dispatchesor Dispatches

Major codesMajor codes IRP_MJ_CREATEIRP_MJ_CREATE IRP_MJ_CLEANUPIRP_MJ_CLEANUP IRP_MJ_CLOSEIRP_MJ_CLOSE IRP_MJ_READIRP_MJ_READ IRP_MJ_WRITEIRP_MJ_WRITE IRP_MJ_DEVICE_CONTROLIRP_MJ_DEVICE_CONTROL IRP_MJ_INTERNAL_DEVICE_CONTROLIRP_MJ_INTERNAL_DEVICE_CONTROL

Others not discussedOthers not discussed IRP_MJ_PNP, IRP_MJ_POWER, IRP_MJ_PNP, IRP_MJ_POWER,

IRP_MJ_SYSTEM_CONTROL, …IRP_MJ_SYSTEM_CONTROL, …

Page 43: IRP Voodoo

File ObjectsFile Objects

File Objects are created when a device is File Objects are created when a device is openedopened

File Objects represent a virtual read/write File Objects represent a virtual read/write head for an individual filehead for an individual file

File Handles identify File ObjectsFile Handles identify File Objects

FileFile

File ObjectFile Object File ObjectFile Object

……010101…010101… ……010101010101

Han

dle

Han

dle

Han

dle

Han

dle

Page 44: IRP Voodoo

IRP_MJ_CREATEIRP_MJ_CREATE

This request is sent when a device is openedThis request is sent when a device is opened A User Mode application calls CreateFileA User Mode application calls CreateFile A kernel mode driver calls ZwCreateFileA kernel mode driver calls ZwCreateFile

The File Object is in the current stack The File Object is in the current stack locationlocation

File ObjectFile Object

IRP HeaderIRP HeaderCurrent Stack LocationCurrent Stack Location

IRP_MJ_CREATEIRP_MJ_CREATE

Flags, Security, OptionsFlags, Security, Options

Page 45: IRP Voodoo

IRP_MJ_CREATEIRP_MJ_CREATE

The file object specifies the name of the file The file object specifies the name of the file to opento open FileObjectFileObjectFileNameFileName

The file object contains two pointers for The file object contains two pointers for driver usedriver use FileObjectFileObjectFsContext and FsContext and

FileObjectFileObjectFsContext2FsContext2 In a PnP stack, only the FDO can use theseIn a PnP stack, only the FDO can use these Filesystem stacks use special functions to share Filesystem stacks use special functions to share

the fields among multiple driversthe fields among multiple drivers

Page 46: IRP Voodoo

IRP_MJ_CLEANUPIRP_MJ_CLEANUP

Cleanup requests are sent when the last Cleanup requests are sent when the last handlehandle to a to a file object has been closedfile object has been closed

A driver should complete any pending IRPs using A driver should complete any pending IRPs using the specified file objectthe specified file object Releasing the IRPs allows the File Object to be destroyedReleasing the IRPs allows the File Object to be destroyed

File ObjectFile Object

IRP HeaderIRP HeaderCurrent Stack LocationCurrent Stack Location

IRP_MJ_CLEANUPIRP_MJ_CLEANUP

Han

dle

Han

dle

Page 47: IRP Voodoo

IRP_MJ_CLOSEIRP_MJ_CLOSE

Close requests are sent whenClose requests are sent when All handles to a file object have been closed All handles to a file object have been closed All references have been droppedAll references have been dropped

IRP HeaderIRP HeaderCurrent Stack LocationCurrent Stack Location

IRP_MJ_CLOSEIRP_MJ_CLOSE

File ObjectFile Object

Page 48: IRP Voodoo

Standard LifetimeStandard Lifetime

1.1. Create - new file object notificationCreate - new file object notification Arrives in the context of the callerArrives in the context of the caller Per-caller information can be recordedPer-caller information can be recorded

2.2. Cleanup - closed file Cleanup - closed file handlehandle notification notification For last handle onlyFor last handle only Arrives in the context of the callerArrives in the context of the caller Per-process information can be released herePer-process information can be released here

3.3. Close - deleted file object notificationClose - deleted file object notification Does *not* arrive in the context of the caller!Does *not* arrive in the context of the caller!

Page 49: IRP Voodoo

Create, Close, and CleanupCreate, Close, and Cleanup

Trap: cleanup can appear in a different context than Trap: cleanup can appear in a different context than create!create! A file handle can be duplicated into another processA file handle can be duplicated into another process

say via fork()!say via fork()! The new handle can be closed lastThe new handle can be closed last

Drivers don’t get notified about thisDrivers don’t get notified about this Be very careful when using process information!Be very careful when using process information!

FileFile

File ObjectFile Object

……010101…010101…

Han

dle

Han

dle

Han

dle

Han

dle

Page 50: IRP Voodoo

Transferring DataTransferring Data

There are three flavors of I/O in NTThere are three flavors of I/O in NT BufferedBuffered

Driver works on a safe kernel snapshot of the Driver works on a safe kernel snapshot of the datadata

DirectDirect Driver directly accesses user data via MDLs Driver directly accesses user data via MDLs

and kernel pointersand kernel pointers

““Neither”Neither” Driver directly accesses user data via user Driver directly accesses user data via user

pointerpointer

Page 51: IRP Voodoo

Standard I/O (reads, writes)Standard I/O (reads, writes) BufferedBuffered

DeviceObjectDeviceObjectFlags has DO_BUFFERED_IO setFlags has DO_BUFFERED_IO set Data is in IrpData is in IrpAssociatedIrp.SystemBufferAssociatedIrp.SystemBuffer

DirectDirect DeviceObjectDeviceObjectFlags has DO_DIRECT_IO setFlags has DO_DIRECT_IO set Data accessed via MDL at IrpData accessed via MDL at IrpMdlAddressMdlAddress

MDL is Memory Descriptor ListMDL is Memory Descriptor List An MDL is essentially a list of virtual and physical pages An MDL is essentially a list of virtual and physical pages

covering the buffercovering the buffer Use MmGetSystemAddressForMdlSafe to get a kernel pointerUse MmGetSystemAddressForMdlSafe to get a kernel pointer

““Neither”Neither” Neither flag is setNeither flag is set Data is at IrpData is at IrpUserBufferUserBuffer

See WinHEC 2002 talk See WinHEC 2002 talk Understanding Driver SecurityUnderstanding Driver Security

Page 52: IRP Voodoo

Standard I/O (cont)Standard I/O (cont)

IRP_MJ_READ reads from the deviceIRP_MJ_READ reads from the device IRP_MJ_WRITE writes to the deviceIRP_MJ_WRITE writes to the device The file (if applicable) is determined by information The file (if applicable) is determined by information

cached in the File Objectcached in the File Object FsContext, FsContext2FsContext, FsContext2

Security was previously resolved at create timeSecurity was previously resolved at create time And is now cached in the handle for use by the OSAnd is now cached in the handle for use by the OS

File ObjectFile Object

IRP HeaderIRP Header

Current Stack LocationCurrent Stack Location

Either SystemBufferEither SystemBuffer or MdlAddress or MdlAddress or UserBuffer or UserBuffer

IRP_MJ_READ orIRP_MJ_READ or

IRP_MJ_WRITEIRP_MJ_WRITE

Length, OffsetLength, Offset

Page 53: IRP Voodoo

I/O Controls (IOCTL)’sI/O Controls (IOCTL)’s

Everything that isn’t read or writeEverything that isn’t read or write Each request has both Input Each request has both Input and and Output buffersOutput buffers I/O method (Direct, Buffered, or Neither) is per I/O method (Direct, Buffered, or Neither) is per

IOCTLIOCTL Specified by Method field in control codeSpecified by Method field in control code

Two separate codesTwo separate codes IRP_MJ_DEVICE_CONTROLIRP_MJ_DEVICE_CONTROL

Only these IOCTL’s can be sent from User ModeOnly these IOCTL’s can be sent from User Mode

IRP_MJ_INTERNAL_DEVICE_CONTROLIRP_MJ_INTERNAL_DEVICE_CONTROL This is for driver to driver communicationThis is for driver to driver communication

MethodMethodFunctionFunctionAccessAccessDevice NumberDevice Number

3131 1515 1313 22

Page 54: IRP Voodoo

METHOD_BUFFERED IOCTLsMETHOD_BUFFERED IOCTLs

Everything is done through snapshot in Everything is done through snapshot in IrpIrpAssociatedIrp.SystemBufferAssociatedIrp.SystemBuffer

““Safest” type of IOCTLSafest” type of IOCTL Read at most InputBufferLengthRead at most InputBufferLength Write at most OutputBufferLengthWrite at most OutputBufferLength

File ObjectFile Object

IRP HeaderIRP HeaderCurrent Stack LocationCurrent Stack Location SystemBufferSystemBuffer

IRP_MJ_DEVICE_CONTROLIRP_MJ_DEVICE_CONTROL

InputBufferLengthInputBufferLengthOutputBufferLengthOutputBufferLength

IoControlCodeIoControlCode

Page 55: IRP Voodoo

METHOD_OUT_DIRECT IOCTLsMETHOD_OUT_DIRECT IOCTLs

OutDirect IOCTLs represent OutDirect IOCTLs represent readsreads Hardware is “Outputting” data to the PCHardware is “Outputting” data to the PC

Yes, this is confusingYes, this is confusing Also written as METHOD_DIRECT_FROM_HARDWAREAlso written as METHOD_DIRECT_FROM_HARDWARE

SystemBuffer points to snapshotted commandSystemBuffer points to snapshotted command ““In” buffer for DeviceIoControl APIIn” buffer for DeviceIoControl API

MdlAddress identifies PC’s receiving bufferMdlAddress identifies PC’s receiving buffer ““Out” buffer for DeviceIoControl APIOut” buffer for DeviceIoControl API

File File ObjectObject

IRP HeaderIRP Header

Current Stack LocationCurrent Stack Location

SystemBuffer SystemBuffer

MdlAddress MdlAddress

IRP_MJ_DEVICE_CONTROLIRP_MJ_DEVICE_CONTROL

InputBufferLengthInputBufferLengthOutputBufferLengthOutputBufferLength

IoControlCodeIoControlCode

CommandCommand

Receiving Receiving BufferBuffer

Page 56: IRP Voodoo

METHOD_IN_DIRECT IOCTLsMETHOD_IN_DIRECT IOCTLs

InDirect IOCTLs represent InDirect IOCTLs represent writeswrites Hardware is “Inputting” data from the PCHardware is “Inputting” data from the PC

Also written as METHOD_DIRECT_TO_HARDWAREAlso written as METHOD_DIRECT_TO_HARDWARE SystemBuffer points to snapshotted commandSystemBuffer points to snapshotted command

““In” buffer to DeviceIoControl APIIn” buffer to DeviceIoControl API MdlAddress identifies PC’s transmit bufferMdlAddress identifies PC’s transmit buffer

““Out” buffer Out” buffer for DeviceIoControl APIfor DeviceIoControl API

File File ObjectObject

IRP HeaderIRP Header

Current Stack LocationCurrent Stack Location

SystemBuffer SystemBuffer

MdlAddress MdlAddress

IRP_MJ_DEVICE_CONTROLIRP_MJ_DEVICE_CONTROL

InputBufferLengthInputBufferLengthOutputBufferLengthOutputBufferLength

IoControlCodeIoControlCode

CommandCommand

Transmit Transmit BufferBuffer

Page 57: IRP Voodoo

METHOD_NEITHER IOCTLsMETHOD_NEITHER IOCTLs

Neither IOCTLs are defined by the driverNeither IOCTLs are defined by the driver There are separate user mode pointers for input There are separate user mode pointers for input

and outputand output IrpSpIrpSpParameters.DeviceIoControl.Parameters.DeviceIoControl.Type3InputBufferType3InputBuffer is is

DeviceIoControl API’s “in” bufferDeviceIoControl API’s “in” buffer IrpIrpUserBufferUserBuffer is DeviceIoControl API’s “out” buffer is DeviceIoControl API’s “out” buffer

File ObjectFile Object

IRP HeaderIRP HeaderCurrent Stack LocationCurrent Stack Location User BufferUser Buffer

IRP_MJ_DEVICE_CONTROLIRP_MJ_DEVICE_CONTROL

InputBufferLengthInputBufferLengthOutputBufferLengthOutputBufferLength

IoControlCodeIoControlCode

Output BufferOutput Buffer( Receiving )( Receiving )

Type3InputBufferType3InputBuffer ( Transmitting ) ( Transmitting )

Page 58: IRP Voodoo

Completing I/O Requests Completing I/O Requests (Revisited)(Revisited) If the entire IRP is completedIf the entire IRP is completed

Method Buffered I/O: data is copied back to the Method Buffered I/O: data is copied back to the caller’s original buffercaller’s original buffer

The results in the IRP’s status block (Status and The results in the IRP’s status block (Status and Information) are copied back to the caller’s original Information) are copied back to the caller’s original blockblock

The event specified by the request’s initiator is The event specified by the request’s initiator is signaledsignaled

None of the above happen though if the IRP None of the above happen though if the IRP was completed with an error statuswas completed with an error status Do Do notnot wait on the event if STATUS_PENDING isn’t wait on the event if STATUS_PENDING isn’t

returned by ZwReadFile, ZwDeviceIoControl, etc…returned by ZwReadFile, ZwDeviceIoControl, etc…

Page 59: IRP Voodoo

Errors and WarningsErrors and Warnings

NTSTATUS codes 0xC0000000 - 0xFFFFFFFF are NTSTATUS codes 0xC0000000 - 0xFFFFFFFF are errorserrors

NTSTATUS codes 0x80000000 - 0xBFFFFFFF are NTSTATUS codes 0x80000000 - 0xBFFFFFFF are warningswarnings

No data is transferred on an errorNo data is transferred on an error IrpIrpIoStatus.Information should always be zeroIoStatus.Information should always be zero Example: STATUS_BUFFER_TOO_SMALLExample: STATUS_BUFFER_TOO_SMALL

Data Data cancan be transferred on a warning however be transferred on a warning however Example: STATUS_BUFFER_OVERFLOWExample: STATUS_BUFFER_OVERFLOW

Page 60: IRP Voodoo

Errors and Warnings (example)Errors and Warnings (example)

Let’s say a driver is filling out a Let’s say a driver is filling out a bufferbuffer The buffer contains both a The buffer contains both a

Length field and Data fieldLength field and Data field Length receives size Length receives size requiredrequired to to

retrieve Dataretrieve Data Application steps areApplication steps are

Send a ULONG sized request to Send a ULONG sized request to get Lengthget Length

Resend with bigger buffer to Resend with bigger buffer to retrieve entire Dataretrieve entire Data

Key pointKey point Driver always expects at least a Driver always expects at least a

ULONG worth of bufferULONG worth of buffer

Output BufferOutput Buffer

ULONG Length;ULONG Length;

UCHAR Data[ANY_SIZE];UCHAR Data[ANY_SIZE];

Page 61: IRP Voodoo

Errors and Warnings (example)Errors and Warnings (example)

If the buffer is big enoughIf the buffer is big enough Length and IrpLength and IrpIoStatus.Information IoStatus.Information

receive the number of bytes transferredreceive the number of bytes transferred The IRP is completed with The IRP is completed with

STATUS_SUCCESSSTATUS_SUCCESS If Data isn’t big enoughIf Data isn’t big enough

Length is updated with the size requiredLength is updated with the size required IrpIrpIoStatus.Information = IoStatus.Information =

sizeof(ULONG);sizeof(ULONG); The IRP is completed with warning code The IRP is completed with warning code

STATUS_BUFFER_OVERFLOWSTATUS_BUFFER_OVERFLOW If buffer size < sizeof(ULONG)If buffer size < sizeof(ULONG)

There isn’t enough room to write out the There isn’t enough room to write out the size required!size required!

IrpIrpIoStatus.Information = 0;IoStatus.Information = 0; Complete with error code Complete with error code

STATUS_BUFFER_TOO_SMALLSTATUS_BUFFER_TOO_SMALL

Output BufferOutput Buffer

ULONG Length;ULONG Length;

UCHAR Data[ANY_SIZE];UCHAR Data[ANY_SIZE];

Page 62: IRP Voodoo

Driver VerifierDriver Verifier

No driver talk is complete without mentioning No driver talk is complete without mentioning the Verifierthe Verifier

The Driver Verifier catches mistakes in The Driver Verifier catches mistakes in everything shown so fareverything shown so far It exists in every copy of WindowsIt exists in every copy of Windows Just run Verifier.exeJust run Verifier.exe Works best with a debugger attachedWorks best with a debugger attached

Page 63: IRP Voodoo

Additional ResourcesAdditional Resources

WinHEC 2002 PresentationsWinHEC 2002 Presentationshttp://www.microsoft.com/hwdev/futurepc/winhec2002/default.asphttp://www.microsoft.com/hwdev/futurepc/winhec2002/default.asp Understanding Driver SecurityUnderstanding Driver Security Windows Driver Power ManagementWindows Driver Power Management

WinHEC 2001 PresentationsWinHEC 2001 Presentationshttp://www.microsoft.com/winhec/winhec2001.mspxhttp://www.microsoft.com/winhec/winhec2001.mspx How to Write a WDM Driver That Cannot Possibly How to Write a WDM Driver That Cannot Possibly

WorkWork

Different Ways of Handling IRPsDifferent Ways of Handling IRPs Part 1Part 1

http://support.microsoft.com/default.aspx?scid=kb;en-us;320275http://support.microsoft.com/default.aspx?scid=kb;en-us;320275

Part 2 Part 2 http://support.microsoft.com/default.aspx?scid=kb;en-us;326315http://support.microsoft.com/default.aspx?scid=kb;en-us;326315


Recommended