Praktik Terbaik Keandalan

Aturan keandalan berikut berorientasi ke SQL Server; namun, juga berlaku untuk aplikasi server berbasis host apa pun. Sangat penting server seperti Microsoft SQL Server tidak membocorkan sumber daya dan tidak diturunkan. Namun, hal 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 akan pulih dari kesalahan apa pun di setiap lokasi dengan kode back-out. Itu akan menjadi tugas yang sulit dengan peluang keberhasilan sedikit. Runtime bahasa umum (CLR) tidak dapat dengan mudah memberikan jaminan yang cukup kuat untuk kode yang dikelola untuk membuat penulisan kode yang sempurna menjadi layak. Perhatikan bahwa tidak seperti ASP.NET, Microsoft SQL Server hanya menggunakan satu proses yang tidak dapat didaur ulang tanpa menurunkan database untuk waktu yang sangat lama.

Dengan jaminan yang lebih lemah ini dan berjalan dalam satu proses, keandalan didasarkan pada penghentian rangkaian atau daur ulang domain aplikasi bila diperlukan dan mengambil tindakan pencegahan untuk memastikan sumber daya sistem operasi seperti handel 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 ke runtime bahasa umum.

  • Jangan pernah merusak status bersama domain lintas aplikasi, memungkinkan daur ulang AppDomain berfungsi dengan lancar.

Meskipun secara teoritis mungkin untuk menulis kode terkelola untuk menangani pengecualian ThreadAbortException, StackOverflowException, dan OutOfMemoryException, mengharapkan pengembang untuk menulis kode yang kuat seperti itu di seluruh aplikasi adalah tidak masuk akal. Karena alasan itu, pengecualian out-of-band mengakibatkan rangkaian pelaksana dihentikan; dan jika rangkaian yang dihentikan sedang mengedit status bersama, yang dapat ditentukan oleh apakah rangkaian tersebut terkunci, maka AppDomain dibongkar. Saat 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 Microsoft SQL Server, Anda harus melakukan pekerjaan keandalan untuk setiap bagian dari rakitan itu, bahkan jika ada fitur khusus yang dinonaktifkan saat dijalankan di database. Ini diperlukan karena mesin analisis kode memeriksa kode di tingkat perakitan dan tidak dapat membedakan kode yang dinonaktifkan. Pertimbangan pemrograman SQL Server lainnya adalah bahwa SQL Server menjalankan semuanya dalam satu proses, dan daur ulang AppDomain digunakan untuk membersihkan semua sumber daya seperti memori dan handel sistem operasi.

Anda tidak dapat bergantung pada pengakhir atau destruktor atau blok try/finally untuk kode back-out. Mereka mungkin terganggu atau tidak dipanggil.

Pengecualian asinkron dapat ditampilkan ke lokasi yang tidak terduga, mungkin setiap instruksi mesin: ThreadAbortException, StackOverflowException, dan OutOfMemoryException.

Rangkaian yang dikelola belum tentu rangkaian Win32 di SQL; ini mungkin fiber.

Status bersama yang dapat diubah di seluruh proses atau lintas aplikasi sangat sulit untuk diubah dengan aman dan harus dihindari bila memungkinkan.

Kondisi kehabisan memori tidak jarang terjadi di Microsoft SQL Server.

Jika pustaka yang dihosting di Microsoft SQL Server tidak memperbarui status bersamanya dengan benar, ada kemungkinan besar bahwa kode tidak akan pulih hingga database dihidupkan ulang. Selain itu, dalam beberapa kasus ekstrem, hal ini mungkin menyebabkan proses Microsoft SQL Server gagal, menyebabkan database melakukan boot ulang. Me-reboot ulang database dapat menghapus situs Web atau memengaruhi operasi perusahaan, mengganggu ketersediaan. Kebocoran lambat sumber daya sistem operasi seperti memori atau handel dapat menyebabkan server akhirnya gagal mengalokasikan handel tanpa kemungkinan pemulihan, atau berpotensi menurunkan performa server secara perlahan dan mengurangi ketersediaan aplikasi pelanggan. Jelas kami ingin menghindari skenario ini.

Aturan praktik terbaik

Pendahuluan berfokus pada tinjauan kode untuk kode terkelola yang berjalan di server yang harus ditangkap untuk meningkatkan stabilitas dan keandalan kerangka kerja. Semua pemeriksaan ini adalah praktik yang baik secara umum dan keharusan mutlak di server.

Dalam menghadapi dead lock atau kendala sumber daya, Microsoft SQL Server akan membatalkan rangkaian atau menggagalkan AppDomain. Ketika ini terjadi, hanya kode back-out di wilayah eksekusi terbatas (CER) yang dijamin untuk dijalankan.

Gunakan SafeHandle untuk menghindari kebocoran sumber daya

Dalam kasus pembongkaran AppDomain, Anda tidak dapat bergantung pada blok finally atau pengakhir yang dijalankan, jadi penting untuk mengabstraksi semua akses sumber daya sistem operasi melalui kelas SafeHandle daripada IntPtr, HandleRef, atau kelas sejenis. Hal ini memungkinkan runtime bahasa umum untuk melacak dan menutup handel yang Anda gunakan bahkan dalam kasus tear-down AppDomain. SafeHandle akan menggunakan pengakhir penting yang akan selalu dijalankan runtime bahasa umum.

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

Sebagian besar kelas yang saat ini memiliki pengakhir untuk sekadar membersihkan handel sistem operasi tidak memerlukan pengakhir lagi. Sebagai gantinya, pengakhir akan berada di kelas turunan SafeHandle.

Perhatikan bahwa SafeHandle bukan pengganti IDisposable.Dispose. Masih ada potensi pertentangan sumber daya dan keunggulan kinerja untuk secara eksplisit membuang sumber daya sistem operasi. Sadarilah bahwa blok finally yang secara eksplisit membuang sumber daya mungkin tidak dijalankan sampai selesai.

SafeHandle memungkinkan Anda menerapkan metode ReleaseHandle Anda sendiri yang melakukan pekerjaan untuk membebaskan handel, seperti meneruskan status ke handel sistem operasi membebaskan rutinitas atau membebaskan satu set handel dalam perulangan. Runtime bahasa umum menjamin bahwa metode ini dijalankan. Adalah tanggung jawab pembuat implementasi ReleaseHandle untuk memastikan bahwa handel dirilis dalam semua keadaan. Kegagalan hal tersebut akan menyebabkan handel bocor, yang sering mengakibatkan kebocoran sumber daya asli yang terkait dengan handel. Oleh karena itu, sangat penting untuk menyusun kelas turunan SafeHandle sedemikian rupa sehingga implementasi ReleaseHandle tidak memerlukan alokasi sumber daya apa pun yang mungkin tidak tersedia pada waktu pemanggilan. Perhatikan bahwa diperbolehkan untuk memanggil metode yang mungkin gagal dalam penerapan ReleaseHandle asalkan kode Anda dapat menangani kegagalan tersebut dan menyelesaikan kontrak untuk merilis handel asli. Untuk tujuan penelusuran kesalahan, ReleaseHandle memiliki nilai kembalian Boolean yang dapat disetel ke false jika terjadi kesalahan besar yang mencegah perilisan sumber daya. Melakukannya akan mengaktifkan releaseHandleFailed MDA, jika diaktifkan, untuk membantu mengidentifikasi masalah. Hal ini tidak memengaruhi runtime bahasa umum dengan cara lain; ReleaseHandle tidak akan dipanggil lagi untuk sumber daya yang sama dan akibatnya handelnya akan bocor.

SafeHandle tidak sesuai dalam konteks tertentu. Karena metode ReleaseHandle dapat dijalankan pada rangkaian pengakhir GC, handel apa pun yang diperlukan untuk dibebaskan pada rangkaian tertentu tidak boleh ditutupi dalam SafeHandle.

Runtime callable wrappers (RCW) dapat dibersihkan oleh runtime bahasa umum tanpa kode tambahan. Untuk kode yang menggunakan platform yang 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 rilis 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 pengakhir tidak harus dijalankan untuk mencegah kebocoran sumber daya sistem operasi

Tinjau pengakhir Anda dengan cermat untuk memastikan bahwa meskipun tidak berjalan, sumber daya sistem operasi yang penting tidak bocor. Tidak seperti pembongkaran AppDomain normal saat aplikasi dijalankan dalam keadaan stabil atau saat server seperti Microsoft SQL Server dimatikan, objek tidak diselesaikan selama pembongkaran AppDomain yang tiba-tiba. Pastikan sumber daya tidak bocor jika terjadi pembongkaran mendadak, karena kebenaran aplikasi tidak dapat dijamin, tetapi integritas server harus dijaga dengan tidak membocorkan sumber daya. Gunakan SafeHandle untuk membebaskan sumber daya sistem operasi apa pun.

Pastikan bahwa klausul terakhir tidak harus dijalankan untuk mencegah kebocoran sumber daya sistem operasi

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

Aturan analisis kode

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

Semua kunci harus melalui kode penguncian terkelola yang ada

CLR harus mengetahui kapan kode terkunci sehingga ia akan tahu untuk membongkar AppDomain daripada membatalkan rangkaian. Membatalkan rangkaian dapat berbahaya karena data yang dioperasikan oleh rangkaian dapat dibiarkan dalam keadaan tidak konsisten. Oleh karena itu, seluruh AppDomain harus didaur ulang. Konsekuensi dari kegagalan mengidentifikasi kunci dapat berupa kebuntuan atau hasil yang salah. Gunakan metode BeginCriticalRegion dan EndCriticalRegion untuk mengidentifikasi wilayah kunci. Itu adalah metode statis pada kelas Thread yang hanya berlaku untuk rangkaian saat ini, membantu mencegah satu rangkaian mengedit jumlah kunci rangkaian lainnya.

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

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

Aturan analisis kode

Tandai dan identifikasi semua kunci menggunakan BeginCriticalRegion dan EndCriticalRegion. Jangan gunakan CompareExchange, Increment, dan Decrement dalam satu perulangan. Jangan lakukan pemanggilan platform dari varian Win32 dari metode ini. Jangan gunakan Sleep dalam satu perulangan. Jangan gunakan bidang yang tidak stabil.

Kode pembersihan harus dalam blok finally atau catch, Bukan mengikuti catch

Kode pembersihan tidak boleh mengikuti blok catch; itu harus di finally atau di blok catch itu sendiri. Ini harus menjadi praktik baik yang normal. Sebuah blok finally umumnya lebih disukai karena menjalankan kode yang sama baik ketika pengecualian ditampilkan dan ketika akhir dari blok try biasanya ditemui. Jika pengecualian tak terduga ditampilkan, misalnya ThreadAbortException, kode pembersihan tidak akan berjalan. Sumber daya apa pun yang tidak dikelola yang akan Anda bersihkan dalam finally idealnya harus dibungkus dalam SafeHandle untuk mencegah kebocoran. Perhatikan kata kunci C# using dapat digunakan secara efektif untuk membuang objek, termasuk handel.

Meskipun daur ulang AppDomain dapat membersihkan sumber daya di rangkaian pengakhir, tetap penting untuk meletakkan kode pembersihan di tempat yang benar. Perhatikan bahwa jika rangkaian menerima pengecualian asinkron tanpa menahan kunci, CLR mencoba mengakhiri rangkaian itu sendiri tanpa harus mendaur ulang AppDomain. Memastikan bahwa sumber daya dibersihkan segera secepatnya membantu dengan membuat lebih banyak sumber daya tersedia, dan dengan mengelola masa pakai dengan lebih baik. Jika Anda tidak secara eksplisit menutup handel ke file di beberapa jalur kode kesalahan kemudian menunggu pengakhir SafeHandle untuk membersihkannya, pada saat kode Anda berjalan mungkin akan gagal mencoba mengakses file yang sama persis jika pengakhir belum berjalan. Karena alasan ini, memastikan bahwa kode pembersihan ada dan berfungsi dengan benar akan membantu pemulihan dari kegagalan dengan lebih bersih dan cepat, meskipun tidak sepenuhnya diperlukan.

Aturan analisis kode

Kode pembersihan setelah catch harus dalam blok finally. Tempatkan panggilan untuk dibuang di blok terakhir. blok catch harus berakhir dalam tampilan atau tampilan balik. 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.

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

Seperti yang dijelaskan dalam pendahuluan, mungkin sangat sulit untuk menulis kode terkelola yang memantau status bersama process-wide di seluruh domain aplikasi dengan cara yang andal. Status bersama process-wide adalah segala jenis struktur data yang dibagikan di antara domain aplikasi, baik dalam kode Win32, di dalam CLR, atau dalam kode terkelola menggunakan kendali jarak jauh. Status bersama apa pun yang dapat diubah sangat sulit untuk ditulis dengan benar dalam kode terkelola, dan status bersama statis apa pun dapat dilakukan hanya dengan sangat hati-hati. Jika Anda memiliki status bersama process-wide atau machine-wide, temukan cara untuk menghilangkannya atau lindungi status bersama menggunakan wilayah eksekusi terbatas (CER). Perhatikan bahwa pustaka apa pun dengan status bersama yang tidak diidentifikasi dan diperbaiki dapat menyebabkan host, seperti Microsoft SQL Server, yang memerlukan pembongkaran bersih AppDomain menjadi crash.

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

Kunci tidak berfungsi di process-wide atau di antara domain aplikasi.

Di masa lalu, Enter dan lock Statement telah digunakan untuk membuat kunci proses global. Misalnya, ini terjadi saat mengunci kelas tangkas AppDomain, seperti instans Type dari rakitan yang tidak dibagikan, objek Thread, string intern, dan beberapa string yang dibagikan di seluruh domain aplikasi menggunakan kendali jarak jauh. Kunci ini tidak lagi mencakup seluruh proses. Untuk mengidentifikasi keberadaan kunci domain interapplication process-wide, tentukan apakah kode di dalam kunci menggunakan sumber daya eksternal apa pun yang bertahan seperti file di disk atau mungkin database.

Perhatikan bahwa mengunci di 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 ke soket untuk seluruh proses. Perubahan ini berarti tidak ada cara mudah, menggunakan kode terkelola, untuk mendapatkan kunci proses-global, selain menggunakan instans Mutex atau Semaphore bernama. Buat kode yang tidak berjalan di dua domain aplikasi secara bersamaan, atau gunakan kelas Mutex atau Semaphore. Jika kode yang ada tidak dapat diubah, jangan gunakan mutex bernama Win32 untuk mencapai sinkronisasi ini karena menjalankan dalam mode fiber berarti Anda tidak dapat menjamin rangkaian sistem operasi yang sama akan memperoleh dan merilis mutex. Anda harus menggunakan kelas Mutex terkelola, atau ManualResetEvent, AutoResetEvent, atau Semaphore bernama untuk menyinkronkan kunci kode dengan cara yang diketahui CLR daripada menyinkronkan kunci menggunakan kode yang tidak dikelola.

Hindari lock(typeof(MyType))

Objek privat dan publik Type dalam rakitan bersama dengan hanya satu salinan kode yang dibagikan di semua domain aplikasi juga menimbulkan masalah. Untuk rakitan bersama, hanya ada satu instans dari Type per proses, artinya beberapa domain aplikasi berbagi instans Type yang sama persis. Mengambil kunci pada instans Type membutuhkan kunci yang memengaruhi seluruh proses, bukan hanya AppDomain. Jika satu AppDomain mengambil kunci pada objek Type maka rangkaian itu tiba-tiba dibatalkan, itu tidak akan merilis kuncinya. Kunci ini kemudian dapat menyebabkan domain aplikasi lain menemui kebuntuan.

Cara yang baik untuk mengambil kunci dalam metode statis melibatkan menambahkan objek sinkronisasi internal statis ke kode. Ini dapat diinisialisasi dalam 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 properti InternalSyncObject 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 kunci(ini)

Secara umum dapat diterima untuk mengambil kunci pada objek individu yang dapat diakses publik. Namun, jika objek adalah objek tunggal yang mungkin menyebabkan seluruh subsistem menemui kebuntuan pertimbangkan untuk menggunakan pola desain di atas juga. Misalnya, kunci pada satu objek SecurityManager dapat menyebabkan kebuntuan di dalam AppDomain yang membuat keseluruhan AppDomain tidak dapat digunakan. Ini adalah praktik yang baik untuk tidak mengunci objek jenis ini yang dapat diakses publik. Namun kunci pada kumpulan atau larik individu biasanya tidak menimbulkan masalah.

Aturan analisis kode

Jangan mengunci jenis yang mungkin digunakan di seluruh domain aplikasi atau tidak memiliki rasa identitas yang kuat. Jangan panggil Enter pada Type, MethodInfo, PropertyInfo, String, ValueType, Thread, atau objek apa pun yang berasal dari MarshalByRefObject.

Hapus panggilan GC.KeepAlive

Sejumlah besar kode yang ada tidak menggunakan KeepAlive saat seharusnya melakukannya atau menggunakannya saat tidak sesuai. Setelah mengonversi ke SafeHandle, kelas tidak perlu memanggil KeepAlive, dengan asumsi mereka tidak memiliki pengakhir tetapi mengandalkan SafeHandle untuk menyelesaikan handel sistem operasi. Meskipun biaya performa untuk mempertahankan panggilan ke KeepAlive mungkin dapat diabaikan, persepsi bahwa panggilan ke KeepAlive diperlukan atau cukup untuk memecahkan masalah lifetime yang mungkin tidak ada lagi membuat kode lebih sulit untuk dipelihara. Namun, saat menggunakan COM interop CLR callable wrappers (RCW), KeepAlive masih diperlukan oleh kode.

Aturan analisis kode

Hapus KeepAlive.

Gunakan Atribut HostProtection

HostProtectionAttribute (HPA) menyediakan penggunaan tindakan keamanan deklaratif untuk menentukan persyaratan perlindungan host, memungkinkan host untuk mencegah bahkan kode dengan kepercayaan penuh dari memanggil metode tertentu yang tidak sesuai untuk host yang diberikan, seperti Exit atau Show untuk Microsoft SQLServer.

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

Penting

Tujuan dari atribut ini adalah untuk menegakkan panduan model pemrograman khusus host, bukan perilaku keamanan. Meskipun permintaan tautan digunakan untuk memeriksa kesesuaian dengan persyaratan model pemrograman, HostProtectionAttribute bukanlah izin keamanan.

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

Atribut ini mengidentifikasi hal berikut:

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

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

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

Catatan

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 kategori sumber daya HostProtectionResource. Anggota pustaka kelas .NET Framework dengan atribut ini hanya menyebabkan pemanggil langsung diperiksa. Anggota pustaka Anda juga harus memeriksa pemanggil langsungnya dengan cara yang sama.

Silakan temukan informasi selengkapnya tentang HPA di HostProtectionAttribute.

Aturan analisis kode

Untuk Microsoft SQL Server, semua metode yang digunakan untuk memperkenalkan sinkronisasi atau rangkaian harus diidentifikasi dengan HPA. Ini termasuk metode yang berbagi status, disinkronkan, atau mengelola proses eksternal. Nilai HostProtectionResource yang memengaruhi Microsoft SQL Server adalah SharedState, Synchronization, dan ExternalProcessMgmt. Namun, metode apa pun yang mengekspos HostProtectionResourceharus diidentifikasi oleh HPA, bukan hanya metode yang menggunakan sumber daya yang memengaruhi SQL.

Jangan memblokir tanpa batas dalam kode yang tidak dikelola

Memblokir dalam kode yang tidak dikelola alih-alih dalam kode yang dikelola dapat menyebabkan penolakan serangan layanan karena Runtime bahasa umum tidak dapat membatalkan rangkaian. Rangkaian yang diblokir mencegah Runtime bahasa umum membongkar AppDomain, setidaknya tanpa melakukan beberapa operasi yang sangat tidak aman. Memblokir menggunakan primitif sinkronisasi Windows adalah contoh jelas dari sesuatu yang tidak dapat kami izinkan. Memblokir panggilan ke ReadFile pada soket harus dihindari jika memungkinkan — idealnya Windows API harus menyediakan mekanisme untuk operasi seperti ini hingga waktu habis.

Metode apa pun yang memanggil ke asli idealnya menggunakan panggilan Win32 dengan batas waktu yang masuk akal dan terbatas. Jika pengguna diizinkan untuk menentukan batas waktu, pengguna tidak boleh diizinkan untuk menentukan batas waktu tanpa batas tanpa izin keamanan tertentu. Sebagai panduan, jika suatu metode akan memblokir lebih dari ~10 detik, Anda harus menggunakan versi yang mendukung batas waktu atau Anda memerlukan dukungan runtime bahasa umum tambahan.

Berikut adalah beberapa contoh API yang bermasalah. Pipe (baik anonim dan bernama) dapat dibuat dengan batas waktu; namun, kode harus memastikan kode 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 pipe anonim akan memblokir hingga semua byte ditulis, artinya jika buffer memiliki data yang belum dibaca di dalamnya, panggilan WriteFile akan memblokir hingga pembaca mengosongkan ruang di buffer pipe. Soket harus selalu menggunakan beberapa API yang mematuhi mekanisme timeout.

Aturan analisis kode

Memblokir tanpa batas waktu dalam kode tak terkelola adalah penolakan serangan layanan. Jangan lakukan panggilan platform ke WaitForSingleObject, WaitForSingleObjectEx, WaitForMultipleObjects, MsgWaitForMultipleObjects, dan MsgWaitForMultipleObjectsEx. Jangan gunakan NMPWAIT_WAIT_FOREVER.

Identifikasi semua fitur STA-Dependent

Identifikasi kode apa pun yang menggunakan COM apartemen berangkaian tunggal (STA). STA dinonaktifkan dalam proses Microsoft SQL Server. Fitur yang tergantung pada CoInitialize, seperti penghitung performa atau clipboard, harus dinonaktifkan dalam Microsoft SQL Server.

Pastikan pengakhir bebas dari masalah sinkronisasi

Beberapa rangkaian pengakhir mungkin ada di versi .NET Framework yang akan datang, yang berarti bahwa pengakhir untuk instans berbeda dari jenis yang sama berjalan secara bersamaan. Ini tidak harus benar-benar aman; pengumpul sampah menjamin bahwa hanya satu rangkaian yang akan menjalankan pengakhir untuk instans objek tertentu. Namun, pengakhir harus diberi kode untuk menghindari kondisi persaingan dan kebuntuan saat berjalan secara bersamaan pada beberapa instans objek yang berbeda. Saat menggunakan status eksternal apa pun, seperti menulis ke file log, di pengakhir, masalah rangkaian harus ditangani. Jangan mengandalkan finalisasi untuk memberikan keamanan rangkaian. Jangan gunakan penyimpanan lokal rangkaian, terkelola atau asli, untuk menyimpan status di rangkaian pengakhir.

Aturan analisis kode

Pengakhir harus bebas dari masalah sinkronisasi. Jangan gunakan status statis yang bisa berubah di pengakhir.

Hindari memori yang tidak dikelola jika memungkinkan

Memori yang tidak dikelola dapat bocor, seperti halnya handel sistem operasi. Jika memungkinkan, coba gunakan memori pada tumpukan menggunakan stackalloc atau objek terkelola yang disematkan seperti pernyataan tetap atau GCHandle menggunakan byte[]. GC pada akhirnya membersihkannya. Namun, jika Anda harus mengalokasikan memori yang tidak dikelola, pertimbangkan untuk menggunakan kelas yang diturunkan dari SafeHandle untuk membungkus alokasi memori.

Perhatikan bahwa setidaknya ada satu kasus di mana SafeHandle tidak memadai. Untuk panggilan metode COM yang mengalokasikan atau mengosongkan memori, biasanya satu DLL mengalokasikan memori melalui CoTaskMemAlloc kemudian DLL lain mengosongkan memori tersebut dengan CoTaskMemFree. Menggunakan SafeHandle di tempat-tempat ini tidak tepat karena akan mencoba mengikat masa pakai memori yang tidak dikelola ke masa pakai SafeHandle alih-alih mengizinkan DLL lain mengontrol masa pakai memori.

Tinjau semua penggunaan catch(Exception)

Catch block yang menangkap semua pengecualian alih-alih satu pengecualian tertentu sekarang juga akan menangkap pengecualian asinkron. Periksa setiap catch(Exception) block, mencari tidak ada pelepasan sumber daya penting atau kode backout yang mungkin dilompati, serta perilaku yang berpotensi salah dalam catch block itu sendiri untuk penanganan ThreadAbortException, StackOverflowException, atau OutOfMemoryException. Perhatikan bahwa kode ini mungkin bisa dilakukan pengelogan atau membuat beberapa asumsi yang mungkin hanya melihat pengecualian tertentu, atau bahwa setiap kali pengecualian terjadi, ini gagal karena satu alasan tertentu. Asumsi ini mungkin perlu diperbarui untuk menyertakan ThreadAbortException.

Pertimbangkan untuk mengubah semua tempat yang menangkap semua pengecualian menjadi menangkap jenis pengecualian tertentu yang Anda harapkan akan ditampilkan, seperti FormatException dari metode pemformatan string. Ini mencegah catch block berjalan pada pengecualian tak terduga dan akan membantu memastikan kode tidak menyembunyikan bug dengan menangkap pengecualian tak terduga. Sebagai aturan umum, jangan pernah menghandel pengecualian dalam kode pustaka (kode yang mengharuskan Anda untuk menangkap pengecualian dapat menunjukkan cacat desain dalam kode yang Anda panggil). Dalam beberapa kasus, Anda mungkin ingin menangkap pengecualian dan menampilkan jenis pengecualian yang berbeda untuk menyediakan lebih banyak data. Gunakan nested exception dalam kasus ini, dengan menyimpan penyebab sebenarnya dari kegagalan di properti InnerException dari pengecualian baru.

Aturan analisis kode

Tinjau semua catch block 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 memastikan kode tidak bertindak dengan cara yang buruk jika menangkap jenis pengecualian yang tidak terduga.

Jangan menganggap rangkaian yang dikelola adalah rangkaian Win32 – Ini adalah Fiber

Menggunakan penyimpanan lokal rangkaian terkelola berfungsi, tetapi Anda tidak boleh menggunakan penyimpanan lokal rangkaian yang tidak dikelola atau menganggap kode akan berjalan lagi di rangkaian sistem operasi saat ini. Jangan mengubah pengaturan seperti lokal rangkaian. Jangan panggil InitializeCriticalSection atau CreateMutex melalui pemanggilan platform karena mereka memerlukan rangkaian sistem operasi yang masuk ke kunci juga keluar dari kunci. Karena ini tidak akan terjadi saat menggunakan fiber, bagian kritis dan mutex Win32 tidak dapat digunakan dalam SQL secara langsung. Perhatikan bahwa kelas Mutex terkelola tidak menangani masalah afinitas rangkaian ini.

Anda dapat dengan aman menggunakan sebagian besar status pada objek Thread terkelola, termasuk penyimpanan lokal rangkaian terkelola dan budaya antarmuka pengguna rangkaian saat ini. Anda juga dapat menggunakan ThreadStaticAttribute, yang membuat nilai variabel statis yang ada hanya dapat diakses oleh rangkaian terkelola saat ini (ini adalah cara lain untuk melakukan penyimpanan lokal fiber di Runtime bahasa umum). Untuk alasan model pemrograman, Anda tidak dapat mengubah budaya rangkaian saat ini saat menjalankan di SQL.

Aturan analisis kode

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

Biarkan SQL Server menangani peniruan identitas

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

Aturan analisis kode

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

Jangan panggil Thread::Suspend

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

Aturan analisis kode

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

Lindungi operasi kritis dengan wilayah eksekusi terbatas dan kontrak keandalan

Saat melakukan operasi kompleks yang memperbarui status bersama atau yang perlu secara deterministik berhasil sepenuhnya atau gagal sepenuhnya, pastikan operasi tersebut dilindungi oleh wilayah eksekusi terbatas (CER). Ini menjamin bahwa kode berjalan dalam setiap kasus, bahkan rangkaian yang tiba-tiba dibatalkan atau AppDomain dibongkar secara tiba-tiba.

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

Melakukannya menginstruksikan kompiler just-in-time untuk menyiapkan semua kode di blok terakhir sebelum menjalankan blok try. Ini menjamin bahwa kode di blok akhir dibuat dan akan berjalan di semua kasus. Tidak jarang di CER memiliki blok try yang kosong. Menggunakan CER melindungi dari pembatalan rangkaian asinkron dan pengecualian kehabisan memori. Lihat ExecuteCodeWithGuaranteedCleanup untuk bentuk CER yang juga menangani stack overflow untuk kode yang sangat dalam.

Lihat juga