diff --git a/go.mod b/go.mod index f1f06c3..8f50185 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/stretchr/testify v1.11.1 github.com/umputun/go-flags v1.5.1 golang.org/x/crypto v0.49.0 - modernc.org/sqlite v1.48.0 + modernc.org/sqlite v1.48.1 ) require ( diff --git a/go.sum b/go.sum index cc11941..1102fb1 100644 --- a/go.sum +++ b/go.sum @@ -148,8 +148,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= -modernc.org/sqlite v1.48.0 h1:ElZyLop3Q2mHYk5IFPPXADejZrlHu7APbpB0sF78bq4= -modernc.org/sqlite v1.48.0/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig= +modernc.org/sqlite v1.48.1 h1:S85iToyU6cgeojybE2XJlSbcsvcWkQ6qqNXJHtW5hWA= +modernc.org/sqlite v1.48.1/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= diff --git a/vendor/modernc.org/sqlite/CHANGELOG.md b/vendor/modernc.org/sqlite/CHANGELOG.md index 4709596..e59a245 100644 --- a/vendor/modernc.org/sqlite/CHANGELOG.md +++ b/vendor/modernc.org/sqlite/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog + - 2026-04-03 v1.48.1: + - Fix memory leaks and double-free vulnerabilities in the multi-statement query execution path. + - Ensure bind-parameter allocations are reliably freed via strict ownership transfer if an error occurs mid-loop or if multiple statements bind parameters. + - Fix a resource leak where a subsequent statement's error could orphan a previously generated `rows` object without closing it, leaking the prepared statement handle. + - See [GitLab merge request #96](https://gitlab.com/cznic/sqlite/-/merge_requests/96), thanks Josh Bleecher Snyder! + - 2026-03-27 v1.48.0: - Add `_timezone` DSN query parameter to apply IANA timezones (e.g., "America/New_York") to both reads and writes. - Writes will convert `time.Time` values to the target timezone before formatting as a string. diff --git a/vendor/modernc.org/sqlite/conn.go b/vendor/modernc.org/sqlite/conn.go index 948eca5..ba9c121 100644 --- a/vendor/modernc.org/sqlite/conn.go +++ b/vendor/modernc.org/sqlite/conn.go @@ -310,9 +310,7 @@ func (c *conn) bind(pstmt uintptr, n int, args []driver.NamedValue) (allocs []ui return } - for _, v := range allocs { - c.free(v) - } + c.freeAllocs(allocs) allocs = nil }() @@ -639,6 +637,12 @@ func (c *conn) free(p uintptr) { } } +func (c *conn) freeAllocs(allocs []uintptr) { + for _, v := range allocs { + c.free(v) + } +} + // C documentation // // const char *sqlite3_errstr(int); diff --git a/vendor/modernc.org/sqlite/rows.go b/vendor/modernc.org/sqlite/rows.go index a176796..8dcbae5 100644 --- a/vendor/modernc.org/sqlite/rows.go +++ b/vendor/modernc.org/sqlite/rows.go @@ -27,8 +27,13 @@ type rows struct { reuseStmt bool // If true, Close() resets instead of finalizing } -func newRows(c *conn, pstmt uintptr, allocs []uintptr, empty bool) (r *rows, err error) { - r = &rows{c: c, pstmt: pstmt, allocs: allocs, empty: empty} +func newRows(c *conn, pstmt uintptr, allocs *[]uintptr, empty bool) (r *rows, err error) { + var a []uintptr + if allocs != nil { + a = *allocs + *allocs = nil + } + r = &rows{c: c, pstmt: pstmt, allocs: a, empty: empty} defer func() { if err != nil { @@ -54,9 +59,7 @@ func newRows(c *conn, pstmt uintptr, allocs []uintptr, empty bool) (r *rows, err // Close closes the rows iterator. func (r *rows) Close() (err error) { - for _, v := range r.allocs { - r.c.free(v) - } + r.c.freeAllocs(r.allocs) r.allocs = nil if r.reuseStmt { diff --git a/vendor/modernc.org/sqlite/stmt.go b/vendor/modernc.org/sqlite/stmt.go index f585205..79494fe 100644 --- a/vendor/modernc.org/sqlite/stmt.go +++ b/vendor/modernc.org/sqlite/stmt.go @@ -137,11 +137,7 @@ func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Res } // Free allocations after step if len(allocs) != 0 { - defer func() { - for _, v := range allocs { - s.c.free(v) - } - }() + defer func() { s.c.freeAllocs(allocs) }() } } @@ -195,11 +191,7 @@ func (s *stmt) exec(ctx context.Context, args []driver.NamedValue) (r driver.Res } if len(allocs) != 0 { - defer func() { - for _, v := range allocs { - s.c.free(v) - } - }() + defer func() { s.c.freeAllocs(allocs) }() } } @@ -270,8 +262,6 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro } } - var allocs []uintptr - defer func() { if ctx != nil && atomic.LoadInt32(&done) != 0 { if r != nil { @@ -279,7 +269,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro } r, err = nil, ctx.Err() } else if r == nil && err == nil { - r, err = newRows(s.c, pstmt, allocs, true) + r, err = newRows(s.c, pstmt, nil, true) } if pstmt != 0 { @@ -297,6 +287,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro // OPTIMIZED PATH: Single Cached Statement if s.pstmt != 0 { + var allocs []uintptr // Bind n, err := s.c.bindParameterCount(s.pstmt) if err != nil { @@ -312,9 +303,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro rc, err := s.c.step(s.pstmt) if err != nil { // On error, we must free allocs manually because 'newRows' won't take ownership - for _, v := range allocs { - s.c.free(v) - } + s.c.freeAllocs(allocs) s.c.reset(s.pstmt) s.c.clearBindings(s.pstmt) return nil, err @@ -324,7 +313,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro switch rc & 0xff { case sqlite3.SQLITE_ROW: // Pass reuseStmt=true - if r, err = newRows(s.c, s.pstmt, allocs, false); err != nil { + if r, err = newRows(s.c, s.pstmt, &allocs, false); err != nil { s.c.reset(s.pstmt) s.c.clearBindings(s.pstmt) return nil, err @@ -341,7 +330,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro // Actually, if we pass reuseStmt=true to an empty set, // rows.Close() will eventually reset it. - if r, err = newRows(s.c, s.pstmt, allocs, true); err != nil { + if r, err = newRows(s.c, s.pstmt, &allocs, true); err != nil { s.c.reset(s.pstmt) s.c.clearBindings(s.pstmt) return nil, err @@ -351,9 +340,7 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro default: // Error case - for _, v := range allocs { - s.c.free(v) - } + s.c.freeAllocs(allocs) s.c.reset(s.pstmt) s.c.clearBindings(s.pstmt) return nil, s.c.errstr(int32(rc)) @@ -363,6 +350,9 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro // FALLBACK PATH: Multi-statement script for psql := s.psql; *(*byte)(unsafe.Pointer(psql)) != 0 && atomic.LoadInt32(&done) == 0; { if pstmt, err = s.c.prepareV2(&psql); err != nil { + if r != nil { + r.Close() + } return nil, err } @@ -371,6 +361,9 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro } err = func() (err error) { + var allocs []uintptr + defer func() { s.c.freeAllocs(allocs) }() + n, err := s.c.bindParameterCount(pstmt) if err != nil { return err @@ -392,15 +385,14 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro if r != nil { r.Close() } - if r, err = newRows(s.c, pstmt, allocs, false); err != nil { + if r, err = newRows(s.c, pstmt, &allocs, false); err != nil { return err } - pstmt = 0 return nil case sqlite3.SQLITE_DONE: if r == nil { - if r, err = newRows(s.c, pstmt, allocs, true); err != nil { + if r, err = newRows(s.c, pstmt, &allocs, true); err != nil { return err } pstmt = 0 @@ -416,10 +408,9 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro if r != nil { r.Close() } - if r, err = newRows(s.c, pstmt, allocs, true); err != nil { + if r, err = newRows(s.c, pstmt, &allocs, true); err != nil { return err } - pstmt = 0 } return nil @@ -435,6 +426,9 @@ func (s *stmt) query(ctx context.Context, args []driver.NamedValue) (r driver.Ro } if err != nil { + if r != nil { + r.Close() // r is from a previous iteration; clean up since we won't return it + } return nil, err } } diff --git a/vendor/modules.txt b/vendor/modules.txt index d64a581..8aba7ce 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -152,7 +152,7 @@ modernc.org/mathutil # modernc.org/memory v1.11.0 ## explicit; go 1.23.0 modernc.org/memory -# modernc.org/sqlite v1.48.0 +# modernc.org/sqlite v1.48.1 ## explicit; go 1.25.0 modernc.org/sqlite modernc.org/sqlite/lib