Hibák a közvetlen I/O-ban

A leggyakoribb közvetlen I/O-probléma a nulla hosszúságú pufferek helytelen kezelése. Mivel az I/O-kezelő nem hoz létre MDL-eket nulla hosszúságú átvitelekhez, a nulla hosszúságú puffer null értéket eredményez az Irp-MdlAddressnél>.

A címtér megfeleltetéséhez az illesztőprogramoknak az MmGetSystemAddressForMdlSafe parancsot kell használniuk, amely null értéket ad vissza, ha a leképezés sikertelen, mivel akkor is, ha egy illesztő egy NULLMdlAddress értéket ad vissza. A visszaadott cím használata előtt az illesztőprogramoknak mindig ellenőrizniük kell a NULL értéket.

A közvetlen I/O magában foglalja a felhasználó címterének kettős leképezését egy rendszercímpufferre, így két különböző virtuális cím ugyanazzal a fizikai címmel rendelkezik. A kettős leképezés a következő következményekkel jár, ami néha problémákat okozhat az illesztőprogramok számára:

  • A felhasználó címének virtuális oldalára történő eltolás a rendszeroldal eltolásává válik.

    A rendszerpufferek végén túli hozzáférés a leképezés oldalrészletességétől függően hosszú ideig észrevétlen marad. Ha a hívó puffere nem van lefoglalva a lap vége közelében, a puffer végén kívül írt adatok mégis megjelennek a pufferben, és a hívó nem fogja tudni, hogy hiba történt. Ha a puffer vége egybeesik egy lap végére, a rendszer virtuális címei a végponton túl bármire mutathatnak, vagy érvénytelenek lehetnek. Az ilyen problémákat rendkívül nehéz megtalálni.

  • Ha a hívási folyamat egy másik szálkal rendelkezik, amely módosítja a felhasználó memórialeképezését, a rendszerpuffer tartalma megváltozik, amikor a felhasználó memórialeképezése megváltozik.

    Ebben az esetben a rendszerpuffer használata ideiglenes adatok tárolására problémákat okozhat. Az ugyanabból a memóriahelyről származó két lekérés eltérő értékeket eredményezhet.

    A következő kódrészlet egy sztringet fogad egy közvetlen I/O-kérelemben, majd megpróbálja nagybetűkké alakítani a sztringet:

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

    Előfordulhat, hogy a puffer nem megfelelően van kialakítva, ezért a kód megpróbál unicode NULL-t kényszeríteni az utolsó puffer karakterként. Ha azonban a mögöttes fizikai memória kétszeresen van leképezve mind felhasználói, mind kernelmódú címhez, a folyamat egy másik szála felülírhatja a puffert, amint ez az írási tevékenység befejeződik.

    Ezzel szemben, ha a NULL nincs jelen, akkor az RtlInitUnicodeString hívása túllépheti a puffer tartományát, és hibaellenőrzést okozhat, ha az nem tartozik a rendszerleképezésen.

Ha egy illesztőprogram létrehozza és leképezi a saját MDL-jét, gondoskodnia kell arról, hogy csak azzal a módszerrel fér hozzá az MDL-hez, amelyhez mintavételezett. Ez azt jelenti, hogy amikor az illesztőprogram meghívja az MmProbeAndLockPagest, egy hozzáférési módszert határoz meg (IoReadAccess, IoWriteAccess vagy IoModifyAccess). Ha az illesztőprogram az IoReadAccesst adja meg, később nem kísérelhet meg írni az MmGetSystemAddressForMdl vagy az MmGetSystemAddressForMdlSafe által elérhetővé tett rendszerpufferbe.