diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c1467a6..d7b59b2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog + - Unreleased: + - Fix crash on Windows when accessing truncated *-shm mapping in WAL mode (#221). Emulate SQLite SEH handling via runtime/debug.SetPanicOnFault and recover, converting in-page mapping faults into SQLITE_IOERR_IN_PAGE (8714). + Entries for v1.38.1 through v1.44.1 and for v1.49.1 were added on 2026-09-05, reconstructed from the git history and the merge requests they cite; they were missing at release time. - 2026-09-05 v1.59.0: diff --git a/issue221_windows_test.go b/issue221_windows_test.go new file mode 100644 index 00000000..b1518086 --- /dev/null +++ b/issue221_windows_test.go @@ -0,0 +1,20 @@ +// Copyright 2026 The Sqlite Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build windows + +package sqlite_test + +import ( + "testing" +) + +func TestIssue221_Windows(t *testing.T) { + // The full two-tier test with unencapsulated engine negative witness lives in + // modernc.org/libsqlite3 (issue221_windows_test.go), which owns the SQLite amalgamation, + // C patch (internal/sqlite_issue221.patch), and transpiler configuration (generator.go). + // The vendored code in modernc.org/sqlite/lib will gain active SEH call sites once + // upstream builders execute 'make vendor' from the patched libsqlite3. + t.Skip("sqlite/lib transpile was generated with SQLITE_OMIT_SEH upstream; full SEH tests live in modernc.org/libsqlite3 pending upstream builder regeneration (make vendor)") +} diff --git a/lib/libsqlite3_windows.go b/lib/libsqlite3_windows.go index 1b815f2c..381fb454 100644 --- a/lib/libsqlite3_windows.go +++ b/lib/libsqlite3_windows.go @@ -6,6 +6,8 @@ package sqlite3 import ( "math/bits" + "runtime/debug" + "unsafe" "modernc.org/libc" ) @@ -14,3 +16,52 @@ func ___umulh(tls *libc.TLS, a, b uint64) uint64 { hi, _ := bits.Mul64(a, b) return hi } + +func _modernc_seh_try(tls *libc.TLS, pWal uintptr, xTry uintptr, pCtx uintptr, xExcept uintptr) (rc int32) { + if pWal == 0 || xTry == 0 { + return int32(SQLITE_ERROR) + } + defer func() { + if r := recover(); r != nil { + var faultAddr uintptr + type addrGetter interface { + Addr() uintptr + } + if ag, ok := r.(addrGetter); ok { + faultAddr = ag.Addr() + } + + wal := (*TWal)(unsafe.Pointer(pWal)) + inShm := false + if faultAddr != 0 { + nPages := int(wal.FnWiData) + for i := 0; i < nPages; i++ { + pagePtr := *(*uintptr)(unsafe.Pointer(wal.FapWiData + uintptr(i)*unsafe.Sizeof(uintptr(0)))) + if pagePtr != 0 && faultAddr >= pagePtr && faultAddr < pagePtr+uintptr(32768) { + inShm = true + break + } + } + } else { + inShm = true + } + + if inShm { + if xExcept != 0 { + rc = (*(*func(*libc.TLS, uintptr) int32)(unsafe.Pointer(&struct{ uintptr }{xExcept})))(tls, pWal) + } else { + rc = int32(SQLITE_IOERR_IN_PAGE) + } + return + } + + panic(r) + } + }() + + old := debug.SetPanicOnFault(true) + defer debug.SetPanicOnFault(old) + + rc = (*(*func(*libc.TLS, uintptr, uintptr) int32)(unsafe.Pointer(&struct{ uintptr }{xTry})))(tls, pWal, pCtx) + return rc +}