1 คะแนน โดย GN⁺ 2025-04-24 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • บั๊กอายุ 20 ปีของ GTA San Andreas ปรากฏขึ้นบน Windows 11 24H2
    • มีรายงานบั๊กที่ทำให้เครื่องบิน Skimmer หายไปใน GTA San Andreas บน Windows 11 24H2
    • ปัญหานี้ไม่สามารถแก้ได้แม้จะใช้ SilentPatch
    • บน Windows 11 23H2 ไม่พบปัญหานี้
    • ผู้ใช้ที่อัปเดตเป็น Windows 11 24H2 ทุกคนพบเจอบั๊กนี้

การสืบหาบั๊ก

อะไรที่ผิดพลาด?

  • เมื่อติดตั้ง SilentPatch เกมจะเกิดอาการค้าง
  • พบว่าเกิดการติดอยู่ในลูปขนาดเล็กภายใน CPlane::PreRender
  • ความเร็วของใบพัดเครื่องบินถูกตั้งไว้สูงผิดปกติ
  • ความเร็วของใบพัดถูกคำนวณตามสัดส่วนของระดับความสูงของเครื่องบิน

ทำไมและอย่างไร?

  • ในการกำหนดค่า vehicles.ide ของ Skimmer มีพารามิเตอร์ที่จำเป็นหายไป
  • ใน Vice City นั้น Skimmer ถูกกำหนดเป็นเรือ
  • ใน San Andreas มันถูกเปลี่ยนเป็นเครื่องบิน แต่ไม่ได้เพิ่มพารามิเตอร์ที่จำเป็นเข้าไป

สาเหตุรากแท้จริง

  • ปัญหานี้เกิดขึ้นจากการเปลี่ยนแปลงวิธีใช้สแตกใน Windows 11 24H2
  • LeaveCriticalSection เริ่มใช้พื้นที่สแตกมากขึ้น
  • ก่อนหน้านี้ fgets และ LeaveCriticalSection ไม่ได้เขียนทับพื้นที่สแตก แต่ตอนนี้เขียนทับแล้ว

ทำไมปัญหานี้เพิ่งเกิดขึ้นตอนนี้

  • การเปลี่ยนแปลงใน Windows 11 24H2 ทำให้พื้นที่สแตกเปลี่ยนไป
  • ปัญหานี้เกิดจากเกมใช้ตัวแปรโลคัลที่ยังไม่ได้ถูกกำหนดค่าเริ่มต้น
  • บนแพลตฟอร์มอื่น ปัญหานี้ได้รับการแก้ไขไปก่อนแล้ว

ถ้าอยากแก้ปัญหานี้ในเกมต้องทำอย่างไร?

  • จะมีการรวมโค้ดแก้ไขไว้ใน SilentPatch hotfix ตัวถัดไป
  • สามารถแก้ปัญหาได้ด้วยการแก้ไฟล์ vehicles.ide ด้วยตนเอง

ทิ้งท้าย

  • บั๊กนี้น่าสนใจเพราะเชื่อมโยงโดยตรงกับ OS รุ่นปล่อยเฉพาะ
  • มันแสดงให้เห็นว่าการเปลี่ยนแปลง layout ของสแตกสามารถส่งผลต่อความเข้ากันได้
  • ควรตรวจสอบความถูกต้องของข้อมูลนำเข้า และไม่ควรเมินเฉยต่อคำเตือนจากคอมไพเลอร์

1 ความคิดเห็น

 
GN⁺ 2025-04-24
ความคิดเห็นจาก Hacker News
  • นี่ดูเป็นงานที่คู่ควรกับ Raymond Chen ซึ่งถือเป็นคำชมในระดับสูงมาก
  • ยินดีที่มีการตามสืบหาสาเหตุของปัญหาได้ลึกขึ้น
  • ความเห็นส่วนตัวคือ สิ่งที่ไม่ได้เป็นส่วนหนึ่งของสัญญาควรถูกทำให้เป็นแบบสุ่ม เช่น ถ้าภาษาหนึ่งไม่ได้รับประกันลำดับการวนซ้ำของแมป ภาษานั้นก็ควรทำให้มันเป็นแบบสุ่ม ไม่อย่างนั้นโค้ดจะเปราะบาง
  • ไม่ควรมองข้ามคำเตือนจากคอมไพเลอร์ โค้ดนี้น่าจะทำให้เกิดคำเตือนในโค้ดต้นฉบับอยู่แล้ว
  • ข้อผิดพลาดจากคอมไพเลอร์ที่คาดได้ที่นี่คืออะไร? อาจเป็นการไม่ตรวจสอบค่าที่ส่งกลับจาก scanf เพื่อดูว่าตรงกับจำนวนพารามิเตอร์หรือไม่ นอกเหนือจากนั้นมันก็ดูเหมือนข้อผิดพลาดของไฟล์ข้อมูลที่คอมไพเลอร์ไม่มีทางรู้
  • ฉันชอบอ่านบทความเชิงเทคนิคแบบนี้เสมอ และสงสัยว่าในยุค AI บทความแบบนี้จะยิ่งหายากขึ้นแค่ไหน
  • สงสัยว่ามีอะไรเปลี่ยนไปใน implementation การล็อก/ปลดล็อก critical section ของ Windows
  • ขอบคุณที่ให้ลิงก์สำหรับคนที่มีปัญหาในการเข้าถึง
  • การอ่านและเขียนเลยขอบเขตสแตกนั้นง่ายเกินไปมาโดยตลอด เรื่องแบบนี้ควรล้มเหลวไปเลย
  • มีมาตรการบรรเทาอยู่แล้ว เช่น ASLR, หน้า NX, การป้องกัน stack smashing เป็นต้น แต่สิ่งเหล่านี้ก็ยังไม่สามารถป้องกันการอ่านข้อมูลเก่าที่อยู่เลยขอบเขตสแตกได้ทั้งหมด
  • มีแนวคิดเชิงทดลองว่า ถ้าฮาร์ดแวร์ทำให้ไม่สามารถอ่านหรือเขียนส่วนที่ไม่ได้ใช้งานของพื้นที่สแตกได้จะเป็นอย่างไร
  • มีข้อเสนอวิธีติดตามที่อยู่เริ่มต้นของสแตก A, ขนาด S, และความลึกปัจจุบัน D
    • เพิ่มคำสั่งที่ใช้บอก CPU ว่าสแตกอยู่ที่แอดเดรส A และมีขนาด S
    • เพิ่มคำสั่งกระโดดที่ใช้จอง N ไบต์บนสแตก
    • เพิ่มความสามารถให้คำสั่ง return เดิมรับรู้เรื่องสแตก
    • ให้การอ่านหรือเขียนพื้นที่สแตกที่เกินความลึกปัจจุบันล้มเหลว
    • ในสถาปัตยกรรมที่สแตกเติบโตลงด้านล่าง การคำนวณจะใช้กลับด้านกัน
  • ข้อเสียคือเป็นการบังคับให้ยึดตาม calling convention เดียว และตัวจัดการหน่วยความจำของ CPU ต้องเก็บสถานะจำนวนมาก
  • สแตกมีอยู่ทุกที่ การให้ฮาร์ดแวร์รับรู้สแตกอาจเปิดทางไปสู่มาตรการบรรเทาแบบใหม่
  • ทำไมแนวคิดนี้ถึงไม่แพร่หลาย? เคยมีการลองทำหรือยัง?
  • สิ่งที่ค้นพบทั้งหมดนี้พิสูจน์ว่าไม่ใช่ปัญหาของ Windows 11 24H2 เกมต่างหากที่พึ่งพา undefined behavior
  • undefined behavior นั้นเจ้าเล่ห์มาก เพราะมันทำให้คุณเชื่อว่าตัวเองทำถูก ทั้งที่จริง ๆ ทำผิดไปแล้ว
  • ถ้าโปรแกรม C หรือ C++ ทำให้เกิด undefined behavior อะไรก็เกิดขึ้นได้ระหว่างการรันโปรแกรม
  • ถ้าโชคดี โปรแกรมอาจแสดงข้อความผิดพลาดที่เหมาะสมหรือ crash เพื่อบอกให้รู้ทันทีว่ามีปัญหา
  • ถ้าโชคร้าย โปรแกรมอาจทำข้อมูลเสียหายอย่างเงียบ ๆ และเมื่อคุณรู้ตัว ต้นเหตุก็ถูกฝังอยู่ในประวัติการรันที่ผ่านมาแล้ว
  • ถ้าโชคร้ายมาก ๆ โปรแกรมอาจทำงานได้ตามต้องการอยู่พักใหญ่ แต่พอเปลี่ยนโค้ดที่ไม่เกี่ยวข้อง หรือเปลี่ยนเวอร์ชันคอมไพเลอร์ ระบบปฏิบัติการ ฯลฯ บั๊กใหม่ก็โผล่ขึ้นมา
  • ข้อเสนอเพื่อให้หาวิธีที่ดีกว่าในฐานะนักพัฒนา
    • อ่าน ทำความเข้าใจ และจดจำเรื่อง undefined behavior ใน C หรือ C++ แล้วหลีกเลี่ยงมัน
    • คอมไพล์แอปพลิเคชันในโหมดดีบักและเปรียบเทียบกับโหมดรีลีส ถ้ามีความต่างกันแปลว่ามีปัญหาร้ายแรง
    • ใช้เครื่องมืออย่าง -fsanitize=undefined,address เพื่อจับ undefined behavior ตอนรันไทม์
    • แนะนำให้ใช้ภาษาแบบ managed เช่น Java, C#, Python หรือใช้ภาษา low-level ที่ปลอดภัยอย่าง Rust
  • แนะนำให้ใช้ดีบักเกอร์ ได้ยินเรื่องราวเกี่ยวกับปัญหาของการไม่ใช้ดีบักเกอร์มาแล้ว
  • ขอส่งความรักมากมายให้ Silent ที่ช่วยปรับปรุงเกมโปรดของฉันมานานกว่าสิบปี