5 คะแนน โดย GN⁺ 2025-01-24 | 4 ความคิดเห็น | แชร์ทาง WhatsApp
  • การแครชที่เกิดขึ้นเฉพาะบน ARM64

    • ระหว่างพอร์ตโค้ด network I/O ของ EdgeDB จาก Python ไปเป็น Rust พบปัญหาที่การทดสอบล้มเหลวเป็นครั้งคราวบน ARM64 CI runner
    • ตอนแรกดูเหมือนจะเป็น deadlock แต่จริง ๆ แล้วโปรเซสแครช และตัวทดสอบไม่สามารถตรวจจับได้
  • ทฤษฎีเบื้องต้น

    • เพื่อทำความเข้าใจว่าทำไมปัญหาจึงเกิดขึ้นเฉพาะบน ARM64 จึงพิจารณาความแตกต่างของ memory model
    • memory model ของ Intel มีความเข้มงวดกว่า ขณะที่ ARM ใช้ memory model ที่อ่อนกว่า
  • การดีบักบนเครื่อง CI

    • เชื่อมต่อเข้า ARM64 runner บน AWS โดยตรงเพื่อตรวจสอบปัญหา
    • โปรเซสแครชและทิ้ง core dump ไว้ จึงใช้ข้อมูลนั้นสืบหาสาเหตุของปัญหา
  • สาเหตุจริง: setenv และ getenv

    • setenv ไม่ปลอดภัยในสภาพแวดล้อมแบบมัลติเธรด และอาจทำให้เกิดการแครชเมื่อทำงานร่วมกับ getenv
    • พบว่าการจัดสรรค่าตัวแปรสภาพแวดล้อมใหม่เป็นสาเหตุของปัญหา
  • ความเชื่อมโยงกับ openssl_probe

    • ปัญหาเกิดขึ้นขณะที่ openssl-probe ตั้งค่าตัวแปรสภาพแวดล้อม SSL_CERT_FILE และ SSL_CERT_DIR
    • ระหว่างที่ rust-native-tls ของ Rust ตั้งค่าตัวแปรสภาพแวดล้อมเหล่านี้ ก็เกิดการแครชขึ้น
  • เหตุผลที่เกิดขึ้นเฉพาะบน ARM64 Linux

    • การแครชจะเกิดขึ้นได้ก็ต่อเมื่อมีหลายเงื่อนไขมาประจวบกัน โดยจำนวนตัวแปรสภาพแวดล้อมและความล้มเหลวของ I/O เป็นหนึ่งในเงื่อนไขเหล่านั้น
  • วิธีแก้ไข

    • ตัดสินใจเปลี่ยนจาก backend rust-native-tls/openssl ของ reqwest ไปใช้ rustls
    • โปรเจ็กต์ Rust มีแผนจะทำให้ฟังก์ชันตั้งค่าสภาพแวดล้อมเป็น unsafe และโปรเจ็กต์ glibc ก็กำลังปรับปรุง thread safety ของ getenv

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

 
y15un 2025-01-24

ผมคิดว่าจะตั้งชื่อว่า "ความไม่ปลอดภัยด้านเธรดของ C stdlib แม้แต่ Rust ที่ว่าปลอดภัยก็ยังช่วยไม่ได้" ครับ :)

 
halfenif 2025-01-24

เข้าใจอย่างชัดเจนแล้ว

 
GN⁺ 2025-01-24
ความเห็นจาก Hacker News
  • Rust กำลังจะทำให้ตัวตั้งค่าสภาพแวดล้อมเป็น unsafe ใน edition ถัดไป ซึ่งอาจส่งผลต่อ crate ที่มีโค้ดขัดแย้งกัน

    • ใน Rust standard library นั้น set_var และ remove_var จะต้องใช้บล็อก unsafe {} ใน edition 2024
    • เอกสารปัจจุบันกล่าวถึงปัญหาด้านความปลอดภัยอยู่แล้ว แต่เดิมทีการทำให้ฟังก์ชันเหล่านี้เป็น safe นั้นเป็นความผิดพลาด
  • มีแพตช์สำหรับ glibc ที่ทำให้ getenv ปลอดภัยขึ้น แต่ C ก็ยังไม่ปลอดภัยอย่างสมบูรณ์ เพราะยังอนุญาตให้เข้าถึง environment ได้โดยตรง

    • ผู้ดูแล C standard library ไม่ค่อยอยากทำให้ setenv ปลอดภัยกับมัลติเธรด แต่อย่างน้อยก็ควรนิยาม API แบบ thread-safe ตัวใหม่ขึ้นมา
    • ผู้ดูแลของ Musl ไม่ได้มั่นใจว่าปัญหานี้แก้ไม่ได้
  • การเจอบั๊กเกี่ยวกับ environment บน Linux ดูเหมือนเป็นเหมือนพิธีผ่านด่านอย่างหนึ่ง

    • Linus และเคอร์เนลมีแนวทางเชิงปฏิบัติในการแก้บั๊กของ POSIX แต่ glibc ยังตามหลังอยู่
    • การมี getenv_r() พร้อมซิงก์กับ setenv() และมีคำเตือนตอนคอมไพล์/ลิงก์ น่าจะช่วยแก้ปัญหานี้ได้
  • การใช้ environment variable สำหรับตั้งค่าเป็นส่วนหนึ่งของแนวคิด "12-factor app" แต่มีความเห็นว่านี่เป็นวิธีที่ไม่ฉลาด

    • คิดว่าการใช้ไฟล์ตั้งค่าอย่าง YAML แทน environment variable เป็นวิธีที่ดีกว่า
  • เครื่อง CI ที่รันบน Amazon AWS มีข้อดีตรงที่ให้สิทธิ์ root จริง

    • ดูเหมือนเรากำลังสูญเสียความสามารถในการ build และ debug โค้ดบนเครื่องโลคัลโดยไม่พึ่งคลาวด์และคอนเทนเนอร์
  • เป็นบทความยอดเยี่ยมที่ขุดลึกบั๊กแบบไม่เป็นไปตามสัญชาตญาณ

    • รายงานการแก้ปัญหาแบบละเอียดเช่นนี้ให้ประสบการณ์ที่ใกล้เคียงกับการลงมือทำเองมากที่สุด
  • env::set_var ตอนนี้กลายเป็น unsafe แล้ว

    • ในโปรแกรมเธรดเดี่ยวสามารถเรียกได้อย่างปลอดภัย
    • บน Windows ปลอดภัยเสมอทั้งในโปรแกรมเธรดเดี่ยวและมัลติเธรด
    • ในโปรแกรมมัลติเธรดบนระบบปฏิบัติการอื่น ทางเลือกเดียวที่ปลอดภัยคืออย่าใช้ set_var หรือ remove_var
  • ทำให้นึกถึงประสบการณ์ที่ setproctitle ใช้งานไม่ได้ในโค้ดเบสบางตัว

    • หลังจาก import numpy แล้ว setproctitle ใช้งานไม่ได้ เพราะระหว่างการ initializa numpy มีการเรียก getenv หรือ setenv จนทำให้ ที่อยู่ของ environ เปลี่ยนไป