ใช่ ISO C ++ อนุญาตให้ใช้งาน (แต่ไม่ต้องการ) เพื่อเลือกตัวเลือกนี้
แต่โปรดทราบว่า ISO C ++ อนุญาตให้คอมไพเลอร์ปล่อยโค้ดที่ผิดพลาดตามวัตถุประสงค์ (เช่นคำสั่งที่ผิดกฎหมาย) หากโปรแกรมพบ UB เช่นเป็นวิธีที่จะช่วยคุณค้นหาข้อผิดพลาด (หรือเพราะเป็น DeathStation 9000 การปฏิบัติตามอย่างเคร่งครัดนั้นไม่เพียงพอสำหรับการใช้งาน C ++ เพื่อเป็นประโยชน์สำหรับวัตถุประสงค์ที่แท้จริง) ดังนั้น ISO C ++ จะอนุญาตให้คอมไพเลอร์สร้าง asm ที่ล้มเหลว (ด้วยเหตุผลที่ต่างกันโดยสิ้นเชิง) แม้ในรหัสที่คล้ายกันที่อ่านค่าuint32_t
เริ่มต้น แม้ว่าสิ่งนั้นจะต้องเป็นประเภทเลย์เอาต์คงที่โดยไม่มีการดักจับ
เป็นคำถามที่น่าสนใจเกี่ยวกับการใช้งานจริง แต่จำไว้ว่าแม้ว่าคำตอบจะแตกต่างกันรหัสของคุณก็ยังไม่ปลอดภัยเพราะ C ++ ที่ทันสมัยไม่ใช่ภาษาแอสเซมบลีรุ่นพกพา
คุณกำลังรวบรวมสำหรับx86-64 System V ABIซึ่งระบุว่าbool
เป็นหาเรื่องฟังก์ชั่นในการลงทะเบียนเป็นตัวแทนจากบิตรูปแบบ- false=0
และtrue=1
ในต่ำ 8 บิตของการลงทะเบียน1 ในหน่วยความจำbool
คือชนิด 1 ไบต์ที่ต้องมีค่าจำนวนเต็มเป็น 0 หรือ 1 อีกครั้ง
(ABI เป็นชุดของตัวเลือกการใช้งานที่คอมไพเลอร์สำหรับแพลตฟอร์มเดียวกันเห็นด้วยเพื่อให้พวกเขาสามารถสร้างรหัสที่เรียกใช้ฟังก์ชั่นซึ่งกันและกันรวมถึงขนาดประเภทกฎการวางเลย์เอาต์ของโครงสร้างและการประชุมที่เรียก)
ISO C ++ ไม่ได้ระบุ แต่การตัดสินใจ ABI เป็นที่แพร่หลายเพราะมันทำให้ bool-> int แปลงราคาถูก (ศูนย์เพียงส่วนขยาย) ฉันไม่ทราบ ABIs ใด ๆ ที่ไม่อนุญาตให้คอมไพเลอร์มีค่าเป็น 0 หรือ 1 สำหรับbool
สถาปัตยกรรมใด ๆ (ไม่ใช่แค่ x86) จะช่วยเพิ่มประสิทธิภาพเช่น!mybool
มีxor eax,1
การพลิกบิตต่ำ: รหัสที่เป็นไปได้ใด ๆ ที่สามารถพลิกบิต / จำนวนเต็ม / บูลระหว่าง 0 และ 1 ในการเรียนการสอนของ หรือรวบรวมa&&b
เป็นบิตและสำหรับbool
ประเภท คอมไพเลอร์บางตัวใช้ประโยชน์จากค่าบูลีนเป็น 8 บิตในคอมไพเลอร์ การดำเนินการกับพวกเขาไม่มีประสิทธิภาพหรือไม่ .
โดยทั่วไปกฎ as-if อนุญาตให้คอมไพเลอร์ใช้ประโยชน์จากสิ่งที่เป็นจริงบนแพลตฟอร์มเป้าหมายที่กำลังรวบรวมเพราะผลลัพธ์สุดท้ายจะเป็นโค้ดที่สามารถเรียกทำงานได้ซึ่งใช้ลักษณะการทำงานภายนอกที่มองเห็นได้เช่นเดียวกับซอร์ส C ++ (ด้วยข้อ จำกัด ทั้งหมดที่ไม่ได้กำหนดพฤติกรรมที่วางอยู่บนสิ่งที่เป็นจริง "มองเห็นภายนอก": ไม่ได้กับดีบักเกอร์ แต่จากหัวข้ออื่นในโปรแกรม C ++ ที่มีรูปแบบถูกต้อง / ถูกกฎหมาย)
คอมไพเลอร์ที่ได้รับอนุญาตอย่างแน่นอนที่จะใช้ประโยชน์จากการรับประกัน ABI ในของรหัส-Gen และรหัสให้เหมือนที่คุณพบที่เพิ่มประสิทธิภาพในการstrlen(whichString)
5U - boolValue
(BTW การเพิ่มประสิทธิภาพนี้เป็นวิธีที่ชาญฉลาด แต่อาจขาดความชัดเจนเมื่อเทียบกับการแตกแขนงและอินไลน์memcpy
เป็นร้านค้าของข้อมูลทันที2 )
หรือคอมไพเลอร์อาจสร้างตารางพอยน์เตอร์และทำดัชนีด้วยค่าจำนวนเต็มของbool
, อีกครั้งโดยสมมติว่าเป็น 0 หรือ 1 ( ความเป็นไปได้นี้คือสิ่งที่คำตอบของ @ Barmar แนะนำ )
คุณคอนสตรัคด้วยการเพิ่มประสิทธิภาพทำงานนำไปสู่เสียงดังกราวเพียงแค่โหลดไบต์จากสแต็คที่จะใช้เป็น__attribute((noinline))
uninitializedBool
มันทำให้พื้นที่สำหรับวัตถุในmain
ด้วยpush rax
(ซึ่งมีขนาดเล็กและด้วยเหตุผลต่าง ๆ เกี่ยวกับการเป็นที่มีประสิทธิภาพsub rsp, 8
) ดังนั้นสิ่งที่เป็นขยะในอัลในการเข้าเป็นค่าที่มันใช้สำหรับmain
นี่คือเหตุผลที่คุณจริงมีค่าที่ไม่ได้เป็นเพียงแค่uninitializedBool
0
5U - random garbage
สามารถห่อค่าที่ไม่ได้ลงนามจำนวนมากได้อย่างง่ายดายนำ memcpy ไปยังหน่วยความจำที่ไม่ได้แมป ปลายทางอยู่ในที่จัดเก็บข้อมูลแบบสแตติกไม่ใช่สแต็กดังนั้นคุณจึงไม่เขียนทับที่อยู่ผู้ส่งคืนหรือสิ่งของ
การใช้งานอื่น ๆ สามารถสร้างทางเลือกที่แตกต่างกันเช่นและfalse=0
true=any non-zero value
จากนั้นเสียงดังกราวอาจจะไม่ให้รหัสที่เกิดปัญหาสำหรับนี้อินสแตนซ์ที่เฉพาะเจาะจงของ UB (แต่มันจะยังคงได้รับอนุญาตหากต้องการ) ฉันไม่รู้เกี่ยวกับการใช้งานที่เลือกสิ่งอื่นใดที่ x86-64 ทำbool
แต่มาตรฐาน C ++ อนุญาตให้มีหลายสิ่งที่ไม่มีใครทำหรืออยากทำ ฮาร์ดแวร์ที่ไม่เหมือนกับซีพียูปัจจุบัน
ISO c ++ bool
ใบมันไม่ได้ระบุสิ่งที่คุณจะพบว่าเมื่อคุณตรวจสอบหรือปรับเปลี่ยนการแสดงวัตถุของ (เช่นโดยmemcpy
การbool
เข้าไปในunsigned char
ซึ่งคุณได้รับอนุญาตให้ทำเพราะchar*
สามารถนามแฝงอะไรและunsigned char
รับประกันว่าจะไม่มีบิตแพ็ดดังนั้นมาตรฐาน C ++ อย่างเป็นทางการช่วยให้คุณเป็นตัวแทนวัตถุ hexdump โดยไม่ต้อง UB ใด ๆ ชี้หล่อเพื่อคัดลอกวัตถุ การเป็นตัวแทนแตกต่างจากการกำหนดchar foo = my_bool
แน่นอนดังนั้นการบูลีนถึง 0 หรือ 1 จะไม่เกิดขึ้นและคุณจะได้รับการแสดงวัตถุดิบ)
คุณได้บางส่วน "ซ่อน" UB noinline
บนเส้นทางการดำเนินการนี้จากคอมไพเลอร์ที่มี แม้ว่ามันจะไม่อินไลน์ แต่การเพิ่มประสิทธิภาพระหว่างโพรซีเดอร์ยังสามารถสร้างเวอร์ชันของฟังก์ชันที่ขึ้นอยู่กับนิยามของฟังก์ชันอื่น (ขั้นแรกเสียงดังกราวกำลังทำให้เรียกใช้งานได้ไม่ใช่ไลบรารีแบบแบ่งใช้ของ Unix ที่สามารถเกิดการแทรกสอดของสัญลักษณ์ได้ประการที่สองความหมายภายในclass{}
คำจำกัดความดังนั้นหน่วยการแปลทั้งหมดจะต้องมีคำจำกัดความเหมือนinline
กัน
ดังนั้นคอมไพเลอร์สามารถเปล่งเพียงret
หรือud2
(คำสั่งที่ผิดกฎหมาย) เป็นคำนิยามสำหรับmain
เพราะเส้นทางของการดำเนินการเริ่มต้นที่ด้านบนของการmain
เผชิญหน้าพฤติกรรมที่ไม่ได้กำหนดอย่างหลีกเลี่ยงไม่ได้ (ซึ่งคอมไพเลอร์สามารถดูเวลาคอมไพล์ได้หากตัดสินใจติดตามพา ธ ผ่านคอนสตรัคเตอร์ที่ไม่ใช่แบบอินไลน์)
โปรแกรมใด ๆ ที่พบ UB นั้นไม่ได้ถูกกำหนดอย่างสมบูรณ์สำหรับการมีอยู่ทั้งหมด แต่ UB ภายในฟังก์ชั่นหรือif()
สาขาที่ไม่เคยทำงานจริง ๆ จะไม่ทำให้โปรแกรมที่เหลือเสียหาย ในทางปฏิบัติซึ่งหมายความว่าคอมไพเลอร์สามารถตัดสินใจที่จะปล่อยคำสั่งที่ผิดกฎหมายหรือ a ret
หรือไม่ปล่อยสิ่งใดและตกอยู่ในบล็อก / ฟังก์ชั่นถัดไปสำหรับบล็อกพื้นฐานทั้งหมดที่สามารถพิสูจน์ได้ในเวลารวบรวมเพื่อนำไปสู่
ในทางปฏิบัติแล้วGCC และ Clang นั้นบางครั้งจะปล่อยud2
UB แทนที่จะพยายามสร้างรหัสสำหรับเส้นทางของการดำเนินการที่ไม่มีเหตุผล หรือสำหรับกรณีเช่นการล้มจุดสิ้นสุดของvoid
ฟังก์ชันที่ไม่ใช่ฟังก์ชันบางครั้ง gcc จะละเว้นret
คำสั่ง หากคุณคิดว่า "ฟังก์ชั่นของฉันจะกลับมาพร้อมกับขยะที่อยู่ใน RAX" คุณเข้าใจผิดอย่างมาก คอมไพเลอร์ C ++ สมัยใหม่ไม่รักษาภาษาเหมือนภาษาแอสเซมบลีแบบพกพาอีกต่อไป โปรแกรมของคุณจะต้องเป็นภาษา C ++ ที่ถูกต้องโดยไม่มีการตั้งสมมติฐานว่าฟังก์ชันของคุณในเวอร์ชันสแตนด์อะโลนอาจไม่ได้อยู่ในรูปแบบเดียว
อีกตัวอย่างที่สนุกคือเหตุใดการเข้าถึงหน่วยความจำ mmap'ed แบบไม่กำหนดแนวบางครั้งจึงแยก segfault บน AMD64 . x86 ไม่ผิดกับจำนวนเต็มที่ไม่ได้จัดใช่ไหม? เหตุใดจึงมีการจัดแนวที่uint16_t*
ไม่ตรงเป็นปัญหา เพราะalignof(uint16_t) == 2
และการละเมิดสมมติฐานนั้นนำไปสู่ segfault เมื่อ auto-vectorizing กับ SSE2
ดู สิ่งที่โปรแกรมเมอร์ C ทุกคนควรรู้เกี่ยวกับพฤติกรรมที่ไม่ได้กำหนด # 1/3ซึ่งเป็นบทความโดยนักพัฒนาเสียงดังกราว
จุดสำคัญ: ถ้าคอมไพเลอร์สังเกตเห็น UB ที่รวบรวมเวลาก็อาจจะ "หยุด" (ปล่อย asm น่าแปลกใจ) เส้นทางรหัสผ่านของคุณที่สาเหตุ UB แม้ว่ากำหนดเป้าหมาย ABI ใด ๆ bool
ที่บิตรูปแบบเป็นตัวแทนวัตถุที่ถูกต้องสำหรับ
คาดหวังความเป็นศัตรูโดยรวมต่อความผิดพลาดจำนวนมากโดยโปรแกรมเมอร์โดยเฉพาะสิ่งที่คอมไพเลอร์สมัยใหม่เตือน นี่คือเหตุผลที่คุณควรใช้-Wall
และแก้ไขคำเตือน C ++ ไม่ใช่ภาษาที่ใช้ง่ายและบางสิ่งใน C ++ อาจไม่ปลอดภัยแม้ว่ามันจะปลอดภัยใน asm บนเป้าหมายที่คุณกำลังรวบรวม (เช่นล้นลงนามคือ UB ใน C ++ และคอมไพเลอร์จะคิดว่ามันจะไม่เกิดขึ้นแม้ว่าจะรวบรวมคอมไพล์ของ x86 2 ตัวยกเว้นว่าคุณใช้clang/gcc -fwrapv
)
การคอมไพล์เวลาที่เห็นได้ของ UB นั้นอันตรายเสมอและเป็นเรื่องยากที่จะแน่ใจ (ด้วยการเพิ่มประสิทธิภาพลิงค์เวลา) ว่าคุณได้ซ่อน UB จากคอมไพเลอร์จริงๆและสามารถให้เหตุผลเกี่ยวกับชนิดของ asm ที่จะสร้าง
ไม่น่าตื่นเต้นเกินไป บ่อยครั้งที่ผู้คอมไพล์เลอร์จะปล่อยให้คุณหนีไปกับบางสิ่งและปล่อยโค้ดอย่างที่คุณคาดหวังแม้จะเป็น UB ก็ตาม แต่บางทีมันอาจจะเป็นปัญหาในอนาคตหากคอมไพเลอร์ devs ใช้การเพิ่มประสิทธิภาพบางอย่างที่ได้รับข้อมูลเพิ่มเติมเกี่ยวกับช่วงค่า (เช่นตัวแปรไม่เป็นลบอาจอนุญาตให้ปรับการขยายสัญญาณให้เป็นศูนย์ฟรีส่วนขยายบน x86- 64) ตัวอย่างเช่นใน gcc และ clang ปัจจุบันการทำtmp = a+INT_MIN
ไม่ได้ปรับให้เหมาะสมa<0
เสมอ - เท็จเท่านั้นที่tmp
เป็นลบเสมอ (เนื่องจากINT_MIN
+ a=INT_MAX
เป็นค่าลบสำหรับเป้าหมายที่สมบูรณ์ของ 2 นี้และa
ไม่สามารถสูงกว่านั้นได้อีก)
ดังนั้น gcc / เสียงดังกราวยังไม่เปลี่ยนใจไปยังข้อมูลช่วงการสืบทอดมาสำหรับปัจจัยการผลิตของการคำนวณเพียงผลอยู่บนสมมติฐานไม่ล้นลงนาม: ตัวอย่าง Godbolt ฉันไม่รู้ว่านี่เป็นการเพิ่มประสิทธิภาพหรือไม่โดยเจตนา "พลาด" ในชื่อที่เป็นมิตรกับผู้ใช้หรืออะไร
นอกจากนี้ทราบว่าการใช้งาน (aka คอมไพเลอร์) ที่ได้รับอนุญาตในการกำหนดพฤติกรรมที่ ISO c ++ ใบไม่ได้กำหนด ตัวอย่างเช่นคอมไพเลอร์ทั้งหมดที่รองรับ Intrinsics ของ Intel (เช่น_mm_add_ps(__m128, __m128)
การปรับเวกเตอร์ SIMD ด้วยตนเอง) จะต้องอนุญาตให้สร้างพอยน์เตอร์ที่จัดแนวผิดซึ่งเป็น UB ใน C ++ แม้ว่าคุณจะไม่ตรวจสอบก็ตาม __m128i _mm_loadu_si128(const __m128i *)
ไม่โหลด unaligned โดยการ misaligned __m128i*
หาเรื่องไม่ได้หรือ void*
`reinterpret_cast` กำลังอยู่ระหว่างตัวชี้เวกเตอร์ฮาร์ดแวร์และประเภทที่เกี่ยวข้องนั้นเป็นพฤติกรรมที่ไม่ได้กำหนดหรือไม่?char*
GNU C / C ++ ยังกำหนดพฤติกรรมของการเปลี่ยนหมายเลขเซ็นชื่อเชิงลบ (ซ้ายโดยไม่มี-fwrapv
) แยกจากกฎ UB ที่ลงชื่อโดยทั่วไปของโอเวอร์โฟลว์ ( นี่คือ UB ใน ISO C ++ในขณะที่การเลื่อนด้านขวาของหมายเลขที่ลงชื่อมีการกำหนดการใช้งาน (ตรรกะเทียบกับเลขคณิต) การใช้งานที่มีคุณภาพดีเลือกเลขคณิตบน HW ที่มีการเลื่อนด้านขวาทางคณิตศาสตร์ แต่ ISO C ++ ไม่ได้ระบุ) สิ่งนี้ได้รับการบันทึกไว้ในหมวด Integer ของคู่มือ GCCพร้อมกับการกำหนดพฤติกรรมที่กำหนดโดยการนำไปปฏิบัติซึ่งมาตรฐาน C ต้องการการนำไปใช้เพื่อกำหนดวิธีใดวิธีหนึ่ง
มีปัญหาเรื่องคุณภาพของการติดตั้งที่นักพัฒนาคอมไพเลอร์ใส่ใจ โดยทั่วไปแล้วพวกเขาไม่ได้พยายามรวบรวมคอมไพเลอร์ที่เป็นศัตรูโดยเจตนา แต่การใช้ประโยชน์จากหลุมบ่อ UB ทั้งหมดใน C ++ (ยกเว้นที่พวกเขาเลือกที่จะกำหนด) เพื่อเพิ่มประสิทธิภาพที่ดีขึ้นสามารถแยกความแตกต่างได้ในบางครั้ง
เชิงอรรถ 1 : 56 บิตด้านบนอาจเป็นขยะที่ผู้ใช้ต้องละเว้นตามปกติสำหรับชนิดที่แคบกว่าการลงทะเบียน
( ABIs อื่น ๆทำให้ทางเลือกที่แตกต่างกันที่นี่ . บางคนไม่จำเป็นต้องมีประเภทจำนวนเต็มแคบจะเป็น zero- หรือลงชื่อเข้าใช้ขยายการกรอกข้อมูลลงทะเบียนเมื่อผ่านไปหรือกลับจากฟังก์ชั่นเช่น MIPS64 และ PowerPC64. ดูส่วนสุดท้ายของคำตอบ x86-64 นี้ ซึ่งเปรียบเทียบกับ ISAs ก่อนหน้านี้ )
ยกตัวอย่างเช่นการโทรอาจมีการคำนวณa & 0x01010101
ใน RDI bool_func(a&1)
และใช้มันอย่างอื่นก่อนที่จะเรียก ผู้เรียกสามารถปรับให้เหมาะที่สุด&1
เพราะมันทำไปแล้วที่ไบต์ต่ำซึ่งเป็นส่วนหนึ่งของand edi, 0x01010101
และมันรู้ว่าจำเป็นต้องมี callee เพื่อละเว้นไบต์สูง
หรือถ้าบูลถูกส่งเป็น ARG ครั้งที่ 3 ผู้โทรอาจปรับให้เหมาะกับขนาดโค้ดโหลดmov dl, [mem]
แทนmovzx edx, [mem]
โดยให้บันทึก 1 ไบต์ด้วยค่าใช้จ่ายของการพึ่งพาที่ผิดพลาดกับค่าเก่าของ RDX (หรือเอฟเฟกต์ลงทะเบียนบางส่วนขึ้นอยู่กับ บนรุ่น CPU) หรือหาเรื่องแรกmov dil, byte [r10]
แทนที่จะเป็นmovzx edi, byte [r10]
เพราะทั้งคู่ต้องการคำนำหน้า REX อยู่ดี
นี่คือเหตุผลที่ส่งเสียงดังกราวmovzx eax, dil
ในแทนSerialize
sub eax, edi
(สำหรับ args จำนวนเต็มเสียงดังกราวละเมิดกฎ ABI นี้ขึ้นอยู่กับพฤติกรรมที่ไม่มีเอกสารของ gcc และเสียงดังกราวเป็นศูนย์ - หรือจำนวนเต็มขยายแคบลงถึง 32 บิต เป็นสัญญาณหรือส่วนขยายศูนย์ที่จำเป็นเมื่อเพิ่ม 32 บิตออฟเซ็ต x86-64 ABI หรือไม่
ดังนั้นฉันสนใจที่จะเห็นว่ามันไม่ได้ทำสิ่งเดียวกันbool
)
เชิงอรรถ 2: หลังจากการแยกคุณจะมีร้านค้าขนาด 4 ไบต์ - mov
กลางหรือ 4 ไบต์ + 1 ไบต์ ความยาวมีความหมายในความกว้างของร้านค้า + ออฟเซ็ต
OTOH, glibc memcpy จะทำการโหลดขนาด 4 ไบต์ / สองร้านโดยมีการทับซ้อนกันซึ่งขึ้นอยู่กับความยาวดังนั้นนี่จะทำให้สิ่งทั้งหมดปราศจากกิ่งที่มีเงื่อนไขบนบูลีน ดูL(between_4_7):
บล็อกใน memcpy / memmove ของ glibc หรืออย่างน้อยก็ไปในทางเดียวกันสำหรับบูลีนในการแยกของ memcpy เพื่อเลือกขนาดของก้อน
หากอินไลน์คุณสามารถใช้ 2x mov
-immediate + cmov
และ offset ตามเงื่อนไขหรือคุณอาจปล่อยให้ข้อมูลสตริงอยู่ในหน่วยความจำ
หรือหากการปรับแต่งสำหรับ Intel Ice Lake ( ด้วยคุณสมบัติ Fast Short REP MOV ) ค่าจริงrep movsb
อาจเหมาะสมที่สุด glibc memcpy
อาจเริ่มใช้rep movsb
งานขนาดเล็ก ๆ บน CPU ที่มีคุณสมบัติดังกล่าวช่วยประหยัดการแตกแขนงได้มากมาย
เครื่องมือสำหรับการตรวจจับ UB และการใช้ค่าที่ไม่ได้กำหนดค่าเริ่มต้น
ใน gcc และ clang คุณสามารถคอมไพล์ด้วย-fsanitize=undefined
เพื่อเพิ่ม instrumentation แบบรันไทม์ที่จะเตือนหรือเกิดข้อผิดพลาดกับ UB ที่เกิดขึ้นขณะรันไทม์ ที่จะไม่จับตัวแปรหน่วยแม้ว่า (เพราะจะไม่เพิ่มขนาดของประเภทเพื่อให้มีที่ว่างสำหรับบิต "ไม่กำหนดค่าเริ่มต้น")
ดูhttps://developers.redhat.com/blog/2014/10/16/gcc-undefined-behavior-sanitizer-ubsan/
ในการค้นหาการใช้งานข้อมูลที่ไม่ได้กำหนดค่าเริ่มต้นมี Address Sanitizer และ Memory Sanitizer ใน clang / LLVM https://github.com/google/sanitizers/wiki/MemorySanitizerจะแสดงตัวอย่างของclang -fsanitize=memory -fPIE -pie
การตรวจจับการอ่านหน่วยความจำที่ไม่ได้เตรียมไว้ มันอาจจะทำงานได้ดีที่สุดถ้าคุณคอมไพล์โดยไม่มีการเพิ่มประสิทธิภาพดังนั้นการอ่านตัวแปรทั้งหมดจะจบลงด้วยการโหลดจากหน่วยความจำใน asm พวกเขาแสดงให้เห็นว่ามันถูกใช้-O2
ในกรณีที่โหลดจะไม่เพิ่มประสิทธิภาพออกไป ฉันไม่ได้ลองเอง (ในบางกรณีเช่นไม่เริ่มต้นการสะสมก่อนที่จะสรุปอาร์เรย์เสียงดังกราว -O3 จะปล่อยรหัสที่รวมอยู่ในการลงทะเบียนแบบเวกเตอร์ที่ไม่ได้เริ่มต้นดังนั้นด้วยการเพิ่มประสิทธิภาพคุณสามารถมีกรณีที่ไม่มีหน่วยความจำที่อ่านเกี่ยวข้องกับ UB แต่-fsanitize=memory
เปลี่ยน asm ที่สร้างขึ้นและอาจส่งผลให้เกิดการตรวจสอบเรื่องนี้)
มันจะทนต่อการคัดลอกของหน่วยความจำเริ่มต้นและตรรกะที่เรียบง่ายและการดำเนินการทางคณิตศาสตร์กับมัน โดยทั่วไป MemorySanitizer จะติดตามการแพร่กระจายของข้อมูลที่ไม่ได้กำหนดค่าเริ่มต้นในหน่วยความจำอย่างเงียบ ๆ และรายงานคำเตือนเมื่อมีการใช้รหัสสาขา (หรือไม่ถ่าย) ขึ้นอยู่กับค่าเริ่มต้น
MemorySanitizer ใช้ฟังก์ชั่นย่อยที่พบใน Valgrind (เครื่องมือ Memcheck)
มันควรจะทำงานสำหรับกรณีนี้เพราะการเรียกร้องให้ glibc memcpy
มีlength
ผลคำนวณได้จากหน่วยความจำจะเตรียม (ภายในห้องสมุด) length
ในสาขาซึ่งเป็นไปตาม หากมี inline รุ่นที่ไม่มีสาขาอย่างสมบูรณ์ที่เพิ่งใช้การcmov
จัดทำดัชนีและร้านค้าสองแห่งมันอาจไม่ทำงาน
Valgrind'smemcheck
จะค้นหาปัญหาประเภทนี้อีกครั้งโดยไม่บ่นว่าโปรแกรมคัดลอกข้อมูลที่ไม่มีการเตรียมข้อมูลเบื้องต้น แต่มันบอกว่ามันจะตรวจจับเมื่อ "การกระโดดหรือการย้ายแบบมีเงื่อนไขขึ้นอยู่กับค่าเริ่มต้น" เพื่อพยายามที่จะจับพฤติกรรมที่มองเห็นจากภายนอกซึ่งขึ้นอยู่กับข้อมูลที่ไม่มีการเริ่มต้น
บางทีความคิดที่อยู่เบื้องหลังการไม่ตั้งค่าสถานะเพียงโหลดคือ structs สามารถมีช่องว่างภายในและคัดลอกโครงสร้างทั้งหมด (รวมถึงช่องว่างภายใน) ด้วยโหลด / เก็บแบบกว้างเวกเตอร์ไม่ใช่ข้อผิดพลาดแม้ว่าสมาชิกแต่ละคนจะถูกเขียนทีละคนเท่านั้น ในระดับ asm ข้อมูลเกี่ยวกับสิ่งที่แพ็ดดิ้งและสิ่งที่เป็นส่วนหนึ่งของค่าได้สูญหายไป