Bagikan melalui


Kesalahan dalam I/O Langsung

Masalah I/O langsung yang paling umum adalah gagal menangani buffer panjang nol dengan benar. Karena manajer I/O tidak membuat MDL untuk transfer panjang nol, buffer panjang nol menghasilkan nilai NULL di Irp-MdlAddress>.

Untuk memetakan ruang alamat, driver harus menggunakan MmGetSystemAddressForMdlSafe, yang mengembalikan NULL jika pemetaan gagal, seperti jika driver melewati NULLMdlAddress. Driver harus selalu memeriksa pengembalian NULL sebelum mencoba menggunakan alamat yang dikembalikan.

I/O langsung melibatkan pemetaan ganda ruang alamat pengguna ke buffer alamat sistem, sehingga dua alamat virtual yang berbeda memiliki alamat fisik yang sama. Pemetaan ganda memiliki konsekuensi berikut, yang terkadang dapat menyebabkan masalah bagi driver:

  • Offset ke halaman virtual alamat pengguna menjadi offset ke halaman sistem.

    Akses di luar akhir buffer sistem ini mungkin luput dari pemberitahuan untuk waktu yang lama tergantung pada granularitas halaman pemetaan. Kecuali buffer penelepon dialokasikan di dekat akhir halaman, data yang ditulis di luar akhir buffer akan muncul di buffer, dan pemanggil tidak akan menyadari bahwa kesalahan apa pun telah terjadi. Jika akhir buffer bertepatan dengan akhir halaman, alamat virtual sistem di luar akhir dapat menunjuk ke apa pun atau bisa tidak valid. Masalah seperti itu bisa sangat sulit ditemukan.

  • Jika proses panggilan memiliki utas lain yang memodifikasi pemetaan memori pengguna, konten buffer sistem akan berubah ketika pemetaan memori pengguna berubah.

    Dalam situasi ini, menggunakan buffer sistem untuk menyimpan data scratch dapat menyebabkan masalah. Dua pengambilan dari lokasi memori yang sama mungkin menghasilkan nilai yang berbeda.

    Cuplikan kode berikut menerima string dalam permintaan I/O langsung, lalu mencoba mengonversi string tersebut menjadi karakter huruf besar:

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

    Karena buffer mungkin tidak terbentuk dengan benar, kode mencoba memaksa Unicode NULL sebagai karakter buffer terakhir. Namun, jika memori fisik yang mendasar dipetakan dua kali lipat ke alamat mode kernel dan pengguna, utas lain dalam proses dapat menimpa buffer segera setelah operasi penulisan ini selesai.

    Sebaliknya, jika NULL tidak ada, maka panggilan ke RtlInitUnicodeString dapat melebihi rentang buffer dan mungkin menyebabkan pemeriksaan bug jika berada di luar pemetaan sistem.

Jika driver membuat dan memetakan MDL-nya sendiri, driver harus memastikan bahwa ia mengakses MDL hanya dengan metode yang telah diselimutinya. Artinya, ketika driver memanggil MmProbeAndLockPages, itu menentukan metode akses (IoReadAccess, IoWriteAccess, atau IoModifyAccess). Jika driver menentukan IoReadAccess, tidak boleh kemudian mencoba menulis ke buffer sistem yang disediakan oleh MmGetSystemAddressForMdl atau MmGetSystemAddressForMdlSafe.