ความซับซ้อนที่ซ่อนอยู่ในภาษา C: เมื่อพฤติกรรมที่ไม่ได้กำหนดมาพบกับโค้ดในโลกความเป็นจริง

ทีมบรรณาธิการ BigGo
ความซับซ้อนที่ซ่อนอยู่ในภาษา C: เมื่อพฤติกรรมที่ไม่ได้กำหนดมาพบกับโค้ดในโลกความเป็นจริง

การถกเถียงล่าสุดเกี่ยวกับปัญหาความเข้ากันได้ของ curl กับ Clang's UndefinedBehaviorSanitizer (UBsan) ได้จุดประเด็นการถกเถียงอย่างเข้มข้นในชุมชนนักพัฒนาเกี่ยวกับพฤติกรรมที่ไม่ได้กำหนด (undefined behavior) ของภาษา C และผลกระทบต่อการใช้งานจริง เหตุการณ์นี้สะท้อนให้เห็นความตึงเครียดที่เพิ่มขึ้นระหว่างข้อกำหนดทางทฤษฎีของภาษาและการปฏิบัติในการเขียนโปรแกรมที่ใช้กันมานานหลายทศวรรษ

เหตุการณ์ที่เกิดขึ้น

จุดเริ่มต้นของการถกเถียงนี้มาจากการเปลี่ยนแปลงล่าสุดในโค้ดของ curl ที่ Daniel Haxx ต้องแก้ไขวิธีการกำหนดประเภท CURL เนื่องจาก UBsan แจ้งเตือนว่าการไม่ตรงกันของประเภทตัวชี้ฟังก์ชันเป็นพฤติกรรมที่ไม่ได้กำหนด สิ่งที่ดูเหมือนจะเป็นรูปแบบการใช้งานที่สมเหตุสมผล - การใช้การกำหนดประเภทที่แตกต่างกันสำหรับโค้ดภายในและภายนอก - กลับกลายเป็นสิ่งที่ไม่ถูกต้องทางเทคนิคตามมาตรฐานของภาษา C

ทำความเข้าใจปัญหาทางเทคนิค

ปัญหาหลักเกี่ยวข้องกับความเข้ากันได้ของตัวชี้ฟังก์ชันและการแปลงประเภท แม้ว่านักเขียนโปรแกรม C จำนวนมากจะเชื่อว่าตัวชี้ประเภทต่างๆ (เช่น void* และ char*) สามารถใช้แทนกันได้อย่างอิสระ แต่มาตรฐาน C จริงๆ แล้วถือว่าการเรียกฟังก์ชันผ่านตัวชี้ที่มีประเภทต่างกันเป็นพฤติกรรมที่ไม่ได้กำหนด

ผลกระทบต่อแพลตฟอร์ม

นักพัฒนาหลายคนในชุมชนได้ชี้ให้เห็นว่านี่ไม่ใช่แค่ความกังวลทางทฤษฎี:

  • Pointer Authentication : แพลตฟอร์มสมัยใหม่ที่มีการตรวจสอบความถูกต้องของตัวชี้อาจเข้ารหัสประเภทฟังก์ชันลงในตัวชี้เอง
  • CHERI Architecture : ระบบที่ใช้ CHERI สามารถบังคับใช้การตรวจสอบประเภทตัวชี้อย่างเข้มงวด
  • Emscripten : แพลตฟอร์มนี้จะทำงานผิดพลาดอย่างชัดเจนเมื่อใช้การแปลงตัวชี้ฟังก์ชันที่ไม่เข้ากัน

บริบทที่กว้างขึ้น

สถานการณ์นี้แสดงให้เห็นการถกเถียงที่ใหญ่ขึ้นในชุมชนผู้เขียนโปรแกรมภาษา C:

  1. มาตรฐานกับการปฏิบัติ : รูปแบบการเขียนโปรแกรมที่ใช้งานได้อย่างน่าเชื่อถือในทางปฏิบัติหลายอย่าง ทางเทคนิคแล้วถือเป็นพฤติกรรมที่ไม่ได้กำหนดตามมาตรฐาน C
  2. วิวัฒนาการของฮาร์ดแวร์ : แม้ว่า C จะได้รับการยกย่องว่าใกล้ชิดกับฮาร์ดแวร์ แต่ฟีเจอร์ฮาร์ดแวร์สมัยใหม่และกลไกความปลอดภัยกำลังทำให้การปฏิบัติแบบดั้งเดิมของ C มีปัญหามากขึ้น
  3. การปรับปรุงประสิทธิภาพของคอมไพเลอร์ : โค้ดที่เคยถือว่าปลอดภัยและน่าเชื่อถืออาจทำงานผิดพลาดเมื่อการปรับปรุงประสิทธิภาพของคอมไพเลอร์ใช้ประโยชน์จากพฤติกรรมที่ไม่ได้กำหนดมากขึ้น

ก้าวต่อไป

ชุมชนได้เสนอแนวทางแก้ไขหลายประการ:

  • การใช้ฟังก์ชัน trampoline เพื่อจัดการการแปลงประเภทอย่างปลอดภัย
  • การเพิ่มหมวดหมู่ใหม่ของการกำหนดพฤติกรรมในมาตรฐาน C
  • การใช้การตรวจสอบเฉพาะแพลตฟอร์มด้วยตัวเลือกของคอมไพเลอร์

ผลกระทบต่อนักพัฒนา

เหตุการณ์นี้เตือนให้นักพัฒนาที่มีประสบการณ์ต้องระมัดระวังเกี่ยวกับข้อสันนิษฐานเกี่ยวกับพฤติกรรมของ C ตามที่สมาชิกชุมชนหลายคนได้กล่าวไว้ เครื่องมืออย่าง UBsan กำลังมีความสำคัญมากขึ้นในการระบุปัญหาที่อาจเกิดขึ้นก่อนที่จะสร้างปัญหาในการใช้งานจริง

การถกเถียงนี้ยังชี้ให้เห็นถึงความต้องการที่เพิ่มขึ้นสำหรับภาษาโปรแกรมสมัยใหม่ที่สามารถให้ทั้งการควบคุมระดับต่ำและการรับประกันความปลอดภัยที่แข็งแกร่ง โดยหลายคนชี้ไปที่ Rust เป็นตัวอย่างของวิธีการรักษาสมดุลนี้

การแก้ไขของ Daniel Haxx สำหรับ curl แสดงให้เห็นถึงการประนีประนอมในทางปฏิบัติ โดยกลับไปใช้การกำหนดประเภทที่ง่ายขึ้นเพื่อให้แน่ใจว่าเข้ากันได้กับทุกแพลตฟอร์ม แม้จะต้องเสียสละความชัดเจนของโค้ดภายในบางส่วน

สถานการณ์นี้ยังคงพัฒนาต่อไปในขณะที่ชุมชน C พยายามรักษาสมดุลระหว่างความเข้ากันได้กับรุ่นเก่า ความปลอดภัย และความสามารถของฮาร์ดแวร์สมัยใหม่