Aracılığıyla paylaş


Arabellek İşleme

Herhangi bir sürücüdeki en yaygın hatalardan biri, arabelleklerin geçersiz veya çok küçük olduğu arabellek işlemeyle ilgilidir. Bu hatalar arabellek taşmasına izin verebilir veya sistem kilitlenmelerine neden olabilir, dolayısıyla sistem güvenliğini tehlikeye atabilir. Bu makalede, arabellek işlemeyle ilgili bazı yaygın sorunlar ve bunların nasıl önlenmeleri anlatılmaktadır. Ayrıca uygun arabellek işleme tekniklerini gösteren WDK örnek kodunu da tanımlar.

Arabellek türleri ve geçersiz adresler

Sürücü açısından bakıldığında, arabellekler iki çeşitten birinde gelir:

  • Bellekte yerleşik olarak bulunan veya olmayan disk belleği arabellekleri.

  • Bellekte yerleşik olması zorunlu olan sayfalanmamış arabellekler.

Geçersiz bir bellek adresi, ne sayfalı ne de sayfasızdır. İşletim sistemi yanlış arabellek işlemenin neden olduğu bir sayfa hatasını çözmek için çalıştığından aşağıdaki adımları uygular:

  • Geçersiz adresi "standart" adres aralıklarından birinde (sayfalanmış çekirdek adresleri, sayfalanmamış çekirdek adresleri veya kullanıcı adresleri) yalıtıyor.

  • Uygun hata türünü tetikler. Sistem, arabellek hatalarını her zaman PAGE_FAULT_IN_NONPAGED_AREA gibi bir hata denetimiyle veya STATUS_ACCESS_VIOLATION gibi bir istisna ile işler. Hata bir hata denetimiyse sistem işlemi durdurur. Bir özel durum söz konusu olduğunda sistem yığın tabanlı özel durum işleyicilerini çağırır. Özel durum işleyicilerinden hiçbiri özel durumu işlemezse sistem bir hata denetimi çağırır.

Ne olursa olsun, bir uygulama programının çağırabileceği ve sürücünün hata denetimine yol açmasına neden olan herhangi bir erişim yolu, sürücü içinde bir güvenlik ihlalidir. Böyle bir ihlal, bir uygulamanın sistemin tamamına hizmet reddi saldırılarına neden olmasını sağlar.

Yaygın varsayımlar ve hatalar

Bu alandaki en yaygın sorunlardan biri, sürücü yazarlarının işletim ortamı hakkında çok fazla şey varsaymış olmasıdır. Bazı yaygın varsayımlar ve hatalar şunlardır:

  • Sürücü yalnızca adreste yüksek bitin ayarlanıp ayarlanmadığını denetler. Adres türünü belirlemek için sabit bir bit desenine güvenmek tüm sistemlerde veya senaryolarda çalışmaz. Örneğin, sistem Dört Gigabayt Ayarlama (4GT) kullanırken bu denetim x86 tabanlı bilgisayarlarda çalışmaz. 4GT kullanılırken, kullanıcı modu adresleri adres alanının üçüncü gigabaytı için yüksek biti ayarlar.

  • Adresi doğrulamak için yalnızca ProbeForRead ve ProbeForWrite kullanan bir sürücü. Bu çağrılar, yoklama sırasında adresin geçerli bir kullanıcı modu adresi olmasını sağlar. Ancak, yoklama işleminden sonra bu adresin geçerli kalacağının garantisi yoktur. Bu nedenle, bu teknik düzenli olarak tekrarlanamaz kilitlenmelere yol açabilecek ince bir yarış koşulu sağlar.

    ProbeForRead ve ProbeForWrite çağrıları hala gereklidir. Eğer bir sürücü doğrulama adımdan kaçınırsa, kullanıcılar bir __try ve __except bloğunun (yapılandırılmış özel durum işleme) yakalayamayacağı ve böylece büyük bir güvenlik açığı oluşturabilecek geçerli çekirdek modu adresleri iletebilir.

    Alt çizgi, hem yoklama hem de yapılandırılmış özel durum işlemenin gerekli olmasıdır:

    • Yoklama, adresin kullanıcı modu adresi olduğunu ve arabellek uzunluğunun kullanıcı adres aralığı içinde olduğunu doğrular.

    • Bir __try/__except blok, erişime karşı koruma oluşturur.

    ProbeForRead'un yalnızca adres ve uzunluğun bellek adresinin geçerli olup olmadığını değil, olası kullanıcı modu adres aralığında (örneğin, 4GT olmayan bir sistem için 2 GB'ın biraz altında) olduğunu doğruladığını unutmayın. Buna karşılık ProbeForWrite , bu baytların geçerli bellek adresleri olduğunu doğrulamak için belirtilen uzunluktaki her sayfada ilk bayta erişmeye çalışır.

  • Adresin geçerli olduğundan emin olmak için MmIsAddressValid gibi bellek yöneticisi işlevlerini kullanan bir sürücü. Yoklama işlevleri için açıklandığı gibi, bu durum tekrarlanabilir kilitlenmelere yol açabilecek bir yarış durumu getirir.

  • Yapılandırılmış özel durum işlemeyi kullanamayan bir sürücü. Derleyici __try/except içindeki işlevler, özel durum işleme için işletim sistemi düzeyinde destek kullanır. Çekirdek düzeyinde özel durumlar, ExRaiseStatus veya ilgili işlevlerden biri aracılığıyla sisteme geri gönderilir. Yapılandırılmış özel durum işleme kullanmayan bir sürücü, özel durum oluşturabilecek herhangi bir çağrıya bağlı olarak hata denetimine (genellikle KMODE_EXCEPTION_NOT_HANDLED) neden olur.

    Hata oluşturması beklenmeyen kodun etrafında yapılandırılmış özel durum işleme kullanmak bir hatadır. Bu kullanım yalnızca başka türlü bulunabilecek gerçek hataları maskeler. __try/__except Bazen sürücü yazarları tarafından denenen refleks çözüm olsa da, bir sarmalayıcıyı rutininizin en üst dağıtım düzeyine yerleştirmek bu sorunun doğru çözümü değildir.

  • Kullanıcı belleği içeriğinin sabit kalacağını varsayan bir sürücü. Örneğin, bir sürücünün kullanıcı modu bellek konumuna bir değer yazdığını ve daha sonra aynı yordamda bu bellek konumuna başvurduğunu varsayalım. Kötü amaçlı bir uygulama, yazma sonrasında bu belleği aktif bir şekilde değiştirebilir ve sonuç olarak sürücünün çökmesine neden olabilir.

Dosya sistemleri genellikle kullanıcı arabelleklerine (METHOD_NEITHER aktarım yöntemi) doğrudan erişmeye bağlı olduğundan bu sorunlar ciddidir. Bu tür sürücüler kullanıcı arabelleklerini doğrudan işler ve bu nedenle işletim sistemi düzeyinde kilitlenmeleri önlemek için arabellek işleme için ihtiyati yöntemler içermelidir. Hızlı G/Ç her zaman ham bellek işaretçilerini geçirir, bu nedenle hızlı G/Ç destekleniyorsa sürücülerin benzer sorunlara karşı korunması gerekir.

Arabellek işleme için örnek kod

WDK, fastfat ve CDFS dosya sistemi sürücü örnek kodunda arabellek doğrulamasına ilişkin aşağıdakiler dahil olmak üzere çok sayıda örnek içerir:

  • fastfat\deviosup.c içindeki FatLockUserBuffer işlevi, MmProbeAndLockPages'i kullanarak kullanıcı arabelleğinin arkasındaki fiziksel sayfaları kilitler ve FatMapUserBuffer'de, kilitli sayfalar için sanal bir eşleme oluşturmak üzere MmGetSystemAddressForMdlSafe'i kullanır.

  • fastfat\fsctl.c içindeki FatGetVolumeBitmap işlevi, birleştirme API'sindeki kullanıcı arabelleklerini doğrulamak için ProbeForRead ve ProbeForWrite kullanır.

  • cdfs\read.c dosyasındaki CdCommonRead işlevi, kullanıcı arabelleklerini sıfırlamak için kod üzerinde __try ve __except kullanır. CdCommonRead içindeki örnek kod, try ve except anahtar sözcüklerini kullanıyor gibi görünür. WDK ortamında, C'deki bu anahtar sözcükler derleyici uzantıları __try ve __exceptaçısından tanımlanır. C++ kodu kullanan herkes, C++ anahtar sözcüğü gibi __try özel durumları düzgün işlemek için yerel derleyici türlerini kullanmalıdır, ancak C anahtar sözcüğünü kullanmamalıdır ve çekirdek sürücüleri için geçerli olmayan bir C++ özel durum işleme biçimi sağlayacaktır.