Erratum GKI 16-6.12 android-mainline

Cette page décrit les problèmes importants et les corrections de bugs trouvés sur android-mainline qui peuvent être importants pour les partenaires.

15 novembre 2024

  • Mise à jour de Clang vers la version 19.0.1 pour android-mainline et android16-6.12

    • Résumé : La nouvelle version de Clang introduit un outil de vérification des limites pour les tableaux, où la taille du tableau est stockée dans une variable distincte liée au tableau à l'aide de l'attribut __counted_by. Cette fonctionnalité peut entraîner une panique du noyau si la taille du tableau n'est pas mise à jour correctement. Le message d'erreur ressemble à ceci :
    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 *[]')
    
    • Détails : Le module de désinfection des limites est essentiel pour protéger l'intégrité du noyau en détectant les accès hors limites. Avec CONFIG_UBSAN_TRAP activé, le vérificateur de limites déclenche un kernel panic pour chaque problème détecté.

      • La version précédente du module de désinfection des limites ne vérifiait que les tableaux de taille fixe et ne pouvait pas vérifier les tableaux alloués de manière dynamique. La nouvelle version utilise l'attribut __counted_by pour déterminer les limites du tableau au moment de l'exécution et détecter davantage de cas d'accès hors limites. Toutefois, dans certains cas, le tableau est consulté avant que la variable de taille ne soit définie, ce qui déclenche le module de désinfection des limites et provoque un plantage du noyau. Pour résoudre ce problème, définissez la taille du tableau immédiatement après avoir alloué la mémoire sous-jacente, comme illustré dans aosp/3343204.
    • À propos de CONFIG_UBSAN_SIGNED_WRAP : la nouvelle version de Clang assainit les dépassements de capacité et les dépassements négatifs d'entiers signés malgré l'indicateur de compilation -fwrapv. L'indicateur -fwrapv est conçu pour traiter les entiers signés comme des entiers non signés en complément à deux avec un comportement de dépassement de capacité défini.

      • Bien que la désinfection du dépassement de capacité des entiers signés dans le noyau Linux puisse aider à identifier les bugs, il existe des cas où le dépassement de capacité est intentionnel, par exemple avec atomic_long_t. Par conséquent, CONFIG_UBSAN_SIGNED_WRAP a été désactivé pour permettre à UBSAN de fonctionner uniquement en tant que vérificateur de limites.
    • À propos de CONFIG_UBSAN_TRAP : UBSAN est configuré pour déclencher une panique du noyau lorsqu'il détecte un problème afin de protéger l'intégrité du noyau. Toutefois, nous avons désactivé ce comportement du 23 octobre au 12 novembre. Nous avons fait cela pour débloquer la mise à jour du compilateur pendant que nous résolvions les problèmes __counted_by connus.

1er novembre 2024

  • Lancement de Linux 6.12-rc4
    • Résumé : CONFIG_OF_DYNAMIC peut entraîner de graves régressions pour les pilotes défectueux.
    • Détails : lors de la fusion de Linux 6.12-rc1 dans android-mainline, nous avons constaté des problèmes de chargement des pilotes hors arbre. La modification qui a révélé les bugs du pilote a été identifiée comme le commit 274aff8711b2 ("clk: Add KUnit tests for clks registered with struct clk_parent_data"). Nous l'avons temporairement annulée dans aosp/3287735. La modification sélectionne CONFIG_OF_OVERLAY, qui sélectionne CONFIG_OF_DYNAMIC. Avec !OF_DYNAMIC, le décompte des références sur of_node_get() et of_node_put() est effectivement désactivé, car ils sont implémentés en tant que noops. L'activation de OF_DYNAMIC révèle à nouveau des problèmes dans les pilotes qui implémentent de manière incorrecte le comptage des références pour struct device_node. Cela entraîne différents types d'erreurs, comme la corruption de la mémoire, l'utilisation après libération et les fuites de mémoire.
    • Toutes les utilisations des API liées à l'analyse OF doivent être inspectées. La liste suivante est partielle, mais contient des cas que nous avons observés :
      • Utilisation après libération de la mémoire (UAF) :
        • Réutilisation du même argument device_node : ces fonctions appellent of_node_put() sur le nœud donné. Il peut être nécessaire d'ajouter un of_node_get() avant de les appeler (par exemple, lors d'appels répétés avec le même nœud comme argument) :
          • 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()
        • Utilisation de device_node après tout type de sortie de certaines boucles :
          • for_each_available_child_of_node_scoped()
          • for_each_available_child_of_node()
          • for_each_child_of_node_scoped()
          • for_each_child_of_node()
        • Conserver des pointeurs directs vers les propriétés char * de device_node, par exemple en utilisant :
          • const char *foo = struct device_node::name
          • of_property_read_string()
          • of_property_read_string_array()
          • of_property_read_string_index()
          • of_get_property()
      • Fuites de mémoire :
        • Obtenir un device_node et oublier de le libérer (of_node_put()). Les nœuds renvoyés par ceux-ci doivent être libérés à un moment donné :
          • 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()
      • Conserver un device_node à partir d'une itération de boucle. Si vous renvoyez ou sortez d'un des éléments suivants, vous devez supprimer la référence restante à un moment donné :
        • for_each_available_child_of_node()
        • for_each_child_of_node()
        • for_each_node_by_type()
        • for_each_compatible_node()
        • of_for_each_phandle()
    • La modification mentionnée précédemment a été restaurée lors de l'atterrissage de Linux 6.12-rc4 (voir aosp/3315251), ce qui a permis de réactiver CONFIG_OF_DYNAMIC et d'exposer potentiellement des pilotes défectueux.