fix(verify): arm64 stack save, adaptive canary and host gating

Assisted-by: GLM 5.3
This commit is contained in:
2026-09-19 23:49:19 +02:00
parent 87b1081c53
commit 375182ef1f
25 changed files with 651 additions and 57 deletions
+50 -11
View File
@@ -20,7 +20,11 @@ type BufSpec struct {
}
// ParseBufSpec parses a "name:size:pattern[,name:size:pattern]" spec string
// into individual buffer specs. Empty input yields an empty slice.
// into individual buffer specs. Empty input yields an empty slice. The
// size must be a plain decimal number over the whole field and the pattern
// must be a known name or a valid hex blob, so typos fail here with the
// offending spec in the message rather than silently allocating a zeroed
// buffer.
func ParseBufSpec(spec string) ([]BufSpec, error) {
if spec == "" {
return nil, nil
@@ -31,15 +35,38 @@ func ParseBufSpec(spec string) ([]BufSpec, error) {
if len(fields) != 3 {
return nil, fmt.Errorf("verify: invalid buffer spec %q (expected name:size:pattern)", part)
}
var size int
if _, err := fmt.Sscanf(fields[1], "%d", &size); err != nil || size <= 0 {
// strconv.Atoi parses the whole field, unlike fmt.Sscanf which
// accepts trailing garbage ("1024abc" parsed as 1024).
size, err := strconv.Atoi(fields[1])
if err != nil || size <= 0 {
return nil, fmt.Errorf("verify: invalid buffer size %q in %q", fields[1], part)
}
if err := validPattern(fields[2]); err != nil {
return nil, fmt.Errorf("verify: invalid pattern %q in %q: %v", fields[2], part, err)
}
out = append(out, BufSpec{Name: fields[0], Size: size, Pattern: fields[2]})
}
return out, nil
}
// validPattern checks one buffer pattern: a known name, or a non-empty hex
// blob. fillBuffer enforces the same rule again at fill time for specs
// that were not built by ParseBufSpec.
func validPattern(pattern string) error {
switch pattern {
case "zero", "ones", "seq":
return nil
}
data, err := hex.DecodeString(pattern)
if err != nil {
return fmt.Errorf("not a known pattern (zero, ones, seq) and not hex: %w", err)
}
if len(data) == 0 {
return fmt.Errorf("not a known pattern (zero, ones, seq) and the hex blob is empty")
}
return nil
}
// allocatedBuf is one live buffer in a pool.
type allocatedBuf struct {
spec BufSpec
@@ -60,11 +87,15 @@ type BufPool struct {
}
// Alloc allocates and fills the buffers described by specs. The returned
// pool must be kept alive until every call using it has returned.
// pool must be kept alive until every call using it has returned. An
// unknown pattern name or invalid hex blob is an error rather than a
// silently zeroed buffer.
func (p *BufPool) Alloc(specs []BufSpec) error {
for _, s := range specs {
data := make([]byte, s.Size+safetyMargin)
fillBuffer(data, s.Pattern)
if err := fillBuffer(data, s.Pattern); err != nil {
return fmt.Errorf("verify: buffer %q: %w", s.Name, err)
}
p.bufs = append(p.bufs, allocatedBuf{spec: s, data: data})
}
return nil
@@ -128,8 +159,10 @@ func (p *BufPool) matchBuf(name string) *allocatedBuf {
}
// fillBuffer fills buf with the named pattern: "zero" (no-op, already zeroed),
// "ones" (0xFF), "seq" (i mod 256), or a hex blob repeated to fill.
func fillBuffer(buf []byte, pattern string) {
// "ones" (0xFF), "seq" (i mod 256), or a hex blob repeated to fill. An
// unknown pattern name or undecodable hex returns an error instead of
// leaving the buffer silently zeroed.
func fillBuffer(buf []byte, pattern string) error {
switch pattern {
case "zero":
// Already zeroed by make.
@@ -142,12 +175,18 @@ func fillBuffer(buf []byte, pattern string) {
buf[i] = byte(i)
}
default:
if data, err := hex.DecodeString(pattern); err == nil && len(data) > 0 {
for i := range buf {
buf[i] = data[i%len(data)]
}
data, err := hex.DecodeString(pattern)
if err != nil {
return fmt.Errorf("unknown pattern (want zero, ones, seq or hex): %w", err)
}
if len(data) == 0 {
return fmt.Errorf("unknown pattern (want zero, ones, seq or hex): empty hex blob")
}
for i := range buf {
buf[i] = data[i%len(data)]
}
}
return nil
}
// ApplyScalarArgs writes user-supplied scalar argument values into an ABI0