1 คะแนน โดย GN⁺ 2 시간 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • การตัด Rust ออกเพียงเพราะมี unsafe และใช้ เกณฑ์ที่มองว่าเฉพาะ Fil-C เท่านั้นที่ปลอดภัย ทำให้มองข้ามขอบเขตการใช้งานจริงของซอฟต์แวร์และการประนีประนอมทางเทคนิค
  • Fil-C เปลี่ยนการเข้าถึงหน่วยความจำที่ผิดพลาดของ C/C++ ให้กลายเป็นแพนิก แต่มีต้นทุนตามมา เช่น ABI ไม่เข้ากัน ประสิทธิภาพลดลงหลายเท่าในบางสถานการณ์ และการนำ GC มาใช้
  • ในโค้ด Rust ของ Android ประมาณ 5 ล้านบรรทัด พบช่องโหว่ด้านความปลอดภัยของหน่วยความจำที่อาจเกิดขึ้น 1 รายการและแก้ไขก่อนปล่อย คิดเป็นประมาณ 0.2 รายการต่อหนึ่งล้านบรรทัด ซึ่งต่ำกว่าข้อมูลเดิมของ C/C++ ที่ประมาณ 1,000 รายการ มากกว่า 1,000 เท่า
  • ไม่จำเป็นต้องเลือกเพียงอย่างเดียวระหว่างเทคโนโลยีที่ป้องกันปัญหาได้ 99.9% ในทุกโปรแกรม กับเทคโนโลยีที่ป้องกันได้ 100% ใน 90% ของโปรแกรม และสำหรับซอฟต์แวร์ที่ยอมรับ ข้อจำกัดของ Fil-C ได้ยาก ทางเลือกอย่าง Rust ก็เหมาะสม
  • ความปลอดภัยของหน่วยความจำต้องพิจารณา ประสิทธิภาพ·ABI·GC·การป้องกัน data race ร่วมกัน และหากวิจารณ์ว่า Rust ยังไม่เพียงพอ ก็ควรใช้เกณฑ์เดียวกันอย่างน้อยกับ C/C++ ทั่วไปและ Zig ที่ไม่ใช่ Fil-C

โมเดลความรับผิดชอบของ Rust และภาษาระบบแบบเดิม

  • การถกเถียงเรื่องความปลอดภัยของหน่วยความจำในภาษาสำหรับ system programming ที่ไม่ใช้ GC ส่วนใหญ่ดำเนินอยู่รอบความแตกต่างของ โมเดลความรับผิดชอบระหว่าง Rust กับ C/C++/Zig
    • Rust พยายามป้องกันไม่ให้โปรแกรมที่อาจก่อปัญหาความปลอดภัยของหน่วยความจำคอมไพล์ผ่าน แม้ต้องแลกด้วยการปฏิเสธบางโปรแกรมที่จริง ๆ แล้วอาจปลอดภัย
    • unsafe เป็นทางออกที่อนุญาตให้ข้ามการรับประกันบางอย่าง เช่น การ dereference พอยน์เตอร์ดิบ
    • ภาษาตระกูล C มอบความรับผิดชอบด้านการรับประกันความปลอดภัยของหน่วยความจำส่วนใหญ่ให้โปรแกรมเมอร์
  • แม้ระดับการสนับสนุนของแต่ละภาษาจะแตกต่างกัน เช่น RAII และ smart pointer ของ C++ หรือ defer ของ Zig แต่โดยหลักการแล้วไม่ได้ปิดกั้นการเข้าถึงหน่วยความจำที่ผิดพลาดโดยตรง

ตัวเลือกที่ Fil-C เพิ่มเข้ามา

  • Fil-C นำเสนอแนวทางใหม่ในการรันโค้ด C และ C++ ให้ปลอดภัยด้านหน่วยความจำ
    • หากเกิดการเข้าถึงหน่วยความจำที่ผิดพลาด เช่น เข้าถึงนอกขอบเขตหรือใช้งานหลังจาก free แล้ว จะทำให้เกิด แพนิก
    • ผสาน GC เข้ากับ InvisiCaps ที่ติดตามหน่วยความจำซึ่งพอยน์เตอร์สามารถเข้าถึงได้
  • ใน Zig ก็มีการเสนอ โหมดคอมไพล์ใหม่ ที่ได้แรงบันดาลใจจาก Fil-C
  • หากโปรเจกต์ C/C++ ยอดนิยมบางส่วนให้บริการรีลีสที่คอมไพล์ด้วย Fil-C ก็อาจเพิ่มทางเลือกในการลดช่องโหว่ด้านความปลอดภัยของหน่วยความจำได้

เกณฑ์ที่มองว่า Rust ไม่ปลอดภัย

  • ผู้พัฒนา Fil-C เคยประเมินบน Twitter ว่า Rust ไม่ใช่ภาษาที่ปลอดภัยด้านหน่วยความจำ เพราะ unsafe สามารถข้ามการรับประกันบางอย่างได้
  • Andrew Kelley ผู้พัฒนา Zig ก็ใช้ชื่อประเด็นที่เกี่ยวข้องเรียกโหมดที่ได้แรงบันดาลใจจาก Fil-C ว่าเป็นโหมดคอมไพล์ที่ “ปลอดภัยด้านหน่วยความจำจริง ๆ ต่างจาก Rust”
  • การถกเถียงบางส่วนเรียกร้องว่า หากผู้ใช้ Rust ให้ความสำคัญกับความปลอดภัยของหน่วยความจำอย่างแท้จริง ก็ควรเลิกใช้ Rust แล้วไปโปรโมต Fil-C ที่ปลอดภัยกว่า
  • เกณฑ์นี้เปรียบเทียบ Rust กับ Fil-C โดยตัด ต้นทุนในโลกจริงของ Fil-C ออกไป และมีท่าทีคล้ายกับคำวิจารณ์เรื่องความคลั่งไคล้ที่ชุมชน Rust มักได้รับ

ข้อจำกัดในการนำ Fil-C ไปใช้

  • Fil-C ไม่ใช่ ตัวแทนแบบ drop-in ที่ไร้ต้นทุน
    • ABI ไม่เข้ากันกับโปรแกรมที่คอมไพล์โดยไม่ใช้ Fil-C
    • ในบางสถานการณ์อาจช้าลงหลายเท่า
    • นำ GC เข้ามาใช้
  • สำหรับโปรแกรมอย่างยูทิลิตีง่าย ๆ ที่แทบไม่รู้สึกถึงประสิทธิภาพที่ลดลง หรือไม่จำเป็นต้องลิงก์แบบไดนามิก ข้อจำกัดเหล่านี้อาจไม่ใช่ปัจจัยชี้ขาด
  • ในทางกลับกัน มีโปรเจกต์ยอดนิยมจำนวนมากที่ไม่สามารถยอมรับ GC และ ABI ที่ไม่เข้ากันได้ และโปรแกรมที่ใช้ Fil-C ในรูปแบบปัจจุบันได้ยากก็มัก เหมาะกับ Rust

ข้อมูลช่องโหว่ของโค้ด Rust ในโลกจริง

  • ข้อมูลสำหรับประเมินความปลอดภัยเชิงปฏิบัติของ Rust ยังมีไม่มาก แต่ก็ยังไม่พบช่องโหว่ด้านความปลอดภัยของหน่วยความจำที่ใช้โจมตีได้จำนวนมากในซอฟต์แวร์ Rust
  • ใน โค้ด Rust ของ Android มากกว่า 5 ล้านบรรทัด พบช่องโหว่ด้านความปลอดภัยของหน่วยความจำที่อาจเกิดขึ้น 1 รายการ และแก้ไขก่อนปล่อย
    • ความหนาแน่นของช่องโหว่ที่ประเมินได้คือ 0.2 รายการต่อหนึ่งล้านบรรทัด
    • ข้อมูล C/C++ ในอดีตของ Android อยู่ที่ประมาณ 1,000 รายการต่อหนึ่งล้านบรรทัด
    • ความหนาแน่นของโค้ด Rust ถูกติดตามว่า ต่ำกว่า C/C++ มากกว่า 1,000 เท่า
  • ตัวเลขอาจแตกต่างกันไปตามแต่ละโปรเจกต์ แต่เป็นหลักฐานว่าในสภาพแวดล้อมจริง Rust ลดความเสี่ยงในการนำปัญหาความปลอดภัยของหน่วยความจำเข้ามาได้อย่างมาก

เหตุผลที่ไม่จำเป็นต้องเลือกเพียงอย่างเดียว

  • ตัวเลือกสมมติระหว่างเทคโนโลยีที่ป้องกันปัญหาได้ 99.9% ในทุกโปรแกรม กับเทคโนโลยีที่ป้องกันปัญหาได้ 100% ใน 90% ของโปรแกรม แสดงให้เห็นว่า ทั้งขอบเขตการใช้งานและระดับการป้องกัน ล้วนสำคัญ
  • แม้จะไม่รู้สัดส่วนจริง แต่ก็ไม่จำเป็นต้องเลือกเพียงแนวทางใดแนวทางหนึ่ง
    • โปรเจกต์ C/C++/Zig ที่ยอมรับการประนีประนอมได้สามารถให้บริการไบนารี Fil-C ได้
    • ซอฟต์แวร์ที่ใช้ Fil-C ไม่ได้สามารถเขียนด้วยภาษาที่กำจัดความเสี่ยงของช่องโหว่ด้านความปลอดภัยของหน่วยความจำได้ทั้งหมดหรือเกือบทั้งหมด

เหตุผลที่เลือก Rust แม้ใช้ GC ได้

  • การใช้ Rust ยังคงสมเหตุสมผล แม้มีตัวเลือกที่ใช้ GC อย่าง Go หรือ Fil-C
  • โปรแกรมที่เขียนด้วยภาษาแบบ GC ได้มักไม่จำเป็นต้องใช้ unsafe และโปรแกรมที่จำเป็นต้องใช้ unsafe ก็มักใช้ GC ไม่ได้
  • อาจประเมินว่าการรับประกันและฟีเจอร์อื่นของภาษา เช่น การป้องกัน data race สำคัญกว่าความเสี่ยงด้านความปลอดภัยของหน่วยความจำเพียงเล็กน้อย
  • Fil-C เปลี่ยนช่องโหว่ด้านความปลอดภัยของหน่วยความจำใน C/C++ เดิมให้กลายเป็นการแครช
    • แม้จะดีกว่าช่องโหว่ด้านความปลอดภัย แต่หากความหนาแน่นในอดีตประมาณ 1,000 รายการต่อหนึ่งล้านบรรทัดยังคงอยู่ ก็ยังเหลือการแครชจำนวนมากที่ต้องแก้ไข
    • ในอดีตเคยมีช่องโหว่ด้านความปลอดภัยที่อาศัยความสามารถของผู้โจมตีในการทำให้โปรแกรมแครชได้

เกณฑ์ความปลอดภัยของหน่วยความจำที่สอดคล้องกัน

  • หากไม่สามารถยอมรับแม้แต่ 0.2 รายการต่อหนึ่งล้านบรรทัดของ Rust ได้ ก็ควรใช้คำวิจารณ์ที่เท่ากันหรือเข้มงวดกว่าเดิมกับ C/C++ ทั่วไปและ Zig ที่ไม่ใช่ Fil-C ด้วย
  • ท่าทีที่ยอมรับทางเลือกที่มีความปลอดภัยต่ำกว่า Rust แต่ตัด Rust ออกเพียงเพราะ unsafe ไม่ได้นำแนวคิดความปลอดภัยของหน่วยความจำแบบสัมบูรณ์มาใช้อย่างสอดคล้อง

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

 
GN⁺ 2 시간 전
ความคิดเห็นใน Lobste.rs
  • คำพูดของ Andrew Kelly ที่ดูเหมือน OP จะรับไปในทางไม่พอใจ มีใจความว่า Zig สามารถคอมไพล์แม้กระทั่ง dependency C/C++ ทั้งหมดให้เป็น ไฟล์ executable ที่ปลอดภัยด้านหน่วยความจำอย่างสมบูรณ์ โดยไม่มีทางออกเลี่ยงได้ และมีต้นทุนด้านประสิทธิภาพราว 1~6 เท่า ขึ้นอยู่กับความถี่ของการติดตาม pointer
    ผู้เขียนมองว่านี่เป็นการโจมตี Rust และท้ายบทความก็โจมตี Zig แต่ Fil-C กับ build mode ใหม่ของ Zig เป็นการมีส่วนร่วมเชิงบวกต่อ ecosystem และให้จุดออกแบบกับการแลกเปลี่ยนที่ต่างจาก Rust
    ผมตีความว่าหมายถึง หากทำตาม data-oriented programming ที่ทีม Zig ชอบ ต้นทุนด้านประสิทธิภาพอาจลดลงจนใกล้ 1 เท่าได้

    • ผู้เขียนกำลังตอบโต้ชื่อเรื่อง “เพิ่มโหมดคอมไพล์ที่ได้แรงบันดาลใจจาก Fil-C ซึ่ง ปลอดภัยด้านหน่วยความจำจริง ๆ ต่างจาก Rust” และโพสต์ Twitter ของผู้พัฒนา Fil-C
      การอ่านว่ามันยั่วยุโดยไม่จำเป็นก็ไม่ใช่เรื่องแปลก และภายหลัง Andrew ก็เปลี่ยนชื่อเรื่องให้ยั่วน้อยลงแล้ว
  • เมื่อคำล้อเล่นเบา ๆ ว่า “ภาษาของพวกคุณไม่ปลอดภัยด้านหน่วยความจำ” ถูกโยนกลับมาในทิศทางตรงข้ามกับปกติ ปริมาณการตอบสนองของนักพัฒนา Rust ก็ค่อนข้างมาก
    ผมเองก็ชอบ Rust มาก แต่จำเป็นต้องรับอย่างเป็นธรรม

    • คำกล่าวว่า “Rust ค่อนข้างปลอดภัยด้านหน่วยความจำ แต่ Fil-C ปลอดภัยกว่าในแง่นั้น” ไม่ได้ดูเป็นประเด็นถกเถียงมากนัก และดูเหมือนเป็นข้อเท็จจริง
      ยิ่งไปกว่านั้น C ที่ผ่านการ formal verification ของ seL4 ก็ปลอดภัยยิ่งขึ้นอีกขั้น
    • เพราะให้ความสำคัญกับโปรแกรมที่ถูกต้อง และพยายามไม่เขียนโค้ดที่ไม่เป็นเช่นนั้นเท่าที่ทำได้ จึงกลับกลายเป็นว่าผมชอบ Rust มากกว่า Zig
      Zig มีข้อดีตรงที่เมื่อการจัดสรรหน่วยความจำล้มเหลวก็ทำให้จบการทำงานอย่างถูกต้องได้ง่าย และคอมไพล์ได้เร็ว
      ผมไม่รู้ว่า Fil-C ปลอดภัยด้านหน่วยความจำกว่า Rust หรือไม่ แต่สำหรับการใช้งานของผม garbage collector และความเข้ากันไม่ได้กับ C ABI เป็นอุปสรรคชี้ขาด
      หากทำเป็น single-thread ก็เลี่ยง race condition ได้ และถ้าใส่ garbage collector ก็ได้ memory safety แต่สิ่งที่ดีของ Rust คือให้ทั้งสองอย่างโดยไม่ต้องยอมแลกสองข้อนั้น
  • เนื่องจากให้ความสำคัญกับ memory safety มาก Fil-C และ การใช้งาน Fil-C ABI ของ Zig จึงเป็นตัวเลือกที่ชัดเจน
    โปรเจกต์ Rust ที่มี dependency เป็น C จะทำให้การรับประกันความปลอดภัยอ่อนลง และแม้จะใช้แต่ Rust ล้วนก็ทำได้ แต่ไม่สะดวก
    Rust เองก็ควร implement Fil-C ABI เพื่อให้สามารถ build dependency C อย่างปลอดภัยและ link เข้ากับ Rust ได้ ผมไม่เข้าใจว่าทำไมเรื่องนี้ถึงเป็นประเด็นขัดแย้ง

    • Rust มี Miri interpreter อยู่แล้ว ซึ่งตรวจสอบว่าโค้ด Rust ที่ unsafe รักษาการรับประกันของ Rust ที่ safe หรือไม่
      หากจะ implement ความสามารถคล้ายกัน ก็อยากให้ใช้เป็นเครื่องมือช่วยปรับปรุงโค้ด FFI เฉพาะใน debug build เท่านั้น
      วิธีที่มี garbage collector และตรวจทุก operation ตอน runtime ไม่เหมาะกับทุกการใช้งาน
  • ก่อนจะปัด Fil-C ว่าเป็นแค่ของสำหรับ utility ง่าย ๆ ควรดู งานนำเสนอ Fil-C จากงานประชุม Software Should Work
    ผู้พูดนำเสนอด้วยแล็ปท็อป Linux ที่ประกอบจาก Fil-C ทั้ง userspace และรวมถึง OpenOffice Impress
    มันอาจไม่เหมาะกับโปรแกรม C/C++ บางส่วน แต่ดูไม่ใช่ เทคโนโลยีของเล่น เท่าที่ผู้เขียนคิด

  • มาจากสาย Python จึงมองว่า memory safety เป็นเงื่อนไขพื้นฐานอยู่แล้ว และเหตุผลที่เลือก Rust มีสามข้อ
    หนึ่ง ระบบ type ที่ทรงพลังช่วยให้มั่นใจความถูกต้องได้ตั้งแต่ compile time และ memory safety ที่ได้จาก #![forbid(unsafe_code)] กับการ audit dependency ด้วย cargo-geiger เป็นการแสดงออกที่น่าสนใจน้อยที่สุดในบรรดานั้น
    สอง มี ecosystem ที่ช่วยให้เขียนโค้ดอย่างปลอดภัยครั้งเดียว แล้วแชร์ข้ามหลายภาษาและ execution environment ได้ง่าย
    สาม มี syntax sugar ที่ช่วยให้เขียนโค้ดระดับสูงได้สะดวก เช่น try!(x) ในสมัยนั้น
    Zig และ Fil-C ดูเหมือนยังไม่ตอบโจทย์ ความสามารถให้ compiler จับ logical error ด้วยการ encode invariant ลงใน type system ผ่าน typestate pattern หรือ newtype เป็นต้น
    ความไม่เข้ากันของ ABI ใน Fil-C ก็เป็นปัญหาเมื่อเขียนโมดูลที่คอมไพล์อย่างปลอดภัยสำหรับ runtime เดิมอย่าง CPython บน shared web hosting

    • ใน Zig ก็สามารถทำ typestate pattern และการ encode type สำหรับ constraint หลากหลายแบบได้ และในบางแง่ comptime มีพลังในการแสดงออกมากกว่า Rust
      มีการแลกเปลี่ยนเรื่องเวลา compile ความยืดยาว และข้อเท็จจริงที่ว่า ecosystem ส่วนใหญ่ไม่ได้พยายามไปถึงระดับนี้ แต่ทำได้จริงและค่อนข้างสนุก
  • สงสัยว่าทำไมเทคโนโลยีอย่าง Fil-C ถึง ไม่เกิดขึ้นเมื่อ 20 ปีก่อน

    • ในวงการ mainframe มีการเตือนเรื่องปัญหาความปลอดภัยของการแชร์และ tagging pointer กับ capability-based permissions มาหลายสิบปีแล้ว
      แต่ไม่มีใครอยากจ่ายต้นทุนด้วย CPU หรือหน่วยความจำ กล่าวคือด้วยค่าใช้จ่ายจริง
    • เมื่อไม่กี่เดือนก่อน ผมถาม Filip ด้วยคำถามคล้ายกัน และได้รับ คำตอบดังนี้
      ช่วงปี 2004~2018 มีไอเดียอยู่แล้ว แต่มองว่าแนวคิด C ที่ปลอดภัยด้านหน่วยความจำเป็นเรื่องโง่ ช่วงปี 2018~2023 เปลี่ยนความคิดแล้ว แต่ยังหาวิธีให้เข้ากันได้แบบสุดขั้วไม่ได้
      Fil-C ระยะแรกในปี 2023~2024 มีความเข้ากันได้และประสิทธิภาพต่ำกว่านี้มาก และปลายปี 2024 ความก้าวหน้าครั้งสำคัญของ InvisiCaps ทำให้ได้ความเข้ากันได้สูงและประสิทธิภาพที่ใช้ได้อย่างในปัจจุบัน
      จุดเปลี่ยนความคิดราวปี 2018 คือการสังเกตว่า C variants ที่ใช้ใน GPU เป็นรูปแบบเรียบง่ายของ C ที่ปลอดภัยด้านหน่วยความจำ
    • เมื่อ 20 ปีก่อน คอมพิวเตอร์ช้ากว่านี้ และอ่อนไหวต่อ ต้นทุนของความปลอดภัย มากกว่ามาก
  • หากตีความคำกล่าวว่า “ถ้าฝั่ง Rust ให้ความสำคัญกับ memory safety จริง ก็ควรสนับสนุน Fil-C ที่ปลอดภัยกว่าและเลิกใช้ Rust” อย่างมีเจตนาดีที่สุด ก็หมายถึงตอนนี้มี Fil-C แล้ว จึงควรหยุดความพยายามเขียนโลกใหม่ด้วย Rust แล้วกลับไป C/C++ เพื่อรักษา ecosystem library ที่รวมเป็นหนึ่งเหมือนเดิม
    แม้ library Rust จะใช้จาก C/C++ ได้ แต่มีนักพัฒนาที่ไม่ต้องการสิ่งนี้ ดังนั้นตรรกะคือ หากผู้ใช้ Rust ยอมรับทางออกที่ดีกว่าและยอมแพ้ ความแตกแยกก็จะหายไป
    แต่ Fil-C มีการแลกเปลี่ยนที่ Rust ไม่มี นั่นคือ garbage collector และรองรับเฉพาะ x86-64 Linux
    นอกจาก memory safety แล้ว Cargo และการไม่มี global namespace ก็เป็นเหตุผลสำคัญในการใช้ Rust และเป็นเรื่องน่าเสียดายที่ความแตกแยกระหว่างค่ายภาษาไปพัวพันกับสงครามวัฒนธรรมที่กว้างกว่า
    แค่อยากสร้าง library ที่ทุกคนยินดีใช้เท่านั้น

    • สงสัยว่าแค่ C กับ C++ ก็เป็น ecosystem ที่แตกเป็นส่วน ๆ อยู่แล้วไม่ใช่หรือ
      ในหมู่นักพัฒนา C ก็อาจมีคนที่ไม่อยากได้ library C++ และตอนนี้ยังมี Zig กับ Odin โผล่มาอีก ต่อให้ Rust หายไป ความแตกแยกก็ยังอยู่
      อยากรู้ว่า Rust สร้างความแตกแยกคนละชนิดเป็นพิเศษหรือไม่
    • ผมเองก็อยากเห็นโลกที่ทุกคนใช้ library อย่างพึงพอใจ และกำลังพัฒนา transpiler ระดับสูงจาก Rust→Zig ที่คง generic ไว้ด้วย comptime
      Rust encode constraint ได้มากกว่า จึงเหมาะเป็นภาษาต้นทาง