Erratum GKI 16-6.12 android-mainline

Cette page décrit les problèmes importants et les corrections de bugs trouvés dans 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 nettoyage 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 correctement mise à jour. Le message d'erreur se présente comme suit :
    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 : L'outil de nettoyage des limites est essentiel pour protéger l'intégrité du noyau en détectant les accès hors limites. Lorsque CONFIG_UBSAN_TRAP est activé, l'outil de nettoyage des limites déclenche une panique du noyau pour chaque résultat.

      • La version précédente de l'outil de nettoyage 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 accessible avant que la variable de taille ne soit définie, ce qui déclenche l'outil de nettoyage des limites et provoque une panique du noyau. Pour résoudre ce problème, définissez la taille du tableau immédiatement après l'allocation de la mémoire sous-jacente, comme illustré dans aosp/3343204.
    • À propos de CONFIG_UBSAN_SIGNED_WRAP : La nouvelle version de Clang nettoie le dépassement et le dépassement négatif d'entiers signés malgré l'indicateur de compilateur -fwrapv. L'indicateur -fwrapv est conçu pour traiter les entiers signés comme des entiers non signés à complément à deux avec un comportement de dépassement défini.

      • Bien que le nettoyage du dépassement d'entiers signés dans le noyau Linux puisse aider à identifier les bugs, il existe des cas où le dépassement 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 comme un outil de nettoyage des 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 procédé ainsi pour débloquer la mise à jour du compilateur pendant que nous corrigions les problèmes connus liés à __counted_by.

1er novembre 2024

  • Déploiement 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 remarqué que les pilotes hors arborescence ne parvenaient pas à se charger. La modification qui a exposé les bugs du pilote a été identifiée comme commit 274aff8711b2 ("clk: Add KUnit tests for clks registered with struct clk_parent_data") et 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 comptage des références sur of_node_get() et of_node_put() est effectivement désactivé, car il est implémenté en tant que noops. L'activation de OF_DYNAMIC expose à 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 les 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()
        • Conservation de pointeurs directs vers les propriétés char * de device_node, par exemple à l'aide de :
          • 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 :
        • Obtention d'un device_node et oubli de le désactiver (of_node_put()). Les nœuds renvoyés par ces éléments 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()
      • Conservation d'un device_node à partir d'une itération de boucle. Si vous renvoyez ou interrompez l'exécution 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 du déploiement de Linux 6.12-rc4 (voir aosp/3315251) ce qui a réactivé CONFIG_OF_DYNAMIC et a potentiellement exposé des pilotes défectueux.