AOSP ปล่อยซอร์สช้า จนนักพัฒนาต้องถอดไลบรารีไล่บั๊ก
Android 17 รุ่นเดือนกันยายน 2026 มีบั๊กใน wcscspn() และ wcspbrk() เมื่อเจออักขระตั้งแต่ U+0100 ทั้งที่ไม่อยู่ในชุดค้นหา นักพัฒนาจึงต้องใช้ disassembly จาก libc.so เพื่อดูเส้นทางการทำงาน เพราะซอร์สที่เปิดเผยของรุ่นเดือนมิถุนายน 2026 ยังไม่ใช่โค้ดชุดเดียวกัน
ผลกระทบเกิดกับซอฟต์แวร์จริงอย่าง Visual Pinball บน Android ซึ่งโหลดตารางที่มี emoji ใน comment ไม่ได้ และ FileSystem.MoveFolder อาจมอง path ที่มีอักขระนอก ASCII เป็น wildcard การทดสอบเดิมของ bionic ใช้ input แบบ ASCII จึงไม่จับเคสนี้ แม้มี test ของ wcscspn() อยู่แล้วก็ตาม.
เมื่อซอร์สโค้ดยังตามไม่ทันไลบรารีที่ใช้งานจริง
Visual Pinball บน Android หยุดโหลดตารางที่มี emoji ใน comment หลังอัปเดตเดือนกันยายน 2026 เพราะ wcspbrk() หาตำแหน่งจบบรรทัดผิด จน lexer รายงานว่าเป็น “Invalid character”
กรณีนี้ทีมพัฒนาไม่ได้เริ่มจากซอร์สโค้ดที่เปิดเผย แต่ต้องไล่ดูอาการจากโปรแกรมจริง แล้วถอด disassembly ของ libc.so เพื่อพบว่าเส้นทางการทำงานในไลบรารีหยุดที่อักขระนอก ASCII ทั้งที่ไม่ควรหยุด สุดท้ายจึงแก้ชั่วคราวด้วยการแทนฟังก์ชันด้วย loop ธรรมดา.
แผนภาพเส้นทางของบั๊กในไลบรารีระบบ

แบ่งเป็นสองฝั่ง: ซอร์ส Android 17 เดือนมิถุนายนที่ใช้ loop ธรรมดาจาก external/llvm-libc และทำงานถูกต้อง เทียบกับไบนารีเดือนกันยายนที่หยุดทันทีเมื่อเจออักขระตั้งแต่ U+0100 แม้ตัวอักษรนั้นไม่อยู่ในชุดค้นหา.
ปัญหาอยู่ตรงไหนในโครงสร้างการเผยแพร่ของ Android
ช่องว่างอยู่ระหว่างซอร์สสาธารณะกับไบนารีที่ติดตั้งจริง: android-17.0.0_r1 จากเดือนมิถุนายนใช้ลูปซ้อนใน external/llvm-libc และทำงานถูกต้อง แต่บิลด์เดือนกันยายนกลับใช้โค้ดคนละชุด จนหยุดเมื่อเจออักขระตั้งแต่ U+0100
ผู้ดูแลยังระบุว่า Android latest release ตามการเปลี่ยนแปลงบางส่วนไม่ทัน ทำให้นักพัฒนาต้องดู disassembly ของ bionic libc.so และเขียนโค้ดทดสอบเองเพื่อชี้จุดผิดพลาด ภาพใหญ่จึงไม่ใช่แค่บั๊กในฟังก์ชัน แต่คือการตรวจสอบปัญหาที่ทำได้ยากเมื่อซอร์สกับไบนารีไม่ตรงกัน
จากโค้ดที่ตรวจสอบได้ สู่ไบนารีที่ต้องแกะรอย
รุ่น Android 17 ที่เปิดเผยในเดือนมิถุนายนใช้โค้ดจาก external/llvm-libc ซึ่งเป็น nested loop อ่านง่าย และทำงานถูกต้องกับ wcscspn() และ wcspbrk()
แต่รุ่นเดือนกันยายนบน CP3A.260905.009 กลับต้องตรวจจาก disassembly ใน bionic/libc.so จึงพบว่าโค้ดหยุดทันทีเมื่อเจออักขระตั้งแต่ U+0100 แม้ตัวนั้นไม่อยู่ในชุดค้นหา นักพัฒนาต้องเขียน probe เอง แล้วเทียบผลลัพธ์กับค่าที่ควรได้ เพราะซอร์สของโค้ดที่มีปัญหายังไม่เปิดเผยในตอนนั้น.
อักขระตัวเดียวทำให้การค้นหาหยุดผิดตำแหน่ง
ทดสอบด้วยชุดตัวคั่น x และ newline ได้ผลแบบนี้:
| ฟังก์ชัน | ผลที่ควรได้ | ผลบน CP3A.260905.009 |
|---|---|---|
wcscspn() | 4 | 1 |
wcspbrk() | index 4 | index 1 |
wcsspn() | 2 | 2 |
ต้นเหตุคือ wcscspn() หยุดทันทีเมื่อเจออักขระตั้งแต่ U+0100 แม้ไม่อยู่ในชุดค้นหา จึงกระทบข้อความที่มีอีโมจิ อักษร CJK ซีริลลิก กรีก หรือละตินส่วนขยาย ส่วน wcsspn(), wcschr(), wcsrchr(), wmemchr() และ wcsstr() ยังทำงานถูกต้อง.
ทางเลือกที่มีอยู่เมื่อไลบรารีระบบให้ผลไม่ตรงกัน

ทางแรกคืออิงลูปธรรมดาจาก external/llvm-libc ซึ่งโค้ดใน Android 17 รุ่นเดือนมิถุนายน 2026 ใช้งานได้ถูกต้อง ข้อจำกัดคือโค้ดในรุ่นเดือนกันยายนแตกต่างออกไป จึงไม่ได้แปลว่าจะได้พฤติกรรมเดียวกันทุก build
ทางที่สองคือเรียก wcscspn() และ wcspbrk() ต่อไป แต่ต้องยอมรับความเสี่ยงเมื่อข้อความมีอักขระตั้งแต่ U+0100 และชุดค้นหามีสมาชิก ASCII ตั้งแต่สองตัวขึ้นไป กรณีนี้อาจหยุดผิดตำแหน่ง
ทางสุดท้ายคือแทนที่การเรียกด้วยลูปธรรมดาในโค้ดปลายทาง ตาม workaround ของ libwinevbs วิธีนี้แก้ปัญหาเฉพาะจุดได้ แต่ต้องแก้โค้ดส่วนที่ได้รับผลกระทบเอง.
ข้อดีและข้อจำกัดของการแกะไลบรารีเพื่อยืนยันบั๊ก
การดู disassembly ช่วยชี้ตำแหน่งผิดพลาดได้ชัดเจน เช่น โค้ดหยุดทันทีเมื่อเจอ wchar_t ตั้งแต่ 0x100 ทั้งที่ตัวอักษรนั้นไม่ได้อยู่ในชุดค้นหา จึงเชื่อมโยงกับอาการจริงใน Visual Pinball ได้ว่า emoji ทำให้การอ่านบรรทัดและเส้นทางไฟล์ผิดพลาด
ข้อจำกัดคือการตรวจสอบต้องใช้บิลด์ Android 17 อุปกรณ์ และคำสั่งเฉพาะอย่าง adb shell อีกทั้งซอร์สของโค้ดที่ได้รับผลกระทบยังไม่พร้อม ทำให้ต้องอาศัย disassembly และการทดสอบจำนวนมากกว่าจะยืนยันเงื่อนไขได้ครบ การแกะไลบรารีจึงเหมาะกับการยืนยันปัญหา แต่ไม่ใช่ทางลัดสำหรับหาสาเหตุทุกกรณี
ราคาที่ต้องจ่ายเมื่อการทดสอบครอบคลุมแค่ ASCII

เทสต์เดิมของ wcspbrk() ใช้ข้อมูล ASCII อย่างเดียว จึงไม่เจอปัญหาเมื่อเจอ emoji, CJK หรืออักขระนอก ASCII ส่วนเทสต์ wcscspn() ที่มีอยู่ก็ใช้เคสผิด ทำให้ regression หลุดไปถึงบิลด์ Android 17 เดือนกันยายน 2026
ภาระจึงตกอยู่กับนักพัฒนา ต้อง sweep code point ตั้งแต่ U+0001 ถึง U+10FFFF บน Pixel 8a ตรวจ disassembly ใน libc.so และรันโปรแกรมผ่าน adb shell เพื่อยืนยันพฤติกรรม สุดท้ายโครงการที่ได้รับผลกระทบต้องทำแพตช์ปลายทางเอง โดยแทนที่ฟังก์ชันด้วย plain loops แม้ต้นเหตุอยู่ในไลบรารีระบบ.
บทเรียนที่ควรติดตามจาก issue นี้
ประเด็นต่อไปคือการเพิ่มเทสต์ non-ASCII ให้ wcscspn() และ wcspbrk() รวมถึงค้นหาจุดอื่นที่ยังทดสอบเฉพาะ ASCII จนพลาดเคสจริงแบบเดียวกัน
อีกเรื่องคือความพยายาม backport แพตช์ เพื่อลดจำนวนอุปกรณ์ที่ได้รับผลกระทบ ทั้งหมดนี้สะท้อนคำถามใหญ่กว่าเดิมว่า เมื่อซอร์ส AOSP ออกช้า นักพัฒนาจะตรวจสอบปัญหาและติดตามความคืบหน้าได้โปร่งใสแค่ไหน.