GKI 16-6.12 android-mainline errata

Na tej stronie opisujemy ważne problemy i poprawki błędów znalezione w android-mainline, które mogą być istotne dla partnerów.

15 listopada 2024 r.

  • Clang został zaktualizowany do wersji 19.0.1 w przypadku android-mainline i android16-6.12

    • Podsumowanie: nowa wersja Clang wprowadza narzędzie do sprawdzania granic tablic, w którym rozmiar tablicy jest przechowywany w osobnej zmiennej połączonej z tablicą za pomocą atrybutu __counted_by. Ta funkcja może spowodować kernel panic, jeśli rozmiar tablicy nie zostanie prawidłowo zaktualizowany. Komunikat o błędzie wygląda tak:
    UBSAN: array-index-out-of-bounds in common/net/wireless/nl80211.c
    index 0 is out of range for type 'struct ieee80211_channel *[] __counted_by(n_channels)' (aka 'struct ieee80211_channel *[]')
    
    • Szczegóły: narzędzie do sprawdzania granic jest niezbędne do ochrony integralności jądra przez wykrywanie dostępu poza granice. Gdy włączona jest opcja CONFIG_UBSAN_TRAP, narzędzie do sprawdzania granic powoduje kernel panic w przypadku wykrycia jakiegokolwiek problemu.

      • Poprzednia wersja narzędzia do sprawdzania granic sprawdzała tylko tablice o stałym rozmiarze i nie mogła sprawdzać tablic przydzielanych dynamicznie. Nowa wersja używa atrybutu __counted_by do określania granic tablicy w czasie działania i wykrywania większej liczby przypadków dostępu poza granice. W niektórych przypadkach jednak dostęp do tablicy następuje przed ustawieniem zmiennej rozmiaru, co powoduje uruchomienie narzędzia do sprawdzania granic i kernel panic. Aby rozwiązać ten problem, ustaw rozmiar tablicy natychmiast po przydzieleniu pamięci bazowej, jak pokazano w aosp/3343204.
    • Informacje o CONFIG_UBSAN_SIGNED_WRAP: nowa wersja Clang sprawdza przepełnienie i niedopełnienie liczb całkowitych ze znakiem pomimo flagi kompilatora -fwrapv. Flaga -fwrapv jest przeznaczona do traktowania liczb całkowitych ze znakiem jako liczb całkowitych bez znaku w systemie uzupełnień do dwóch z określonym zachowaniem w przypadku przepełnienia.

      • Chociaż sprawdzanie przepełnienia liczb całkowitych ze znakiem w jądrze systemu Linux może pomóc w identyfikowaniu błędów, istnieją przypadki, w których przepełnienie jest zamierzone, np. w przypadku atomic_long_t. W związku z tym opcja CONFIG_UBSAN_SIGNED_WRAP została wyłączona, aby UBSAN działał wyłącznie jako narzędzie do sprawdzania granic.
    • Informacje o CONFIG_UBSAN_TRAP: UBSAN jest skonfigurowany tak, aby w przypadku wykrycia problemu powodować kernel panic w celu ochrony integralności jądra. Od 23 października do 12 listopadawyłączyliśmy jednak to zachowanie. Zrobiliśmy to, aby odblokować aktualizację kompilatora, gdy naprawialiśmy znane problemy z __counted_by.

1 listopada 2024 r.

  • Wprowadzenie Linuksa 6.12-rc4
    • Podsumowanie: CONFIG_OF_DYNAMIC może powodować poważne regresje w przypadku wadliwych sterowników.
    • Szczegóły: podczas scalania Linuksa 6.12-rc1 z android-mainline zauważyliśmy problemy z wczytywaniem sterowników spoza drzewa. Zmiana, która ujawniła błędy sterownika, została zidentyfikowana jako zatwierdzenie 274aff8711b2 ("clk: Add KUnit tests for clks registered with struct clk_parent_data"), a my tymczasowo cofnęliśmy ją w aosp/3287735. Zmiana wybiera CONFIG_OF_OVERLAY, która wybiera CONFIG_OF_DYNAMIC. W przypadku !OF_DYNAMIC zliczanie odwołań w of_node_get() i of_node_put() jest skutecznie wyłączone, ponieważ są one zaimplementowane jako noops. Ponowne włączenie OF_DYNAMIC ujawnia problemy w sterownikach, które nieprawidłowo implementują zliczanie odwołań dla struct device_node. Powoduje to różne typy błędów, takie jak uszkodzenie pamięci, odwołanie do pamięci po jej zwolnieniu i wycieki pamięci.
    • Należy sprawdzić wszystkie użycia interfejsów API związanych z analizowaniem OF. Poniższa lista jest niepełna, ale zawiera przypadki, które zaobserwowaliśmy:
      • Odwołanie do pamięci po jej zwolnieniu:
        • Ponowne użycie tego samego argumentu device_node: te funkcje wywołują of_node_put() w danym węźle, co może wymagać dodania of_node_get() przed ich wywołaniem (np. w przypadku wielokrotnego wywoływania z tym samym węzłem jako argumentem):
          • of_find_compatible_node()
          • of_find_node_by_name()
          • of_find_node_by_path()
          • of_find_node_by_type()
          • of_get_next_cpu_node()
          • of_get_next_parent()
          • of_get_next_child()
          • of_get_next_available_child()
          • of_get_next_reserved_child()
          • of_find_node_with_property()
          • of_find_matching_node_and_match()
        • Użycie device_node po dowolnym typie wyjścia z niektórych pętli:
          • for_each_available_child_of_node_scoped()
          • for_each_available_child_of_node()
          • for_each_child_of_node_scoped()
          • for_each_child_of_node()
        • Zachowywanie bezpośrednich wskaźników do właściwości char * z device_node, np. za pomocą:
          • const char *foo = struct device_node::name
          • of_property_read_string()
          • of_property_read_string_array()
          • of_property_read_string_index()
          • of_get_property()
      • Wycieki pamięci:
        • Pobieranie device_node i zapominanie o jego zwolnieniu (of_node_put()). Węzły zwracane przez te węzły muszą zostać w pewnym momencie zwolnione:
          • of_find_compatible_node()
          • of_find_node_by_name()
          • of_find_node_by_path()
          • of_find_node_by_type()
          • of_find_node_by_phandle()
          • of_parse_phandle()
          • of_find_node_opts_by_path()
          • of_get_next_cpu_node()
          • of_get_compatible_child()
          • of_get_child_by_name()
          • of_get_parent()
          • of_get_next_parent()
          • of_get_next_child()
          • of_get_next_available_child()
          • of_get_next_reserved_child()
          • of_find_node_with_property()
          • of_find_matching_node_and_match()
      • Zachowywanie device_node z iteracji pętli. Jeśli wracasz lub przerywasz działanie w tych pętlach, musisz w pewnym momencie usunąć pozostałe odwołanie:
        • for_each_available_child_of_node()
        • for_each_child_of_node()
        • for_each_node_by_type()
        • for_each_compatible_node()
        • of_for_each_phandle()
    • Wcześniej wspomniana zmiana została przywrócona podczas wprowadzania Linuksa 6.12-rc4 (patrz aosp/3315251) co ponownie włącza CONFIG_OF_DYNAMIC i może ujawnić wadliwe sterowniki.