From c23579f031ecd09bf37c644723b33736dffa8b92 Mon Sep 17 00:00:00 2001 From: Damien Neil Date: Tue, 23 Jan 2024 15:59:47 -0800 Subject: database/sql: avoid clobbering driver-owned memory in RawBytes Depending on the query, a RawBytes can contain memory owned by the driver or by database/sql: If the driver provides the column as a []byte, RawBytes aliases that []byte. If the driver provides the column as any other type, RawBytes contains memory allocated by database/sql. Prior to this CL, Rows.Scan will reuse existing capacity in a RawBytes to permit a single allocation to be reused across rows. When a RawBytes is reused across queries, this can result in database/sql writing to driver-owned memory. Add a buffer to Rows to store RawBytes data, and reuse this buffer across calls to Rows.Scan. Fixes #65201 Change-Id: Iac640174c7afa97eeb39496f47dec202501b2483 Reviewed-on: https://go-review.googlesource.com/c/go/+/557917 Reviewed-by: Brad Fitzpatrick Reviewed-by: Roland Shoemaker LUCI-TryBot-Result: Go LUCI --- src/database/sql/sql.go | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'src/database/sql/sql.go') diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go index 5b4a3f5409..fdbe4b2172 100644 --- a/src/database/sql/sql.go +++ b/src/database/sql/sql.go @@ -2930,6 +2930,13 @@ type Rows struct { // not to be called concurrently. lastcols []driver.Value + // raw is a buffer for RawBytes that persists between Scan calls. + // This is used when the driver returns a mismatched type that requires + // a cloning allocation. For example, if the driver returns a *string and + // the user is scanning into a *RawBytes, we need to copy the string. + // The raw buffer here lets us reuse the memory for that copy across Scan calls. + raw []byte + // closemuScanHold is whether the previous call to Scan kept closemu RLock'ed // without unlocking it. It does that when the user passes a *RawBytes scan // target. In that case, we need to prevent awaitDone from closing the Rows @@ -3124,6 +3131,32 @@ func (rs *Rows) Err() error { return rs.lasterrOrErrLocked(nil) } +// rawbuf returns the buffer to append RawBytes values to. +// This buffer is reused across calls to Rows.Scan. +// +// Usage: +// +// rawBytes = rows.setrawbuf(append(rows.rawbuf(), value...)) +func (rs *Rows) rawbuf() []byte { + if rs == nil { + // convertAssignRows can take a nil *Rows; for simplicity handle it here + return nil + } + return rs.raw +} + +// setrawbuf updates the RawBytes buffer with the result of appending a new value to it. +// It returns the new value. +func (rs *Rows) setrawbuf(b []byte) RawBytes { + if rs == nil { + // convertAssignRows can take a nil *Rows; for simplicity handle it here + return RawBytes(b) + } + off := len(rs.raw) + rs.raw = b + return RawBytes(rs.raw[off:]) +} + var errRowsClosed = errors.New("sql: Rows are closed") var errNoRows = errors.New("sql: no Rows available") @@ -3331,6 +3364,7 @@ func (rs *Rows) Scan(dest ...any) error { if scanArgsContainRawBytes(dest) { rs.closemuScanHold = true + rs.raw = rs.raw[:0] } else { rs.closemu.RUnlock() } -- cgit v1.3