darwin/arm64: checkptr panic in _unixModeBit when using race detector
darwin/arm64: checkptr panic in _unixModeBit when using race detector
Summary
When running with Go's race detector (-race), calling FileControlPersistWAL triggers a fatal checkptr: pointer arithmetic result points to invalid allocation panic in _unixModeBit.
Environment
- OS: macOS 15.3 (Darwin 25.1.0)
- Architecture: arm64 (Apple Silicon M1/M2/M3)
- Go version: go1.25.5 darwin/arm64
- modernc.org/sqlite version: v1.44.2
Minimal Reproducer
package main
import (
"database/sql"
"os"
"path/filepath"
"testing"
"modernc.org/sqlite"
)
func TestFileControlPersistWAL_Checkptr(t *testing.T) {
dir := t.TempDir()
dbPath := filepath.Join(dir, "test.db")
db, err := sql.Open("sqlite", dbPath+"?_pragma=journal_mode(wal)")
if err != nil {
t.Fatalf("failed to open database: %v", err)
}
defer db.Close()
if _, err := db.Exec("CREATE TABLE test (id INTEGER PRIMARY KEY)"); err != nil {
t.Fatalf("failed to create table: %v", err)
}
conn, err := db.Conn(t.Context())
if err != nil {
t.Fatalf("failed to get connection: %v", err)
}
defer conn.Close()
err = conn.Raw(func(driverConn interface{}) error {
fc, ok := driverConn.(sqlite.FileControl)
if !ok {
t.Fatal("driver does not implement FileControl")
}
// This triggers the checkptr panic with -race
_, err := fc.FileControlPersistWAL("main", 1)
return err
})
if err != nil {
t.Fatalf("FileControlPersistWAL failed: %v", err)
}
}
Steps to Reproduce
- Save the above code as
main_test.go - Run
go mod init reproducer && go get modernc.org/sqlite@v1.44.2 - Run
go test -v ./...- PASSES - Run
go test -race -v ./...- PANICS
Stack Trace
fatal error: checkptr: pointer arithmetic result points to invalid allocation
goroutine 8 gp=0xc00060a8c0 m=0 mp=0x103209120 [running]:
runtime.throw({0x102deac4c?, 0x102871d30?})
/opt/homebrew/Cellar/go/1.25.5/libexec/src/runtime/panic.go:1094 +0x34
runtime.checkptrArithmetic(0x10c400060?, {0x0, 0x0, 0x10?})
/opt/homebrew/Cellar/go/1.25.5/libexec/src/runtime/checkptr.go:69 +0xa8
modernc.org/sqlite/lib._unixModeBit(0xc000129e10?, 0x10c4719b0, 0x4, 0xc0001144bc)
/Users/.../modernc.org/sqlite@v1.44.2/lib/sqlite_darwin_arm64.go:27318 +0x4c
modernc.org/sqlite/lib._unixFileControl(0xc000129e10, 0x10c4719b0, 0xa, 0xc0001144bc)
/Users/.../modernc.org/sqlite@v1.44.2/lib/sqlite_darwin_arm64.go:27362 +0x36c
modernc.org/sqlite/lib._sqlite3OsFileControl(...)
/Users/.../modernc.org/sqlite@v1.44.2/lib/sqlite_darwin_arm64.go:14670
modernc.org/sqlite/lib.Xsqlite3_file_control(...)
/Users/.../modernc.org/sqlite@v1.44.2/lib/sqlite_darwin_arm64.go:167706 +0x3bc
modernc.org/sqlite.(*conn).fileControl(...)
/Users/.../modernc.org/sqlite@v1.44.2/fcntl.go:42 +0xd8
modernc.org/sqlite.(*conn).FileControlPersistWAL(...)
/Users/.../modernc.org/sqlite@v1.44.2/fcntl.go:31 +0xc8
Analysis
The panic occurs in _unixModeBit at lib/sqlite_darwin_arm64.go:27318. Looking at the code:
func _unixModeBit(tls *libc.TLS, pFile uintptr, unixOp int8, pArg uintptr) int32 {
// ... pointer arithmetic that triggers checkptr
}
The checkptr instrumentation in Go's race detector validates that pointer arithmetic results point to valid allocations. The transpiled C code in _unixModeBit appears to perform pointer arithmetic that the race detector flags as invalid.
This is likely a false positive from checkptr's perspective - the code works correctly without -race - but it prevents using the race detector for testing applications that use FileControlPersistWAL.
Impact
This affects projects like Litestream which use FileControlPersistWAL and want to run their test suites with the race detector enabled.
Related: https://github.com/benbjohnson/litestream/issues/1014
Possible Solutions
- Investigate the pointer arithmetic in
_unixModeBitand adjust the ccgo transpilation to produce checkptr-compatible code - Add
//go:nocheckptrdirective to the affected functions (if appropriate) - Document as a known limitation when using
-race