Zpracování vyrovnávací paměti

Jedna z nejběžnějších chyb v rámci jakéhokoli ovladače souvisí s zpracováním vyrovnávací paměti, kdy vyrovnávací paměti jsou neplatné nebo příliš malé. Tyto chyby můžou umožňovat přetečení vyrovnávací paměti nebo způsobit chybové ukončení systému, což může ohrozit zabezpečení systému. Tento článek popisuje některé běžné problémy se zpracováním vyrovnávací paměti a jak se těmto problémům vyhnout. Identifikuje také vzorový kód WDK, který demonstruje správné techniky zpracování vyrovnávací paměti.

Typy vyrovnávací paměti a neplatné adresy

Z pohledu řidiče přicházejí nárazníky v jedné ze dvou typů:

  • Stránkované vyrovnávací paměti, které můžou nebo nemusí být v paměti rezidentní.

  • Nestránkové vyrovnávací paměti, které musí být trvale umístěny v paměti.

Neplatná adresa paměti není stránkovaná ani nestránkovaná. Vzhledem k tomu, že operační systém vyřeší chybu stránky způsobenou nesprávným zpracováním vyrovnávací paměti, provede následující kroky:

  • Izoluje neplatnou adresu do jednoho ze standardních rozsahů adres (stránkované adresy jádra, nestránkové adresy jádra nebo adresy uživatelů).

  • Vyvolá odpovídající typ chyby. Systém vždy zpracovává chyby vyrovnávací paměti buď kontrolou chyb, jako je PAGE_FAULT_IN_NONPAGED_AREA, nebo výjimkou, jako je STATUS_ACCESS_VIOLATION. Pokud se jedná o kontrolu chyb, systém zastaví operaci. V případě výjimky systém vyvolá obslužné rutiny výjimek na zásobníku založené. Pokud žádný z obslužných rutin výjimek výjimku nezpracuje, systém vyvolá kontrolu chyb.

Bez ohledu na to, jakákoli přístupová cesta, kterou může program aplikace zavolat a která způsobuje kontrolu chyb ze strany ovladače, představuje porušení zabezpečení v rámci ovladače. Takové porušení umožňuje aplikaci způsobit útoky typu odmítnutí služby na celý systém.

Běžné předpoklady a chyby

Jedním z nejběžnějších problémů v této oblasti je, že tvůrci ovladačů mají přílišné předpoklady o provozním prostředí. Mezi běžné předpoklady a chyby patří:

  • Ovladač jednoduše zkontroluje, jestli je v adrese nastavený vysoký bit. Spoléhání na pevný bitový vzor k určení typu adresy nefunguje ve všech systémech nebo scénářích. Tato kontrola například nefunguje na počítačích s x86 architekturou, když systém používá čtyřgigabajtové ladění (4GT). Při použití 4GT nastaví adresy uživatelského režimu vysoký bit pro třetí gigabajt adresního prostoru.

  • Ovladač, který výhradně používá ProbeForRead a ProbeForWrite k ověření adresy. Tato volání zajišťují, aby adresa byla v okamžiku kontroly platná adresa v uživatelském režimu. Neexistují však žádné záruky, že tato adresa zůstane po operaci sondy platná. Tato technika tedy zavádí jemnou podmínku závodu, která může vést k periodickým nereprodukovatelným pádům.

    Volání ProbeForRead a ProbeForWrite jsou stále nutná. Pokud ovladač vynechá sondu, uživatelé mohou zadat platné adresy režimu jádra, které nezachytí ani __try, ani __except (strukturované zpracování výjimek), což může vést k otevření velké bezpečnostní díry.

    Podstatné je, že pro účinné zpracování chyb je nezbytné jak zkoumání, tak strukturované zpracování výjimek.

    • Ověření potvrdí, že adresa je v režimu uživatele a že délka vyrovnávací paměti je v povoleném rozsahu uživatelských adres.

    • Blok __try/__except chrání před přístupem.

    Všimněte si, že ProbeForRead pouze ověří, že adresa a délka spadají do možného rozsahu adres v uživatelském režimu (mírně pod 2 GB systému bez 4GT, například), ne zda je adresa paměti platná. Naproti tomu ProbeForWrite se pokusí získat přístup k prvnímu bajtu na každé stránce délky určené k ověření, že tyto bajty jsou platné adresy paměti.

  • Ovladač, který spoléhá na funkce správce paměti, jako je Například MmIsAddressValid , aby se zajistilo, že adresa je platná. Jak je popsáno pro funkce sondy, tato situace představuje závodní podmínku, která může vést k neopravitelným pádům.

  • Ovladač selhává při zpracování strukturovaných výjimek. Funkce __try/except v kompilátoru používají podporu na úrovni operačního systému pro zpracování výjimek. Výjimky na úrovni jádra jsou vyvolány zpět do systému prostřednictvím volání ExRaiseStatus nebo jedné ze souvisejících funkcí. Ovladač, který nepoužívá strukturované zpracování výjimek kolem jakéhokoli volání, které může vyvolat výjimku, povede ke kontrole chyb (obvykle KMODE_EXCEPTION_NOT_HANDLED).

    Jedná se o chybu použití strukturovaného zpracování výjimek kolem kódu, u kterého se neočekává, že by se vyvolaly chyby. Toto použití pouze maskuje skutečné chyby, které by jinak byly nalezeny. Umístění zabalujícího kódu __try/__except na nejvyšší úrovni dispečera ve vaší rutině není správným řešením tohoto problému, i když je to někdy instinktivní řešení, které se pokoušejí použít autoři ovladačů.

  • Ovladač za předpokladu, že obsah uživatelské paměti zůstane stabilní. Předpokládejme například, že ovladač zapsal hodnotu do paměťového místa v uživatelském režimu a poté se v téže rutině znovu odvolal na toto místo v paměti. Škodlivá aplikace by mohla aktivně upravit paměť po zápisu a v důsledku toho způsobit chybové ukončení ovladače.

U systémů souborů jsou tyto problémy závažné, protože systémy souborů obvykle spoléhají na přímý přístup k uživatelským vyrovnávacím pamětím (metoda přenosu METHOD_NEITHER). Tyto ovladače přímo manipulují s vyrovnávacími paměťmi uživatelů, a proto musí zahrnovat preventivní metody pro zpracování vyrovnávací paměti, aby nedocházelo k chybám na úrovni operačního systému. Rychlé vstupně-výstupní operace vždy předávají nezpracované ukazatele paměti, takže ovladače musí se chránit před podobnými problémy, pokud jsou rychlé vstupně-výstupní operace podporovány.

Ukázkový kód pro zpracování vyrovnávací paměti

WDK obsahuje řadu příkladů ověření vyrovnávací paměti v ukázkovém kódu ovladače systému souborů FASTFAT a CDFS , včetně:

  • Funkce FatLockUserBuffer v fastfat\deviosup.c používá MmProbeAndLockPages k uzamčení fyzických stránek za vyrovnávací pamětí uživatele a MmGetSystemAddressForMdlSafe v FatMapUserBuffer k vytvoření virtuálního mapování stránek, které jsou uzamčeny.

  • Funkce FatGetVolumeBitmap v fastfat\fsctl.c používá ProbeForRead a ProbeForWrite k ověření vyrovnávací paměti uživatele v rozhraní API defragmentace.

  • Funkce CdCommonRead v cdfs\read.c používá __try a __except kolem kódu k vynulování uživatelských vyrovnávacích pamětí. Vzorový kód v CdCommonRead se zdá používat klíčová slova try a except. V prostředí WDK jsou tato klíčová slova v jazyce C definována z hlediska rozšíření __try kompilátoru a __except. Každý, kdo používá kód C++, musí ke správnému zpracování výjimek používat nativní typy kompilátoru, stejně jako __try klíčové slovo jazyka C++, ale ne klíčové slovo jazyka C++ a poskytne formu zpracování výjimek jazyka C++, které není platné pro ovladače jádra.