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

  1. Save the above code as main_test.go
  2. Run go mod init reproducer && go get modernc.org/sqlite@v1.44.2
  3. Run go test -v ./... - PASSES
  4. 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

  1. Investigate the pointer arithmetic in _unixModeBit and adjust the ccgo transpilation to produce checkptr-compatible code
  2. Add //go:nocheckptr directive to the affected functions (if appropriate)
  3. Document as a known limitation when using -race

Related Issues

  • #77 - Race in ./lib.unixTempFileDir
  • #62 - Race on sqlite3_open_v2/sqlite3_initialize on darwin/amd64
  • cznic/libc#8 - windows/amd64: race detector triggers checkptr failure
Assignee Loading
Time tracking Loading