Avoiding Misalignment of Fixed-Precision Data Types

Unfortunately,it is possible for a data type to have the same size, but different alignmentrequirements, for 32-bit and 64-bit programming. Thus not all IOCTL/FSCTLbuffer misalignment problems can be avoided by changing pointer-precision datatypes to fixed-precision types. This means that kernel-mode driver IOCTLs andFSCTLs that pass buffers containing certain fixed-precision data types (orpointers to them) may also need to be thunked.

WhichData Types Are Affected

The problemaffects fixed-precision data types that are themselves structures. This isbecause the rules for determining alignment requirements for structures areplatform-specific.

For example, __int64, LARGE_INTEGER, andKFLOATING_SAVE must be aligned on a 4-byte boundary on x86 platforms. However,on Itanium-based machines, they must be aligned on an 8-byte boundary.

To determinethe alignment requirement for a given data type on a particular platform, usethe TYPE_ALIGNMENT macro on thatplatform.

HowTo Fix the Problem

In thefollowing example, the IOCTL is a METHOD_NEITHER IOCTL, so the Irp->UserBuffer pointer is passeddirectly from the user-mode application to the kernel-mode driver. No validationis performed on buffers used in IOCTLs and FSCTLs. Thus a call to ProbeForRead or ProbeForWrite is required before thebuffer pointer can be safely dereferenced.

Assuming thatthe 32-bit application has passed a valid value for Irp->UserBuffer, the LARGE_INTEGERstructure pointed to by p->DeviceTime will be aligned on a 4-byteboundary. ProbeForRead checks this alignment against the value passed in its Alignment parameter, which inthis case is TYPE_ALIGNMENT (LARGE_INTEGER). On x86platforms, this macro expression returns 4 (bytes). However, on Itanium-basedmachines, it returns 8, causing ProbeForRead to raise aSTATUS_DATATYPE_MISALIGNMENT exception.

Note   Removing the ProbeForRead call does not fix theproblem, but only makes it harder to diagnose.


typedefstruct _IOCTL_PARAMETERS2 {

LARGE_INTEGER DeviceTime;

}IOCTL_PARAMETERS2, *PIOCTL_PARAMETERS2;


#defineSETTIME_FUNCTION 1

#defineIOCTL_SETTIME CTL_CODE(FILE_DEVICE_UNKNOWN, \

SETTIME_FUNCTION, METHOD_NEITHER,FILE_ANY_ACCESS)


...


caseIOCTL_SETTIME:

PIOCTL_PARAMETERS2 p = (PIOCTL_PARAMETERS2)Irp->UserBuffer;


try {

if (Irp->RequestorMode !=KernelMode) {

ProbeForRead ( p->DeviceTime,

sizeof( LARGE_INTEGER ),

TYPE_ALIGNMENT(LARGE_INTEGER ));

}

status = DoSomeWork(p->DeviceTime);


} except( EXCEPTION_EXECUTE_HANDLER ) {


The followingsections tell how to fix the problem described above. Note that all codesnippets have been edited for brevity.

Solution1: Copy the Buffer

The safest wayto avoid misalignment problems is to make a copy of the buffer before accessingits contents, as in the following example.


caseIOCTL_SETTIME: {

PIOCTL_PARAMETERS2 p =(PIOCTL_PARAMETERS2)Irp->UserBuffer;

#if_WIN64

IOCTL_PARAMETERS2 LocalParams2;


RtlCopyMemory(&LocalParams2, p,sizeof(IOCTL_PARAMETERS2));

p = &LocalParams2;

#endif


status = DoSomeWork(p->DeviceTime);

break;

}

This solutioncan be optimized for better performance by first checking whether the buffercontents are correctly aligned. If so, the buffer can be used as is. Otherwise,the driver makes a copy of the buffer.


caseIOCTL_SETTIME: {

PIOCTL_PARAMETERS2 p =(PIOCTL_PARAMETERS2)Irp->UserBuffer;

#if_WIN64

IOCTL_PARAMETERS2 LocalParams2;


if ( (ULONG_PTR)p &(TYPE_ALIGNMENT(IOCTL_PARAMETERS2)-1)) {

// The buffer contents are notcorrectly aligned for this

// platform, so copy them into aproperly aligned local

// buffer.

RtlCopyMemory(&LocalParams2, p, sizeof(IOCTL_PARAMETERS2));

p = &LocalParams2;

}

#endif


status = DoSomeWork(p->DeviceTime);

break;

}

Solution2: Use the UNALIGNED Macro

The UNALIGNED macro tells the Ccompiler to generate code that can access the DeviceTime field without takingan alignment fault. Note that using this macro on Itanium-based platforms islikely to make your driver significantly larger and slower.


typedefstruct _IOCTL_PARAMETERS2 {

LARGE_INTEGER DeviceTime;

}IOCTL_PARAMETERS2;

typedefIOCTL_PARAMETERS2 UNALIGNED *PIOCTL_PARAMETERS2;

PointersAre Also Affected

Themisalignment problem described earlier can also occur in buffered I/O requests.In the following example, the IOCTL buffer contains an embedded pointer to aLARGE_INTEGER structure.


typedefstruct _IOCTL_PARAMETERS3 {

LARGE_INTEGER *pDeviceCount;

}IOCTL_PARAMETERS3, *PIOCTL_PARAMETERS3;0


#defineCOUNT_FUNCTION 1

#defineIOCTL_GETCOUNT CTL_CODE(FILE_DEVICE_UNKNOWN, \

COUNT_FUNCTION, METHOD_BUFFERED,FILE_ANY_ACCESS)

Like theMETHOD_NEITHER IOCTL and FSCTL buffer pointers described earlier, pointersembedded in buffered I/O requests are also passed directly from the user-modeapplication to the kernel-mode driver. No validation is performed on thesepointers. Thus a call to ProbeForRead or ProbeForWrite, enclosed in a try/except block, is requiredbefore the embedded pointer can be safely dereferenced.

As in theearlier example, assuming that the 32-bit application has passed a valid valuefor pDeviceCount, the LARGE_INTEGERstructure pointed to by pDeviceCount will be aligned on a 4-byteboundary. ProbeForRead and ProbeForWrite check this alignment against thevalue of the Alignment parameter, which in this case is TYPE_ALIGNMENT(LARGE_INTEGER). On x86 platforms, this macro expression returns 4 (bytes).However, on Itanium-based machines, it returns 8, causing ProbeForRead or ProbeForWrite to raise aSTATUS_DATATYPE_MISALIGNMENT exception.

This problemcan be fixed either by making a properly aligned copy of the LARGE_INTEGERstructure, as in Solution 1, or by using the UNALIGNED macro as follows:


typedefstruct _IOCTL_PARAMETERS3 {

LARGE_INTEGER UNALIGNED *pDeviceCount;

}IOCTL_PARAMETERS3, *PIOCTL_PARAMETERS3;


你可能感兴趣的:(type,misalignment)