Dela via


Fel i Direkt-I/O

Det vanligaste direkta I/O-problemet är att det inte går att hantera buffertar med noll längd korrekt. Eftersom I/O-chefen inte skapar MDL:er för nolllängdsöverföringar resulterar en nolllängdsbuffert i ett NULL-värde vid Irp-MdlAddress>.

För att mappa adressutrymmet ska drivrutiner använda MmGetSystemAddressForMdlSafe, som returnerar NULL om mappningen misslyckas, som om en drivrutin skickar en NULLMdlAddress. Drivrutiner bör alltid söka efter en NULL-retur innan de försöker använda den returnerade adressen.

Direkt I/O omfattar dubbelmappning av användarens adressutrymme till en systemadressbuffert, så att två olika virtuella adresser har samma fysiska adress. Dubbelmappning har följande konsekvenser, som ibland kan orsaka problem för programvarudrivrutiner:

  • Förskjutningen till den virtuella sidan för användarens adress blir förskjutningen till systemsidan.

    Åtkomst bortom slutet av dessa systembuffertar kan gå obemärkt förbi under långa tidsperioder beroende på mappningens sidkornighet. Om inte en anropares buffert allokeras nära slutet av en sida visas data som skrivits utanför buffertens slut ändå i bufferten, och anroparen är inte medveten om att något fel har inträffat. Om slutet av bufferten sammanfaller med slutet av en sida kan de virtuella systemadresserna utanför slutet peka på vad som helst eller vara ogiltiga. Sådana problem kan vara mycket svåra att hitta.

  • Om anropsprocessen har en annan tråd som ändrar användarens mappning av minnet ändras innehållet i systembufferten när användarens minnesmappning ändras.

    I den här situationen kan det orsaka problem att använda systembufferten för att lagra scratch-data. Två hämtningar från samma minnesplats kan ge olika värden.

    Följande kodfragment tar emot en sträng i en direkt I/O-begäran och försöker sedan konvertera strängen till versaler:

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

    Eftersom bufferten kanske inte är korrekt utformad försöker koden framtvinga en Unicode NULL som det sista bufferttecknet. Men om det underliggande fysiska minnet är dubbelt mappat till både en användaradress och en kernellägesadress, kan en annan tråd i processen skriva över bufferten så snart skrivningen har slutförts.

    Om NULL inte finns kan anropet till RtlInitUnicodeString överskrida buffertintervallet och eventuellt orsaka en buggkontroll om den hamnar utanför systemmappningen.

Om en drivrutin skapar och mappar sin egen MDL bör den se till att den endast kommer åt MDL med den metod som den har avsökt för. När drivrutinen anropar MmProbeAndLockPages anger den en åtkomstmetod (IoReadAccess, IoWriteAccess eller IoModifyAccess). Om drivrutinen anger IoReadAccess får den inte senare försöka skriva till systembufferten som gjorts tillgänglig av MmGetSystemAddressForMdl eller MmGetSystemAddressForMdlSafe.