Skip to content

Commit 58e2090

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 <[email protected]>
1 parent 8ac0a9c commit 58e2090

File tree

1 file changed

+20
-1
lines changed

1 file changed

+20
-1
lines changed

cgosqlite/cgosqlite.go

Lines changed: 20 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,20 @@ 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. This is
69+
// safe to call at runtime; the setting will apply to subsequent calls to
70+
// [Stmt.ColumnBlob].
71+
//
72+
// This was added to help detect misuse of [sql.RawBytes] where we might be
73+
// modifying data internal to SQLite, retaining it after it's no longer valid,
74+
// and so on.
75+
func SetAlwaysCopyBlob(copy bool) {
76+
alwaysCopyBlob.Store(copy)
77+
}
78+
6479
func init() {
6580
C.sqlite3_initialize()
6681
}
@@ -368,7 +383,11 @@ func (stmt *Stmt) ColumnBlob(col int) []byte {
368383
return nil
369384
}
370385
n := int(C.sqlite3_column_bytes(stmt.stmt, C.int(col)))
371-
return unsafe.Slice((*byte)(unsafe.Pointer(res)), n)
386+
slice := unsafe.Slice((*byte)(unsafe.Pointer(res)), n)
387+
if alwaysCopyBlob.Load() {
388+
return append([]byte(nil), slice...)
389+
}
390+
return slice
372391
}
373392

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

0 commit comments

Comments
 (0)