Bagikan melalui


Praktik Terbaik Keandalan

Aturan keandalan berikut berorientasi pada SQL Server; namun, aplikasi ini juga berlaku untuk aplikasi server berbasis host apa pun. Sangatlah penting bahwa server seperti SQL Server tidak membocorkan sumber daya dan tidak dimatikan. Namun, itu tidak dapat dilakukan dengan menulis kode back-out untuk setiap metode yang mengubah status objek. Tujuannya bukan untuk menulis kode terkelola yang 100 persen andal yang dapat pulih dari kesalahan apa pun di setiap lokasi dengan kode pemulihan. Itu akan menjadi tugas yang menakutkan dengan sedikit kemungkinan keberhasilan. Runtime bahasa umum (CLR) tidak dapat dengan mudah memberikan jaminan yang cukup kuat kepada kode yang dikelola untuk memungkinkan penulisan kode yang sempurna menjadi layak. Perhatikan bahwa tidak seperti ASP.NET, SQL Server hanya menggunakan satu proses yang tidak dapat didaur ulang tanpa menghentikan database untuk waktu lama yang tidak dapat diterima.

Dengan jaminan yang lebih lemah ini dan beroperasi dalam satu proses, keandalan didasarkan pada penghentian utas atau mendaur ulang domain aplikasi bila perlu dan mengambil tindakan pencegahan untuk memastikan sumber daya sistem operasi seperti penanganan atau memori tidak bocor. Bahkan dengan batasan keandalan yang lebih sederhana ini, masih ada persyaratan keandalan yang signifikan:

  • Jangan pernah membocorkan sumber daya sistem operasi.

  • Identifikasi semua kunci yang dikelola dalam semua bentuk terhadap CLR.

  • Jangan pernah merusak keadaan yang dibagi bersama dalam domain lintas aplikasi, memungkinkan AppDomain daur ulang berfungsi dengan lancar.

Meskipun secara teoritis mungkin untuk menulis kode terkelola guna menangani pengecualian ThreadAbortException, StackOverflowException, dan OutOfMemoryException, mengharapkan pengembang untuk menulis kode yang sebegitu kuat di seluruh aplikasi tidaklah masuk akal. Untuk alasan itu, pengecualian di luar jalur mengakibatkan utas yang sedang dijalankan dihentikan; dan jika utas yang dihentikan sedang memodifikasi status bersama, yang dapat ditentukan dengan apakah utas tersebut memegang kunci, maka AppDomain dibongkar. Ketika metode yang mengedit status bersama dihentikan, status akan rusak karena tidak mungkin menulis kode back-out yang andal untuk pembaruan ke status bersama.

Di .NET Framework versi 2.0, satu-satunya host yang memerlukan keandalan adalah SQL Server. Jika rakitan Anda akan dijalankan di SQL Server, Anda harus melakukan pekerjaan keandalan untuk setiap bagian rakitan tersebut, bahkan jika ada fitur khusus yang dinonaktifkan saat berjalan dalam database. Ini diperlukan karena mesin analisis kode memeriksa kode pada tingkat perakitan dan tidak dapat membedakan kode yang dinonaktifkan. Pertimbangan pemrograman SQL Server lainnya adalah bahwa SQL Server menjalankan semuanya dalam satu proses, dan AppDomain daur ulang digunakan untuk membersihkan semua sumber daya seperti memori dan handel sistem operasi.

Anda tidak dapat bergantung pada finalizer atau destruktor atau blok try/finally untuk kode pemulihan. Mereka mungkin diinterupsi atau tidak dapat dipanggil.

Pengecualian asinkron dapat dilemparkan di lokasi yang tidak terduga, mungkin setiap instruksi mesin: ThreadAbortException, , StackOverflowExceptiondan OutOfMemoryException.

Thread yang dikelola tidak selalu merupakan thread Win32 di SQL; mereka mungkin merupakan fiber.

Status berbagi yang dapat diubah domain seluruh proses atau lintas aplikasi sangat sulit diubah dengan aman dan harus dihindari jika memungkinkan.

Kondisi kehabisan memori tidak jarang terjadi di SQL Server.

Jika pustaka yang dihosting di SQL Server tidak memperbarui status bersama mereka dengan benar, ada kemungkinan besar bahwa kode tidak akan pulih sampai database dimulai ulang. Selain itu, dalam beberapa kasus ekstrem, ada kemungkinan ini dapat menyebabkan proses SQL Server gagal, menyebabkan database di-boot ulang. Mem-boot ulang database dapat menyebabkan situs web terhenti atau memengaruhi operasi perusahaan, mengganggu ketersediaan. Kebocoran lambat sumber daya sistem operasi seperti memori atau handle dapat menyebabkan server akhirnya gagal mengalokasikan handle tanpa kemungkinan pemulihan, atau berpotensi server dapat secara perlahan menurunkan kinerja dan mengurangi tingkat ketersediaan aplikasi pelanggan. Jelas kita ingin menghindari skenario ini.

Aturan praktik terbaik

Pengenalan berfokus pada hal-hal yang harus ditangkap oleh tinjauan kode untuk kode terkelola yang berjalan di server guna meningkatkan stabilitas dan keandalan kerangka kerja. Semua pemeriksaan ini adalah praktik yang baik secara umum dan mutlak harus dilakukan di server.

Dalam menghadapi deadlock atau batasan sumber daya, SQL Server akan membatalkan sebuah utas atau menghentikan sebuah AppDomain. Ketika ini terjadi, hanya kode back-out di wilayah eksekusi yang dibatasi (CER) yang dijamin akan dijalankan.

Menggunakan SafeHandle untuk menghindari kebocoran sumber daya

Dalam kasus pembongkaran AppDomain, Anda tidak dapat bergantung pada blok finally atau finalizer yang dijalankan, jadi penting untuk mengabstraksi semua akses sumber daya sistem operasi melalui kelas SafeHandle daripada IntPtr, HandleRef, atau kelas serupa. Ini memungkinkan CLR untuk melacak dan menutup handel yang Anda gunakan bahkan dalam AppDomain kasus robek. SafeHandle akan menggunakan finalizer kritis yang selalu akan dijalankan oleh Common Language Runtime (CLR).

Pegangan sistem operasi disimpan dalam pegangan aman dari saat dibuat hingga saat dilepaskan. Tidak ada jendela di mana ThreadAbortException dapat terjadi untuk membocorkan handel. Selain itu, pemanggilan platform akan menghitung referensi handle, yang memungkinkan pelacakan yang erat terhadap masa pakai handle, mencegah masalah keamanan dengan kondisi persaingan antara Dispose dan metode yang saat ini menggunakan handle.

Sebagian besar kelas yang saat ini memiliki finalizer untuk mengelola handle sistem operasi tidak akan membutuhkan finalizer lagi. Sebagai gantinya, finalizer akan berada di kelas turunan SafeHandle.

Perhatikan bahwa SafeHandle bukan pengganti untuk IDisposable.Dispose. Masih ada potensi kontensi sumber daya dan keuntungan performa dengan melepaskan sumber daya sistem operasi secara eksplisit. Sadarilah bahwa finally blok yang secara eksplisit membuang sumber daya mungkin tidak dijalankan hingga selesai.

SafeHandle memungkinkan Anda untuk menerapkan metode Anda sendiri ReleaseHandle yang melakukan pekerjaan untuk membebaskan handle, seperti meneruskan status ke rutinitas membebaskan handle pada sistem operasi atau membebaskan serangkaian handle dalam sebuah perulangan. CLR menjamin bahwa metode ini dijalankan. Ini adalah tanggung jawab penulis implementasi ReleaseHandle agar handle dirilis dalam semua keadaan. Kegagalan untuk melakukannya akan menyebabkan handle bocor, yang sering mengakibatkan kebocoran sumber daya bawaan yang terkait dengan handle. Oleh karena itu sangat penting untuk menyusun SafeHandle kelas turunan sehingga ReleaseHandle implementasi tidak memerlukan alokasi sumber daya apa pun yang mungkin tidak tersedia pada waktu pemanggilan. Perhatikan bahwa diizinkan untuk memanggil metode yang mungkin gagal dalam implementasi ReleaseHandle asalkan kode Anda dapat menangani kegagalan tersebut dan menyelesaikan kontrak untuk merilis handel asli. Untuk tujuan penelusuran kesalahan, ReleaseHandle memiliki Boolean nilai pengembalian yang dapat diatur ke false jika terjadi kesalahan bencana yang menghalangi pelepasan sumber daya. Melakukannya akan mengaktifkan releaseHandleFailed MDA, jika diaktifkan, untuk membantu mengidentifikasi masalah. Ini tidak mempengaruhi runtime dengan cara lain; ReleaseHandle tidak akan dipanggil lagi untuk sumber daya yang sama dan akibatnya handle akan menjadi bocor.

SafeHandle tidak sesuai dalam konteks tertentu. Karena metode ReleaseHandle dapat dijalankan pada utas finalizer GC, handle apa pun yang harus dibebaskan pada utas tertentu tidak boleh dibungkus dalam SafeHandle.

Pembungkus yang dapat dipanggil runtime (RCW) dapat dibersihkan oleh CLR tanpa kode tambahan. Untuk kode yang menggunakan platform memanggil dan memperlakukan objek COM sebagai IUnknown* atau IntPtr, kode harus ditulis ulang untuk menggunakan RCW. SafeHandle mungkin tidak memadai untuk skenario ini karena kemungkinan metode pelepasan yang tidak dikelola memanggil kembali ke kode terkelola.

Aturan analisis kode

Gunakan SafeHandle untuk merangkum sumber daya sistem operasi. Jangan gunakan HandleRef atau bidang jenis IntPtr.

Pastikan finalizer tidak perlu berjalan untuk mencegah kebocoran sumber daya sistem operasi

Tinjau finalizer Anda dengan hati-hati untuk memastikan bahwa meskipun tidak berjalan, sumber daya sistem operasi penting tidak bocor. Tidak seperti penghentian normal AppDomain ketika aplikasi dijalankan dalam keadaan stabil atau ketika server seperti SQL Server dimatikan, objek tidak dirampungkan selama penghentian tiba-tiba AppDomain. Pastikan sumber daya tidak bocor dalam kasus pembongkaran mendadak, karena kebenaran aplikasi tidak dapat dijamin, tetapi integritas server harus dipertahankan dengan tidak membocorkan sumber daya. Gunakan SafeHandle untuk membebaskan sumber daya sistem operasi apa pun.

Pastikan bahwa klausul akhirnya tidak perlu dijalankan untuk mencegah kebocoran sumber daya sistem operasi

finally klausa tidak dijamin dijalankan di luar CER, mengharuskan pengembang pustaka agar tidak mengandalkan kode dalam blok finally untuk membebaskan sumber daya yang tidak dikelola. Menggunakan SafeHandle adalah solusi yang direkomendasikan.

Aturan analisis kode

Gunakan SafeHandle untuk membersihkan sumber daya sistem operasi alih-alih Finalize. Jangan gunakan IntPtr; gunakan SafeHandle untuk merangkum sumber daya. Jika klausul akhirnya harus dijalankan, letakkan dalam CER.

Semua kunci harus melalui kode penguncian terkelola yang ada

CLR harus tahu kapan kode berada dalam kunci sehingga akan tahu untuk merobohkan AppDomain daripada hanya membatalkan utas. Menghentikan utas bisa berbahaya karena data yang dioperasikan oleh utas tersebut dapat tertinggal dalam keadaan inkonsisten. Oleh karena itu, seluruhnya AppDomain harus didaur ulang. Konsekuensi dari gagal mengidentifikasi kunci dapat berupa kebuntuan atau hasil yang salah. Gunakan metode BeginCriticalRegion dan EndCriticalRegion untuk mengidentifikasi wilayah kunci. Mereka adalah metode statis pada Thread kelas yang hanya berlaku untuk utas saat ini, membantu mencegah satu utas mengedit jumlah kunci utas lain.

Enter dan Exit memiliki pemberitahuan CLR ini bawaan, sehingga penggunaannya direkomendasikan serta penggunaan Pernyataan kunci, yang menggunakan metode ini.

Mekanisme penguncian lainnya seperti kunci putaran dan AutoResetEvent harus memanggil metode ini untuk memberi tahu CLR bahwa bagian penting sedang dimasukkan. Metode ini tidak mengambil kunci apa pun; mereka memberi tahu CLR bahwa kode dijalankan di bagian penting dan membatalkan utas dapat membuat status bersama tidak konsisten. Jika Anda telah menentukan jenis kunci Anda sendiri, seperti kelas kustom ReaderWriterLock , gunakan metode jumlah kunci ini.

Aturan analisis kode

Tandai dan identifikasi semua kunci menggunakan BeginCriticalRegion dan EndCriticalRegion. Jangan gunakan CompareExchange, Increment, dan Decrement dalam perulangan. Jangan melakukan pemanggilan lintas platform pada varian Win32 dari metode-metode ini. Jangan gunakan Sleep dalam perulangan. Jangan gunakan bidang volatil.

Kode pembersihan harus berada di blok penangkapan atau akhirnya, Tidak mengikuti tangkapan

Kode pembersihan tidak boleh mengikuti catch blok; harus berada di finally atau di catch blok itu sendiri. Ini seharusnya menjadi praktik yang baik dan normal. Blok finally umumnya lebih disukai karena menjalankan kode yang sama baik ketika pengecualian telah dilemparkan maupun ketika akhir blok try tercapai secara normal. Jika terjadi pengecualian tak terduga yang dilemparkan, misalnya ThreadAbortException, kode pembersihan tidak akan berjalan. Setiap sumber daya yang tidak terkelola yang akan Anda bersihkan dalam finally idealnya harus dibungkus dalam SafeHandle untuk mencegah kebocoran. Catat bahwa kata kunci C# using dapat digunakan secara efektif untuk mengelola sumber daya objek, termasuk handle.

Meskipun AppDomain daur ulang dapat membersihkan sumber daya pada utas finalizer, masih penting untuk menempatkan kode pembersihan di tempat yang benar. Perhatikan bahwa jika suatu utas menerima pengecualian asinkron tanpa memegang kunci, CLR akan mencoba mengakhiri utas tersebut sendiri tanpa perlu mendaur ulang AppDomain. Memastikan bahwa sumber daya dibersihkan lebih cepat daripada kemudian membantu dengan membuat lebih banyak sumber daya tersedia, dan dengan mengelola masa pakai dengan lebih baik. Jika Anda tidak secara eksplisit menutup handle file di beberapa jalur kode error, maka tunggu hingga finalizer yang bertugas membersihkannya, lain kali kode Anda berjalan mungkin gagal mencoba mengakses file yang persis sama jika finalizer belum berjalan. Untuk alasan ini, memastikan bahwa kode pembersihan ada dan berfungsi dengan benar akan membantu memulihkan dari kegagalan dengan lebih bersih dan cepat, meskipun tidak benar-benar diperlukan.

Aturan analisis kode

Kode pembersihan setelah catch perlu berada di blok finally. Lakukan panggilan untuk dibuang di blok akhirnya. catch blok harus diakhiri dengan lemparan atau lempar ulang. Meskipun akan ada pengecualian, seperti kode yang mendeteksi apakah koneksi jaringan dapat dibuat di mana Anda mungkin mendapatkan salah satu dari sejumlah besar pengecualian, kode apa pun yang memerlukan penangkapan sejumlah pengecualian dalam keadaan normal harus memberikan indikasi bahwa kode harus diuji untuk melihat apakah kode akan berhasil.

Process-Wide status bersama yang dapat diubah antara domain aplikasi harus dihilangkan atau menggunakan wilayah eksekusi yang dibatasi

Seperti yang dijelaskan dalam pengenalan, mungkin sangat sulit untuk menulis kode terkelola yang memantau status bersama di seluruh proses di seluruh domain aplikasi dengan cara yang dapat diandalkan. Keadaan bersama di seluruh proses adalah struktur data bersama di antara domain aplikasi, baik dalam kode Win32, di dalam CLR, atau dalam kode yang dikelola menggunakan remoting. Setiap status bersama yang dapat diubah sangat sulit untuk ditulis dengan benar dalam kode terkelola, dan status bersama statis mungkin dilakukan hanya dengan sangat hati-hati. Jika Anda memiliki status bersama seluruh proses atau di seluruh komputer, temukan beberapa cara untuk menghilangkannya atau melindungi status bersama menggunakan wilayah eksekusi yang dibatasi (CER). Perhatikan bahwa pustaka apa pun dengan status bersama yang tidak diidentifikasi dan diperbaiki dapat menyebabkan host, seperti SQL Server, yang memerlukan pembongkaran bersih AppDomain untuk crash.

Jika kode menggunakan objek COM, hindari berbagi objek COM tersebut di antara domain aplikasi.

Kunci tidak berfungsi di seluruh proses atau di antara domain aplikasi.

Di masa lalu, Enter dan Pernyataan kunci telah digunakan untuk membuat kunci proses global. Misalnya, ini terjadi saat mengunci pada kelas AppDomain agile, seperti Type instans dari rakitan yang tidak dibagikan, objek, Thread string yang disimpan, dan beberapa string yang dibagikan di seluruh domain aplikasi menggunakan komunikasi jarak jauh. Penguncian ini tidak lagi berlaku di seluruh proses. Untuk mengidentifikasi keberadaan kunci domain interaplikasi di seluruh proses, tentukan apakah kode dalam kunci menggunakan sumber daya eksternal dan bertahan seperti file pada disk atau mungkin database.

Perhatikan bahwa mengambil kunci dalam AppDomain dapat menyebabkan masalah jika kode yang dilindungi menggunakan sumber daya eksternal karena kode tersebut dapat berjalan secara bersamaan di beberapa domain aplikasi. Ini bisa menjadi masalah saat menulis ke satu file log atau mengikat soket untuk seluruh proses. Perubahan ini berarti tidak ada cara mudah, menggunakan kode terkelola, untuk mendapatkan kunci global proses, selain menggunakan bernama Mutex atau Semaphore instans. Buat kode yang tidak berjalan di dua domain aplikasi secara bersamaan, atau gunakan Mutex kelas atau Semaphore . Jika kode yang ada tidak dapat diubah, jangan gunakan win32 bernama mutex untuk mencapai sinkronisasi ini karena berjalan dalam mode serat berarti Anda tidak dapat menjamin utas sistem operasi yang sama akan memperoleh dan melepaskan mutex. Anda harus menggunakan kelas terkelola Mutex , atau bernama ManualResetEvent, AutoResetEvent, atau Semaphore untuk menyinkronkan kunci kode dengan cara yang diketahui CLR alih-alih menyinkronkan kunci menggunakan kode yang tidak dikelola.

Hindari penggunaan lock(typeof(MyType))

Objek privat dan publik Type dalam rakitan bersama hanya dengan satu salinan kode yang dibagikan di semua domain aplikasi juga menimbulkan masalah. Untuk rakitan bersama, hanya ada satu instans Type per proses, yang berarti bahwa beberapa domain aplikasi berbagi instans yang sama Type persis. Mengunci pada instans Type mengambil kunci yang memengaruhi seluruh proses, bukan hanya AppDomain. Jika satu AppDomain mengambil kunci pada Type objek maka utas itu terhenti tiba-tiba, tidak akan melepaskan kunci. Kunci ini kemudian dapat menyebabkan domain aplikasi lain kebuntuan.

Cara yang baik untuk mengambil kunci dalam metode statis melibatkan penambahan objek sinkronisasi internal statis ke kode. Ini dapat diinisialisasi di konstruktor kelas jika ada, tetapi jika tidak dapat diinisialisasi seperti ini:

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

Kemudian saat mengambil kunci, gunakan InternalSyncObject properti untuk mendapatkan objek yang akan dikunci. Anda tidak perlu menggunakan properti jika Anda telah menginisialisasi objek sinkronisasi internal di konstruktor kelas Anda. Kode inisialisasi kunci pemeriksaan ganda akan terlihat seperti contoh ini:

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

Catatan tentang lock(ini)

Umumnya dapat diterima untuk mengambil kunci pada objek individual yang dapat diakses publik. Namun, jika objek adalah objek singleton yang dapat menyebabkan seluruh subsistem mengalami kebuntuan, pertimbangkan untuk menggunakan pola desain di atas juga. Misalnya, penguncian pada satu SecurityManager objek dapat menyebabkan kebuntuan di dalam AppDomain, sehingga seluruh AppDomain tidak dapat digunakan. Adalah praktik yang baik untuk tidak mengambil kunci pada objek yang dapat diakses publik dari jenis ini. Namun kunci pada koleksi atau larik individu umumnya tidak boleh menyajikan masalah.

Aturan analisis kode

Jangan mengunci tipe data yang mungkin digunakan di seluruh domain aplikasi atau yang tidak memiliki identitas yang kuat. Jangan memanggil Enter pada Type, MethodInfo, PropertyInfo, String, ValueType, Thread, atau objek apa pun yang merupakan turunan dari MarshalByRefObject.

Hapus panggilan GC.KeepAlive

Sejumlah besar kode yang ada tidak menggunakan KeepAlive ketika seharusnya atau menggunakannya ketika tidak sesuai. Setelah mengonversi ke SafeHandle, kelas tidak perlu memanggil KeepAlive, dengan asumsi mereka tidak memiliki finalizer tetapi mengandalkan SafeHandle untuk menyelesaikan handel sistem operasi. Meskipun biaya performa untuk mempertahankan panggilan KeepAlive mungkin dapat diabaikan, persepsi bahwa panggilan ke KeepAlive diperlukan atau cukup untuk menyelesaikan masalah seumur hidup yang mungkin tidak ada lagi membuat kode lebih sulit dipertahankan. Namun, saat menggunakan COM Interop CLR Callable Wrappers (RCWs), KeepAlive masih diperlukan oleh kode.

Aturan analisis kode

Hapus KeepAlive.

Gunakan Atribut HostProtection

HostProtectionAttribute (HPA) menyediakan penggunaan aksi keamanan deklaratif untuk menentukan persyaratan perlindungan host, memungkinkan host mencegah kode yang sepenuhnya tepercaya memanggil metode tertentu yang tidak sesuai untuk host tersebut, seperti Exit atau Show untuk SQL Server.

HPA hanya memengaruhi aplikasi tak terkelola yang menghosting runtime bahasa umum dan menerapkan perlindungan host, seperti SQL Server. Saat diterapkan, tindakan keamanan menghasilkan pembuatan permintaan tautan berdasarkan sumber daya host yang diekspos oleh kelas atau metode. Jika kode dijalankan dalam aplikasi klien atau di server yang tidak dilindungi host, atribut "menguap"; tidak terdeteksi dan karenanya tidak diterapkan.

Penting

Tujuan dari atribut ini adalah untuk memberlakukan pedoman model pemrograman khusus host, bukan perilaku keamanan. Meskipun permintaan tautan digunakan untuk memeriksa kesuaian dengan persyaratan model pemrograman, HostProtectionAttribute itu bukan izin keamanan.

Jika host tidak memiliki persyaratan model pemrograman, tuntutan tautan tidak terjadi.

Atribut ini mengidentifikasi hal-hal berikut:

  • Metode atau kelas yang tidak sesuai dengan model pemrograman host, tetapi tetap tidak berbahaya.

  • Metode atau kelas yang tidak sesuai dengan model pemrograman host dan dapat menyebabkan destabilisasi kode pengguna yang dikelola server.

  • Metode atau kelas yang tidak sesuai dengan model pemrograman host dan dapat menyebabkan destabilisasi proses server itu sendiri.

Nota

Jika Anda membuat pustaka kelas yang akan dipanggil oleh aplikasi yang dapat dijalankan di lingkungan yang dilindungi host, Anda harus menerapkan atribut ini ke anggota yang mengekspos HostProtectionResource kategori sumber daya. Anggota pustaka kelas .NET Framework dengan atribut ini hanya menyebabkan pemeriksaan dilakukan pada pemanggil langsung. Anggota pustaka Anda juga harus memastikan pemeriksaan terhadap pemanggil langsungnya dengan cara yang sama.

Silakan temukan informasi lebih lanjut tentang HPA di HostProtectionAttribute.

Aturan analisis kode

Untuk SQL Server, semua metode yang digunakan untuk memperkenalkan sinkronisasi atau thread harus diidentifikasi dengan HPA. Ini termasuk metode yang berbagi status, disinkronkan, atau mengelola proses eksternal. Nilai HostProtectionResource yang berdampak pada SQL Server adalah SharedState, , Synchronizationdan ExternalProcessMgmt. Namun, setiap metode yang menekspose setiap HostProtectionResource harus diidentifikasi oleh HPA, bukan hanya yang menggunakan sumber daya yang memengaruhi SQL.

Jangan memblokir tanpa batas waktu dalam kode yang tidak dikelola

Pemblokiran dalam kode tak terkelola, alih-alih dalam kode terkelola, dapat menyebabkan serangan penolakan layanan karena CLR tidak dapat membatalkan utas. Utas yang diblokir mencegah CLR membongkar AppDomain, setidaknya tanpa melakukan beberapa operasi yang sangat tidak aman. Pemblokiran menggunakan primitif sinkronisasi Windows adalah contoh yang jelas dari sesuatu yang tidak dapat kami izinkan. Pemblokiran dalam panggilan ke ReadFile pada soket harus dihindari jika memungkinkan - idealnya API Windows harus menyediakan mekanisme untuk operasi seperti ini kehabisan waktu.

Idealnya, metode apa pun yang memanggil ke kode asli harus menggunakan panggilan Win32 dengan batas waktu yang wajar dan terbatas. Jika pengguna diizinkan untuk menentukan batas waktu, pengguna tidak boleh diizinkan untuk menentukan batas waktu tak terbatas tanpa izin keamanan tertentu. Sebagai pedoman, jika metode akan memblokir selama lebih dari ~10 detik, Anda harus menggunakan versi yang mendukung batas waktu atau Anda memerlukan dukungan CLR tambahan.

Berikut adalah beberapa contoh API yang bermasalah. Pipa (baik anonim maupun bernama) dapat dibuat dengan batas waktu; namun, kode harus memastikan tidak pernah memanggil CreateNamedPipe atau WaitNamedPipe dengan NMPWAIT_WAIT_FOREVER. Selain itu, mungkin ada pemblokiran yang tidak terduga bahkan jika batas waktu ditentukan. Memanggil WriteFile pada pipa anonim akan memblokir hingga semua byte ditulis, yang berarti jika buffer memiliki data yang belum dibaca di dalamnya, panggilan WriteFile akan memblokir hingga pembaca membebaskan ruang di buffer pipa. Soket harus selalu menggunakan beberapa API yang mematuhi mekanisme batas waktu.

Aturan analisis kode

Pemblokiran tanpa batas waktu dalam kode yang tidak dikelola adalah serangan penolakan layanan. Jangan melakukan panggilan platform ke WaitForSingleObject, WaitForSingleObjectEx, WaitForMultipleObjects, MsgWaitForMultipleObjects, dan MsgWaitForMultipleObjectsEx. Jangan gunakan NMPWAIT_WAIT_FOREVER.

Mengidentifikasi segala fitur STA-Dependent

Identifikasi kode apa pun yang menggunakan com single-threaded apartments (STAs). STAs dinonaktifkan dalam proses SQL Server. Fitur yang bergantung pada CoInitialize, seperti penghitung kinerja atau clipboard, harus dinonaktifkan dalam SQL Server.

Pastikan finalizer bebas dari masalah sinkronisasi

Beberapa thread finalizer mungkin ada di versi .NET Framework di masa mendatang, yang berarti finalizer untuk beberapa instance dari tipe yang sama berjalan secara bersamaan. Mereka tidak harus benar-benar aman utas; pengumpul sampah menjamin bahwa hanya satu utas yang akan menjalankan finalizer untuk instans objek tertentu. Namun, finalizer-finalizer perlu diprogram untuk menghindari kondisi saling berebut dan kebuntuan saat dijalankan secara bersamaan pada beberapa instans objek yang berbeda. Saat menggunakan status eksternal apa pun, seperti menulis ke file log, di finalizer, masalah utas harus ditangani. Jangan mengandalkan finalisasi untuk memberikan keamanan thread. Jangan gunakan storage lokal thread, terkelola atau asli, untuk menyimpan status pada thread finalizer.

Aturan analisis kode

Finalizer harus bebas dari masalah sinkronisasi dalam kinerjanya. Jangan gunakan status statis yang dapat diubah dalam finalizer.

Hindari memori yang tidak dikelola jika memungkinkan

Memori yang tidak dikelola dapat mengalami kebocoran, seperti halnya pengelolaan sistem operasi. Jika memungkinkan, coba gunakan memori pada tumpukan menggunakan stackalloc atau objek terkelola yang disematkan seperti pernyataan tetap atau GCHandle menggunakan byte[]. Pada akhirnya, GC akan membersihkan semuanya. Namun, jika Anda harus mengalokasikan memori yang tidak dikelola, pertimbangkan untuk menggunakan kelas yang berasal dari SafeHandle untuk membungkus alokasi memori.

Perhatikan bahwa setidaknya ada satu kasus di mana SafeHandle tidak memadai. Untuk panggilan metode COM yang mengalokasikan atau membebaskan memori, adalah umum bagi satu DLL untuk mengalokasikan memori melalui CoTaskMemAlloc DLL lain membebaskan memori tersebut dengan CoTaskMemFree. Menggunakan SafeHandle di tempat-tempat ini tidak tepat karena akan mencoba mengikat masa pakai memori tak terkelola ke masa pakai SafeHandle, alih-alih memungkinkan DLL lain untuk mengontrol masa pakai memori tersebut.

Tinjau semua penggunaan tangkapan (Pengecualian)

Tangkap blok yang menangkap semua pengecualian alih-alih satu pengecualian tertentu sekarang akan menangkap pengecualian asinkron juga. Periksa setiap blok catch(Exception) untuk memastikan tidak ada sumber daya penting yang dilewati atau kode pemulihan yang mungkin dilewati, serta memeriksa perilaku yang berpotensi salah dalam blok tangkapan itu sendiri untuk menangani ThreadAbortException, StackOverflowException, atau OutOfMemoryException. Perhatikan bahwa ada kemungkinan kode ini mungkin mencatat atau membuat beberapa asumsi bahwa kode ini mungkin hanya melihat pengecualian tertentu, atau bahwa setiap kali pengecualian terjadi gagal karena satu alasan tertentu. Asumsi ini mungkin perlu diperbarui untuk menyertakan ThreadAbortException.

Pertimbangkan untuk mengubah semua tempat yang menangkap semua pengecualian untuk menangkap jenis pengecualian tertentu yang Anda harapkan akan dilemparkan, seperti FormatException dari metode pemformatan string. Ini mencegah blok tangkapan berjalan pada pengecualian yang tidak terduga dan akan membantu memastikan kode tidak menyembunyikan bug dengan menangkap pengecualian yang tidak terduga. Sebagai aturan umum tidak pernah menangani pengecualian dalam kode pustaka (kode yang mengharuskan Anda untuk menangkap pengecualian dapat menunjukkan kelemahan desain dalam kode yang Anda panggil). Dalam beberapa kasus, Anda mungkin ingin menangkap pengecualian dan melemparkan jenis pengecualian yang berbeda untuk memberikan lebih banyak data. Gunakan pengecualian berlapis dalam hal ini, menyimpan penyebab sebenarnya dari kegagalan dalam InnerException properti pengecualian baru.

Aturan analisis kode

Tinjau semua blok tangkapan dalam kode terkelola yang menangkap semua objek atau menangkap semua pengecualian. Dalam C#, ini berarti menandai catch{} dan catch(Exception){}. Pertimbangkan untuk membuat jenis pengecualian sangat spesifik, atau tinjau kode untuk memastikannya tidak bertindak dengan cara yang buruk jika menangkap jenis pengecualian yang tidak terduga.

Jangan berasumsi bahwa utas yang dikelola adalah utas Win32 - Itu adalah Fiber

Menggunakan penyimpanan lokal utas terkelola memang berfungsi, tetapi Anda mungkin tidak menggunakan penyimpanan lokal utas yang tidak dikelola atau mengasumsikan kode akan berjalan pada utas sistem operasi saat ini lagi. Jangan ubah pengaturan seperti lokal utas. Jangan memanggil InitializeCriticalSection atau CreateMutex melalui pemanggilan platform karena memerlukan utas sistem operasi yang memasukkan kunci juga keluar dari kunci. Karena ini tidak akan terjadi ketika menggunakan serat, bagian dan mutex kritis Win32 tidak dapat digunakan di SQL secara langsung. Perhatikan bahwa kelas terkelola Mutex tidak menangani masalah afinitas utas ini.

Anda dapat dengan aman menggunakan sebagian besar status pada objek terkelola Thread, termasuk penyimpanan lokal utas terkelola dan kultur UI saat ini dari utas tersebut. Anda juga dapat menggunakan ThreadStaticAttribute, yang membuat nilai variabel statis yang ada hanya dapat diakses oleh utas terkelola saat ini (ini adalah cara lain untuk melakukan penyimpanan lokal serat di CLR). Untuk alasan model pemrograman, Anda tidak dapat mengubah kultur utas saat ini ketika dijalankan di SQL.

Aturan analisis kode

SQL Server berjalan dalam mode serat; jangan gunakan penyimpanan lokal utas. Hindari panggilan invoke platform ke TlsAlloc, TlsFree, TlsGetValue, dan TlsSetValue.

Biarkan SQL Server menangani peniruan identitas

Karena peniruan beroperasi pada tingkat utas dan SQL dapat berjalan dalam mode serat, kode terkelola tidak boleh meniru pengguna, dan tidak boleh memanggil RevertToSelf.

Aturan analisis kode

Biarkan SQL Server menangani peniruan identitas. Jangan gunakan RevertToSelf, , ImpersonateAnonymousToken, DdeImpersonateClientImpersonateDdeClientWindow, ImpersonateLoggedOnUser, ImpersonateNamedPipeClient, ImpersonateSelf, RpcImpersonateClient, RpcRevertToSelf, , RpcRevertToSelfEx, atau SetThreadToken.

Jangan panggil Thread::Suspend

Kemampuan untuk menangguhkan sebuah utas mungkin tampak sebagai operasi sederhana, tetapi dapat menyebabkan deadlock. Jika utas yang memegang kunci ditangguhkan oleh utas kedua dan kemudian utas kedua mencoba mengambil kunci yang sama, kebuntuan terjadi. Suspend dapat mengganggu keamanan, pemuatan kelas, pengoperasian jarak jauh, dan refleksi saat ini.

Aturan analisis kode

Jangan memanggil Suspend. Pertimbangkan untuk menggunakan primitif sinkronisasi nyata, seperti Semaphore atau ManualResetEvent .

Melindungi operasi penting dengan wilayah eksekusi dan kontrak keandalan yang dibatasi

Saat melakukan operasi kompleks yang memperbarui status bersama atau yang harus sepenuhnya berhasil atau sepenuhnya gagal secara pasti, pastikan operasi tersebut dilindungi oleh wilayah eksekusi yang dibatasi (CER). Ini menjamin bahwa kode berjalan dalam setiap kasus, bahkan terjadi penghentian utas secara tiba-tiba atau penghentian secara tiba-tiba AppDomain.

CER adalah blok tertentu try/finally yang langsung didahului oleh panggilan ke PrepareConstrainedRegions.

Melakukannya menginstruksikan pengkompilator just-in-time untuk mempersiapkan semua kode di blok finally sebelum menjalankan blok try. Ini menjamin bahwa kode di blok akhirnya dibangun dan akan berjalan dalam semua kasus. Tidak jarang terdapat blok kosong try dalam CER. Menggunakan CER melindungi dari pembatakan utas asinkron dan pengecualian di luar memori. Lihat ExecuteCodeWithGuaranteedCleanup untuk bentuk CER yang juga menangani luapan tumpukan untuk kode yang sangat dalam.

Lihat juga