ปูสีส้มแดงต้นฉบับกำลังซ่อมซี่ที่หายไปบนวงล้อฟังก์ชันไม้ในโต๊ะทำงานสว่าง

Rust 1.98.1 อุด Miscompilation ใน Vtable: อัปเดตก่อนส่ง Trait Object ขึ้น Production

พี่โชว์Engineering

ถ้าโปรแกรม Rust ใช้ trait object แล้วคอมไพเลอร์วาง pointer ผิด ต่อให้ source code ดูถูกต้องและ test ผ่านบางเครื่อง binary ที่ได้ก็อาจ crash หรือมีพฤติกรรมคาดเดาไม่ได้ครับ Rust 1.98.1 จึงเป็น patch ที่ควรจัดลำดับสูงกว่า feature release ทั่วไป

ประกาศของทีม Rust ระบุว่า Rust 1.98.0 อาจสร้าง vtable ที่ใส่ null pointer ในช่องที่ควรเป็น function pointer ภายใต้บางเงื่อนไข เมื่อโปรแกรมเรียกเมธอดผ่าน trait object ผลอย่างเบาอาจเป็น segmentation fault แต่ในภาษา compiler คำว่า undefined behavior หมายถึงผลลัพธ์ไม่ได้จำกัดอยู่แค่ crash

WHY: ปัญหาอยู่ใน Binary ไม่ใช่แค่ Source

vtable เปรียบเหมือนสารบัญฟังก์ชันที่ติดมากับ trait object ตัว object ถือข้อมูลหนึ่งส่วนและตาราง pointer อีกส่วน เมื่อเรียกเมธอด runtime จะหยิบตำแหน่งจากตารางแล้วกระโดดไปยังโค้ดจริง ถ้าช่องนั้นเป็น null จุดผิดไม่ได้อยู่ใน business logic ที่ reviewer เห็น แต่เกิดระหว่างการแปลง source เป็น machine code

นี่ทำให้การค้นด้วย dependency scanner อย่างเดียวไม่พอ เพราะ package graph อาจสะอาดทุกตัว แต่ binary ถูกสร้างด้วย toolchain ที่มีบั๊ก ทีมควรบันทึก compiler version คู่กับ artifact digest เสมอ เพื่อย้อนตอบได้ว่า binary ไหนสร้างด้วย 1.98.0

ใครควรรีบอัปเดต

ให้ความสำคัญกับ service หรือ library ที่ใช้ dyn Trait, callback แบบ erased type, plugin boundary หรือ abstraction ที่มี generic ซ้อนหลายชั้นก่อน โค้ดที่ไม่ใช้ trait object อาจไม่ได้เดินเข้ากรณีนี้ แต่การพิสูจน์ว่า binary ทั้งระบบไม่แตะเส้นทางดังกล่าวมักแพงกว่าการอัป patch toolchain

ไม่ควรพยายามสร้าง exploit หรือ reproduction บนเครื่อง production ประกาศทางการให้ข้อมูลพอสำหรับการตัดสินใจแล้ว งานที่ปลอดภัยกว่าคืออัป CI image เป็น 1.98.1 สร้าง artifact ใหม่จาก commit เดิม แล้วรัน regression, integration และ smoke test เดิม

Rollout แบบเห็นเหตุและย้อนกลับได้

เริ่มจากล็อก rust-toolchain.toml หรือ image digest ให้เป็น 1.98.1 จากนั้น rebuild โดยไม่รวมการอัป crate หรือ refactor เพื่อให้ diff ของ source เป็นศูนย์ เปรียบเทียบ test result และพฤติกรรมสำคัญกับ artifact เดิม ถ้ามี native extension หรือ FFI ให้รวม test ข้าม boundary ด้วย

เมื่อ canary ผ่าน ค่อยแทน artifact ที่สร้างด้วย 1.98.0 และเก็บรายการ digest ที่ถูกยกเลิกไว้ใน incident note การ rollback ควรย้อน application version ได้ แต่ไม่ควรย้อนกลับไปแจก binary ที่รู้ว่าใช้ compiler มี miscompilation

Recap

  • ตรวจว่า CI และ developer image ยังเป็น Rust 1.98.0 หรือไม่
  • อัปเฉพาะ toolchain เป็น 1.98.1 แล้ว rebuild จาก source commit เดิม
  • รัน test ที่ผ่าน trait object, plugin และ FFI boundary
  • ผูก compiler version กับ artifact digest เพื่อค้น binary เก่าให้ครบ

พรุ่งนี้เริ่มจาก inventory artifact ที่ปล่อยหลัง 20 สิงหาคมหนึ่งชุด แล้ว rebuild ด้วย 1.98.1 ก่อนขยาย rollout ครับ