1 คะแนน โดย GN⁺ 2025-12-14 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • ความปลอดภัยของหน่วยความจำ และ แซนด์บ็อกซ์ เป็นแนวคิดด้านความปลอดภัยที่แยกจากกัน และต้องมีทั้งสองอย่างจึงจะสร้างระบบป้องกันที่แข็งแกร่งได้
  • Fil-C เป็น อิมพลีเมนเทชันที่ทำให้ C/C++ ปลอดภัยด้านหน่วยความจำ โดยรับประกันความปลอดภัยไปถึงระดับ system call ของ Linux และสามารถใช้ได้แม้กับองค์ประกอบของระบบอย่าง OpenSSH
  • ในกระบวนการพอร์ต แซนด์บ็อกซ์แบบ seccomp-BPF ของ OpenSSH มายัง Fil-C ประเด็นสำคัญคือข้อจำกัดในการสร้างเธรดและการปรับแต่งตัวกรอง seccomp
  • เพื่อจัดการ เธรดเบื้องหลังของรันไทม์ ของ Fil-C จึงมีการเพิ่ม API zlock_runtime_threads() เพื่อควบคุมการทำงานของเธรดภายในแซนด์บ็อกซ์
  • Fil-C ออกแบบให้ใช้การเรียก prctl แบบซิงโครไนซ์กับเธรดรันไทม์ทั้งหมด เพื่อให้ no_new_privs และ ตัวกรอง seccomp ถูกบังคับใช้อย่างสอดคล้องกันกับทั้งโปรเซส

ความสัมพันธ์ระหว่างความปลอดภัยของหน่วยความจำกับแซนด์บ็อกซ์

  • ความปลอดภัยของหน่วยความจำและแซนด์บ็อกซ์เป็นคนละชั้นของความปลอดภัย ซึ่งมีเพียงอย่างเดียวไม่สามารถให้การป้องกันที่สมบูรณ์ได้
    • ตัวอย่างที่ปลอดภัยด้านหน่วยความจำแต่ไม่ถูกแซนด์บ็อกซ์: โปรแกรม Java ที่สามารถเขียนทับไฟล์ผ่านข้อมูลที่ผู้ใช้ป้อนเข้ามาได้
    • ตัวอย่างที่ถูกแซนด์บ็อกซ์แต่ไม่ปลอดภัยด้านหน่วยความจำ: โปรแกรมที่เขียนด้วยแอสเซมบลีและถูกจำกัดสิทธิ์
  • แซนด์บ็อกซ์จริงยังมีช่องโหว่เชิงโครงสร้างอยู่ เช่น การอนุญาตให้สื่อสารกับโปรเซสนายหน้า
  • ดังนั้น การใช้ทั้งความปลอดภัยของหน่วยความจำและแซนด์บ็อกซ์ร่วมกัน จึงเป็นแนวทางป้องกันที่ดีที่สุด

การผสาน Fil-C กับแซนด์บ็อกซ์ของ OpenSSH

  • Fil-C เป็น อิมพลีเมนเทชันที่ทำให้ C/C++ ปลอดภัยด้านหน่วยความจำ และคงความปลอดภัยไว้ได้ถึงระดับ system call ของ Linux
    • รันไทม์ของ Fil-C สามารถทำงานได้แม้ใน องค์ประกอบระบบระดับล่าง อย่าง init, udevd
    • OpenSSH ทำงานบน Fil-C ได้ตามปกติ และใช้ แซนด์บ็อกซ์ seccomp-BPF
  • บน Linux นั้น OpenSSH สร้างแซนด์บ็อกซ์ด้วยเครื่องมือต่อไปนี้
    • จำกัดการเข้าถึงระบบไฟล์ด้วย chroot
    • รันด้วยผู้ใช้/กลุ่ม sshd
    • จำกัดการเปิดไฟล์และการสร้างโปรเซสด้วย setrlimit
    • อนุญาตเฉพาะ system call ที่กำหนดด้วย ตัวกรอง seccomp-BPF
  • Fil-C รองรับ chroot และการสลับผู้ใช้โดยพื้นฐานอยู่แล้ว แต่ setrlimit และ seccomp-BPF อาจขัดกับการทำงานของรันไทม์ จึงต้องมีการปรับเพิ่มเติม

การควบคุมเธรดของรันไทม์ Fil-C

  • รันไทม์ของ Fil-C ใช้ เธรดเบื้องหลังสำหรับ garbage collection และจะหยุดหรือเริ่มใหม่โดยอัตโนมัติเมื่อจำเป็น
  • แซนด์บ็อกซ์ setrlimit ของ OpenSSH มีเป้าหมายเพื่อ ห้ามสร้างโปรเซสใหม่ ดังนั้นการสร้างเธรดของรันไทม์อาจละเมิดเงื่อนไขนี้ได้
  • เพื่อแก้ปัญหานี้ จึงเพิ่ม API zlock_runtime_threads() ใน <stdfil.h>
    • ให้รันไทม์สร้างเธรดที่จำเป็นทั้งหมดทันที และปิดการหยุดทำงานอัตโนมัติหลังจากนั้น
    • เรียกใช้ในฟังก์ชัน ssh_sandbox_child ของ OpenSSH ก่อนเรียก setrlimit หรือ seccomp-BPF

การปรับตัวกรอง seccomp ของ OpenSSH

  • หลังใช้ zlock_runtime_threads() แล้ว ฟังก์ชันส่วนใหญ่ของแซนด์บ็อกซ์ยังทำงานได้ตามเดิม
  • มีการเปลี่ยนแปลงต่อไปนี้ในตัวกรอง seccomp
    • เมื่อละเมิดให้ตั้งค่าเป็น SECCOMP_RET_KILL_PROCESS เพื่อให้ เธรดเบื้องหลังของ Fil-C ถูกปิดพร้อมกันด้วย
    • เพิ่ม MAP_NORESERVE เข้าในรายการที่อนุญาต เพื่อรองรับการใช้งานตัวจัดสรรหน่วยความจำของ Fil-C
    • อนุญาตการเรียก sched_yield ซึ่งถูกใช้ใน อิมพลีเมนเทชันล็อก ของ Fil-C
  • การเรียก futex สำหรับการซิงโครไนซ์ของ Fil-C ได้รับอนุญาตอยู่แล้ว จึงไม่ต้องแก้ไขเพิ่มเติม

วิธีการอิมพลีเมนต์ prctl ของ Fil-C

  • OpenSSH ใช้การเรียก prctl สองแบบเมื่อติดตั้งตัวกรอง seccomp
    • PR_SET_NO_NEW_PRIVS เพื่อ ป้องกันการได้สิทธิ์เพิ่มเติม
    • PR_SET_SECCOMP, SECCOMP_MODE_FILTER เพื่อ เปิดใช้งานตัวกรอง
  • ปัญหาคือ prctl มีผล เฉพาะกับเธรดที่เรียกใช้เท่านั้น ทำให้มีความเสี่ยงที่เธรดรันไทม์อื่นของ Fil-C จะยังคงอยู่โดยไม่มีตัวกรอง
  • เพื่อป้องกันเรื่องนี้ Fil-C ใช้ API filc_runtime_threads_handshake() เพื่อ บังคับใช้แบบซิงโครไนซ์ กับเธรดรันไทม์ทั้งหมด
    • รับประกันว่าแต่ละเธรดจะเรียก prctl เดียวกัน
    • หากมีหลาย user thread อยู่ จะทำให้เกิด ข้อผิดพลาดด้านความปลอดภัยของ Fil-C เพื่อเพิ่มการป้องกัน

บทสรุป

  • การผสานความปลอดภัยของหน่วยความจำกับแซนด์บ็อกซ์ คือชุดความปลอดภัยที่ทรงพลังที่สุด
  • Fil-C ผสานแซนด์บ็อกซ์แบบ seccomp ของ OpenSSH ได้อย่างสมบูรณ์ พร้อม คงความปลอดภัยของหน่วยความจำไว้โดยไม่ลดระดับการป้องกัน
  • ในสภาพแวดล้อม Linux การใช้ Fil-C ช่วยให้ได้ทั้งความปลอดภัยระดับระบบและความปลอดภัยระดับภาษาไปพร้อมกัน

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

 
GN⁺ 2025-12-14
ความเห็นจาก Hacker News
  • สงสัยว่าทำไมถึงไม่มีการพูดถึง landlock

  • มี แนวทางคอมไพล์แบบไฮบริด ที่เป็น C → WASM → C
    วิธีนี้ทำให้ควบคุมการโต้ตอบกับ OS ได้ทั้งหมด และทำ sandbox การเข้าถึงหน่วยความจำแบบ WASM ได้ ขณะที่ในเชิงเทคนิคก็ยังคงเป็นโค้ด C อยู่
    ดูข้อมูลที่เกี่ยวข้องได้ที่ RLBox

    • WASM sandbox ไม่ได้รับประกัน ความถูกต้องเชิงตรรกะ (soundness) ของโปรแกรม
      มันยังทำให้หน่วยความจำเสียหายได้ เพียงแต่ขอบเขตจะถูกจำกัดให้อยู่ภายใน sandbox เท่านั้น
      ระบบอย่าง SECCOMP ต้องกำหนดนโยบายการโต้ตอบทั้งหมดอย่างละเอียด จึงค่อนข้าง เป็นงานเชิงระบบระเบียบมาก
      ตรงกันข้าม Fil-C ใช้แนวทางที่ให้ภาษาและรันไทม์พยายามรับประกันการทำงานที่ถูกต้องของโปรแกรมเอง
      ไบนารีของ Fil-C เป็นไฟล์รันได้ปกติ จึงสามารถใช้ร่วมกับ sandbox อย่าง SECCOMP ได้ด้วย
      Linux ใช้เวลา 20 ปีกว่าจะสร้างอินเทอร์เฟซ prctl ได้ ดังนั้นถ้าจะเห็นอะไรคล้ายกันใน WASI ก็น่าจะต้องรออีก 10 ปี
    • RLBox เป็นเทคโนโลยี sandboxing ไม่ใช่ เทคโนโลยี memory safety
      แม้แต่ภายใน sandbox ก็ยังสร้าง execution flow แปลก ๆ ได้
  • ผู้สร้าง Fil-C มักทำ สิ่งประดิษฐ์ที่น่าสนใจในเชิงเทคนิค ได้ดี แต่ก็กังวลว่ายังมีการตรวจสอบความถูกต้องของ implementation ไม่เพียงพอหรือไม่
    มีการบอกว่าสามารถคอมไพล์โปรแกรม setuid ด้วย Fil-C ได้ แต่ส่วนที่แก้ไข ld.so อาจมีความเสี่ยง
    แอป setuid ต้องเขียนแบบป้องกันตัวอย่างมากในเรื่อง environment variable, file descriptor, rlimit, signal ฯลฯ
    ส่วนเหล่านี้ยังไม่สมบูรณ์ จึงมี ความเสี่ยง หากจะนำไปใช้กับโครงสร้างพื้นฐานจริง
    ถึงอย่างนั้น การลองทดสอบ codebase ด้วย Fil-C ก็อาจช่วยค้นพบบั๊กที่น่าสนใจได้

    • ตอนนี้กำลัง ทดสอบ อยู่ว่ามีวิธีทำ Fil-C พังได้จริงหรือไม่
    • ถ้ากังวลจริง ก็ควรลองทดลองเองแล้วแชร์ผลลัพธ์
    • เป้าหมายคือทำให้รันไทม์โปร่งใสเพื่อให้สามารถ audit ได้
      การแก้ไข ld.so เป็นเพียงการเปลี่ยนเล็กน้อยระดับสอนให้มันเข้าใจ layout ของ libc
      บั๊ก getenv ที่เกี่ยวกับ setuid ก็แก้เป็น secure_getenv แล้ว
      สิ่งที่วิจารณ์มามีทั้งส่วนที่จริงและส่วนที่เป็น FUD ปนกันอยู่
    • น่าเสียดายที่ผู้สร้าง Fil-C ตอบสนองต่อคำวิจารณ์แบบ ไม่ค่อยให้ความร่วมมือ
      ในสถานการณ์ data race, pointer และ capability ของ Fil-C อาจไม่สอดคล้องกัน
      ซึ่งอาจนำไปสู่ การละเมิด memory safety ได้
      ผู้สร้างปฏิเสธเรื่องนี้ แต่การเอาไปเทียบกับ Java ก็ไม่เหมาะสม
      เทคโนโลยีนั้นยอดเยี่ยม แต่ท่าทีของผู้สร้างทำให้ความน่าเชื่อถือลดลง
  • WASM เป็นทั้ง sandbox และ execution environment ซึ่งอาจให้ memory safety ได้ในระดับหนึ่งขึ้นอยู่กับวิธีใช้งาน
    ถ้าคอมไพล์ C ไปเป็น WASM บั๊กก็ยังคงอยู่ แต่ขอบเขตผลกระทบจะถูกจำกัด
    ดังนั้นการจัด WASM เป็นเทคโนโลยี sandboxing ก็ถูกต้อง แต่ในฐานะ execution environment มันยังมีความเป็นไปได้มากกว่านั้น

    • สุดท้ายแล้ว WASM ก็คือ เทคโนโลยี sandboxing
      บั๊กในโมดูล B อาจทำให้อ่านข้อมูลของโมดูล A ได้
      กล่าวคือ WASM เป็นเพียงตัวแทนของ process sandbox แบบเบาเท่านั้น
    • คำพูดว่า “ขึ้นอยู่กับวิธีใช้งาน” เองก็เป็นหลักฐานว่า WASM ไม่ได้ปลอดภัยอย่างสมบูรณ์
      เพราะกับ C ก็สามารถพูดได้เหมือนกันว่า “ปลอดภัยได้ ขึ้นอยู่กับวิธีใช้”
    • WASM ไม่เข้าใจ memory model ของ C จึง ไม่สามารถทำกลไกป้องกันแบบ Fil-C ได้
      WASM กันการหนีออกจากรันไทม์ได้ แต่กันการหนีออกจากหน่วยความจำของโปรแกรมภายในไม่ได้
  • มีคนขอให้เปรียบเทียบ Fil-C กับ Rust

    • ทั้งสองเทคโนโลยียอดเยี่ยม แต่แนวทางต่างกัน
      Fil-C เหมาะกับการ เสริมความแข็งแรงให้โปรแกรม C เดิมโดยเน้นความเข้ากันได้และความปลอดภัย
      Rust เหมาะกับการสร้าง codebase ใหม่ที่ต้องการ ความปลอดภัยแบบสถิต และ ประสิทธิภาพ
      Fil-C มีการเสียประสิทธิภาพอยู่บ้าง แต่มีประโยชน์กับโค้ด C เดิมอย่าง ffmpeg, nginx, sudo เป็นต้น
      Rust เด่นเรื่อง multithreading และ type system
    • Fil-C เพิ่ม GC เข้ามา จึงอาจมีผลต่อประสิทธิภาพ
      เป้าหมายคือทำให้ได้ memory safety มากกว่าปรับปรุงการออกแบบภาษา
      คู่แข่งที่ใกล้เคียงจึงน่าจะเป็น D, Nim, Go มากกว่า Rust
    • Fil-C ใช้ runtime check เพื่อตรวจจับการเข้าถึงหน่วยความจำที่ไม่ปลอดภัย และจะหยุดโปรแกรมทันที
      ส่วน Rust ป้องกันตั้งแต่ตอน compile time
      ทั้งสองแนวทางเป็นคนละแกนกัน และใน Rust เองก็สามารถเพิ่ม runtime check แบบ Fil-C ได้
  • MicroVM กำลังได้รับความนิยมมากขึ้นเรื่อย ๆ
    เลยสงสัยว่า Fil-C จะใช้ประโยชน์จากมันได้อย่างไร

    • อาจต้องพอร์ตเล็กน้อย แต่ก็อาจยกระดับบางความสามารถของ microVM ขึ้นมาอยู่ใน userland ที่มี memory safety ของ Fil-C ได้
  • อยากให้โปรเจกต์นี้ได้รับความสนใจมากกว่านี้
    คงจะดีถ้าเครื่องมือสำคัญอย่าง sudo หรือ polkit ถูกแจกจ่ายในรูปแบบที่ปลอดภัยต่อหน่วยความจำ

  • อยากเห็นการใช้ sandboxing มากขึ้นแม้ในภาษาที่มี memory safety
    น่าเสียดายที่แม้แต่ใน Rust หรือ Go ก็ยังไม่ค่อยใช้ sandbox ระดับ OS กัน

    • Seccomp มีปัญหาเรื่อง portability และ composability
      ตั้งค่าในระดับไลบรารีได้ยาก และอ่อนไหวต่อเวอร์ชันเคอร์เนลหรือ implementation ของ libc
      อีกทั้งยังกรองอินพุตที่อยู่หลัง pointer อย่าง file path ไม่ได้ จึงมีข้อจำกัด
      เพราะแบบนี้จึงมักต้องตั้งค่าโดยตรงในระดับแอปพลิเคชัน
    • Rust แทบไม่มีรันไทม์ จึงเหมาะกับการทำ sandbox
      ส่วน Go มีรันไทม์ขนาดใหญ่ จึงทำให้ปลอดภัยแบบ Fil-C ได้ยากกว่า
  • มีคนสงสัยว่า Fil-C ต่างจาก Address Sanitizer (ASan) ของ clang อย่างไร
    ถ้าแค่ทำให้เกิด runtime panic ก็คงเรียกว่า “memory safe” ได้ยากไม่ใช่หรือ

    • ASan จับบั๊กได้ดี แต่ไม่ได้รับประกัน memory safety แบบสมบูรณ์
      บางบั๊กก็ตรวจไม่พบ
      มันใช้วิธีวาง “red zone” รอบหน่วยความจำ จึงจะตรวจพบได้ถ้าโชคดี
    • ถ้าความผิดพลาดด้านหน่วยความจำถูกหยุดด้วย panic ก่อนที่จะก่อผลกระทบ ก็อาจถือว่าเป็น memory safety ได้
      memory safety ไม่ได้หมายถึง “ไม่ crash” แต่หมายถึง “การเข้าถึงที่ผิดพลาดไม่สามารถก่อผลได้”
  • มีคนถามว่าทำไมไม่ใช้ VM เต็มรูปแบบเป็น sandbox ไปเลย

    • VM เป็น sandbox ที่ยอดเยี่ยม แต่แอปอย่าง Chrome หรือ OpenSSH ต้องการ การแยกสิทธิ์
      โปรเซสหนึ่งจะ parse อินพุตโดยไม่มีสิทธิ์ ส่วนอีกโปรเซสทำงานด้วยสิทธิ์สูง
      ทั้งสองโปรเซสสื่อสารกันผ่าน IPC
      ถ้าใช้ VM ความปลอดภัยจะสูงขึ้น แต่ก็มี overhead สูง และความสามารถอย่างการเข้าถึง GPU หรือไฟล์ก็ซับซ้อนขึ้น
      เพราะฉะนั้นโดยทั่วไป sandbox ระดับ OS จึงสะอาดกว่า
    • VM แก้ปัญหาได้เกือบทั้งหมด แต่ การเร่งกราฟิกบนเดสก์ท็อป ยังยากอยู่
      ต้องจัดสรร GPU แบบเฉพาะให้ และแม้แต่ Qubes ก็ยังเชื่อมต่อผ่าน X11 forwarding เท่านั้น จึง ไม่มีการเร่งความเร็ว