fix(asm): bound the data section to what the image can materialise

Assisted-by: GLM 5.3 Flash
This commit is contained in:
petrbalvin committed 2026-10-07 01:14:58 +02:00
1 parent dd074dbafd
commit 7878112b93
2 files changed
+24 -7

No files matched your search

+2
View File
@@ -82,7 +82,9 @@ func FuzzAssembleAMD64(f *testing.F) {
// above never reach.
f.Add("TEXT ·f(SB), $0\n\tBOGUSINSTR AX, BX\n\tRET\n")
f.Add("GLOBL d(SB), $-8\n")
f.Add("GLOBL d(SB), $-1\n")
f.Add("GLOBL d(SB), $0x7FFFFFFFFFFFFFFF\n")
f.Add("GLOBL d(SB), $0x4000000\nDATA e+0(SB)/8, $1\nGLOBL e(SB), $0x4000000\nDATA f+0(SB)/8, $1\nGLOBL f(SB), $0x4000000\n")
f.Add("DATA d+0(SB)/9, $1\nGLOBL d(SB), $8\n")
f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tADJSP $16\n\tRET\n")
f.Add("#define A A\nA\n")
+22 -7
View File
@@ -607,13 +607,20 @@ func checkDuplicateDecls(f *ast.File) error {
return nil
}
// maxSymbolSize is the ceiling on one GLOBL's declared size. The image
// materialises the symbol's bytes at assembly time, where the toolchain
// defers the cost to its linker, so the bound is the toolchain's own: obj
// rejects anything over 2,000,000,000 bytes as "symbol too large", and a
// negative size, which the toolchain's int64 field tolerates and its linker
// refuses, is diagnosed here rather than attempted.
const maxSymbolSize = 2_000_000_000
// The data section's size ceilings. The image materialises every GLOBL's
// bytes at assembly time, where the toolchain defers the cost to its linker,
// so gasm needs its own bound and a diagnostic in place of an allocation
// failure. The toolchain's own limit (obj's "symbol too large", 2,000,000,000
// bytes) would let a twenty-five byte input demand four gigabytes of resident
// memory (measured: `GLOBL d(SB), $2000000000` peaks at 3.92 GiB RSS), which
// starves the memory fence the test recipes run under when the fuzz workers
// share it. 64 MiB per symbol and 128 MiB per file sit two orders above any
// real assembly symbol (the runtime's largest GLOBL is KiB-scale) and keep a
// worker's worst-case peak near its fence share.
const (
maxSymbolSize = 64 << 20
maxDataSize = 128 << 20
)
// collectData gathers the file's static symbols (GLOBL) and their initial
// contents (DATA) into byte buffers. Two passes: the Plan 9 convention puts
@@ -627,6 +634,7 @@ func collectData(f *ast.File) ([]dataSym, error) {
}
index := map[string]int{}
var syms []dataSym
total := 0
for _, d := range f.Decls {
gd, ok := d.(*ast.Globl)
if !ok {
@@ -639,9 +647,16 @@ func collectData(f *ast.File) ([]dataSym, error) {
size := 0
if gd.Size != nil && gd.Size.Imm.HasVal {
size = int(gd.Size.Imm.Val)
if gd.Size.Imm.Neg {
size = -size
}
if size < 0 || size > maxSymbolSize {
return nil, fmt.Errorf("GLOBL %q: symbol too large (%d bytes > %d bytes)", name, size, maxSymbolSize)
}
if total+size > maxDataSize {
return nil, fmt.Errorf("GLOBL %q: data section too large (%d bytes > %d bytes)", name, total+size, maxDataSize)
}
total += size
}
index[name] = len(syms)
ds := dataSym{