Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Desafortunadamente, es posible que un tipo de datos tenga el mismo tamaño, pero diferentes requisitos de alineación, para la programación de 32 bits y 64 bits. Por lo tanto, no se pueden evitar todos los problemas de alineación incorrecta del búfer IOCTL/FSCTL cambiando los tipos de datos de precisión de puntero a tipos de precisión fija. Esto significa que los IOCTLs y FSCL del controlador en modo kernel que pasan búferes que contienen determinados tipos de datos de precisión fija (o punteros a ellos) también deben ser matizado.
Qué tipos de datos se ven afectados
El problema afecta a los tipos de datos de precisión fija que son estructuras propias. Esto se debe a que las reglas para determinar los requisitos de alineación de las estructuras son específicas de la plataforma.
Por ejemplo, __int64, LARGE_INTEGER y KFLOATING_SAVE deben alinearse en un límite de 4 bytes en plataformas x86. Sin embargo, en las máquinas basadas en Itanium, deben alinearse en un límite de 8 bytes.
Para determinar el requisito de alineación de un tipo de datos determinado en una plataforma determinada, use la macro TYPE_ALIGNMENT en esa plataforma.
Cómo corregir el problema
En el ejemplo siguiente, el IOCTL es un METHOD_NEITHER IOCTL, por lo que el puntero Irp-UserBuffer> se pasa directamente desde la aplicación en modo de usuario al controlador en modo kernel. No se realiza ninguna validación en los búferes usados en ioCTLs y FSCTLs. Por lo tanto, se requiere una llamada a ProbeForRead o ProbeForWrite para que el puntero del búfer se pueda desreferenciar de forma segura.
Suponiendo que la aplicación de 32 bits ha pasado un valor válido para Irp-UserBuffer>, la estructura de LARGE_INTEGER a la que apunta p-DeviceTime> se alineará en un límite de 4 bytes. ProbeForRead comprueba esta alineación con el valor pasado en su parámetro Alignment , que en este caso es TYPE_ALIGNMENT (LARGE_INTEGER). En las plataformas x86, esta expresión de macro devuelve 4 (bytes). Sin embargo, en las máquinas basadas en Itanium, devuelve 8, lo que hace que ProbeForRead genere una excepción de STATUS_DATATYPE_MISALIGNMENT.
Nota Quitar la llamada ProbeForRead no corrige el problema, sino que solo dificulta el diagnóstico.
typedef struct _IOCTL_PARAMETERS2 {
LARGE_INTEGER DeviceTime;
} IOCTL_PARAMETERS2, *PIOCTL_PARAMETERS2;
#define SETTIME_FUNCTION 1
#define IOCTL_SETTIME CTL_CODE(FILE_DEVICE_UNKNOWN, \
SETTIME_FUNCTION, METHOD_NEITHER, FILE_ANY_ACCESS)
...
case IOCTL_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 ) {
En las secciones siguientes se explica cómo corregir el problema descrito anteriormente. Tenga en cuenta que todos los fragmentos de código se han editado para mayor brevedad.
Solución 1: Copiar el búfer
La manera más segura de evitar problemas de desalineación es realizar una copia del búfer antes de acceder a su contenido, como en el ejemplo siguiente.
case IOCTL_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;
}
Esta solución se puede optimizar para mejorar el rendimiento comprobando primero si el contenido del búfer está alineado correctamente. Si es así, el búfer se puede usar tal cual. De lo contrario, el controlador realiza una copia del búfer.
case IOCTL_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 not correctly aligned for this
// platform, so copy them into a properly aligned local
// buffer.
RtlCopyMemory(&LocalParams2, p, sizeof(IOCTL_PARAMETERS2));
p = &LocalParams2;
}
#endif
status = DoSomeWork(p->DeviceTime);
break;
}
Solución 2: Usar la macro UNALIGNED
La macro UNALIGNED indica al compilador de C que genere código que pueda acceder al campo DeviceTime sin tener un error de alineación. Tenga en cuenta que es probable que el uso de esta macro en plataformas basadas en Itanium haga que el controlador sea significativamente mayor y más lento.
typedef struct _IOCTL_PARAMETERS2 {
LARGE_INTEGER DeviceTime;
} IOCTL_PARAMETERS2;
typedef IOCTL_PARAMETERS2 UNALIGNED *PIOCTL_PARAMETERS2;
Los punteros también se ven afectados
El problema de desalineación descrito anteriormente también puede producirse en las solicitudes de E/S almacenadas en búfer. En el ejemplo siguiente, el búfer IOCTL contiene un puntero incrustado a una estructura de LARGE_INTEGER.
typedef struct _IOCTL_PARAMETERS3 {
LARGE_INTEGER *pDeviceCount;
} IOCTL_PARAMETERS3, *PIOCTL_PARAMETERS3;0
#define COUNT_FUNCTION 1
#define IOCTL_GETCOUNT CTL_CODE(FILE_DEVICE_UNKNOWN, \
COUNT_FUNCTION, METHOD_BUFFERED, FILE_ANY_ACCESS)
Al igual que los punteros de búfer de METHOD_NEITHER IOCTL y FSCTL descritos anteriormente, los punteros incrustados en solicitudes de E/S almacenadas en búfer también se pasan directamente desde la aplicación en modo de usuario al controlador en modo kernel. No se realiza ninguna validación en estos punteros. Por lo tanto, se requiere una llamada a ProbeForRead o ProbeForWrite, incluida en un bloque try/except , para que el puntero incrustado se pueda desreferenciar de forma segura.
Como en el ejemplo anterior, suponiendo que la aplicación de 32 bits ha pasado un valor válido para pDeviceCount, la estructura de LARGE_INTEGER a la que apunta pDeviceCount se alineará en un límite de 4 bytes. ProbeForRead y ProbeForWrite comprueban esta alineación con el valor del parámetro Alignment , que en este caso es TYPE_ALIGNMENT (LARGE_INTEGER). En las plataformas x86, esta expresión de macro devuelve 4 (bytes). Sin embargo, en las máquinas basadas en Itanium, devuelve 8, lo que hace que ProbeForRead o ProbeForWrite generen una excepción de STATUS_DATATYPE_MISALIGNMENT.
Este problema se puede corregir mediante la realización de una copia alineada correctamente de la estructura de LARGE_INTEGER, como en la solución 1, o mediante la macro UNALIGNED de la siguiente manera:
typedef struct _IOCTL_PARAMETERS3 {
LARGE_INTEGER UNALIGNED *pDeviceCount;
} IOCTL_PARAMETERS3, *PIOCTL_PARAMETERS3;