- มีการพบบั๊ก การไม่ปลดล็อกทรัพยากร ในโค้ด Apollo Guidance Computer(AGC) ซึ่งเคยถูกมองว่าเกือบสมบูรณ์แบบมาตลอด 57 ปี
- ทีม JUXT ใช้ภาษาเขียนสเปก AI Allium และ Claude วิเคราะห์แอสเซมบลี 130,000 บรรทัด และระบุข้อบกพร่องที่วิธีตรวจสอบแบบเดิมไม่สามารถเผยให้เห็นได้
- ใน เส้นทางการจบการทำงานแบบผิดปกติ (BADEND) ของรูทีนควบคุมไจโร มีการไม่ปลดล็อก
LGYROทำให้ฟังก์ชันที่เกี่ยวข้องกับไจโรทั้งหมดอาจหยุดทำงานในภายหลัง - หากระหว่างภารกิจจริงในวงโคจรรอบดวงจันทร์มีการกด สวิตช์ Cage ผิดพลาด งานจัดแนว Program 52 อาจหยุดลง และ Collins อาจเข้าใจผิดว่าเป็นความขัดข้องของฮาร์ดแวร์
- กรณีนี้แสดงให้เห็นว่าการวิเคราะห์แบบ อิงสเปกเชิงพฤติกรรม เป็นวิธีที่ทรงพลังในการค้นหาข้อบกพร่องใหม่ ๆ แม้ในโค้ดเก่า
ข้อบกพร่องแฝงที่ค้นพบในคอมพิวเตอร์นำทางของ Apollo 11
- Apollo Guidance Computer(AGC) เป็นหนึ่งในโค้ดเบสที่ถูกตรวจสอบอย่างละเอียดที่สุดในประวัติศาสตร์ และได้รับการวิเคราะห์โดยนักพัฒนาและนักวิจัยจำนวนมาก
- อย่างไรก็ตาม มีการยืนยันว่ามี บั๊กการไม่ปลดล็อกทรัพยากร ในโค้ดควบคุมไจโรที่ไม่ถูกค้นพบมานาน 57 ปี
- ปัญหาคือในเส้นทางข้อผิดพลาดไม่มีการปลดล็อก ทำให้ความสามารถในการจัดแนวแพลตฟอร์มนำทางใหม่ถูกปิดใช้งาน
- ทีม JUXT ใช้ภาษาเขียนสเปกที่ขับเคลื่อนด้วย AI อย่าง Allium และ Claude แปลงแอสเซมบลี AGC 130,000 บรรทัดให้เป็นสเปกเชิงพฤติกรรม 12,500 บรรทัด
- ดึงสเปกจากโค้ดโดยตรงเพื่อสร้างแบบจำลองทุกเส้นทางของการได้มาและการปล่อยทรัพยากร
- ในกระบวนการนี้จึงพบข้อบกพร่องที่การอ่านโค้ดหรือการจำลองการทำงานแบบเดิมไม่สามารถเปิดเผยได้
โครงสร้างโค้ดและแนวทางการวิเคราะห์
- ซอร์สของ AGC ถูกเผยแพร่ในปี 2003 เมื่อ Ron Burkey และอาสาสมัครถอดความจากเอกสารพิมพ์ของ MIT Instrumentation Laboratory ด้วยมือ
- ในปี 2016 มีการเผยแพร่คลัง GitHub ของ Chris Garry ทำให้นักพัฒนาทั่วโลกได้ศึกษารหัสแอสเซมบลีของเครื่องที่มี RAM 2KB และ CPU 1MHz
- โปรแกรมถูกเก็บไว้ใน core rope memory ขนาด 74KB โดยใช้การร้อยสายทองแดงผ่านแกนแม่เหล็กด้วยมือเพื่อแทนค่า 1 และ 0
- ช่างเทคนิคหญิงที่ทำงานนี้ถูกเรียกว่า “Little Old Ladies” และหน่วยความจำนี้จึงถูกเรียกว่า LOL memory
- จนถึงตอนนี้ยังไม่มีบันทึกว่ามีการทำ formal verification, model checking, static analysis กับ AGC
- การตรวจสอบส่วนใหญ่เน้นที่การอ่านโค้ด การจำลองการทำงาน และการยืนยันความถูกต้องของการถอดความ
- JUXT เขียนสเปกเชิงพฤติกรรมของซับซิสเต็ม IMU(Inertial Measurement Unit) ด้วย Allium
- โดยสร้างแบบจำลอง จังหวะการได้มาและการปล่อย ของทรัพยากรที่ใช้ร่วมกันแต่ละตัวเพื่อระบุข้อบกพร่อง
การไม่ปลดล็อกในรูทีนควบคุมไจโร
- AGC จัดการ IMU ผ่านล็อกร่วมชื่อ
LGYRO- เมื่อต้องจ่ายแรงบิดให้ไจโรจะมีการยึดล็อกไว้ และจะปลดเมื่อครบทั้งสามแกนแล้ว
- เพื่อป้องกันไม่ให้สองรูทีนควบคุมฮาร์ดแวร์พร้อมกัน
- แต่ใน เส้นทางการจบแบบผิดปกติ ไม่มีการปลดล็อก
- ในการจบแบบปกติ รูทีน
STRTGYR2จะปลดLGYROแต่หากเกิด ‘Caging’ (การล็อกทางกายภาพเพื่อปกป้องไจโร) จะหลุดไปยังรูทีนBADEND - ใน
BADENDขาดคำสั่งCAF ZEROและTS LGYROสองคำสั่ง (รวม 4 ไบต์) ทำให้ล็อกค้างอยู่
- ในการจบแบบปกติ รูทีน
- เมื่อ
LGYROไม่ถูกปลด รูทีนจ่ายแรงบิดไจโรทั้งหมดหลังจากนั้นจะค้างรอการปลดล็อก- ทั้งการจัดแนวละเอียด การชดเชยดริฟต์ และการจ่ายแรงบิดแบบแมนนวล รวมถึงฟังก์ชันที่เกี่ยวข้องกับไจโรทั้งหมดจะหยุดลง
ผลกระทบที่อาจเกิดขึ้นขณะอยู่ด้านไกลของดวงจันทร์
- วันที่ 21 กรกฎาคม 1969 Michael Collins ได้รัน Program 52 (โปรแกรมจัดแนวด้วยการสังเกตดาว) เป็นระยะ ๆ ระหว่างอยู่ในวงโคจรรอบดวงจันทร์
- หากแพลตฟอร์มเกิดดริฟต์ ก็มีความเสี่ยงที่ทิศทางของเครื่องยนต์สำหรับเดินทางกลับจะคลาดเคลื่อน
- หากระหว่างการจ่ายแรงบิดมีการกด สวิตช์ Cage ผิดพลาดจนเข้าสู่เส้นทาง
BADENDก็เป็นไปได้ว่าLGYROจะไม่ถูกปลดและทำให้ P52 หลังจากนั้นทั้งหมดหยุดทำงาน- DSKY (จอแสดงผลและคีย์บอร์ด) จะรับอินพุตได้แต่ไม่ตอบสนอง
- ฟังก์ชันอื่นยังทำงานปกติ ดังนั้น Collins อาจเข้าใจผิดว่าเป็นความขัดข้องของฮาร์ดแวร์
- หากมีการรีสตาร์ต (hard reset) ปัญหาก็จะถูกแก้ไข แต่จากประสบการณ์ สัญญาณเตือน 1202 ระหว่างการลงจอดบนดวงจันทร์ การตัดสินใจรีเซ็ตทันทีคงไม่ใช่เรื่องง่าย
การออกแบบเชิงป้องกันของระบบเดิมและข้อจำกัด
- ทีม MIT ที่นำโดย Margaret Hamilton ได้นำแนวคิดพื้นฐานของวิศวกรรมซอฟต์แวร์สมัยใหม่มาใช้ เช่น การจัดตารางตามลำดับความสำคัญ การทำงานหลายงานแบบอะซิงโครนัส และการป้องกันการรีสตาร์ต
- ด้วยการออกแบบนี้จึงยังสามารถลงจอดได้แม้อยู่ระหว่างสัญญาณเตือน 1202
- ข้อบกพร่องสำคัญส่วนใหญ่มักเป็น ข้อผิดพลาดในสเปก และมีกรณีที่ Don Eyles บันทึกไว้ว่า การขาดการซิงก์เฟสระหว่างฮาร์ดแวร์ทำให้ภาระ CPU เพิ่มขึ้น
- ข้อบกพร่องครั้งนี้ก็มีโครงสร้างคล้ายกัน
BADENDเป็นรูทีนจบการทำงานทั่วไปสำหรับการสลับโหมดของ IMU จึงปล่อยทรัพยากรร่วมอย่างMODECADRแต่ไม่ได้จัดการLGYRO- เนื่องจากลอจิกการรีสตาร์ตจะรีเซ็ต
LGYROปัญหาจึงถูกกู้คืนได้ระหว่างการทดสอบและซ่อนข้อบกพร่องนี้ไว้
- แต่ใน สถานการณ์จริงที่ไม่มีการรีสตาร์ต ไจโรอาจค้างอยู่ในสถานะถูกล็อกถาวรได้
กระบวนการค้นหาข้อบกพร่องด้วย Allium
- สเปกของ Allium สร้างแบบจำลอง วงจรชีวิต (lifecycle) ของทรัพยากรแต่ละชนิด
- ฟิลด์
gyros_busyใช้แทนLGYROโดยกฎGyroTorqueกำหนดการยึดล็อก และกฎGyroTorqueBusyกำหนดสถานะรอเมื่อมีการล็อกอยู่แล้ว
- ฟิลด์
- สเปกระบุข้อบังคับว่า “เมื่อล็อกกลายเป็น true แล้ว จะต้องกลับมาเป็น false ในภายหลังเสมอ”
- เมื่อ Claude ติดตามทุกเส้นทาง จึงพบว่าในเส้นทางปกติ (
STRTGYR2) มีการปลดล็อก แต่ในเส้นทางข้อผิดพลาด (BADEND) ไม่มี
- เมื่อ Claude ติดตามทุกเส้นทาง จึงพบว่าในเส้นทางปกติ (
- แนวทางแบบอิงสเปกตรวจสอบไม่ใช่ว่าโค้ด ทำอะไรอยู่ แต่เป็นว่า ควรทำอะไร
- การทดสอบยืนยันพฤติกรรมปัจจุบันของโค้ด แต่สเปกใช้ตรวจสอบพฤติกรรมที่ตั้งใจไว้
- สเปก Allium และโค้ดสำหรับทำซ้ำบั๊กถูกเผยแพร่บน GitHub
นัยสมัยใหม่และบทเรียน
- ในเวลานั้น โปรแกรมเมอร์ต้องปลดล็อกด้วยคำสั่ง
CAF ZEROและTS LGYROด้วยตนเอง- ต้องจำทุกเส้นทางของการทำงานและแทรกโค้ดปลดล็อกด้วยมือ
- ภาษาโปรแกรมสมัยใหม่ป้องกัน ปัญหาการไม่ปล่อยทรัพยากร ลักษณะนี้ในเชิงโครงสร้าง
- เช่น
deferของ Go,try-with-resourcesของ Java,withของ Python และ ระบบ ownership ของ Rust ที่ช่วยรับประกันการปล่อยโดยอัตโนมัติ
- เช่น
- ถึงอย่างนั้น ข้อบกพร่องประเภท CWE-772 (การไม่ปล่อยทรัพยากร) ก็ยังคงมีอยู่
- การเชื่อมต่อฐานข้อมูล, distributed lock, file handle และลำดับการปิดระบบโครงสร้างพื้นฐาน ยังเป็นความรับผิดชอบของโปรแกรมเมอร์อยู่ดี
- แม้ภารกิจ Apollo ทั้งหมดจะกลับมาได้อย่างปลอดภัย แต่ข้อบกพร่องนี้ยังคงอยู่ในซอฟต์แวร์ COMANCHE(ยานบังคับการ) และ LUMINARY(ยานลงจอดบนดวงจันทร์) โดยไม่ได้รับการแก้ไข
- ข้อบกพร่องที่ซ่อนอยู่มา 57 ปีนี้เป็นตัวอย่างที่แสดงถึงความสำคัญของ การวิเคราะห์แบบอิงสเปกเชิงพฤติกรรม
1 ความคิดเห็น
ความเห็นบน Hacker News
ผมคือ Mike Stewart เป็นผู้นำโครงการฟื้นฟู AGC บน ช่อง CuriousMarc และเป็นผู้ดูแลร่วมของ VirtualAGC
บั๊กที่พูดถึงครั้งนี้เป็น ข้อบกพร่องของซอฟต์แวร์ AGC ที่มีอยู่จริง แต่ไม่ได้ถูกปล่อยทิ้งไว้ตลอดอายุของโปรแกรมทั้งหมด
มันถูกพบระหว่างการทดสอบระยะที่ 3 ของ SATANCHE และถูกบันทึกไว้เป็น L-1D-02 จากนั้นได้รับการแก้ไขระหว่าง Apollo 14 กับ 15
สามารถดูรายงานที่เกี่ยวข้องได้ที่ เอกสาร ibiblio 1 และ เอกสาร 2
การแก้ไขไม่ได้เป็นแค่การเพิ่มสองบรรทัดเพื่อทำให้ LGYRO เป็น 0 เท่านั้น แต่ยังรวมถึง การปรับโครงสร้างโค้ด และลอจิกสำหรับปลุกงานที่ค้างรออยู่ด้วย ถ้าเทียบโค้ดของ Apollo 14 และ 15 (โค้ด Apollo 14, โค้ด Apollo 15) ก็จะเห็นความต่าง
มันไม่ใช่บั๊กที่เกิดขึ้นแบบเงียบ ๆ ตามที่บทความอธิบายไว้ LGYRO ถูกรีเซ็ตใน STARTSB2 ด้วย ซึ่งจะรันทุกครั้งที่มีการสลับโปรแกรมและช่วยเคลียร์ปัญหาโดยอัตโนมัติ ดังนั้นในความเป็นจริงมันจึงเกิดขึ้นได้ยากมาก และจะเกิดเฉพาะตอนทำ การทำงานของ torque ระหว่าง BADEND เท่านั้น
ถ้าบั๊กค้างอยู่ต่อไป งานหลายตัวจะสะสมและสุดท้ายทำให้เกิดข้อผิดพลาด 31202 (เวอร์ชันถัดจาก 1202) ปัญหานี้ถูกค้นพบก่อนเที่ยวบิน Apollo 14 และมีการเพิ่มขั้นตอนกู้คืนไว้ใน Apollo 14 Program Notes
อีกอย่าง แม้จะมีการบอกว่า Ken Shirriff เป็นคนวิเคราะห์ระดับเกต แต่จริง ๆ แล้วผมเป็นคนทำส่วนใหญ่
Virtual AGC ตรวจสอบความตรงกันแบบ ตรงกันระดับไบต์ กับ core rope dump ต้นฉบับได้ครบเพียงบางโปรแกรมเท่านั้น และยังทำกับทั้งชุดโปรแกรมไม่ได้ ซอร์สส่วนใหญ่ถูกกู้คืนจากฉบับพิมพ์หรือเช็กซัม
Margaret Hamilton เป็น ‘rope mother’ ของ Comanche (ซอฟต์แวร์ยานบังคับการ) ส่วน Luminary (ยานลงจอดดวงจันทร์) เป็นความรับผิดชอบของ Jim Kernan สามารถยืนยันได้จาก ผังองค์กรปี 1969
ตอนเกิดสัญญาณเตือน 1202 นั้น AGC ไม่ได้ถูกออกแบบมาให้ตัดงานลำดับความสำคัญต่ำออก ตรงกันข้าม การนำร่อนลงจอดกลับเป็น งานลำดับความสำคัญต่ำที่สุด ขณะที่การควบคุมเสาอากาศและการอัปเดตหน้าจอมีลำดับสูงกว่า เอกสารนี้ สรุปลำดับความสำคัญของแต่ละงานไว้
สุดท้าย สาเหตุของสัญญาณเตือน 1202 ไม่ใช่ความต่างของเฟส แต่เป็น ความต่างของแรงดันไฟฟ้า (28V vs 15V) ซึ่งได้รับการยืนยันจากการทดสอบฮาร์ดแวร์จริง วิดีโอการทดลองที่เกี่ยวข้องดูได้ที่ ลิงก์ YouTube
ถ้าคุณสนใจ Apollo AGC ขอแนะนำให้ดู ช่อง YouTube CuriousMarc อย่างยิ่ง ทีมที่เก่งมากทางเทคนิคได้บันทึกกระบวนการฟื้นฟูและวิเคราะห์ชิ้นส่วนต่าง ๆ ของ AGC เอาไว้
ส่วนที่น่าสนใจเป็นพิเศษคือ การตีความสัญญาณเตือน 1202 ใหม่ ซึ่งโดยทั่วไปมักอธิบายว่าเป็นความผิดพลาดของเซ็นเซอร์ที่มองข้ามได้ แต่จริง ๆ แล้วในบางเงื่อนไขมันอาจร้ายแรงมาก
ชิ้นส่วนโค้ดที่ผมชอบที่สุดคืออันนี้
ลิงก์ GitHub
ผมสงสัยว่ามีการตรวจสอบหรือยืนยันไหมว่าบั๊กนี้มีอยู่จริง AI เก่งกับ การวิเคราะห์เชิงสำรวจ แบบนี้ แต่ก็ยังมี อัตราการแจ้งพลาด สูง
ขึ้นอยู่กับบริบทด้วยว่ามันสำคัญหรือไม่สำคัญ และก็ยังมีบั๊กอีกมากที่ AI หาไม่เจอ
ผมไม่มีความเชี่ยวชาญพอจะตรวจเองได้ แต่คิดว่าน่าสนใจมาก
แต่คำอธิบายเรื่อง การได้ล็อกและสถานการณ์ตอนล้มเหลว ก็ค่อนข้างน่าเชื่อถือทีเดียว
ที่พวกเขาหาบั๊กจริงเจอได้นั้นน่าสนใจ แต่ การทำให้เรื่องดูดราม่า ในบทความแทบจะเป็นเรื่องแต่ง
เช่นการเอาศอกไปโดนสวิตช์ หรือการขยายความปัญหาที่รีเซ็ตก็แก้ได้ จุดพวกนี้ทำให้บทความดูเร้าอารมณ์แต่ความน่าเชื่อถือลดลง แถมยังมี สำนวนแบบที่ AI เขียน ยิ่งทำให้ขัดใจ
แต่ถ้าจะอธิบายบั๊กละเอียดอ่อนให้ผู้อ่านทั่วไปเข้าใจ ก็ต้องมี การเล่าเรื่อง อยู่บ้าง ถ้าเทคนิคเกินไปคนก็หมดความสนใจ ถ้าดราม่าเกินไปผู้เชี่ยวชาญก็จะวิจารณ์ ผมว่าการหาจุดสมดุลตรงนี้ยากมาก
ผมลองรัน โค้ดสำหรับทำซ้ำปัญหา (repro) ที่อยู่ในบทความด้วยตัวเองแล้ว
ลิงก์ GitHub
มันรันได้ก็จริง แต่ใน Phase 5 (สาธิต deadlock) เป็น เอาต์พุตปลอม ทั้งหมด คือไม่ได้รันอีมูเลเตอร์จริง แค่พิมพ์ผลลัพธ์ที่คาดว่าจะได้ออกมา
เลยทำให้ผมส่ง PR ของตัวเอง เพื่อแก้ให้มันทำงานจริงบนอีมูเลเตอร์ และตรวจสอบด้วยว่าแพตช์สองบรรทัดนั้นแก้บั๊กได้จริงหรือไม่
โค้ดที่ AI สร้างมักชอบหยุดที่จุด “ตอนนี้สมบูรณ์แบบแล้ว!” และคนที่เชื่อมันก็เอาไปปล่อยต่อทั้งอย่างนั้น นี่แหละคือปัญหาจริง
ตัวบทความเองก็น่าสนใจ แต่ให้ความรู้สึก ประดิษฐ์แบบเหมือน LLM เขียน ค่อนข้างแรง
การที่ซอฟต์แวร์ที่พามนุษย์ไปดวงจันทร์ด้วยหน่วยความจำเพียง 4KB ยังมี บั๊กที่ไม่ถูกค้นพบ อยู่ แสดงให้เห็นว่าความซับซ้อนสามารถซ่อนอยู่ได้แม้ในโค้ดขนาดเล็ก
การพูดว่า “ดึงสเปกออกมาจากโค้ด” เป็นการใช้คำผิด ควรกลับไปดูความหมายของ specification ใหม่
ทั้งบทความและรีโพซิทอรีเป็น งานคุณภาพต่ำแบบลวก ๆ
ถ้าดู ลิงก์รีโพซิทอรี จะเห็นว่ามันอ้างว่าเป็น “การทำซ้ำปัญหา” แต่ไม่ได้รันบั๊กจริงเลย แค่พิมพ์ ข้อความเอาต์พุตว่ามันน่าจะเป็นแบบนี้ ออกมาเท่านั้น