가장 일반적인 직접 I/O 문제는 길이가 0인 버퍼를 올바르게 처리하지 못하는 것입니다. I/O 관리자는 길이가 0인 전송에 대한 MDL을 만들지 않으므로 길이가 0인 버퍼는 Irp-MdlAddress>에서 NULL 값을 생성합니다.
주소 공간을 매핑하려면 드라이버는 MmGetSystemAddressForMdlSafe를 사용해야 하며, 이 함수는 드라이버가 NULLMdlAddress를 전달하는 경우처럼 매핑이 실패할 때 NULL을 반환합니다. 드라이버는 반환된 주소를 사용하기 전에 항상 NULL 반환을 확인해야 합니다.
직접 I/O에는 사용자의 주소 공간을 시스템 주소 버퍼에 두 번 매핑하여 두 개의 서로 다른 가상 주소가 동일한 실제 주소를 갖도록 합니다. 이중 매핑에는 다음과 같은 결과가 발생하며, 이로 인해 드라이버에 문제가 발생할 수 있습니다.
사용자 주소의 가상 페이지에 대한 오프셋은 시스템 페이지의 오프셋으로 변환됩니다.
이러한 시스템 버퍼가 끝날 때까지의 액세스는 매핑의 페이지 세분성에 따라 오랜 시간 동안 눈에 띄지 않을 수 있습니다. 호출자의 버퍼가 페이지 끝 근처에 할당되지 않는 한 버퍼의 끝 부분에 기록된 데이터는 버퍼에 표시되고 호출자는 오류가 발생했음을 인식하지 못합니다. 버퍼의 끝이 페이지 끝과 일치하는 경우 끝 너머의 시스템 가상 주소는 아무 것도 가리키거나 유효하지 않을 수 있습니다. 이러한 문제는 찾기가 매우 어려울 수 있습니다.
호출 프로세스에 사용자의 메모리 매핑을 수정하는 다른 스레드가 있는 경우 사용자의 메모리 매핑이 변경되면 시스템 버퍼의 내용이 변경됩니다.
이 경우 시스템 버퍼를 사용하여 스크래치 데이터를 저장하면 문제가 발생할 수 있습니다. 동일한 메모리 위치에서 두 개의 인출은 서로 다른 값을 생성할 수 있습니다.
다음 코드 조각은 직접 I/O 요청에서 문자열을 수신한 다음 해당 문자열을 대문자로 변환하려고 시도합니다.
PWCHAR PortName = NULL; PortName = (PWCHAR)MmGetSystemAddressForMdlSafe(irp->MdlAddress, NormalPagePriority); // // Null-terminate the PortName so that RtlInitUnicodeString will not // be invalid. // PortName[Size / sizeof(WCHAR) - 1] = UNICODE_NULL; RtlInitUnicodeString(&AdapterName, PortName);버퍼가 올바르게 구성되지 않을 수 있으므로 코드는 유니코드 NULL 을 마지막 버퍼 문자로 강제 적용하려고 시도합니다. 그러나 기본 실제 메모리가 사용자 및 커널 모드 주소 모두에 두 배로 매핑되는 경우 이 쓰기 작업이 완료되는 즉시 프로세스의 다른 스레드가 버퍼를 덮어쓸 수 있습니다.
반대로 NULL 이 없으면 RtlInitUnicodeString에 대한 호출이 버퍼의 범위를 초과할 수 있으며 시스템 매핑을 벗어나면 버그 검사가 발생할 수 있습니다.
드라이버가 자신의 MDL을 만들고 매핑하는 경우, 확인된 메서드를 사용하여서만 MDL에 액세스해야 합니다. 즉, 드라이버가 MmProbeAndLockPages를 호출할 때 액세스 메서드(IoReadAccess, IoWriteAccess 또는 IoModifyAccess)를 지정합니다. 드라이버가 IoReadAccess를 지정하는 경우 나중에 MmGetSystemAddressForMdl 또는 MmGetSystemAddressForMdlSafe에서 사용할 수 있는 시스템 버퍼에 쓰려고 시도해서는 안 됩니다.