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.
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.
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.
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;
}
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;
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;