GKI 16-6.12 android-mainline errata

本页介绍了在 android-mainline 中发现的可能对合作伙伴来说非常关键的重要问题和 bug 修复。

2024 年 11 月 15 日

  • Clang 已针对 android-mainlineandroid16-6.12 更新为 19.0.1

    • 摘要:新版 Clang 针对数组引入了边界排错程序,其中数组的大小存储在使用 __counted_by 属性关联到数组的单独变量中。如果数组大小未正确更新,此功能可能会导致内核崩溃。错误消息如下所示:
    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 *[]')
    
    • 详细信息:对于通过检测越界访问来保护内核的完整性来说,边界排错程序至关重要。CONFIG_UBSAN_TRAP 处于启用状态时,边界排错程序会在发现任何问题时触发内核崩溃。

      • 以前版本的边界排错程序仅检查固定大小的数组,无法检查动态分配的数组。新版本使用 __counted_by 属性在运行时确定数组边界,能够检测更多越界访问情况。不过,在某些情况下,系统会在设置大小变量之前访问数组,从而触发边界排错程序并导致内核崩溃。为了解决此问题,请在分配底层内存后立即设置数组的大小,如 aosp/3343204 中所示。
    • 关于 CONFIG_UBSAN_SIGNED_WRAP:新版 Clang 会对有符号整数溢出和下溢进行排错,即使有 -fwrapv 编译器标志也是如此。-fwrapv 标志旨在将有符号整数视为具有已定义溢出行为的二进制补码无符号整数。

      • 虽然对 Linux 内核中的有符号整数溢出进行排错有助于发现 bug,但有些情况下溢出是故意的,例如 atomic_long_t。因此,CONFIG_UBSAN_SIGNED_WRAP 已停用,以便 UBSAN 仅用作边界排错程序。
    • 关于 CONFIG_UBSAN_TRAP:UBSAN 被配置为在检测到问题时触发内核崩溃,以保护内核的完整性。不过,我们在 10 月 23 日11 月 12 日期间停用了此行为。我们这样做是为了在修复已知的 __counted_by 问题后取消屏蔽编译器更新。

2024 年 11 月 1 日

  • 推出 Linux 6.12-rc4
    • 摘要:CONFIG_OF_DYNAMIC 可能会导致有问题的驱动程序出现严重回归问题。
    • 详细信息:将 Linux 6.12-rc1 合并到 android-mainline 时,我们注意到树外驱动程序无法加载的问题。导致驱动程序 bug 的变更被标识为“提交 274aff8711b2 ("clk: Add KUnit tests for clks registered with struct clk_parent_data")”,我们已在 aosp/3287735 中暂时恢复了该变更。该变更会选择 CONFIG_OF_OVERLAY,而后者会选择 CONFIG_OF_DYNAMIC。使用 !OF_DYNAMIC 时,对 of_node_get()of_node_put() 的引用计数实际上会被停用,因为它们已实现为 noops。重新启用 OF_DYNAMIC 会导致驱动程序中针对 struct device_node 错误实现引用计数的问题。这会导致各种类型的错误,例如内存损坏、释放后再使用和内存泄漏。
    • 必须检查所有使用 OF 解析相关 API 的情况。下面的列表中仅列出了部分情况,但包含我们一直在观察的情况:
      • 释放后再使用 (UAF):
        • 重复使用相同的 device_node 参数:这些函数会针对给定的节点调用 of_node_put(),在进行该调用之前可能需要添加 of_node_get()(例如,当使用同一节点作为参数进行重复调用时):
          • 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()
        • 在任何类型的退出特定循环行为之后使用 device_node
          • for_each_available_child_of_node_scoped()
          • for_each_available_child_of_node()
          • for_each_child_of_node_scoped()
          • for_each_child_of_node()
        • 保留从 device_node 指向 char * 属性的直接指针,例如,使用以下代码:
          • const char *foo = struct device_node::name
          • of_property_read_string()
          • of_property_read_string_array()
          • of_property_read_string_index()
          • of_get_property()
      • 内存泄漏:
        • 获取 device_node 但忘记取消引用 (of_node_put())。从这些中返回的节点需要在某个时间点被释放:
          • 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()
      • 保留循环迭代中的 device_node。如果您要在执行以下代码时返回或中断,则需要在某个时间点丢弃剩余的引用:
        • for_each_available_child_of_node()
        • for_each_child_of_node()
        • for_each_node_by_type()
        • for_each_compatible_node()
        • of_for_each_phandle()
    • 在发布 Linux 6.12-rc4(请参阅 aosp/3315251)时恢复了前面提到的变更,再次启用了 CONFIG_OF_DYNAMIC,这可能会导致驱动程序出现问题。