Sdílet prostřednictvím


Chyby v přímých vstupně-výstupních operacích

Nejběžnějším problémem přímého I/O je nesprávné zpracování vyrovnávacích pamětí nulové délky. Vzhledem k tomu, že správce vstupně-výstupních operací nevytvoří knihovny MDLS pro přenosy nulové délky, výsledkem vyrovnávací paměti nulové délky je hodnota NULL v Irp-MdlAddress>.

K mapování adresního prostoru by ovladače měly použít MmGetSystemAddressForMdlSafe, který v případě selhání mapování vrátí NULL, například pokud ovladač předá NULLMdlAddress. Ovladače by před pokusem o použití vrácené adresy měly vždy zkontrolovat vrácení hodnoty NULL .

Přímé vstupně-výstupní operace zahrnují dvojité mapování adresního prostoru uživatele na vyrovnávací paměť systémových adres, aby dvě různé virtuální adresy měly stejnou fyzickou adresu. Dvojité mapování má následující důsledky, které mohou někdy způsobit problémy s ovladači.

  • Posun na virtuální stránku adresy uživatele se změní na posun na systémovou stránku.

    Přístup nad rámec těchto bufferů systému může v závislosti na granularitě stránky mapování zůstat nepovšimnutý po dlouhé časové období. Pokud není vyrovnávací paměť volajícího přidělena na konci stránky, data zapsaná za koncem vyrovnávací paměti se však zobrazí ve vyrovnávací paměti a volající nebude vědět, že došlo k chybě. Pokud se konec vyrovnávací paměti shoduje s koncem stránky, mohou systémové virtuální adresy za koncem odkazovat na cokoli nebo mohou být neplatné. Takové problémy mohou být extrémně obtížné najít.

  • Pokud má volající proces další vlákno, které upravuje mapování paměti uživatele, obsah systémové vyrovnávací paměti se změní při změně mapování paměti uživatele.

    V takovém případě může použití systémové vyrovnávací paměti k ukládání pomocných dat způsobit problémy. Dva načtení ze stejného umístění paměti můžou přinést různé hodnoty.

    Následující fragment kódu obdrží řetězec v přímém V/V požadavku a pak se pokusí tento řetězec převést na velká písmena:

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

    Protože vyrovnávací paměť nemusí být správně vytvořena, kód se pokusí vynutit poslední znak vyrovnávací paměti jako Unicode NULL. Pokud je však základní fyzická paměť dvakrát namapována jak na adresu v uživatelském režimu, tak na adresu v režimu jádra, může jiné vlákno v procesu přepsat vyrovnávací paměť hned po dokončení této operace zápisu.

    Pokud hodnota NULL není k dispozici, může volání RtlInitUnicodeString překročit rozsah vyrovnávací paměti a případně způsobit kontrolu chyb, pokud spadá mimo mapování systému.

Pokud ovladač vytvoří a mapuje svůj vlastní MDL, musí zajistit, aby k MDL přistupoval pouze pomocí metody, pro kterou má prozkoumal. To znamená, že když ovladač volá MmProbeAndLockPages, určuje metodu přístupu (IoReadAccess, IoWriteAccess nebo IoModifyAccess). Pokud ovladač určuje IoReadAccess, nesmí se později pokoušet zapisovat do systémové vyrovnávací paměti zpřístupněné MmGetSystemAddressForMdl nebo MmGetSystemAddressForMdlSafe.