返回

文章详情

AOSP 源代码发布太慢,开发者拆解库以报告错误

Hacker News2026年10月14日 06:18

描述 在 2026 年 9 月的 Android 17 构建中,当拒绝集有两个或更多字符且全部 < U+0100 时,wcscspn() 和 wcspbrk() 停止在 >= U+0100 的第一个字符上。尽管该字符不在集合中,但仍报告为匹配。wcsspn()、wcschr()、wcsrchr()、wmemchr() 和 wcsstr() 是正确的。任何扫描宽字符串中 ASCII 定界符的代码(例如换行符或通配符字符)在文本包含表情符号、CJK、思韦文、希腊或拉丁扩展字符时划断。受影响 / 未受影响的设备构建结果 Pixel 7 google/panther/panther:17/CP3A.260905.009/16091614:user/release-keys 失败 Pixel 8a google/akita/akita:17/CP3A.260905.009/16091614:user/release-keys (补丁 2026-09-05) 失败 Nothing Phone 2 Nothing/PongEEA/Pong:16/BQ2A.250721.001-BP2A.250605.031.A3 (补丁 2026-08-01) 正常 公共 android17-release 源(android-17.0.0_r1,2026 年 6 月)从 external/llvm-libc 构建 wcscspn / wcspbrk,这是一层简单的 wchar_t 嵌套循环,并是正确的。9 月的构建中的代码是不同的(见“机制”),因此回归是在 6 月和 9 月的发布之间引入的。重现步骤 使用 NDK 构建(aarch64-linux-android30-clang),推送到 /data/local/tmp,使用 adb shell 运行: #include <stdio.h> #include <wchar.h> int main ( void ) { wchar_t s [] = { 'a' , 0x1F680 , 0x100 , 'b' , 'x' , ' ' , 0 }; printf ( "wcscspn(s, L\"x\n\") = %zu (预期 4)\n" , wcscspn ( s , L"x\n" )); printf ( "wcspbrk(s, L\"x\n\") = index %ld (预期 4)\n" , ( long )( wcspbrk ( s , L"x\n" ) - s )); printf ( "wcsspn(s, L\"a\U0001F680\") = %zu (预期 2)\n" , wcsspn ( s , L"a\U0001F680" )); return 0 ; } 在 CP3A.260905.009 的输出: wcscspn(s, L"x\n") = 1 (预期 4) wcspbrk(s, L"x\n") = index 1 (预期 4) wcsspn(s, L"a\U0001F680") = 2 (预期 2) 从 U+0001 到 U+10FFFF 的每个代码点的扫描(跳过代理和两个集合成员)使用 wcscspn(L"<cp>\n", L"x\n") 返回 0 而不是 1,适用于 1,111,808 个代码点:从 U+0100 到 U+10FFFF 中的每一个。U+0001 到 U+00FF 是正确的。哪个集合触发它(均在 Pixel 8a 上测量): 拒绝集合 结果 单字符 正常 两个或更多字符,全部 < U+0100(例如 L"x\n",L"\n") 失败 两个或更多字符,至少一个 >= U+0100 正常 匹配限制在 U+0080..U+00FF 正常 机制(来自出货的 libc.so)在 CP3A.260905.009 中 /apex/com.android.runtime/lib64/bionic/libc.so 中的 wcscspn 的反汇编显示了通过集合选择的三条路径: 单字符:一个简单的比较循环。正确。 两个或更多字符:一个 256 字节的 bool 表在堆栈上根据集合填充,同时集合字符在 1..0xFF 中。如果看到一个 >= 0x100 的集合字符,函数将回退到为每个 haystack 字符调用 wcschr()。正确。否则,haystack 会根据表进行扫描: a2564: ldrb w8, [x9, x8] ; table[c] a2568: tbnz w8, #0, a2578 ; 在集合中 -> 停止(正确) a256c: ldr w8, [x22, #0x4]! ; 下一个字符 a2570: cmp w8, #0x100 a2574: b.lo a2564 ; c < 0x100 -> 继续扫描 a2578: ... ; c >= 0x100 -> 停止,报告为匹配 一个 >= 0x100 的字符不能在所有成员都 < 0x100 的集合中,因此对于 wcscspn,扫描必须越过它。代码反而停止。wcsspn 具有相同的结构,在那里停止是正确答案(字符不在保持集中),这就是为什么只有补充功能是错误的。wcspbrk 是在此扫描上实现的,并继承了该错误。现有的 bionic 测试 TEST(wchar, wcspbrk) 在 tests/wchar_test.cpp 中仅使用 ASCII 输入,所以它未能捕捉到此。测试如 EXPECT_EQ(3u, wcscspn(L"aĀb\n", L"x\n")) 会捕捉到此。 现实世界的影响 Android 上的 Visual Pinball(libwinevbs,Wine 的 vbscript 引擎的 Unix 构建)在 9 月更新后停止加载任何包含注释的表,其 VBScript 源包含表情符号:词法分析器使用 wcspbrk(ptr, L"\n\r") 跳过注释,得到了第一个表情符号的位置而不是行结束,并报告“无效字符”。FileSystem.MoveFolder 同样将包含字符 >= U+0100 的每个路径视为通过 wcspbrk(src, L"*?><") 的通配符模式。通过用简单的循环替换调用的下游解决了这个问题:vpinball/libwinevbs#41(在 vpinball/vpinball@e7837322f4 中提供)。受影响功能的完整反汇编和探针源可根据要求提供。 我正在使用受支持的 NDK 我已检查并且我正在使用的 NDK 目前是受支持的 受影响版本 r29

赞助内容

NordVPN Next-gen Antivirus

本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。

☕请我喝杯咖啡