Skip to content

Commit 7034e6c

Browse files
committed
cgosqlite: add feature flag to always copy []bytes returned
When enabled, this ensures that sql.RawBytes values do not actually contain pointers to memory owned by SQLite. Updates tailscale/corp#35671 Signed-off-by: Andrew Dunham <andrew@tailscale.com>
1 parent 8ac0a9c commit 7034e6c

1 file changed

Lines changed: 18 additions & 1 deletion

File tree

cgosqlite/cgosqlite.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ package cgosqlite
5151
import "C"
5252
import (
5353
"sync"
54+
"sync/atomic"
5455
"time"
5556
"unsafe"
5657

@@ -61,6 +62,18 @@ import (
6162
// avoid the need to allocate new storage in each invocation.
6263
var emptyChar [1]C.char
6364

65+
var alwaysCopyBlob atomic.Bool
66+
67+
// SetAlwaysCopyBlob sets whether [Stmt.ColumnBlob] should copy the blob data
68+
// instead of returning a slice that aliases SQLite's internal memory.
69+
//
70+
// This was added to help detect misuse of [sql.RawBytes] where we might be
71+
// modifying data internal to SQLite, retaining it after it's no longer valid,
72+
// and so on.
73+
func SetAlwaysCopyBlob(copy bool) {
74+
alwaysCopyBlob.Store(copy)
75+
}
76+
6477
func init() {
6578
C.sqlite3_initialize()
6679
}
@@ -368,7 +381,11 @@ func (stmt *Stmt) ColumnBlob(col int) []byte {
368381
return nil
369382
}
370383
n := int(C.sqlite3_column_bytes(stmt.stmt, C.int(col)))
371-
return unsafe.Slice((*byte)(unsafe.Pointer(res)), n)
384+
slice := unsafe.Slice((*byte)(unsafe.Pointer(res)), n)
385+
if alwaysCopyBlob.Load() {
386+
return append([]byte(nil), slice...)
387+
}
388+
return slice
372389
}
373390

374391
func (stmt *Stmt) ColumnDouble(col int) float64 {

0 commit comments

Comments
 (0)