Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Jednym z najczęstszych błędów w dowolnym sterowniku jest obsługa buforów, które są nieprawidłowe lub zbyt małe. Te błędy mogą zezwalać na przepełnienie buforu lub powodować awarie systemu, co może naruszyć bezpieczeństwo systemu. W tym artykule omówiono niektóre typowe problemy z obsługą buforu i sposoby ich unikania. Identyfikuje również przykładowy kod zestawu WDK, który demonstruje odpowiednie techniki obsługi buforu.
Typy bufora i nieprawidłowe adresy
Z perspektywy kierowcy, bufory występują w jednej z dwóch odmian:
Bufory stronicowane, które mogą, ale nie muszą być rezydentne w pamięci.
Bufory niestronicowane, które muszą znajdować się w pamięci.
Nieprawidłowy adres pamięci nie jest ani stronicowany, ani bezstronicowy. Ponieważ system operacyjny działa w celu rozwiązania błędu strony spowodowanego nieprawidłową obsługą buforu, należy wykonać następujące czynności:
Oddziela nieprawidłowy adres do jednego ze "standardowych" zakresów adresów (adresów jądra stronicowanych, adresów jądra niestronicowanych lub adresów użytkownika).
Zgłasza odpowiedni typ błędu. System zawsze obsługuje błędy buforu poprzez mechanizm sprawdzania błędów, takich jak PAGE_FAULT_IN_NONPAGED_AREA, albo przez wyjątek, taki jak STATUS_ACCESS_VIOLATION. Jeśli błąd jest sprawdzaniem błędów, system zatrzyma operację. W przypadku wyjątku system wywołuje programy obsługi wyjątków oparte na stosie. Jeśli żaden z procedur obsługi wyjątków nie obsługuje wyjątku, system wywołuje sprawdzanie błędów.
Niezależnie od tego, każda ścieżka dostępu, którą program aplikacyjny może wywołać, a która prowadzi do sprawdzenia błędów przez sterownik, stanowi naruszenie zabezpieczeń w sterowniku. Takie naruszenie pozwala aplikacji na ataki typu "odmowa usługi" na cały system.
Typowe założenia i błędy
Jednym z najczęstszych problemów w tym obszarze jest to, że autorzy sterowników zakładają zbyt wiele na temat środowiska operacyjnego. Niektóre typowe założenia i błędy obejmują:
Sterownik po prostu sprawdza, czy najbardziej znaczący bit jest ustawiony w adresie. Poleganie na stałym wzorcu bitowym w celu określenia typu adresu nie działa we wszystkich systemach ani scenariuszach. Na przykład ta kontrola nie działa na komputerach opartych na architekturze x86, gdy system korzysta z Czterogigabajtowego Tunowania (4GT). Przy użyciu 4GT adresy w trybie użytkownika ustawiają na wysoki bit dla trzeciego gigabajta przestrzeni adresowej.
Sterownik korzystający wyłącznie z ProbeForRead i ProbeForWrite w celu zweryfikowania adresu. Te wywołania zapewniają, że adres jest poprawnym adresem trybu użytkownika w momencie sondowania. Nie ma jednak gwarancji, że ten adres pozostanie prawidłowy po operacji sondy. W związku z tym technika ta wprowadza subtelny warunek wyścigu, który może prowadzić do okresowych niereprodukowalnych awarii.
Wywołania ProbeForRead i ProbeForWrite są nadal niezbędne. Jeśli sterownik pomija sondę, użytkownicy mogą przekazać prawidłowe adresy trybu jądra, których bloki
__tryi__except(obsługa wyjątków strukturalnych) nie przechwycą, otwierając tym samym poważną lukę bezpieczeństwa.Najważniejsze jest to, że niezbędne są zarówno sondowanie, jak i obsługa wyjątków strukturalnych:
Sondowanie sprawdza, czy adres jest adresem trybu użytkownika i czy długość buforu mieści się w zakresie adresów użytkownika.
Blok
__try/__exceptchroni przed dostępem.
Należy pamiętać, że ProbeForRead sprawdza tylko, czy adres i długość mieszczą się w możliwym zakresie adresów trybu użytkownika (na przykład w zakresie trochę poniżej 2 GB dla systemu bez 4GT), a nie tego, czy adres pamięci jest prawidłowy. Natomiast parametr ProbeForWrite próbuje uzyskać dostęp do pierwszego bajtu na każdej stronie określonej długości, aby sprawdzić, czy te bajty są prawidłowymi adresami pamięci.
Sterownik korzystający z funkcji menedżera pamięci, takich jak MmIsAddressValid , aby upewnić się, że adres jest prawidłowy. Jak opisano w przypadku funkcji sondy, sytuacja ta wprowadza stan wyścigu, który może prowadzić do nieodwracalnych awarii.
Sterownik nie może używać obsługi wyjątków strukturalnych. Funkcje
__try/exceptw kompilatorze korzystają z obsługi wyjątków na poziomie systemu operacyjnego. Wyjątki na poziomie jądra są zwracane do systemu za pomocą wywołania funkcji ExRaiseStatus lub jednej z powiązanych funkcji. Niewykorzystanie przez sterownik obsługi wyjątków strukturalnych wokół dowolnego wywołania, które może zgłosić wyjątek, doprowadzi do sprawdzenia błędu (zazwyczaj KMODE_EXCEPTION_NOT_HANDLED).Błędem jest użycie obsługi wyjątków strukturalnych wokół kodu, który nie powinien zgłaszać błędów. To użycie spowoduje po prostu zamaskowanie rzeczywistych usterek, które w przeciwnym razie zostaną znalezione.
__try/__exceptUmieszczenie otoki na najwyższym poziomie wysyłki rutynowej nie jest właściwym rozwiązaniem tego problemu, chociaż czasami rozwiązanie refleksyjne wypróbowane przez pisarzy kierowców.Sterownik zakłada, że zawartość pamięci użytkownika pozostanie stabilna. Załóżmy na przykład, że sterownik zapisał wartość w lokalizacji pamięci trybu użytkownika, a następnie później w tej samej procedurze odwoływał się do tej lokalizacji pamięci. Złośliwa aplikacja może aktywnie zmodyfikować pamięć po zapisie i w rezultacie spowodować awarię sterownika.
W przypadku systemów plików te problemy są poważne, ponieważ systemy plików zwykle polegają na bezpośrednim dostępie do buforów użytkowników (metoda transferu "METHOD_NEITHER"). Takie sterowniki bezpośrednio manipulują buforami użytkowników, a tym samym muszą uwzględniać metody ostrożnego obsługiwania, aby uniknąć awarii na poziomie systemu operacyjnego. Szybkie I/O zawsze przekazuje nieprzetworzone wskaźniki pamięci, więc sterowniki muszą zabezpieczać przed podobnymi problemami, jeśli szybkie I/O jest obsługiwane.
Przykładowy kod obsługi buforu
Zestaw WDK zawiera wiele przykładów weryfikacji buforu w przykładowym kodzie sterownika systemu plikówfastfat i CDFS, w tym:
Funkcja FatLockUserBuffer w fastfat\deviosup.c używa MmProbeAndLockPages do zablokowania fizycznych stron bufora użytkownika oraz MmGetSystemAddressForMdlSafe w FatMapUserBuffer, aby utworzyć wirtualne mapowanie dla zablokowanych stron.
Funkcja FatGetVolumeBitmap w fastfat\fsctl.c używa ProbeForRead i ProbeForWrite do sprawdzania poprawności buforów użytkownika w API defragmentacji.
Funkcja CdCommonRead w cdfs\read.c używa
__tryi__exceptwokół kodu do zerowania buforów użytkownika. Przykładowy kod w CdCommonRead wydaje się używać słów kluczowychtryiexcept. W środowisku WDK te słowa kluczowe w języku C są definiowane pod względem rozszerzeń kompilatora__tryi__except. Każda osoba używająca kodu C++ musi korzystać z natywnych typów kompilatora, aby prawidłowo obsługiwać wyjątki, ponieważ__tryjest słowem kluczowym C++, ale nie słowem kluczowym C, i zapewni formę obsługi wyjątków języka C++, która nie jest odpowiednia dla sterowników jądra.