From 7878112b93600cdf6c1a547e63a9857fe9ea528d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Balv=C3=ADn?= Date: Wed, 7 Oct 2026 00:02:44 +0200 Subject: [PATCH] fix(asm): bound the data section to what the image can materialise Assisted-by: GLM 5.3 Flash --- asm/assembler_fuzz_test.go | 2 ++ asm/link.go | 29 ++++++++++++++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/asm/assembler_fuzz_test.go b/asm/assembler_fuzz_test.go index 9bc4048..a5a8101 100644 --- a/asm/assembler_fuzz_test.go +++ b/asm/assembler_fuzz_test.go @@ -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") diff --git a/asm/link.go b/asm/link.go index bdfa9ad..78434c2 100644 --- a/asm/link.go +++ b/asm/link.go @@ -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{