Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Az illesztőprogramok egyik leggyakoribb hibája a pufferkezeléssel kapcsolatos, ahol a pufferek érvénytelenek vagy túl kicsik. Ezek a hibák lehetővé teszik a puffer túlcsordulását, vagy rendszer összeomlását okozhatják, ami veszélyeztetheti a rendszer biztonságát. Ez a cikk a pufferkezelés néhány gyakori problémáját és azok elkerülésének módját ismerteti. Emellett azonosítja a WDK-mintakódot, amely bemutatja a megfelelő pufferkezelési technikákat.
Puffertípusok és érvénytelen címek
A vezető szempontjából a pufferek két fajtából származnak:
Lapozott pufferek, amelyek a memóriában lehetnek, de lehet, hogy nem.
Nem lapított pufferek, amelyeknek memóriában kell lenniük.
Az érvénytelen memóriacím sem lapozott, sem nem lapozott. Mivel az operációs rendszer a helytelen pufferkezelés által okozott laphiba megoldásán dolgozik, a következő lépéseket hajtja végre:
Elkülöníti az érvénytelen címet az egyik "standard" címtartományba (lapozott kernelcímek, nem lapozott kernelcímek vagy felhasználói címek).
A megfelelő hibátípusra hivatkozik. A rendszer mindig egy hibaellenőrzéssel(például PAGE_FAULT_IN_NONPAGED_AREA) vagy egy kivétellel(például STATUS_ACCESS_VIOLATION) kezeli a pufferhibákat. Ha a hiba hibaellenőrzés, a rendszer leállítja a műveletet. Kivétel esetén a rendszer meghívja a veremalapú kivételkezelőket. Ha a kivételt egyik kivételkezelő sem kezeli, a rendszer hibaellenőrzést hív meg.
Függetlenül attól, bármely olyan elérési út, amelyet egy alkalmazásprogram meghívhat és amely az illesztőprogramot hibaellenőrzéshez vezeti, biztonsági megsértésnek számít az illesztőprogramon belül. Ez a szabálysértés lehetővé teszi, hogy az alkalmazás szolgáltatásmegtagadási támadásokat okozzon a teljes rendszerre.
Gyakori feltételezések és hibák
Ezen a területen az egyik leggyakoribb probléma, hogy az illesztőprogram-írók túl sokat feltételeznek az üzemeltetési környezetről. Néhány gyakori feltételezés és hiba:
Az illesztőprogram egyszerűen ellenőrzi, hogy a magas bit be van-e állítva a címben. Ha rögzített bitmintára támaszkodik a címtípus meghatározásához, az nem minden rendszeren vagy forgatókönyvben működik. Ez az ellenőrzés például nem működik x86-alapú számítógépeken, ha a rendszer négy gigabájtos hangolást (4GT) használ. A 4GT használata esetén a felhasználói módú címek a címtér harmadik gigabájtjára állítják be a nagy bitet.
Egy illesztőprogram kizárólag ProbeForRead és ProbeForWrite használatával ellenőrzi a címet. Ezek a hívások biztosítják, hogy a cím érvényes felhasználói módú cím legyen a mintavétel időpontjában. A mintavételi művelet után azonban nincs garancia arra, hogy ez a cím érvényes marad. Így ez a technika egy finom versenyhelyzetet vezet be, amely rendszeres, helyrehozhatatlan összeomlásokhoz vezethet.
A ProbeForRead és a ProbeForWrite hívásokra továbbra is szükség van. Ha egy illesztőprogram kihagyja a vizsgálatot, a felhasználók olyan érvényes kernel-módú címeket adhatnak át, amelyeket egy
__tryés__exceptblokk (strukturált kivételkezelés) nem érzékel, és így jelentős biztonsági rést nyitnak meg.A lényeg az, hogy mind a próba, mind a strukturált kivételkezelés szükséges:
A érvényesítés ellenőrzi, hogy a cím felhasználói módú cím-e, és hogy a puffer hossza a felhasználói címtartományon belül van-e.
A
__try/__exceptblokkok védik a hozzáférést.
Vegye figyelembe, hogy a ProbeForRead csak azt ellenőrzi, hogy a cím és a hossz a lehetséges felhasználói módú címtartományba esik -e (például egy 4GT-t nem tartalmazó rendszer esetén valamivel 2 GB alatt), nem pedig azt, hogy a memóriacím érvényes-e. Ezzel szemben a ProbeForWrite megpróbálja elérni az egyes lapok első bájtját a megadott hosszúságban annak ellenőrzéséhez, hogy ezek a bájtok érvényes memóriacímek-e.
Az illesztőprogramok memóriakezelői függvényekre, például az MmIsAddressValid-ra támaszkodnak, hogy biztosítsák a cím érvényességét. A mintavételi függvények leírása szerint ez a helyzet olyan versenyhelyzetet vezet be, amely visszafordíthatatlan összeomlásokhoz vezethet.
Egy sofőr nem használ strukturált kivételkezelést. A
__try/exceptfordítóban lévő függvények operációs rendszerszintű támogatást használnak a kivételkezeléshez. A rendszer kernelszintű kivételeket az ExRaiseStatus vagy a kapcsolódó függvények egyikének hívásával ad vissza a rendszernek. Ha az illesztőprogram nem használ strukturált kivételkezelést bármely olyan hívás körül, amely kivételt okozhat, hibaellenőrzéshez vezet (általában KMODE_EXCEPTION_NOT_HANDLED).Hiba strukturált kivételkezelést használni olyan kód körül, amely várhatóan nem okoz hibákat. Ez a használat csak elfedi az egyébként megtalálható valódi hibákat. Nem a megfelelő megoldás erre a problémára, ha egy
__try/__exceptburkolót a rutin legfelső diszpécserszintjéhez helyezünk, bár néha ez az illesztőprogram-írók által kipróbált reflexmegoldás.Az illesztőprogram feltételezi, hogy a felhasználói memória tartalma stabil marad. Tegyük fel például, hogy egy illesztőprogram egy értéket írt egy felhasználói módú memóriahelyre, majd később ugyanabban a rutinban hivatkozott az adott memóriahelyre. Egy rosszindulatú alkalmazás aktívan módosíthatja ezt a memóriát az írás után, és ennek következtében az illesztőprogram összeomlik.
A fájlrendszerek esetében ezek a problémák súlyosak, mivel a fájlrendszerek általában a felhasználói pufferek közvetlen elérésére támaszkodnak (az METHOD_NEITHER átviteli módszer). Az ilyen illesztőprogramok közvetlenül manipulálják a felhasználói puffereket, ezért az operációs rendszerszintű összeomlások elkerülése érdekében be kell építeni a pufferkezelésre vonatkozó elővigyázatossági módszereket. A gyors I/O mindig nyers memóriamutatókat ad át, így a gyors I/O támogatása esetén az illesztőprogramoknak védelmet kell nyújtaniuk a hasonló problémák ellen.
Mintakód pufferkezeléshez
A WDK számos példát tartalmaz a pufferérvényesítésre a fastfat és a CDFS fájlrendszer-illesztőprogram mintakódjában, beleértve a következőket:
A fastfat\deviosup.cfatLockUserBuffer függvénye az MmProbeAndLockPages használatával zárolja a felhasználói puffer mögötti fizikai lapokat, a FatMapUserBufferben pedig az MmGetSystemAddressForMdlSafe függvényt a zárolt lapok virtuális leképezésének létrehozásához.
A fastfat\fsctl.cfatGetVolumeBitmapfüggvénye a ProbeForRead és a ProbeForWrite használatával ellenőrzi a felhasználói puffereket a töredezettségmentesítési API-ban.
A CdCommonRead függvény a cdfs\read.c fájlban a felhasználói pufferek nullázására használja a kód körüli
__tryés__exceptszakaszokat. A CdCommonRead mintakódjában úgy tűnik, hogy atryés aexceptkulcsszavakat használja. A WDK-környezetben ezek a C nyelvű kulcsszavak az alábbi fordítóbővítményekkel vannak meghatározva:__tryés__except. Bárki, aki C++ kódot használ, a natív fordítótípusokat kell használnia a kivételek megfelelő kezeléséhez, ahogyan__tryaz egy C++ kulcsszó, de nem C kulcsszó, és olyan C++ kivételkezelést biztosít, amely nem érvényes a kernelillesztőkre.