Aracılığıyla paylaş


Doğrudan G/Ç'deki hatalar

En yaygın doğrudan G/Ç sorunu sıfır uzunluklu arabellekleri doğru işleyememesidir. G/Ç yöneticisi sıfır uzunluklu aktarımlar için MDL'ler oluşturmadığından, sıfır uzunluklu bir arabellek Irp-MdlAddress'de NULL değerine neden olur.

Adres alanını eşlemek için sürücüler, eşleme başarısız olursa NULL döndüren MmGetSystemAddressForMdlSafe kullanmalıdır. Bu, bir sürücünün NULLMdlAddress geçirmesi durumunda olduğu gibi null döndürür. Sürücüler, döndürülen adresi kullanmaya çalışmadan önce her zaman NULL dönüş olup olmadığını denetlemelidir.

Doğrudan G/Ç, iki farklı sanal adresin aynı fiziksel adrese sahip olması için kullanıcının adres alanını bir sistem adresi arabelleğine çift eşlemeyi içerir. Çift eşlemenin aşağıdaki sonuçları vardır ve bu da bazen sürücüler için sorunlara neden olabilir:

  • Kullanıcının adresinin sanal sayfasına uzaklık, sistem sayfasına uzaklık haline gelir.

    Bu sistem arabelleklerinin sonunun ötesindeki erişim, eşlemenin sayfa ayrıntı düzeyine bağlı olarak uzun süreler boyunca gözlerden kaçabilir. Çağıranın arabelleği sayfanın sonuna yakın bir şekilde ayrılmadığı sürece, arabelleğin sonuna kadar yazılan veriler yine de arabellekte görünür ve çağıran herhangi bir hata oluştuğunun farkında olmaz. Arabelleğin sonu bir sayfanın sonuyla çakışıyorsa, bitişin ötesindeki sistem sanal adresleri herhangi bir şeyi işaret edebilir veya geçersiz olabilir. Bu tür sorunları bulmak son derece zor olabilir.

  • Çağıran işlemde kullanıcının bellek eşlemesini değiştiren başka bir iş parçacığı varsa, kullanıcının bellek eşlemesi değiştiğinde sistem arabelleğinin içeriği değişir.

    Bu durumda, geçici verileri depolamak için sistem arabelleğinin kullanılması sorunlara neden olabilir. Aynı bellek konumundan iki getirme farklı değerler verebilir.

    Aşağıdaki kod parçacığı doğrudan G/Ç isteğinde bir dize alır ve bu dizeyi büyük harf karakterlere dönüştürmeye çalışır:

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

    Arabellek doğru biçimlendirilmeyebileceği için kod, son arabellek karakteri olarak Unicode NULL'yi zorlamaya çalışır. Ancak, temel alınan fiziksel bellek hem kullanıcı hem de çekirdek modu adresiyle iki kez eşlenirse, bu yazma işlemi tamamlanır tamamlanmaz işlemdeki başka bir iş parçacığı arabelleğin üzerine yazabilir.

    Buna karşılık NULL yoksa, RtlInitUnicodeString çağrısı arabelleğin aralığını aşabilir ve hafıza sistem eşlemesinin dışına düşerse bir hata denetimine yol açabilir.

Bir sürücü kendi MDL'sini oluşturur ve eşlerse, yalnızca test ettiği yöntemle MDL'ye eriştiğinden emin olmalıdır. Yani, sürücü MmProbeAndLockPages'i çağırdığında bir erişim yöntemi (IoReadAccess,IoWriteAccess veya IoModifyAccess) belirtir. Sürücü IoReadAccess belirtiyorsa, daha sonra MmGetSystemAddressForMdl veya MmGetSystemAddressForMdlSafe tarafından sağlanan sistem arabelleğine yazmaya çalışmamalıdır.