Ajánlott megbízhatósági eljárások

A következő megbízhatósági szabályok az SQL Serverre vonatkoznak, de érvényesek minden szerveralapú alkalmazásra is. Rendkívül fontos, hogy az olyan kiszolgálók, mint az SQL Server, ne szivárogtatják ki az erőforrásokat, és ne legyenek lehozva. Ezt azonban nem lehet úgy megtenni, hogy minden olyan metódushoz írunk visszaugró kódot, amely módosítja egy objektum állapotát. A cél nem az, hogy 100%-ra megbízható felügyelt kódot írjon, amely minden helyen helyreállítja a biztonsági mentési kóddal kapcsolatos hibákat. Ez ijesztő feladat lenne, kevés esély van a sikerre. A közös nyelvi futtatókörnyezet (CLR) nem tud elég erős garanciát nyújtani a felügyelt kódhoz, hogy a tökéletes kódírás megvalósítható legyen. Vegye figyelembe, hogy a ASP.NET ellentétben az SQL Server csak egy olyan folyamatot használ, amelyet nem lehet újrahasznosítani anélkül, hogy egy adatbázist elfogadhatatlanul hosszú időre leállítani kellene.

Mivel ezek a gyengébb garanciák és egyetlen folyamaton futnak, a megbízhatóság a szálak megszüntetésén vagy az alkalmazástartományok szükség esetén történő újrahasznosításán alapul, és óvintézkedéseket kell tennie annak érdekében, hogy az operációs rendszer erőforrásai, például a fogantyúk vagy a memória ne szivárogjanak ki. Még ennél az egyszerűbb megbízhatósági korlátozásnál is jelentős megbízhatósági követelmény áll fenn:

  • Soha ne szivárogtasd ki az operációs rendszer erőforrásait.

  • Azonosítsa az összes felügyelt zárolást a CLR minden formájában.

  • Soha ne szakítsa meg az alkalmazások közötti megosztott állapotot, így az AppDomain újrahasznosítás zökkenőmentesen működik.

Bár elméletileg lehetséges felügyelt kódokat írni, hogy kezelni tudják ThreadAbortException, StackOverflowExceptionés OutOfMemoryException kivételeket, elvárják, hogy a fejlesztők ilyen robusztus kódot írjanak egy teljes alkalmazáson keresztül, ésszerűtlen. Emiatt a sávon kívüli kivételek a végrehajtási szál leállítását eredményezik; és ha a megszakított szál megosztott állapotot szerkesztett, amelyet az határozhat meg, hogy a szál rendelkezik-e zárolással, akkor a rendszer eltávolítja a AppDomain szálat. Ha egy megosztott állapotot szerkesztő metódus leáll, az állapot sérült lesz, mert nem lehet megbízható háttérkódot írni a megosztott állapot frissítéséhez.

A .NET-keretrendszer 2.0-s verziójában az egyetlen gazda, amelynek megbízhatóságra van szüksége, az SQL Server. Ha a szerelvény sql serveren fog futni, akkor a szerelvény minden részén végezze el a megbízhatósági munkát, még akkor is, ha az adatbázisban való futtatáskor bizonyos funkciók le vannak tiltva. Erre azért van szükség, mert a kódelemző motor a szerelvény szintjén vizsgálja a kódot, és nem tudja megkülönböztetni a letiltott kódot. Az SQL Server másik programozási szempontja, hogy az SQL Server egy folyamat alatt futtat mindent, és AppDomain az újrahasznosítás az összes erőforrás, például a memória és az operációs rendszer fogópontjainak tisztítására szolgál.

A háttérkód véglegesítőitől, destruktoraitól vagy try/finally blokkjaitól nem függhet. Lehet, hogy megszakadnak, vagy nem hívják őket.

Az aszinkron kivételek nem várt helyeken, esetleg minden gépi utasításban előfordulhatnak: ThreadAbortException, StackOverflowExceptionés OutOfMemoryException.

A felügyelt szálak nem feltétlenül Win32-szálak az SQL-ben, lehetnek szálkapcsok.

A folyamatszintű vagy az alkalmazásközi megosztott tartomány megosztott állapota rendkívül nehéz biztonságosan módosítani, és lehetőség szerint kerülni kell.

A memóriakihasználtság nem ritka az SQL Serverben.

Ha az SQL Serveren üzemeltetett kódtárak nem frissítik megfelelően a megosztott állapotukat, nagy a valószínűsége annak, hogy a kód nem áll helyre az adatbázis újraindításáig. Emellett bizonyos szélsőséges esetekben előfordulhat, hogy ez az SQL Server-folyamat meghiúsulását okozhatja, ami az adatbázis újraindítását okozhatja. Az adatbázis újraindítása leállhat egy webhelyen, vagy hatással lehet a vállalati műveletekre, és ronthatja a rendelkezésre állást. Az operációs rendszer erőforrásainak, például a memóriának vagy a leíróknak a lassú szivárgása miatt a kiszolgáló végül nem lesz képes leírókat kiosztani, és nincs lehetőség a helyreállításra, vagy előfordulhat, hogy a kiszolgáló teljesítménye lassan romlik, és csökken az ügyfélalkalmazások elérhetősége. Egyértelműen el szeretnénk kerülni ezeket a forgatókönyveket.

Ajánlott eljárások szabályai

A bevezetés arra összpontosított, hogy a kiszolgálón futó felügyelt kód kódvizsgálatának mit kell elkapnia a keretrendszer stabilitásának és megbízhatóságának növelése érdekében. Ezek az ellenőrzések általában jó gyakorlatnak számítanak, és abszolút kötelező a kiszolgálón.

Holtpont vagy erőforrás-korlátozás esetén az SQL Server megszakít egy szálat, vagy leállít egy AppDomain. Ilyen esetben a rendszer csak a korlátozott végrehajtási régióban (CER) lévő biztonsági mentési kódot futtatja.

Az erőforrásszivárgások elkerülése a SafeHandle használatával

Kirakodás esetén AppDomain nem támaszkodhat finally blokkokra vagy véglegesítőkre, ezért fontos, hogy az operációs rendszer összes erőforrás-hozzáférését az SafeHandle osztályon keresztül valósítsa meg, ahelyett, hogy például a IntPtr, HandleRef vagy hasonló osztályokat használna. Ez lehetővé teszi a CLR számára, hogy nyomon kövesse és bezárja a lebontás esetén is AppDomain használt fogópontokat. SafeHandle kritikus finalizert fog használni, amelyet a CLR mindig futtat.

Az operációs rendszer leírója a biztonságos leíróban lesz tárolva a létrehozásának pillanatától egészen a kiadásának pillanatáig. Nincs olyan ablak, amely alatt ThreadAbortException előfordulhat, hogy kiszivárog egy fogantyú. A platformhívás emellett referencia számlálni fogja a fogantyút, amely lehetővé teszi a fogantyú élettartamának alapos nyomon követését, megakadályozva a versenyfeltételek közötti biztonsági problémát és a fogantyút jelenleg használó metódust.

A legtöbb osztálynak, amely jelenleg rendelkezik véglegesítővel az operációs rendszer leírójának egyszerű törléséhez, már nem lesz szüksége a véglegesítőre. Ehelyett a véglegesítő a SafeHandle származtatott osztályban lesz.

Vegye figyelembe, hogy SafeHandle ez nem helyettesíti a IDisposable.Dispose. Továbbra is fennáll az operációs rendszer erőforrásainak explicit módon történő felszabadítása, amely lehetőségeket nyújt az erőforrás-ütközés és teljesítménybeli előnyök terén. Ne feledje, hogy finally az erőforrásokat explicit módon megsemmisítő blokkok végrehajtása nem feltétlenül fejeződik be.

SafeHandle Lehetővé teszi saját ReleaseHandle metódusának implementálását, amely elvégzi a fogópont felszabadítása érdekében végzett munkát, mint például az állapot átadását egy operációs rendszer fogópont-felszabadító rutinjának vagy fogópontok készletének hurkos felszabadítását. A CLR garantálja, hogy ez a módszer fut. A ReleaseHandle megvalósítás szerzőjének felelőssége annak biztosítása, hogy a kezelőt minden körülmények között felszabadítsák. Ennek elmulasztása a fogópont kiszivárgását okozza, ami gyakran a fogóponthoz társított natív erőforrások kiszivárgását eredményezi. Ezért kritikus fontosságú a származtatott osztályok strukturálása SafeHandle , hogy a ReleaseHandle megvalósításhoz ne legyen szükség olyan erőforrások lefoglalására, amelyek esetleg nem érhetők el a meghívás időpontjában. Vegye figyelembe, hogy olyan metódusok hívása megengedett, amelyek meghiúsulhatnak ReleaseHandle végrehajtása során, feltéve, hogy a kód képes kezelni az ilyen hibákat, és teljesítse a natív leíró kiadásának szerződését. Hibakeresési célokra a ReleaseHandle olyan visszatérési értékkel rendelkezik, amely Boolean értékre állítható, ha olyan katasztrofális hiba történik, amely megakadályozza az erőforrás kiadását. Ha engedélyezve van, aktiválja a releaseHandleFailed MDA-t, hogy segítsen a probléma azonosításában. Ez más módon nem befolyásolja a futási időt; ReleaseHandle nem lesz ismét meghívva ugyanahhoz az erőforráshoz, és ennek következtében a kezelő kiszivárog.

SafeHandle bizonyos kontextusokban nem megfelelő. Mivel a ReleaseHandle metódus egy GC véglegesítő szálon futtatható, azokat a fogópontokat, amelyeket egy adott szálon kell felszabadítani, ne csomagolják be egy SafeHandle-be.

A futtatókörnyezet futtatható burkolói (runtime callable wrappers, RCW-k) további kód nélkül tisztíthatók a CLR által. A platformhívást használó és a COM-objektumokat IUnknown* vagy IntPtr-ként kezelő kód esetében a kódot újra kell írni RCW használatára. SafeHandle lehet, hogy nem megfelelő ehhez a forgatókönyvhöz, mert előfordulhat, hogy egy nem kezelt kiadási módszer visszahívást kezdeményez a kezelt kódba.

Kódelemzési szabály

Használja a SafeHandle elemet az operációs rendszer erőforrásainak kapszulázására. Ne használjon HandleRef vagy IntPtr típusú mezőket.

Győződjön meg arról, hogy a véglegesítőknek nem kell futniuk, hogy megakadályozzák az operációs rendszer erőforrásainak kiszivárgását

Alaposan tekintse át a véglegesítőket, hogy még ha nem is futnak is, ne szivárogjon ki egy kritikus operációsrendszer-erőforrás. A normál AppDomain kiürítéstől eltérően, ha az alkalmazás állandó állapotban fut, vagy amikor egy kiszolgáló, például az SQL Server leáll, az objektumok nem lesznek véglegesítve a hirtelen AppDomain kiürítés során. Győződjön meg arról, hogy az erőforrások nem szivárognak ki hirtelen kiürítés esetén, mivel az alkalmazás helyessége nem garantálható, de a kiszolgáló integritását úgy kell fenntartani, hogy nem szivárognak ki az erőforrások. Az operációs rendszer erőforrásainak felszabadítására használható SafeHandle .

Győződjön meg arról, hogy a záradékoknak nem kell futniuk az operációs rendszer erőforrásainak kiszivárgásának megakadályozása érdekében

finally az utasítások futtatása a CER-eken kívül nem garantált, ezért a könyvtár-fejlesztőknek nem szabad kódra támaszkodniuk egy finally blokkon belül, ha nem felügyelt erőforrásokat akarnak felszabadítani. A SafeHandle használata a javasolt megoldás.

Kódelemzési szabály

Használja a SafeHandle-t az operációs rendszer erőforrásainak tisztítására, ahelyett, hogy Finalize-t használna. Ne használja IntPtr; erőforrások beágyazására használható SafeHandle . Ha a végső záradéknak futnia kell, helyezze el egy CER-ben.

Minden zárolásnak át kell haladnia a meglévő felügyelt zárolási kódon

A CLR-nek tudnia kell, hogy a kód mikor van zárolva, hogy tudja, hogy a AppDomain szál megszakítása helyett inkább bontsa le a kódot. A szál megszakítása veszélyes lehet, mivel a szál által kezelt adatok inkonzisztens állapotban maradhatnak. Ezért az egész AppDomain-t újra kell hasznosítani. A zárolás azonosításának elmulasztásának következményei lehetnek holtpontok vagy helytelen eredmények. Használja a BeginCriticalRegion és EndCriticalRegion metódusokat a zárolási régiók azonosítására. Ezek statikus metódusok az Thread osztályon, amelyek csak az aktuális szálra vonatkoznak, így megakadályozható, hogy az egyik szál szerkessze a másik szál zárolási számát.

Enter és Exit beépített CLR-értesítéssel rendelkezik, ezért ajánlott a használatuk, valamint a zárolási utasítás alkalmazása, amely ezekkel a módszerekkel működik.

Más zárolási mechanizmusok, mint például a spinzárak és AutoResetEvent, meg kell hívni ezeket a metódusokat, hogy értesítsék a CLR-t arról, hogy beléptek egy kritikus szakaszba. Ezek a módszerek nem alkalmaznak zárolást; tájékoztatják a CLR-t arról, hogy a kód egy kritikus szakaszban fut, és a szál megszakítása a megosztott állapotot inkonzisztenssé teheti. Ha saját zárolástípust (például egyéni ReaderWriterLock osztályt) definiált, használja ezeket a zárolásszám-metódusokat.

Kódelemzési szabály

Jelölje meg és azonosítsa az összes zárakat a BeginCriticalRegion és EndCriticalRegion használatával. Ne használja a CompareExchange, Increment és Decrement elemeket egy hurokban. Ne kezdeményezz platformhívást ezen módszerek Win32-változataival. Ne használd Sleep ciklusban. Ne használjon illékony mezőket.

A tisztítási kódnak végül vagy egy catch blokkban kell lennie, a catch blokkot nem követve.

A törlési kódnak soha nem szabad követnie a catch blokkot; magában a finally blokkban vagy a catch blokkban kell lennie. Ez egy normális jó gyakorlat. A finally blokk általában előnyben részesítendő, mert ugyanazt a kódot futtatja mind kivétel esetén, mind pedig amikor a try blokk végét rendszerint eléri. Egy váratlan kivétel (például egy ThreadAbortException) esetén a törlési kód nem fog futni. Azokat a nem felügyelt erőforrásokat, amelyeket egy finally-ban takarítana el, célszerű egy SafeHandle-ba burkolni a szivárgások elkerülése érdekében. Vegye figyelembe, hogy a C# using kulcsszó hatékonyan használható az objektumok, köztük a fogópontok megsemmisítésére.

Bár AppDomain az újrahasznosítás megtisztíthatja az erőforrásokat a véglegesítő szálon, még mindig fontos, hogy a tisztítási kódot a megfelelő helyre helyezze. Vegye figyelembe, hogy ha egy szál aszinkron kivételt kap zárolás nélkül, a CLR megpróbálja megszüntetni magát a szálat anélkül, hogy újra kellene hasznosítaniuk a AppDomainszálat. Ha az erőforrásokat hamarabb, mint később megtisztítják, az több erőforrás elérhetővé tételével és az élettartam jobb kezelésével segít. Ha nem zár be explicit módon egy fogantyút egy fájlhoz valamilyen hibakód elágazásában, akkor várja meg, amíg a SafeHandle véglegesítő megtisztítja azt, a kód következő futtatásakor előfordulhat, hogy nem tudja elérni ugyanazt a fájlt, ha a véglegesítő még nem futott. Ezért a törlési kód meglétének és megfelelő működésének biztosítása segít a hibák utáni helyreállításban, még akkor is, ha ez nem feltétlenül szükséges.

Kódelemzési szabály

A catch utáni takarítókódnak egy finally blokkban kell lennie. Hívásokat kezdeményezhet a végső blokkban való megsemmisítéshez. catch blokkoknak dobással vagy újradobással kell véget érniük. Bár vannak kivételek, például olyan kód, amely észleli, hogy létre lehet-e hozni egy hálózati kapcsolatot, ahol nagy számú kivételt kaphat, minden olyan kódnak, amely normál körülmények között több kivétel elfogását igényli, jeleznie kell, hogy a kódot tesztelni kell annak ellenőrzéséhez, hogy sikeres lesz-e.

Process-Wide mutable megosztott állapotot az alkalmazástartományok között meg kell szüntetni, vagy korlátozott végrehajtási régiót kell használni

A bevezetőben leírtak szerint nagyon nehéz lehet olyan felügyelt kódot írni, amely megbízható módon figyeli a folyamatszintű megosztott állapotot az alkalmazástartományokban. A folyamatszintű megosztott állapot bármilyen adatstruktúra, amelyet az alkalmazástartományok között osztanak meg, akár Win32-kódban, akár a CLR-ben, vagy felügyelt kódban távoli kommunikációval. A megosztott mutable állapotokat nagyon nehéz helyesen írni felügyelt kódba, és a statikus megosztott állapotok csak nagy körültekintéssel végezhetők el. Ha folyamatszintű vagy gépszintű megosztott állapottal rendelkezik, keressen valamilyen módot a megosztott állapot eltávolítására vagy a megosztott állapot védelmére egy korlátozott végrehajtási régió (CER) használatával. Ne feledje, hogy bármely olyan megosztott állapotú könyvtár, amely nincs azonosítva és kijavítva, a megszakítás kockázatát jelentheti egy gazdagépre, például az SQL Serverre, amely tiszta AppDomain eltávolítást igényel.

Ha a kód COM-objektumot használ, ne ossza meg ezt a COM-objektumot az alkalmazástartományok között.

A zárolások nem működnek folyamatszintű vagy alkalmazástartományok között.

Korábban a Enterzárolási utasítást globális folyamatzárak létrehozására használták. Például ez akkor fordul elő, amikor agilis osztályokra, mint például nem megosztott összetevőkből származó példányok, objektumok, internált sztringek, valamint alkalmazástartományok között megosztott néhány sztring remoting használatával vannak zárolva. Ezek a zárolások már nem folyamatszintűek. A folyamatszintű alkalmazástartomány-zárolás jelenlétének azonosításához állapítsa meg, hogy a zároláson belüli kód külső, tartós erőforrást, például lemezen lévő fájlt vagy esetleg adatbázist használ-e.

Vegye figyelembe, hogy a zárolás egy AppDomain adott tartományon belüli rögzítése problémákat okozhat, ha a védett kód külső erőforrást használ, mert a kód egyszerre több alkalmazástartományban is futhat. Ez problémát jelenthet, ha egy naplófájlba ír, vagy egy hálózati foglalathoz köti a teljes folyamatot. Ezek a módosítások azt jelentik, hogy a felügyelt kód használatával nem lehet egyszerűen lekérni egy folyamat-globális zárolást, kivéve egy elnevezett Mutex vagy Semaphore példány használatát. Hozzon létre olyan kódot, amely nem fut egyszerre két alkalmazás domainben, vagy használja a Mutex vagy Semaphore osztályokat. Ha a meglévő kód nem módosítható, ne használjon win32 nevű mutexet a szinkronizálás eléréséhez, mert a szálas módban való futtatás azt jelenti, hogy nem garantálható, hogy ugyanaz az operációsrendszer-szál lekér és felszabadít egy mutexet. A zárolások szinkronizálását a CLR által elismert módon, a felügyelt Mutex osztály, vagy egy elnevezett ManualResetEvent, AutoResetEvent, vagy Semaphore használatával kell elvégezni, és nem felügyelt kóddal.

Kerüld el a zárást (typeof(MyType))

A megosztott szerelvényekben található privát és nyilvános Type objektumok, amelyek egyetlen kódpéldányt használnak megosztva az összes alkalmazási tartományban, szintén problémákat okozhatnak. Megosztott szerelvények esetén egy folyamatnak csak egy Type példánya van, ami azt jelenti, hogy több alkalmazástartomány ugyanazt a példányt Type használja. A Type példány zárolása olyan zárolást hoz létre, amely a teljes folyamatot érinti, nem csak a AppDomain-t. Ha valaki AppDomain zárol egy Type objektumot, akkor a szál hirtelen megszakad, az nem fogja feloldni a zárolást. Ez a zárolás ezt követően más alkalmazástartományok holtponthoz vezethetnek.

A statikus metódusok zárolásának jó módja, ha statikus belső szinkronizálási objektumot ad hozzá a kódhoz. Ez inicializálható az osztálykonstruktorban, ha van ilyen, de ha nem, akkor a következőképpen inicializálható:

private static Object s_InternalSyncObject;
private static Object InternalSyncObject
{
    get
    {
        if (s_InternalSyncObject == null)
        {
            Object o = new Object();
            Interlocked.CompareExchange(
                ref s_InternalSyncObject, o, null);
        }
        return s_InternalSyncObject;
    }
}

Ezt követően a zárolás során a InternalSyncObject tulajdonság használatával szerezze be a zároláshoz szükséges objektumot. Nem kell használnia a tulajdonságot, ha inicializálta a belső szinkronizálási objektumot az osztálykonstruktorban. A kettős ellenőrzés zár inicializálási kódjának a következő példához hasonlóan kell kinéznie:

public static MyClass SingletonProperty
{
    get
    {
        if (s_SingletonProperty == null)
        {
            lock(InternalSyncObject)
            {
                // Do not use lock(typeof(MyClass))
                if (s_SingletonProperty == null)
                {
                    MyClass tmp = new MyClass(…);
                    // Do all initialization before publishing
                    s_SingletonProperty = tmp;
                }
            }
        }
        return s_SingletonProperty;
    }
}

Megjegyzés a lock(this) kifejezésről

Általánosan elfogadható, hogy egy nyilvánosan elérhető objektumot zároljon. Ha azonban az objektum egy önálló objektum, amely egy teljes alrendszer holtpontját okozhatja, fontolja meg a fenti tervezési minta használatát is. Egy objektum zárolása SecurityManager például holtpontot okozhat az AppDomain egész AppDomain használhatatlanná tételében. Célszerű nem zárolni egy ilyen típusú, nyilvánosan elérhető objektumot. Az egyes gyűjtemények vagy tömbök zárolása azonban általában nem jelenthet problémát.

Kódelemzési szabály

Ne zárjon le olyan típusokat, amelyek alkalmazástartományokon keresztül használhatók, vagy amelyek nem rendelkeznek erős identitásérzékkel. Ne hívjon meg egy Enter, Type, MethodInfo, PropertyInfo, String, ValueType, Thread vagy bármilyen objektumot, amely származik MarshalByRefObject.

A GC.KeepAlive hívások eltávolítása

A meglévő kód jelentős része vagy nem használja KeepAlive , amikor szükséges, vagy akkor használja, ha az nem megfelelő. A konvertálás SafeHandle után az osztályoknak nem kell meghívniuk KeepAlive, feltéve, hogy nincs véglegesítőjük, hanem az operációs rendszer leírók véglegesítésére SafeHandle támaszkodnak. Bár a hívás megtartásának KeepAlive teljesítményköltsége elhanyagolható lehet, az a felfogás, hogy a hívás KeepAlive vagy szükséges, vagy elegendő egy olyan életen át tartó probléma megoldásához, amely esetleg már nem létezik, megnehezíti a kód karbantartását. A COM interop CLR hívható burkolóinak (RCW-k) KeepAlive használata esetén azonban a kód továbbra is kötelező.

Kódelemzési szabály

Távolítsa el KeepAlive.

A HostProtection attribútum használata

A HostProtectionAttribute (HPA) lehetővé teszi deklaratív biztonsági műveletek használatát a gazdagépek védelmi követelményeinek meghatározásához, ezáltal a gazdagép megakadályozhatja, hogy a teljes mértékben megbízható kód meghívjon bizonyos metódusokat, amelyek nem megfelelőek az adott gazdagép számára, például az SQL Server számára Exit vagy Show.

A HPA csak azokat a nem felügyelt alkalmazásokat érinti, amelyek a közös nyelvi futtatókörnyezetet üzemeltetik, és gazdavédelmet implementálnak, például az SQL Servert. Alkalmazás esetén a biztonsági művelet egy hivatkozási igény létrehozását eredményezi az osztály vagy metódus által elérhetővé tett gazdagép-erőforrások alapján. Ha a kód egy ügyfélalkalmazásban vagy egy nem gazdagép által védett kiszolgálón fut, az attribútum "elpárolog"; a rendszer nem észleli, ezért nem alkalmazza.

Fontos

Ennek az attribútumnak az a célja, hogy a gazdagépspecifikus programozási modellre vonatkozó irányelveket kényszerítsen ki, nem pedig a biztonsági viselkedést. Bár a hivatkozási igény a programozási modell követelményeinek való megfelelés ellenőrzésére szolgál, ez HostProtectionAttribute nem biztonsági engedély.

Ha a gazdagép nem rendelkezik programozási modellre vonatkozó követelményekkel, a hivatkozási követelmények nem lépnek fel.

Ez az attribútum a következőket azonosítja:

  • Azok a metódusok vagy osztályok, amelyek nem illenek a host programozási modelljébe, de egyébként ártalmatlanok.

  • Azok a metódusok vagy osztályok, amelyek nem illeszkednek a gazdagép programozási modelljéhez, és a kiszolgáló által kezelt felhasználói kód destabilizációját okozhatják.

  • Olyan metódusok vagy osztályok, amelyek nem felelnek meg a gazdagép programozási modelljének, és magának a kiszolgálófolyamatnak a destabilizálásához vezethetnek.

Megjegyzés:

Ha olyan osztálytárat hoz létre, amelyet gazdagép által védett környezetben végrehajtható alkalmazásoknak kell meghívnia, ezt az attribútumot az erőforráskategóriákat közzétesző HostProtectionResource tagokra kell alkalmaznia. Az attribútummal rendelkező .NET-keretrendszer osztálykönyvtár tagjai csak az aktuális hívót ellenőrzik. A könyvtári tagnak ugyanúgy ellenőriznie kell a közvetlen hívóját.

További információt a HPA-ról itt HostProtectionAttributetalál.

Kódelemzési szabály

Az SQL Server esetében a szinkronizálás vagy a szálkezelés bevezetéséhez használt összes módszert azonosítani kell a HPA-val. Ide tartoznak az állapotot megosztó, szinkronizált vagy külső folyamatokat kezelő metódusok. Az SQL Servert befolyásoló értékek a következők: HostProtectionResource, SharedState, és Synchronization. Azonban bármely módszert, amely bármely HostProtectionResource-t felfed, egy HPA-nak kell azonosítania, nemcsak azokat, amelyek SQL-t érintő erőforrásokat használnak.

Ne tiltsa le határozatlan ideig a nem felügyelt kódban

A felügyelt kód helyett nem felügyelt kódban való blokkolás szolgáltatásmegtagadási támadást okozhat, mivel a CLR nem tudja megszakítani a szálat. A blokkolt szál megakadályozza, hogy a CLR kirakodja a AppDomain-t, legalábbis anélkül, hogy rendkívül veszélyes műveleteket hajtana végre. A Windows-szinkronizálás primitív használatának blokkolása egyértelmű példa arra, amit nem tudunk engedélyezni. Ha lehetséges, kerülni kell a socket-en történő ReadFile hívás blokkolását — ideális esetben a Windows API-nak biztosítania kellene egy olyan mechanizmust, amely időtúllépést tesz lehetővé egy ilyen művelethez.

Azoknak a metódusoknak, amelyek natív hívásokat kezdeményeznek, ideális esetben egy Win32-hívást kell használniuk ésszerű, véges időtúllépéssel. Ha a felhasználó megadhatja az időtúllépést, a felhasználónak nem szabad engedélyezni, hogy meghatározott biztonsági engedély nélkül végtelen időtúllépést adjon meg. Ha egy metódus több mint ~10 másodpercig blokkolni fog, akkor olyan verziót kell használnia, amely támogatja az időtúllépéseket, vagy további CLR-támogatásra van szüksége.

Íme néhány példa a problémás API-kra. Az anonim és elnevezett csövek időtúllépéssel hozhatók létre; a kódnak azonban biztosítania kell, hogy soha ne hívja meg CreateNamedPipe és soha ne használja az NMPWAIT_WAIT_FOREVER értéket WaitNamedPipe esetén. Emellett váratlan blokkolás is előfordulhat, még ha időtúllépés is van megadva. A névtelen adatcsövön WriteFile hívás blokkolva marad, amíg az összes bájt meg nem íródik. Ez azt jelenti, hogy ha a puffer olvasatlan adatokat tartalmaz, akkor a WriteFile hívás blokkolva marad, amíg az olvasó nem felszabadít helyet a cső pufferében. A socketeknek mindig olyan API-t kell használniuk, amely kezeli az időtúllépési mechanizmust.

Kódelemzési szabály

A nem felügyelt kódban időtúllépés nélkül történő blokkolás szolgáltatásmegtagadásos támadás. Ne végezzen platformhívásokat a WaitForSingleObject, WaitForSingleObjectEx, WaitForMultipleObjects, MsgWaitForMultipleObjects és MsgWaitForMultipleObjectsEx. Ne használja a NMPWAIT_WAIT_FOREVER.

Az STA-Dependent funkciók azonosítása

Azonosítsa a COM egyszálas apartmanokat (STA-kat) használó kódokat. Az STA-k le vannak tiltva az SQL Server-folyamatban. A teljesítményszámlálóktól vagy a vágólaptól függő CoInitializefunkciókat le kell tiltani az SQL Serveren belül.

Győződjön meg arról, hogy a véglegesítők nem okoznak szinkronizálási problémákat

A .NET-keretrendszer későbbi verzióiban több véglegesítő szál is létezhet, ami azt jelenti, hogy az azonos típusú különböző példányok véglegesítői egyszerre futnak. Nem kell teljesen programszál-biztosnak lenniük; a szemétgyűjtő garantálja, hogy egy adott objektumpéldány véglegesítőjét csak egy programszál futtatja. A véglegesítőket azonban kódolt módon kell megadni, hogy elkerülje a versenyfeltételeket és a holtpontokat, amikor egyszerre futnak több különböző objektumpéldányon. Ha bármilyen külső állapotot használ, például naplófájlba ír, a véglegesítőben a szálkezeléssel kapcsolatos problémákat kell kezelni. Ne támaszkodjon a véglegesítésre a szálbiztonság érdekében. A véglegesítő szál állapotának tárolásához ne használjon helyi, felügyelt vagy natív szálat.

Kódelemzési szabály

A véglegesítőknek szinkronizálási problémáktól mentesnek kell lenniük. A véglegesítőben ne használjon statikus mutable állapotot.

Ha lehetséges, kerülje a nem felügyelt memóriát

A nem felügyelt memória kiszivároghat, akárcsak egy operációs rendszer leírója. Ha lehetséges, próbáljon meg memóriahasználatot használni a veremen a stackalloc vagy egy rögzített felügyelt objektum, például a rögzített utasítás vagy a GCHandle bájt[] használatával. A GC végül megtisztítja ezeket. Ha azonban nem felügyelt memóriát kell lefoglalnia, érdemes megfontolni egy olyan osztály használatát, amely a SafeHandle részből származik a memóriafoglalás burkolására.

Vegye figyelembe, hogy legalább egy eset SafeHandle nem megfelelő. A memóriát lefoglaló vagy szabad memóriát felszabadító COM-metódushívások esetében gyakori, hogy egy DLL-nek a memóriát egy másik DLL-en keresztül CoTaskMemAlloc kell lefoglalnia, és ezzel felszabadítja a memóriát CoTaskMemFree. Nem lenne megfelelő ezekre a helyekre használni a SafeHandle-t, mivel a nem kezelt memória élettartamát a SafeHandle-éhez próbálja kötni, ahelyett, hogy a másik DLL szabályozná a memória élettartamát.

Az összes catch(Exception) használatának áttekintése

Az összes kivételt egy adott kivétel helyett elkapó blokkok mostantól az aszinkron kivételeket is elkapják. Vizsgálja meg az összes catch(Exception) blokkot, ügyelve arra, hogy sehol se maradjon ki fontos erőforrás-felszabadítás vagy visszavonási kód, valamint hogy a ThreadAbortException, StackOverflowException vagy OutOfMemoryException kezelésére szolgáló fogási blokkban ne legyen esetleges helytelen viselkedés. Vegye figyelembe, hogy lehetséges, hogy ez a kód naplózást végez, vagy feltételezi, hogy csak bizonyos kivételeket lát, vagy hogy amikor kivétel történik, az pontosan egy adott okból meghiúsult. Előfordulhat, hogy ezeket a feltételezéseket frissíteni kell, beleértve ThreadAbortException.

Fontolja meg az összes olyan hely módosítását, amely az összes kivételt elfogja, hogy egy adott típusú kivételre korlátozza, amelyet elvár, hogy bekövetkezzen, például egy FormatException a sztringformázási módszerekből. Ez megakadályozza, hogy a try-catch blokk fut váratlan kivételek esetén, ezáltal biztosítva, hogy a kód ne rejtse el a hibákat a váratlan kivételek elfogásával. Általános szabályként soha ne kezeljen kivételt könyvtárkódban (a kivétel kezelése, amelyre a hívott kód kényszerít, a hívott kód tervezési hibájára utalhat). Bizonyos esetekben előfordulhat, hogy egy kivételt szeretne elkapni, és egy másik kivételtípust kell megadnia, hogy több adatot adjon meg. Ebben az esetben használjon beágyazott kivételeket, és tárolja a hiba valódi okát az InnerException új kivétel tulajdonságában.

Kódelemzési szabály

Tekintse át a felügyelt kód összes olyan fogási blokkját, amely az összes objektumot elfogja, vagy az összes kivételt elfogja. A C#-ban ez azt jelenti, hogy mind catch{} és catch(Exception){}. Fontolja meg, hogy a kivétel típusa nagyon specifikus legyen, vagy tekintse át a kódot, hogy ne működjön rosszul, ha váratlan kivételtípust kap.

Ne feltételezd, hogy egy felügyelt szál Win32-szál – Ez egy Fiber.

A felügyelt szálon belüli tároló használata működik, de előfordulhat, hogy nem használhat nem felügyelt szálon belüli tárolót, vagy hogy nem feltételezheti, a kód újra fut az aktuális operációs rendszer szálán. Ne módosítsa a beállításokat, például a szál területi beállítását. Ne hívja meg a InitializeCriticalSection vagy CreateMutex elemeket platformhívás révén, mert a zárolásba belépő operációs rendszerszálnak ki is kell lépnie a zárolásból. Mivel rostok használata esetén ez nem történik meg, a Win32 kritikus szakaszai és a mutexek nem használhatók közvetlenül az SQL-ben. Vegye figyelembe, hogy a felügyelt Mutex osztály nem kezeli ezeket a szál-affinitási problémákat.

Az állapot nagy részét biztonságosan használhatja egy felügyelt Thread objektumon, beleértve a felügyelt szál helyi tárolóját és a szál aktuális felhasználói felületi kultúráját. Használhatja azt ThreadStaticAttributeis, amely egy meglévő statikus változó értékét csak az aktuális felügyelt szálon teszi elérhetővé (ez egy másik módszer a szálalapú helyi tárolásra a CLR-ben). A programozási modell okból nem módosíthatja a szál aktuális kultúráját az SQL-ben való futtatáskor.

Kódelemzési szabály

Az SQL Server szálas módban fut; ne használjon szál helyi tárolót. Kerülje el a platformhívások használatát a következőkre: TlsAlloc, TlsFree, TlsGetValue, és TlsSetValue.

Az SQL Server kezelje a megszemélyesítést

Mivel a megszemélyesítés a szál szintjén működik, és az SQL szál módban futtatható, a felügyelt kód nem szabad megszemélyesíteni a felhasználókat, és nem szabad meghívni RevertToSelf.

Kódelemzési szabály

Hagyja, hogy az SQL Server kezelje a megszemélyesítést. Ne használja RevertToSelfa , ImpersonateAnonymousToken, DdeImpersonateClient, ImpersonateDdeClientWindow, ImpersonateLoggedOnUser, ImpersonateNamedPipeClient, ImpersonateSelf, RpcImpersonateClient, RpcRevertToSelf, RpcRevertToSelfEx, vagy SetThreadToken.

Ne hívja a Thread::Suspendet.

A szál felfüggesztése egyszerű műveletnek tűnhet, de holtpontot okozhat. Ha egy zárolást tartalmazó szálat egy második szál függeszt fel, majd a második szál megpróbálja ugyanazt a zárolást venni, holtpont jön létre. Suspend jelenleg zavarhatja a biztonságot, az osztály betöltését, a távoli kapcsolódást és a tükrözést.

Kódelemzési szabály

Ne hívjon.Suspend Érdemes lehet inkább valós szinkronizálási primitívet használni, például egy Semaphore vagy ManualResetEvent .

Kritikus műveletek védelme korlátozott végrehajtási régiókkal és megbízhatósági szerződésekkel

Ha olyan összetett műveletet hajt végre, amely frissíti a megosztott állapotot, vagy determinisztikusan vagy teljes mértékben sikeresnek vagy teljes mértékben sikertelennek kell lennie, győződjön meg arról, hogy egy korlátozott végrehajtási régió (CER) védi. Ez garantálja, hogy a kód minden esetben fut, még akkor is, ha egy szálat hirtelen leállítanak, vagy egy AppDomain hirtelen kirakodás történik.

A CER egy adott try/finally blokk, amelyet közvetlenül a hívás PrepareConstrainedRegionselőz meg.

Ezzel arra utasítja a just-in-time fordítót, hogy a try blokk futtatása előtt készítse elő a finally blokk összes kódját. Ez garantálja, hogy a végül blokkban lévő kód létre van hozva, és minden esetben futni fog. A CER-ben nem ritka, hogy üres try blokk van. A CER használata védelmet nyújt az aszinkron szál megszakításai és a memóriakivételek ellen. Tekintse meg a ExecuteCodeWithGuaranteedCleanup a CER egy olyan formáját, amely emellett kezeli a verem túlcsordulását rendkívül mély kód esetén.

Lásd még