[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
This commit is contained in:
Petr Beneš 2026-02-10 14:26:03 +01:00 committed by GitHub
parent b46d6dcac1
commit 6558595ca3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 67 additions and 2 deletions

View File

@ -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<uint32_t>(c->getSize()), rawSize);
}
memset(secBuf + prevEnd, 0xCC, sec->getRawSize() - prevEnd);
memset(secBuf + prevEnd, 0xCC, rawSize - prevEnd);
}
parallelForEach(sec->chunks, [&](Chunk *c) {

View File

@ -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
...