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 yang terjadi jika driver melewati NULLMdlAddress. Para pengemudi 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 alamat halaman virtual pengguna menjadi offset ke halaman sistem.

    Akses yang melampaui batas akhir buffer sistem ini mungkin tidak terdeteksi untuk waktu yang lama, tergantung pada tingkat kehalusan halaman peta. Kecuali buffer penelepon dialokasikan di dekat akhir halaman, data yang ditulis di luar akhir buffer tetap 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 pengguna dan mode kernel, 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, harus dipastikan bahwa MDL diakses hanya dengan metode yang telah dilakukan pengujiannya. Artinya, ketika driver memanggil MmProbeAndLockPages, driver 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.