1 คะแนน โดย GN⁺ 2024-09-30 | 1 ความคิดเห็น | แชร์ทาง WhatsApp

ความเสี่ยงของการเปลี่ยนไปใช้ time_t แบบ 64 บิต

  • การใช้ชนิด time_t แบบ 32 บิตอาจทำให้แอปพลิเคชัน 32 บิตเกิดข้อผิดพลาดในปี 2038
  • มีการเสนอให้เปลี่ยน time_t เป็นชนิด 64 บิตเพื่อแก้ปัญหา
  • Musl เปลี่ยนผ่านเสร็จแล้ว, glibc รองรับแบบเป็นตัวเลือก, และดิสทริบิวชันหลายราย เช่น Debian ก็เปลี่ยนผ่านเสร็จแล้ว
  • ดิสทริบิวชันแบบ source-based เช่น Gentoo เปลี่ยนผ่านได้ยาก

ย้อนกลับไปที่ Large File Support

  • สถาปัตยกรรม 32 บิตใช้ off_t สำหรับระบุ file offset และ ino_t สำหรับระบุหมายเลข inode ในรูปแบบ 32 บิต
  • ส่งผลให้ไม่สามารถเปิดไฟล์ที่มีขนาดเกิน 2 GiB ได้ และไม่สามารถเปิดไฟล์ที่มีหมายเลข inode เกินช่วง 32 บิตได้
  • การนำ Large File Support มาใช้ช่วยแก้ปัญหานี้ โดยใน glibc ยังคงเป็นตัวเลือก
  • การรองรับ time64 จำเป็นต้องใช้ LFS

ใช้ ABI แบบใด?

  • มี sub-ABI ที่เป็นไปได้ 3 แบบ:
    1. ABI ดั้งเดิมที่ใช้ชนิด 32 บิต
    2. LFS ที่ใช้ off_t และ ino_t แบบ 64 บิต แต่ใช้ time_t แบบ 32 บิต
    3. time64 ที่ใช้ LFS + time_t แบบ 64 บิต
  • การบิลด์ glibc สามารถเข้ากันได้กับทั้ง 3 แบบ แต่ไลบรารีที่ใช้ชนิดเหล่านี้ใน API จะไม่เข้ากัน

ทำไมการเปลี่ยน ABI ถึงเป็นเรื่องไม่ดี?

  • การแทนที่ชนิด 32 บิตด้วยชนิด 64 บิตทำให้ความเข้ากันได้พัง
  • ในกรณีของ struct หากมี time_t อยู่ภายใน struct ตำแหน่งของฟิลด์จะเปลี่ยนไป ทำให้อ่านหรือเขียนฟิลด์ผิดได้
  • ในกรณีของพารามิเตอร์ฟังก์ชัน ตำแหน่งของพารามิเตอร์ที่ส่งผ่านบนสแตกจะเปลี่ยนไป ทำให้อ่านหรือเขียนพารามิเตอร์ผิดได้
  • ปัญหาเหล่านี้อาจนำไปสู่ข้อผิดพลาดขณะรันไทม์และปัญหาด้านความปลอดภัย

จะทำให้ปลอดภัยได้อย่างไร?

  • มี 3 แนวคิด:
    1. เปลี่ยน platform tuple (CHOST) เพื่อแยก ABI ใหม่
    2. เปลี่ยน libdir สำหรับ ABI ใหม่
    3. เพิ่มการแยก ABI ในระดับไบนารี เพื่อป้องกันไม่ให้ไบนารีที่ใช้ sub-ABI ต่างกันถูกลิงก์เข้าด้วยกัน

การเปลี่ยน platform tuple

  • platform tuple ใช้ระบุแพลตฟอร์มที่ toolchain ตั้งเป้าไว้
  • หากต้องการนำ ABI ใหม่มาใช้ สามารถเปลี่ยนฟิลด์ vendor หรือเพิ่มข้อกำหนด ABI เพิ่มเติมในฟิลด์ libc
  • ตัวอย่าง: i686-gentoo_t64-linux-gnu, i686-pc-linux-gnut64

การเปลี่ยน libdir

  • libdir คือชื่อพื้นฐานของไดเรกทอรีติดตั้งไลบรารี
  • สำหรับ time64 สามารถเปลี่ยนค่า libdir เพื่อติดตั้งไลบรารี time64 ลงใน libdir ใหม่
  • วิธีนี้ช่วยป้องกันไม่ให้ไฟล์ปฏิบัติการ time64 ไปเชื่อมลิงก์กับไลบรารี time32
  • ใช้ฟีเจอร์ preserved-libs ของ Portage เพื่อเก็บรักษาไลบรารีเดิมไว้

การรับประกันความเข้ากันได้ของไบนารี

  • ไม่สามารถผสมไบนารีที่ใช้ ABI ต่างกันได้
  • ใช้ ELF class, machine identifier, flag field ฯลฯ เพื่อตรวจสอบความเข้ากันได้
  • มีการพิจารณาเพิ่ม ELF note section ใหม่เพื่อแยกระบบ time32 และ time64

แอปพลิเคชันแบบ prebuilt รุ่นเก่า

  • แอปพลิเคชันแบบ prebuilt รุ่นเก่าจะเจอทั้งปัญหาความเข้ากันได้กับไลบรารีของระบบและปัญหา y2k38
  • สามารถแก้ปัญหาความเข้ากันได้ด้วยการใช้เลย์เอาต์แบบ multilib
  • สำหรับปัญหา y2k38 อาจแก้ได้ด้วยการปรับเวลาในระบบหรือใช้ VM

สรุปโดย GN⁺

  • หลังปี 2038 แอปพลิเคชันที่ใช้ time_t แบบ 32 บิตอาจเกิดข้อผิดพลาดได้
  • จำเป็นต้องเปลี่ยนไปใช้ time_t แบบ 64 บิต แต่การทำเช่นนั้นมาพร้อมกับการเปลี่ยน ABI และก่อให้เกิดปัญหาที่ซับซ้อน
  • สามารถจัดทำเส้นทางการเปลี่ยนผ่านอย่างปลอดภัยได้ด้วยการเปลี่ยน platform tuple, เปลี่ยน libdir และรับประกันความเข้ากันได้ของไบนารี
  • แอปพลิเคชันแบบ prebuilt รุ่นเก่ายังต้องแก้ปัญหาความเข้ากันได้เฉพาะทางและปัญหา y2k38 แยกต่างหาก

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

 
GN⁺ 2024-09-30
ความคิดเห็นจาก Hacker News
  • Gentoo มีตัวเลือกสำหรับคอมไพล์โดยไม่ติดตั้งแพ็กเกจค่อนข้างน้อย

    • Gentoo ทำขั้นตอนคอมไพล์และติดตั้งแพ็กเกจรวมเป็นขั้นตอนเดียว
    • เมื่อมีการเปลี่ยนแปลง ABI ระบบอาจพังได้ง่ายระหว่างการอัปเดต
    • ปัญหา time_t แบบ 64 บิตเป็นตัวอย่างของการเปลี่ยน ABI ที่เป็นที่รู้จักกันอย่างกว้างขวาง
  • วิธีจัดการการเปลี่ยน ABI ผ่านการจัดการเวอร์ชันของ .so

    • ไฟล์ .so มีหมายเลขเวอร์ชันรวมอยู่ด้วย
    • ตัวแพ็กเกจเองก็จัดการหมายเลขเวอร์ชันภายในอยู่แล้ว
    • หากต้องการรองรับ time_t แบบ 64 บิต จำเป็นต้องมีองค์ประกอบเพิ่มเติมที่สามารถควบคุม ABI ที่สืบทอดมาได้
  • วิธีที่ Mac OS X จัดการกับ off_t และ ino_t

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

    • ดิสโทรแบบ source-based ต้องเผชิญกระบวนการเปลี่ยนผ่านที่ยากกว่า
  • ประสบการณ์การแทนที่ time_t บนระบบยูนิกซ์ 32 บิตด้วย unsigned 32 บิต

    • ทำให้ใช้งานต่อได้อีก 68 ปีหลังปี 2038
    • แต่จะไม่สามารถแสดงวันที่ก่อน Unix epoch ได้
  • ประสบการณ์การนำ time_t แบบ 64 บิตมาใช้ตอนทำพอร์ต amd64 ของ FreeBSD

    • อาร์กิวเมนต์ฟังก์ชันแบบ 32 บิตถูกแปลงเป็น 64 บิตโดยอัตโนมัติ
    • ใช้ time_t แบบ 64 บิตตั้งแต่แรกจึงหลีกเลี่ยงปัญหาได้
    • แต่ก็พบปัญหาบางอย่างเพราะ tzcode ยังไม่ปลอดภัยกับ 64 บิต
  • มุกตลกในส่วน "Bugs" ของหน้าแมนนวล BSD

    • "You can tune a file system, but you can't tune a fish."
  • มีความเห็นว่าอยากย้ายจากดิสโทรแบบ source-based ไปใช้ดิสโทรที่ไม่ใช่ source-based อย่าง Debian

  • ความต่างของออฟเซ็ตในโครงสร้างระหว่าง time_t แบบ 32 บิตและ 64 บิต

    • ในชนิดข้อมูลแบบ 64 บิต b ต้องการการจัดแนว 64 บิต จึงมีการเพิ่ม padding
  • เคยคิดว่าการใช้ type alias ใน C เปิดโอกาสให้เปลี่ยนได้ภายหลัง แต่ในความเป็นจริงไม่ได้เป็นเช่นนั้น

  • มีความเห็นว่าควรรีบแก้ปัญหานี้ให้เร็ว

    • OpenBSD ใช้ time_t แบบ 64 บิตบนทุกสถาปัตยกรรม