次の方法で共有


精度が固定されているデータ型の不整合回避

残念ながら、32 ビットと 64 ビットのプログラミングでは、データ型のサイズは同じですが、整列要件が異なる可能性があります。 したがって、ポインター精度データ型を固定精度型に変更することで、IOCTL/FSCTL バッファーの不整列の問題をすべて回避できるわけではありません。 つまり、特定の固定精度データ型 (またはそれらに対するポインター) を含むバッファーを渡すカーネル モード ドライバーの IOCTL と FSCTL もサンクする必要があります。

影響を受けるデータ型

この問題は、それ自体が構造である固定精度データ型に影響します。 これは、構造の整列要件を決定するための規則がプラットフォーム固有であるためです。

たとえば、 __int64、LARGE_INTEGER、KFLOATING_SAVEは、x86 プラットフォームの 4 バイト境界に整列する必要があります。 ただしItanium ベースのマシンでは、8 バイト境界に整列する必要があります。

特定のプラットフォーム上の特定のデータ型の配置要件を判断するには、そのプラットフォームで TYPE_ALIGNMENT マクロを使用します。

問題の解決方法

次の例では、IOCTL は METHOD_NEITHER IOCTL であるため、 Irp-UserBuffer> ポインターはユーザー モード アプリケーションからカーネル モード ドライバーに直接渡されます。 IOCTL および FSCTL で使用されるバッファーに対して検証は実行されません。 したがって、バッファー ポインターを安全に逆参照するには、 ProbeForRead または ProbeForWrite の呼び出しが必要です。

32 ビット アプリケーションが Irp->UserBuffer の有効な値を渡したと仮定すると、 p->DeviceTime が指すLARGE_INTEGER構造体は 4 バイト境界に整列されます。 ProbeForRead は、この整列を 整列 パラメータに渡された値(この場合は TYPE_ALIGNMENT (LARGE_INTEGER))と照合します。 x86 プラットフォームでは、このマクロ式は 4 (バイト) を返します。 ただし、Itanium ベースのマシンでは 8 が返され、 ProbeForRead によってSTATUS_DATATYPE_MISALIGNMENT例外が発生します。

ProbeForRead 呼び出しを削除しても問題は解決されず、診断は困難になります。

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 ) {

次のセクションでは、上記の問題を解決する方法について説明します。 簡潔にするために、すべてのコード スニペットが編集されているので注意してください。

解決策 1:バッファーをコピーする

不整列の問題を回避する最も安全な方法は、次の例のように、バッファーの内容にアクセスする前にバッファーのコピーを作成することです。

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

最初にバッファーの内容が正しく整列されているかどうかを確認することで、パフォーマンスを向上させるためにこのソリューションを最適化できます。 その場合は、バッファーをそのまま使用できます。 それ以外の場合、ドライバーはバッファーのコピーを作成します。

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

解決策 2:不整列のマクロを使用する

不整列 マクロは、配置エラーを発生させずに DeviceTime フィールドにアクセスできるコードを生成するようC コンパイラに指示します。 Itanium ベースのプラットフォームでこのマクロを使用すると、ドライバーが大幅に大きく、低速になる可能性があることに注意してください。

typedef struct _IOCTL_PARAMETERS2 {
    LARGE_INTEGER DeviceTime;
} IOCTL_PARAMETERS2;
typedef IOCTL_PARAMETERS2 UNALIGNED *PIOCTL_PARAMETERS2;

ポインターも影響を受けます

前述の不整列の問題は、バッファー内の I/O 要求でも発生する可能性があります。 次の例では、IOCTL バッファーに、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)

前述の METHOD_NEITHER IOCTL および FSCTL バッファー ポインターと同様に、バッファー I/O 要求に埋め込まれたポインターも、ユーザー モード アプリケーションからカーネル モード ドライバーに直接渡されます。 これらのポインターに対して検証は実行されません。 したがって、埋め込みポインターを安全に逆参照するには、 try/except ブロックで囲まれた ProbeForRead または ProbeForWrite の呼び出しが必要です。

前の例と同様に、32 ビット アプリケーションが pDeviceCount の有効な値を渡したと仮定すると、 pDeviceCount が指すLARGE_INTEGER構造体は 4 バイト境界に配置されます。 ProbeForReadProbeForWrite は、 整列 パラメーターの値に対してこのアラインメントを確認します(この場合はTYPE_ALIGNMENT (LARGE_INTEGER))。 x86 プラットフォームでは、このマクロ式は 4 (バイト) を返します。 ただし、Itanium ベースのマシンでは 8 が返され、 ProbeForRead または ProbeForWrite によってSTATUS_DATATYPE_MISALIGNMENT例外が発生します。

この問題はソリューション 1 のように、LARGE_INTEGER構造の適切に整列されたコピーを作成するか、不整列マクロを使用して次のように修正できます。

typedef struct _IOCTL_PARAMETERS3 {
    LARGE_INTEGER UNALIGNED *pDeviceCount;
} IOCTL_PARAMETERS3, *PIOCTL_PARAMETERS3;