บล็อกโพสต์ล่าสุดที่สนับสนุนแนวทาง parse, don't validate ในการเขียนโปรแกรม C ได้จุดประกายการอย่างเข้มข้นในหมู่นักพัฒนาเกี่ยวกับความปลอดภัยของหน่วยความจำ การกำหนดรูปแบบประเภทข้อมูล และความท้าทายในการนำไปใช้งานจริง เทคนิคนี้ซึ่งมีต้นกำเนิดจาก functional programming มีเป้าหมายเพื่อลดช่องโหว่ด้านความปลอดภัยโดยการแปลงข้อมูลที่ไม่น่าเชื่อถือให้เป็นประเภทข้อมูลเฉพาะเจาะจงครั้งเดียวที่ขอบเขตของระบบ แทนที่จะตรวจสอบความถูกต้องของข้อมูลดิบซ้ำแล้วซ้ำเล่าตลอดทั้งโค้ดเบส
แนวคิดหลักนี้เกี่ยวข้องกับการสร้างประเภทข้อมูลแบบกำหนดเองเช่น email_t
และ name_t
แทนที่จะใช้ตัวชี้ char*
ทั่วไปทุกที่ แนวทางนี้สัญญาว่าจะขจัดข้อผิดพลาดทั้งหมดในหลายประเภทโดยให้คอมไพเลอร์บังคับใช้ความปลอดภัยของประเภทข้อมูลและป้องกันการสับสนพารามิเตอร์
แนวทางเทคนิคหลักที่กล่าวถึง:
- การสร้างประเภทข้อมูลแบบกำหนดเองโดยใช้ opaque structs (
email_t
,name_t
) - การตรวจสอบความถูกต้องเฉพาะที่ขอบเขตระบบ โดย
char*
ถูกจำกัดให้ใช้เฉพาะที่ขอบของระบบ - การจัดการหน่วยความจำผ่าน buffer ที่จัดสรรโดยผู้เรียกใช้
- การใช้งาน newtype pattern เพื่อความปลอดภัยของประเภทข้อมูล
- การจัดการข้อผิดพลาดผ่านการคืนค่า NULL pointer เทียบกับสถานะข้อผิดพลาดที่ฝังอยู่ภายใน
ความขัดแย้งเรื่องการตั้งชื่อแบ่งแยกชุมชน
การอภิปรายได้เน้นไปที่ความไม่เห็นด้วยพื้นฐานเกี่ยวกับการกำหนดรูปแบบการตั้งชื่อใน C อย่างรวดเร็ว การใช้คำต่อท้าย _t
สำหรับประเภทข้อมูลแบบกำหนดเองในบทความต้นฉบับได้รับการวิพากษ์วิจารณ์อย่างรุนแรงจากนักพัฒนาบางคนที่โต้แย้งว่าสิ่งนี้ละเมิดมาตรฐานการตั้งชื่อที่สงวนไว้ นักวิจารณ์ชี้ให้เห็นว่าคำต่อท้าย _t
ถูกสงวนไว้โดยมาตรฐาน POSIX และอาจนำไปสู่ความขัดแย้งในการตั้งชื่อในอนาคต
อย่างไรก็ตาม สมาชิกชุมชนคนอื่นๆ ได้โต้แย้งกลับต่อความกังวลนี้ พวกเขาโต้แย้งว่า POSIX และ C เป็นมาตรฐานที่แยกจากกัน และคำนำหน้าเนมสเปซสามารถป้องกันการชนกันได้อย่างง่ายดาย การถกเถียงนี้เผยให้เห็นความตึงเครียดที่ลึกซึ้งกว่าเกี่ยวกับมาตรฐานการเขียนโค้ดและว่าความขัดแย้งในอนาคตที่เป็นเพียงทฤษฎีควรกำหนดแนวปฏิบัติปัจจุบันหรือไม่
การตรวจสอบความเป็นจริงของการจัดการหน่วยความจำ
การแลกเปลี่ยนความคิดเห็นที่เข้มข้นที่สุดเน้นไปที่การอ้างสิทธิ์ของบทความเกี่ยวกับการป้องกันข้อผิดพลาด double-free โพสต์ต้นฉบับแนะนำว่าการตั้งค่าตัวชี้เป็น NULL หลังจากปลดปล่อยหน่วยความจำจะแก้ไขช่องโหว่ C ทั่วไปนี้ได้ การตอบสนองของชุมชนเป็นไปอย่างรวดเร็วและวิพากษ์วิจารณ์
การอ้างสิทธิ์เรื่องการป้องกัน double free ทั้งหมดเป็นเรื่องเท็จอย่างสิ้นเชิง การตั้งค่าตัวแปรเป็น NULL ใช้ได้เฉพาะกรณีที่มีเจ้าของเพียงคนเดียวที่ชัดเจน ซึ่งไม่ใช่สถานการณ์ที่ double free มักจะเกิดขึ้นตั้งแต่แรก
การวิพากษ์วิจารณ์นี้เน้นย้ำถึงความท้าทายพื้นฐานในการเขียนโปรแกรม C: ตัวชี้หลายตัวสามารถอ้างอิงไปยังตำแหน่งหน่วยความจำเดียวกันได้ และการตั้งค่าตัวหนึ่งเป็น NULL ไม่ส่งผลต่อตัวอื่นๆ ฉันทามติของชุมชนแนะนำว่าความปลอดภัยของหน่วยความจำที่แท้จริงต้องการการออกแบบความเป็นเจ้าของอย่างระมัดระวัง ซึ่งเป็นสิ่งที่ C ต่อสู้ด้วยโดยธรรมชาติ
ข้อจำกัดที่ระบุโดยชุมชน:
- การใช้ส่วนต่อท้าย
_t
ขัดแย้งกับแนวทางการตั้งชื่อที่สงวนไว้ของ POSIX - การอ้างว่าป้องกันการ free หน่วยความจำซ้ำทำงานได้เฉพาะในสถานการณ์ที่มีเจ้าของเพียงรายเดียว
- ความท้าทายด้านการใช้งานจริงเมื่อต้องแปลงประเภทข้อมูลกลับเป็นสตริง
- ข้อจำกัดของระบบประเภทข้อมูลของ C ที่ต้องการการตรวจสอบข้อผิดพลาดด้วยตนเอง
- ความซับซ้อนของการจัดสรรหน่วยความจำสำหรับฟังก์ชันแปลงสตริง
อุปสรรคในการนำไปใช้งานจริง
นักพัฒนายังแสดงความกังวลเกี่ยวกับความท้าทายในทางปฏิบัติของการนำแนวทางนี้ไปใช้ ปัญหาหลักเน้นไปที่วิธีการใช้ประเภทข้อมูลแบบกำหนดเองเหล่านี้จริงๆ เมื่อสร้างขึ้นแล้ว การแปลง email_t
กลับเป็นสตริงที่พิมพ์ได้ต้องการฟังก์ชันเพิ่มเติมและการตัดสินใจจัดการหน่วยความจำอย่างระมัดระวัง
บางคนแนะนำว่าผู้เรียกควรจัดสรรหน่วยความจำสำหรับการแปลงสตริง คล้ายกับวิธีที่ strncpy
ทำงาน คนอื่นๆ เสนอให้ทำประเภทข้อมูลแบบห่อหุ้มให้โปร่งใสน้อยลงเพื่อรักษาความสามารถในการใช้งานในขณะที่ยังคงได้รับประโยชน์จากความปลอดภัยของประเภทข้อมูล การอภิปรายเผยให้เห็นความตึงเครียดอย่างต่อเนื่องระหว่างความปลอดภัยและความเป็นจริงในการเขียนโปรแกรม C
ประโยชน์ด้านความปลอดภัยหลักที่อ้างว่ามี:
- ความปลอดภัยของประเภทข้อมูลที่บังคับใช้โดย compiler เพื่อป้องกันการสลับพารามิเตอร์
- ลดพื้นผิวการโจมตีโดยการกำจัดข้อมูลนำเข้าที่ไม่ได้รับการตรวจสอบในฟังก์ชันหลัก
- การห่อหุ้ม raw
char*
strings ณ ขอบเขตของระบบ - การแยกวิเคราะห์จุดเดียวที่กำจัดการตรวจสอบความถูกต้องซ้ำซ้อนตลอดทั้ง codebase
ข้อจำกัดของระบบประเภทข้อมูลถูกเปิดเผย
การถกเถียงทางเทคนิคเฉพาะเจาะจงเกิดขึ้นรอบการจัดการข้อผิดพลาดในแนวทางการแปลง นักวิจารณ์สังเกตว่าการส่งคืนทั้งอีเมลที่ถูกต้องและข้อผิดพลาดในการแปลงจากฟังก์ชันเดียวกันละเมิดหลักการหลักของ parse, don't validate หาก email_t
สามารถแสดงทั้งสถานะที่ถูกต้องและไม่ถูกต้อง นักพัฒนายังคงต้องจำที่จะตรวจสอบข้อผิดพลาด - ซึ่งโดยพื้นฐานแล้วนำปัญหาการตรวจสอบความถูกต้องที่แนวทางนี้ตั้งใจจะแก้ไขกลับมา
การวิพากษ์วิจารณ์นี้โจมตีหัวใจของข้อจำกัดระบบประเภทข้อมูลของ C ซึ่งแตกต่างจากภาษาที่มีกลไกการจัดการข้อผิดพลาดที่ซับซ้อน โปรแกรมเมอร์ C มักจะใช้ค่า sentinel หรือรหัสส่งคืนพิเศษ ซึ่งสามารถทำลายประโยชน์ด้านความปลอดภัยของประเภทข้อมูลที่แนวทางนี้สัญญาไว้
การถกเถียงแสดงให้เห็นความท้าทายที่กว้างขึ้นที่นักพัฒนา C เผชิญที่ต้องการเขียนโค้ดที่ปลอดภัยกว่าในขณะที่ทำงานภายในข้อจำกัดของภาษา แม้ว่าแนวคิด parse, don't validate จะให้ประโยชน์ในทางทฤษฎี แต่การนำไปใช้อย่างมีประสิทธิภาพใน C ต้องการการพิจารณาอย่างระมัดระวังต่อข้อจำกัดพื้นฐานของภาษาและการแลกเปลี่ยนในทางปฏิบัติ