A Surface Team illesztőprogram-fejlesztési ajánlott eljárásai

Bevezetés

Ezeket az illesztőprogram-fejlesztési irányelveket a Microsoft illesztőprogram-fejlesztői hosszú éveken keresztül fejlesztették ki. Idővel, amikor a vezetők helytelenül viselkedtek, és leckéket tanultak, ezeket a leckéket rögzítették és továbbfejlesztették, hogy ez legyen az útmutatók készlete. Ezeket az ajánlott eljárásokat a Microsoft Surface Hardver csapata használja az egyedi Surface hardverélményt támogató eszközillesztő-kód fejlesztéséhez és karbantartásához.

Mint minden iránymutatás, itt is lesznek jogos kivételek és alternatív megközelítések, amelyek egyformán érvényesek lesznek. Érdemes lehet ezeket az irányelveket beépíteni a fejlesztési szabványokba, vagy használni őket a fejlesztési környezetre és az egyedi követelményekre vonatkozó tartományspecifikus irányelvek elindításához.

Az illesztőprogram-fejlesztők által elkövetett gyakori hibák

I/O kezelése

  1. Az IOCTL-ekből lekért pufferek elérése a hossz ellenőrzése nélkül. Lásd pufferek méretének ellenőrzésének sikertelenségét.
  2. Az I/O blokkolásának végrehajtása egy felhasználói szál vagy véletlenszerű szál kontextusában. Lásd Bevezetés a Kernel Dispatcher Objectscímű témakört.
  3. Szinkron I/O küldése egy másik illesztőprogramnak időtúllépés nélkül. Lásd: I/O-kérések szinkronizálása.
  4. A neither-io IOCTL-ek használata anélkül, hogy megértenénk a biztonsági következményeket. Lásd a sem pufferelt, sem közvetlen I/O használatát.
  5. Nem ellenőrzi a WdfRequestForwardToIoQueue visszatérési állapotát, vagy nem kezeli megfelelően a kudarcot, ami elhagyott WDFREQUEST-eket eredményez.
  6. A WDFREQUEST nem lemondható állapotban tartása az üzenetsoron kívül. Lásd: I/O-üzenetsorok kezelése, I/O-kérelmek végrehajtása és I/O-kérelmek megszakítása.
  7. A lemondás kezelése a Mark/UnmarkCancelable függvény használatával az IoQueues használata helyett. Lásd: Keretrendszer sorobjektumai.
  8. Nem tudja, mi a különbség a fájlkezelő törlési és a bezárási műveletek között. Lásd: A tisztítási és a bezárási műveletekkezelésével kapcsolatos hibák.
  9. Az esetleges rekurziók figyelmen kívül hagyása az I/O művelet befejezésével és a befejezési rutinból való ismételt benyújtással.
  10. A WDFQUEUE-k energiagazdálkodási attribútumai nem explicitek. Nem dokumentálja egyértelműen az energiagazdálkodási választási lehetőségeket. Ez a hibaellenőrzési 0x9F elsődleges oka: DRIVER_POWER_STATE_FAILURE WDF-illesztőprogramokban. Az eszköz eltávolításakor a keretrendszer az eltávolítási folyamat különböző szakaszaiban törli az IO-t az energiakezeléssel felügyelt és a nem energiakezeléssel felügyelt üzenetsorokból. A nem energiagazdálkodású sorok törlődnek, amikor megérkezik a végső IRP_MN_REMOVE_DEVICE. Ha tehát az I/O nem egy energia felügyelet alatt álló sorban van, akkor célszerű kifejezetten törölni az I/O-t az EvtDeviceSelfManagedIoFlush kontextusában a holtpont elkerülése érdekében.
  11. Nem követi az IRP-k kezelésére vonatkozó szabályokat. Lásd: A tisztítási és a bezárási műveletekkezelésével kapcsolatos hibák.

Szinkronizálás

  1. Olyan kód zárolásának megtartása, amely nem igényel védelmet. Ne tartsa zárolva az egész függvényt, ha csak néhány műveletet kell biztosítani.
  2. Kihívja a zárral rendelkező sofőröket. Ez a holtpontok elsődleges okai.
  3. Egymással összekapcsolt primitívek használata zárolási séma létrehozásához a megfelelő rendszer helyett olyan zárolási primitíveket biztosított, mint a mutex, a szemaphore és a spinlock. Lásd: Bevezetés a Mutex-objektumokba, Szemafor objektumok és Bevezetés a spin zárakba.
  4. Spinlock használata, ahol a passzív zár valamilyen típusa megfelelőbb lenne. Lásd: gyors mutexek és őrzött mutexek és eseményobjektumok. A zárolásokkal kapcsolatos további szempontokért tekintse át az OSR-cikket – A szinkronizálás állapota.
  5. A WDF-szinkronizálási és végrehajtási szintű modell kiválasztása a következmények teljes ismerete nélkül. Lásd: A keretrendszer zárai használata. Hacsak az illesztőprogram nem monolitikus legfelső szintű illesztőprogram, amely közvetlenül kommunikál a hardverrel, kerülje a WDF-szinkronizálást, mivel az rekurzió miatt holtponthoz vezethet.
  6. A KEVENT, Semaphore, ERESOURCE, UnsafeFastMutex beszerzése több szál kontextusában, kritikus régióba való belépés nélkül. Ez DOS-támadáshoz vezethet, mert egy ilyen zárolást tartalmazó szál felfüggeszthető. Lásd Bevezetés a Kernel Dispatcher Objectscímű témakört.
  7. A KEVENT kiosztása a szálveremen, és visszatérés a hívóhoz, amíg az ESEMÉNY még használatban van. Általában akkor készül, amikor IoBuildSyncronousFsdRequest vagy IoBuildDeviceIoControlRequestvan használatban. Ezeknek a hívásoknak a hívójának gondoskodnia kell arról, hogy ne lazítsanak a veremről, amíg az I/O-kezelő nem jelzi az eseményt az IRP befejezésekor.
  8. Határozatlan ideig várakozik a diszpécseri rutinokban. Általában bármilyen várakozás a diszpécser rutinban rossz gyakorlat.
  9. Az objektum érvényességének helytelen ellenőrzése (ha blah == NULL) a törlés előtt. Ez általában azt jelenti, hogy a szerző nem ismeri teljes mértékben az objektum élettartamát vezérlő kódot.

Objektumkezelés

  1. WDF-objektumok szülőként való nem explicit kezelése. Lásd: Bevezetés a keretrendszer-objektumokhoz.
  2. A WDF-objektum WDFDRIVER-hez való szülőbe helyezése ahelyett, hogy olyan objektumra mutat, amely jobb élettartam-felügyeletet biztosít, és optimalizálja a memóriahasználatot. Például egy WDFREQUEST-nek szülője lehet a WDFDEVICE az IOTARGET helyett. Lásd: Általános keretrendszerobjektumok, Keretrendszerobjektumok életciklusának és Keretrendszerobjektumokösszegzése.
  3. Nem végzi el az illesztőprogramok között elérhető megosztott memóriaerőforrások lefuttatásos védelmét. Lásd ExInitializeRundownProtection függvény.
  4. A munkaelem hibás újbóli sorba állítása, miközben az előző már a sorban van vagy már fut. Ez akkor lehet probléma, ha az ügyfél feltételezi, hogy minden várólistán lévő munkaelem végrehajtásra kerül. Lásd a keretrendszer munkatárgyainak használatát. A WorkItems sorba állításával kapcsolatos további információkért tekintse meg a DMF_QueuedWorkitem modult a Driver Module Framework (DMF) projektben – https://github.com/Microsoft/DMF.
  5. Az üzenet közzététele előtt az időzítő várakozik a feldolgozásra. Lásd Időzítők használata.
  6. Egy munkaelemben olyan műveletet hajt végre, amely blokkolhatja a folyamatot, vagy határozatlan ideig tarthat a befejezése.
  7. Olyan megoldás tervezése, amely a sorban álló munkaelemek tömegét eredményezi. Ez nem válaszoló rendszerhez vagy DOS-támadáshoz vezethet, ha a rossz ember képes vezérelni a műveletet (például az I/O-t egy olyan illesztőprogramba pumpálja, amely minden I/O-hoz új munkaelemet vár). Lásd: Keretrendszer munkaelemeinek használata.
  8. Nem biztosítja, hogy a munkaelem DPC-visszahívásai befejeződjenek az objektum törlése előtt. Lásd A DPC-rutinok írásának irányelvei és a WdfDpcCancel függvény.
  9. Szálak létrehozása munkafeladatok használata helyett rövid időtartamú/nem lekérdezős feladatokhoz. Lásd: rendszermunkaszálak.
  10. Az illesztőprogram törlése vagy eltávolítása előtt nem biztosítja, hogy a szálak lefutottak és befejeződtek. A szállefuttatás szinkronizálásával kapcsolatos további információkért tekintse meg a Driver Module Framework (DMF) projekt DMF_Thread moduljához kapcsolódó kódot – https://github.com/Microsoft/DMF.
  11. Egyetlen illesztőprogram használata különböző, de egymástól függő eszközök kezelésére, valamint globális változók használatával az információk megosztására.

Memória

  1. Ha lehetséges, ne jelölje meg a passzív végrehajtási kódot PAGEABLE-ként. A lapozóillesztő-kód csökkentheti az illesztőprogram kódlábnyomának méretét, így szabadíthat fel rendszerteret más célokra. Legyen óvatos a lapozható kód megjelölésével, amely az IRQL >= DISPATCH_LEVEL értékét emeli, vagy hívható emelt IRQL-nél. Lásd: Mikor kell a kódnak és az adatoknak lapozhatónak lenniük és illesztőprogramok lapozhatóvá tétele és olyan kód észlelése, amely lapozható.
  2. Ha nagy struktúrákat deklarál a veremben, inkább használja a halmot vagy a tárhelyet. Lásd: A KernelStack használata és System-Space memóriakiosztása.
  3. A WDF-objektumkörnyezet szükségtelen nullázása. Ez azt jelezheti, hogy nem egyértelmű, hogy a memória mikor lesz automatikusan nullázva.

Általános illesztőprogram-irányelvek

  1. WDM és WDF primitívek keverése. WDM-primitívek használata, ahol WDF-primitívek használhatók. A WDF primitívek használata megvédi Önt a gotchastól, javítja a hibakeresést, és ami még fontosabb, hordozhatóvá teszi az illesztőprogramot a usermode-ba.
  2. Az FDO-k elnevezése és szimbolikus hivatkozások létrehozása, ha nincs szükség rá. Lásd a Illesztőprogramok hozzáférésének szabályozásapontot.
  3. Másolás és beillesztés, valamint GUID-ek és más állandó értékek használata a mintaillesztőkből.
  4. Fontolja meg az illesztőmodul-keretrendszer (DMF) nyílt forráskódú kódjának használatát az illesztőprogram-projektben. A DMF a WDF bővítménye, amely további funkciókat tesz lehetővé a WDF-illesztőprogram-fejlesztők számára. Lásd a illesztőprogram-modul keretrendszerénekbemutatását.
  5. A beállításjegyzék használata folyamatközi értesítési mechanizmusként vagy postaládaként. Alternatív megoldásként tekintse meg a DMF-projektben elérhető DMF_NotifyUserWithEvent és DMF_NotifyUserWithRequest modulokat – https://github.com/Microsoft/DMF.
  6. Feltételezve, hogy a beállításjegyzék minden része elérhető lesz a rendszer korai rendszerindítási fázisában.
  7. Függőséget vállal egy másik illesztőprogram vagy szolgáltatás terhelési sorrendjén. Mivel a terhelési sorrend az illesztőprogram vezérlésén kívül módosítható, ez azt eredményezheti, hogy az illesztőprogram kezdetben működik, de később kiszámíthatatlan módon meghiúsul.
  8. A már elérhető illesztőprogram-könyvtárak újrateremtése, mint például a WDF által biztosított PnP támogatás, leírtak szerint a PnP és az energiagazdálkodás támogatása az illesztőprogramban vagy a busz felületében biztosított könyvtárak az OSR cikkben, amely a Buszfelületek használata az illesztőprogramok közötti kommunikációhozrészletezi.

PnP/Power

  1. Nem PnP-barát módon működik együtt egy másik illesztőprogrammal – nem regisztrál a PnP-eszközök változására vonatkozó értesítésekhez. Lásd: Regisztrálás eszközillesztő-változás értesítésekhez.
  2. AcpI-csomópontok létrehozása az eszközök számbavételéhez és energiafüggőségek létrehozásához a buszillesztő vagy a rendszer által biztosított szoftvereszköz-létrehozási felületek használata helyett elegáns módon a PNP-hez és az energiafüggőségekhez. Lásd: A PnP és az energiagazdálkodás támogatása a függvényillesztőkben.
  3. Az eszköz nem letiltható megjelölése – újraindítás kényszerítése az illesztőprogram frissítésére.
  4. Az eszköz elrejtése az eszközkezelőben. Lásd Eszközök elrejtése a Device Manager-ból című témakört.
  5. Feltételezve, hogy az illesztőprogram csak az eszköz egy példányához lesz használva.
  6. Feltételezve, hogy a meghajtó soha nem lesz eltávolítva. Lásd: a PnP-illesztőprogram kiürítési rutinját.
  7. Nem kezeli a hamis felület érkezési értesítését. Előfordulhat, és az autóvezetőktől elvárják, hogy biztonságosan kezeljék ezt a helyzetet.
  8. Nem implementálható az S0 tétlen energiaházirendje, amely fontos a DRIPS-korlátozásokat vagy azok gyermekeit érintő eszközök esetében. Lásd a Készenléti lekapcsolás támogatását alatt.
  9. A WdfDeviceStopIdle visszatérési állapotának nem ellenőrzése WdfDeviceStopIdle/ResumeIdle egyensúlyhiány miatt energiaforrás-szivárgáshoz és végül 9F hibaellenőrzéshez vezet.
  10. Nem tudja, hogy a PrepareHardware/ReleaseHardware többször is meghívható az erőforrás-újraegyensúlyozás miatt. Ezeket a visszahívásokat a hardvererőforrások inicializálására kell korlátozni. Lásd: EVT_WDF_DEVICE_PREPARE_HARDWARE.
  11. A PrepareHardware/ReleaseHardware használata szoftvererőforrások kiosztásához. Az eszközre statikus szoftvererőforrás-foglalást az AddDevice-ben vagy a SelfManagedIoInitben kell elvégezni, ha a hardverrel való interakcióhoz szükséges erőforrások kiosztása szükséges. Lásd: EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT.

Kódolási irányelvek

  1. Nem használ biztonságos sztring- és egész számfüggvényeket. Lásd: Biztonságos karakterláncfüggvények használata és Biztonságos egész függvények használata.
  2. Az állandók meghatározásához nem használ típusdefeket.
  3. Globális és statikus változók használata. Kerülje az eszközönkénti tárolást globális környezetben. A globálisak az információk több eszközpéldányon való megosztására szolgálnak. Alternatív megoldásként fontolja meg a WDFDRIVER objektumkörnyezet használatát az információk több eszközpéldányon való megosztásához.
  4. Nem használ leíró neveket a változókhoz.
  5. Nem konzisztens az elnevezési változókban – kis- és nagybetűk konzisztenciája. A meglévő kód frissítésekor nem követi a meglévő kódolási stílust. Például különböző változónevek használata a különböző függvények közös struktúráihoz.
  6. Fontos tervezési választások nem kommentálása – energiagazdálkodás, zárak, állapotkezelés, munkaelemek, DPC-k, időzítők, globális erőforrás-használat, erőforrás-előfoglalás, összetett kifejezések/feltételes utasítások.
  7. Megjegyzés azokról a dolgokról, amelyek nyilvánvalóak a meghívott API nevéből. A megjegyzés angol nyelvű megfelelője a függvénynévnek (például a WdfDeviceCreate meghívásakor a "Create the Device Object" megjegyzés írása).
  8. Ne hozzon létre makrókat, amelyek visszatérési hívással rendelkeznek. Lásd: Függvények (C++).
  9. Nincsenek vagy hiányos forráskód-széljegyzetek (SAL). Lásd Windows-illesztőprogramok SAL 2.0-s megjegyzései.
  10. Makrók használata beágyazott függvények helyett.
  11. Makrók használata állandókhoz constexpr helyett C++ használata esetén
  12. Az illesztőprogramot fordítsa a C fordítóval, ne a C++ fordítóval, hogy biztosítsa az erős típusellenőrzést.

Hibakezelés

  1. Nem jelenti a kritikus illesztőprogram-hibákat, és kecsesen jelöli az eszközt nem működőképesnek.
  2. Nem adja vissza a megfelelő NT-hibaállapotot, amely értelmezhető WIN32-hibaállapotra fordít le. Lásd: NTSTATUS-értékek használata.
  3. Nem használ NTSTATUS-makrókat a rendszerfüggvények visszaadott állapotának ellenőrzéséhez.
  4. Szükség esetén nem érvényes állapotváltozókra vagy jelzőkre.
  5. Annak ellenőrzése, hogy a mutató érvényes-e, mielőtt hozzáférünk hozzá, hogy elkerüljük a versenyfeltételekből adódó problémákat.
  6. NULL mutatóval való ellenőrzés. Ha null mutatóval próbál hozzáférni a memóriához, a Windows hibaellenőrzést fog végezni. A hibaellenőrzés paraméterei biztosítják a null mutató javításához szükséges információkat. Túlóra, amikor sok szükségtelen ASSERT utasítást adnak hozzá a kódhoz, memóriát használnak fel, és lelassítják a rendszert.
  7. ÉRVÉNYESÍTÉS az objektumkörnyezet mutatóján. Az illesztőprogram-keretrendszer garantálja, hogy az objektum mindig a környezettel együtt lesz foglalva.

Nyomkövetés

  1. Nem definiálja a WPP egyéni típusait, és nem használja nyomkövetési hívásokban az emberi olvasható nyomkövetési üzenetek lekéréséhez. Lásd: WPP-szoftverkövetés hozzáadása Windows-illesztőhöz.
  2. Nem használ IFR-nyomkövetést. Lásd Az Inflight Trace Recorder (IFR) használata a KMDF-ben és az UMDF 2 illesztőprogramokban.
  3. A függvénynevek megjelenítése a WPP-nyomkövetési hívásokban. A WPP már nyomon követi a függvényneveket és a sorszámokat.
  4. Nem használ ETW-eseményeket a teljesítmény és az eseményeket befolyásoló egyéb kritikus felhasználói élmény mérésére. Lásd: Eseménykövetés hozzáadása Kernel-Mode illesztőprogramokhoz.
  5. Nem jelenti a kritikus hibákat az eseménynaplóban, és finoman nem működőképesként jelöli meg az eszközt.

Ellenőrzés

  1. A fejlesztés és tesztelés során nem futtatja a szabványos és a speciális beállításokkal rendelkező illesztőprogram-ellenőrzőt. Lásd: Illesztőprogram-ellenőrző. A speciális beállításokban ajánlott minden szabályt engedélyezni, kivéve az alacsony erőforrás-szimulációhoz kapcsolódó szabályokat. Célszerű az alacsony erőforrás-szimulációs teszteket külön-külön futtatni, hogy megkönnyítse a hibák hibakeresését.
  2. A DevFund-teszt nem fut az illesztőprogramon vagy azon eszközosztályon, amelynek az illesztőprogram része, amikor a speciális ellenőrző beállítások engedélyezve vannak. Lásd a DevFund-tesztek futtatása parancssori módban.
  3. Nem ellenőrzi, hogy az illesztőprogram HVCI-kompatibilis-e. Lásd: HVCI-kompatibilitási kód implementálása.
  4. Nem futtatják az AppVerifier WUDFhost.exe-n a felhasználói módú illesztőprogramok fejlesztése és tesztelése során. Lásd: Application Verifier.
  5. Ne ellenőrizze a memóriahasználatot a !wdfpoolusage hibakeresőbővítménnyel futásidőben, hogy a WDF-objektumok ne legyenek elhagyva. A memória, a kérések és a munkaelemek gyakori áldozatai ezeknek a problémáknak.
  6. Ha nem használja a !wdfkd hibakereső bővítményt, ellenőrizze az objektumfát, hogy meggyőződjön arról, hogy az objektumok megfelelően vannak-e fölérendelve, és ellenőrizze a főbb objektumok(például WDFDRIVER, WDFDEVICE, IO) attribútumait.