Skip to content

Commit 651ddbd

Browse files
committed
database/sql: buffers provided to Rows.Next should not be modified by drivers
Previously we allowed drivers to modify the row buffer used to scan values when closing Rows. This is no longer acceptable and can lead to data races. Fixes #23519 Change-Id: I91820a6266ffe52f95f40bb47307d375727715af Reviewed-on: https://go-review.googlesource.com/89936 Run-TryBot: Daniel Theophanes <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Russ Cox <[email protected]>
1 parent 7350297 commit 651ddbd

File tree

4 files changed

+14
-42
lines changed

4 files changed

+14
-42
lines changed

doc/go1.10.html

+7
Original file line numberDiff line numberDiff line change
@@ -814,6 +814,13 @@ <h3 id="minor_library_changes">Minor changes to the library</h3>
814814
<dl id="database/sql/driver"><dt><a href="/pkg/database/sql/driver/">database/sql/driver</a></dt>
815815
<dd>
816816
<p>
817+
Drivers that currently hold on to the destination buffer provdied by
818+
<a href="/pkg/database/sql/driver/#Rows.Next"><code>driver.Rows.Next</code></a> should ensure they no longer
819+
write to a buffer assignedd to the destination array outside of that call.
820+
Drivers must be careful that underlying buffers are not modified when closing
821+
<a href="/pkg/database/sql/driver/#Rows"><code>driver.Rows</code></a>.
822+
</p>
823+
<p>
817824
Drivers that want to construct a <a href="/pkg/database/sql/#DB"><code>sql.DB</code></a> for
818825
their clients can now implement the <a href="/pkg/database/sql/driver/#Connector"><code>Connector</code></a> interface
819826
and call the new <a href="/pkg/database/sql/#OpenDB"><code>sql.OpenDB</code></a> function,

src/database/sql/driver/driver.go

+4
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,10 @@ type Rows interface {
379379
// size as the Columns() are wide.
380380
//
381381
// Next should return io.EOF when there are no more rows.
382+
//
383+
// The dest should not be written to outside of Next. Care
384+
// should be taken when closing Rows not to modify
385+
// a buffer held in dest.
382386
Next(dest []Value) error
383387
}
384388

src/database/sql/fakedb_test.go

-5
Original file line numberDiff line numberDiff line change
@@ -1020,11 +1020,6 @@ func (rc *rowsCursor) touchMem() {
10201020
}
10211021

10221022
func (rc *rowsCursor) Close() error {
1023-
if !rc.closed {
1024-
for _, bs := range rc.bytesClone {
1025-
bs[0] = 255 // first byte corrupted
1026-
}
1027-
}
10281023
rc.touchMem()
10291024
rc.parentMem.touchMem()
10301025
rc.closed = true

src/database/sql/sql_test.go

+3-37
Original file line numberDiff line numberDiff line change
@@ -663,43 +663,6 @@ func TestPoolExhaustOnCancel(t *testing.T) {
663663
}
664664
}
665665

666-
func TestByteOwnership(t *testing.T) {
667-
db := newTestDB(t, "people")
668-
defer closeDB(t, db)
669-
rows, err := db.Query("SELECT|people|name,photo|")
670-
if err != nil {
671-
t.Fatalf("Query: %v", err)
672-
}
673-
type row struct {
674-
name []byte
675-
photo RawBytes
676-
}
677-
got := []row{}
678-
for rows.Next() {
679-
var r row
680-
err = rows.Scan(&r.name, &r.photo)
681-
if err != nil {
682-
t.Fatalf("Scan: %v", err)
683-
}
684-
got = append(got, r)
685-
}
686-
corruptMemory := []byte("\xffPHOTO")
687-
want := []row{
688-
{name: []byte("Alice"), photo: corruptMemory},
689-
{name: []byte("Bob"), photo: corruptMemory},
690-
{name: []byte("Chris"), photo: corruptMemory},
691-
}
692-
if !reflect.DeepEqual(got, want) {
693-
t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want)
694-
}
695-
696-
var photo RawBytes
697-
err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo)
698-
if err == nil {
699-
t.Error("want error scanning into RawBytes from QueryRow")
700-
}
701-
}
702-
703666
func TestRowsColumns(t *testing.T) {
704667
db := newTestDB(t, "people")
705668
defer closeDB(t, db)
@@ -3192,8 +3155,11 @@ func TestIssue18429(t *testing.T) {
31923155
// reported.
31933156
rows, _ := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
31943157
if rows != nil {
3158+
var name string
31953159
// Call Next to test Issue 21117 and check for races.
31963160
for rows.Next() {
3161+
// Scan the buffer so it is read and checked for races.
3162+
rows.Scan(&name)
31973163
}
31983164
rows.Close()
31993165
}

0 commit comments

Comments
 (0)