v1.10.0
A hardening + maintenance release. The centerpiece is a security-focused code
review swept across the untrusted-input boundary — aikit is the parser layer
the ecosystem routes every hostile GGUF/safetensors byte and every persisted
index through. Alongside the fixes: two additive API surfaces, real allocation
and throughput wins, arm64 now gated in CI, and a toolchain/dependency refresh.
No Hard-tier API breakage. Everything here is additive or behavioral; the
changed kernels stay bit-identical, pinned by the exact-equality parity gates.
Safe drop-in upgrade from 1.9.0.
Highlights
- 🔒 Untrusted-input boundary hardened. A crafted or merely-mismatched model
file / persisted index could previously cause a path-traversal read, an
uncatchable crash (stack exhaustion, worker-goroutine panic), a~250 GB
over-allocation, aSIGBUS/checkptrthrow from an unalignedunsafecast, or
a use-after-munmapsegfault. All closed — see Security below. - ✨ New API (additive).
linalg.MatmulBTW4A8Into— a zero-alloc int4 decode
matmul (0 allocs/op steady-state, the int4 twin ofMatmulBTW8A8Into) — and
Close()onModel/BERT/CrossEncoder/SPLADEfor deterministic
release of mmapped weights instead of waiting on a GC finalizer. - ⚡ Perf. BERT/SPLADE/CrossEncoder attention now runs through the pooled
scratch arena: 432 → 3 allocs per forward, and a lone forward parallelizes
instead of running single-threaded.MatmulBTQ8widens each weight row once
(was M times) at prefill. - 🧪 arm64 in CI. The NEON asm and the bit-identity gates (M-/width-invariance,
packed-path identity) now run on a realubuntu-24.04-armrunner — an
arm64-only regression can no longer ship green. Plus staticcheck via
golangci-lint (54 findings → 0).
Upgrade notes
- Version is a MINOR bump for the additive API above. Existing callers need no
changes. - Behavioral hardening you might notice: several parsers/loaders now return a
cleanErrFormat(or a recoverable, caller-side panic) where a malformed input
previously slipped through or crashed deep in a kernel.DotI8now panics on a
length mismatch (it read out of bounds before). Vision loaders validate tensor
shapes and config at load. None of this affects well-formed input. chunk/treesitterbumpedgotreesitter0.20.2 → 0.40.0. Its exact chunk
boundaries are best-effort (ADR-010), so a few may shift on real code; the
load-bearing contracts (byte-fidelity, AST-meaningful boundaries) are gated and
hold. The pure-Go core is unaffected — this is isolated to the quarantined
submodule.- Toolchain moved to Go 1.26.5;
golang.org/x/text 0.37→0.40,x/sys 0.45→0.47.
The full categorized changelog (Added / Changed / Fixed / Security, with the
per-finding detail) is in
CHANGELOG.md → 1.10.0.
Security (the untrusted-input boundary)
- safetensors sharded load — path traversal. Shard names from the untrusted
index JSON were joined to the model dir;"../../../etc/passwd"mmap'd a file
outside the bundle. Rejected asErrFormat. - safetensors header — no shape × dtype cross-check. A giant shape with a
tiny byte range parsed, then panicked at inference. Now requires overflow-safe
∏shape · dtypeSize == byteRange. - safetensors typed accessors — no alignment check. The
unsafereinterpret
cast assumed alignment the format doesn't guarantee (a-race/checkptr throw,
SIGBUS on strict-alignment ports). Zero-copy when aligned, byte-copy otherwise. - GGUF metadata — unbounded array-of-array recursion. Millions of frames past
the goroutine-stack limit → unrecoverable abort. Depth capped at 128. - HNSW load — missing product allocation guard. The f32 branch clamped
ndocs/dimindividually but not their product (~250 GB attempt). Guarded. - mmap lifetime — use-after-unmap. Three zero-copy accessors could be
munmap'd mid-read → SIGSEGV.runtime.KeepAlivebackstops added. - Unrecoverable matmul panics. A shape violation above the parallel threshold
panicked on a worker goroutine (uncatchable). Validation now runs before the
fan-out; constructors reject negative dims / overflow.