From 6558595ca3ac531270a6691c758b835ee8adfdd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Bene=C5=A1?= Date: Tue, 10 Feb 2026 14:26:03 +0100 Subject: [PATCH] [LLD][COFF] Fix out-of-bounds write when filling gaps with INT3 in code sections (#180411) When merging `.bss` into a code section (e.g., `/MERGE:.bss=.text`), the INT3 gap-filling loop in `writeSections()` would write past the output buffer. This happens because `.bss` chunks have `hasData=false`, so they contribute to `VirtualSize` but not `SizeOfRawData`. The loop was using chunk RVAs without checking if they exceeded the raw data region. This caused a crash on Windows with `/FILEALIGN:1` (access violation 0xC0000005). The tight alignment leaves no slack in the mapped buffer, so the overflow immediately hits unmapped memory. The fix bounds all memset operations to `rawSize` and exits early when encountering chunks beyond the raw data boundary. Fixes #180406 --- lld/COFF/Writer.cpp | 9 ++- lld/test/COFF/merge-bss-text-filealign1.test | 60 ++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) create mode 100644 lld/test/COFF/merge-bss-text-filealign1.test diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index 559bd387fa9c..a5e30e26b9e5 100644 --- a/lld/COFF/Writer.cpp +++ b/lld/COFF/Writer.cpp @@ -2619,12 +2619,17 @@ void Writer::writeSections() { if ((sec->header.Characteristics & IMAGE_SCN_CNT_CODE) && (ctx.config.machine == AMD64 || ctx.config.machine == I386)) { uint32_t prevEnd = 0; + uint32_t rawSize = sec->getRawSize(); for (Chunk *c : sec->chunks) { uint32_t off = c->getRVA() - sec->getRVA(); + // Chunks without data (e.g., .bss) have virtual addresses beyond + // rawSize; stop filling when we reach the end of raw data. + if (off >= rawSize) + break; memset(secBuf + prevEnd, 0xCC, off - prevEnd); - prevEnd = off + c->getSize(); + prevEnd = std::min(off + static_cast(c->getSize()), rawSize); } - memset(secBuf + prevEnd, 0xCC, sec->getRawSize() - prevEnd); + memset(secBuf + prevEnd, 0xCC, rawSize - prevEnd); } parallelForEach(sec->chunks, [&](Chunk *c) { diff --git a/lld/test/COFF/merge-bss-text-filealign1.test b/lld/test/COFF/merge-bss-text-filealign1.test new file mode 100644 index 000000000000..4b03a11fae16 --- /dev/null +++ b/lld/test/COFF/merge-bss-text-filealign1.test @@ -0,0 +1,60 @@ +# REQUIRES: x86 +# Test that merging .bss into .text with /FILEALIGN:1 doesn't crash. +# +# RUN: yaml2obj %s -o %t.obj +# RUN: lld-link /out:%t.exe /entry:main /subsystem:console \ +# RUN: /merge:.bss=.text /filealign:1 %t.obj +# RUN: llvm-readobj --sections %t.exe | FileCheck %s + +# CHECK: Name: .text +# CHECK-NEXT: VirtualSize: 0x104 +# CHECK: RawDataSize: +# CHECK-NOT: Name: .bss + +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [ ] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 'C3' + SizeOfRawData: 1 + - Name: .bss + Characteristics: [ IMAGE_SCN_CNT_UNINITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: '' + SizeOfRawData: 256 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 1 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + - Name: .bss + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 256 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 2 + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +...