mirror of
https://github.com/golang/go
synced 2024-11-19 18:04:40 -07:00
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 <kardianos@gmail.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Russ Cox <rsc@golang.org>
This commit is contained in:
parent
7350297eb6
commit
651ddbdb50
@ -814,6 +814,13 @@ formats the X.509 distinguished name in the standard RFC 2253 format.
|
||||
<dl id="database/sql/driver"><dt><a href="/pkg/database/sql/driver/">database/sql/driver</a></dt>
|
||||
<dd>
|
||||
<p>
|
||||
Drivers that currently hold on to the destination buffer provdied by
|
||||
<a href="/pkg/database/sql/driver/#Rows.Next"><code>driver.Rows.Next</code></a> should ensure they no longer
|
||||
write to a buffer assignedd to the destination array outside of that call.
|
||||
Drivers must be careful that underlying buffers are not modified when closing
|
||||
<a href="/pkg/database/sql/driver/#Rows"><code>driver.Rows</code></a>.
|
||||
</p>
|
||||
<p>
|
||||
Drivers that want to construct a <a href="/pkg/database/sql/#DB"><code>sql.DB</code></a> for
|
||||
their clients can now implement the <a href="/pkg/database/sql/driver/#Connector"><code>Connector</code></a> interface
|
||||
and call the new <a href="/pkg/database/sql/#OpenDB"><code>sql.OpenDB</code></a> function,
|
||||
|
@ -379,6 +379,10 @@ type Rows interface {
|
||||
// size as the Columns() are wide.
|
||||
//
|
||||
// Next should return io.EOF when there are no more rows.
|
||||
//
|
||||
// The dest should not be written to outside of Next. Care
|
||||
// should be taken when closing Rows not to modify
|
||||
// a buffer held in dest.
|
||||
Next(dest []Value) error
|
||||
}
|
||||
|
||||
|
@ -1020,11 +1020,6 @@ func (rc *rowsCursor) touchMem() {
|
||||
}
|
||||
|
||||
func (rc *rowsCursor) Close() error {
|
||||
if !rc.closed {
|
||||
for _, bs := range rc.bytesClone {
|
||||
bs[0] = 255 // first byte corrupted
|
||||
}
|
||||
}
|
||||
rc.touchMem()
|
||||
rc.parentMem.touchMem()
|
||||
rc.closed = true
|
||||
|
@ -663,43 +663,6 @@ func TestPoolExhaustOnCancel(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestByteOwnership(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
rows, err := db.Query("SELECT|people|name,photo|")
|
||||
if err != nil {
|
||||
t.Fatalf("Query: %v", err)
|
||||
}
|
||||
type row struct {
|
||||
name []byte
|
||||
photo RawBytes
|
||||
}
|
||||
got := []row{}
|
||||
for rows.Next() {
|
||||
var r row
|
||||
err = rows.Scan(&r.name, &r.photo)
|
||||
if err != nil {
|
||||
t.Fatalf("Scan: %v", err)
|
||||
}
|
||||
got = append(got, r)
|
||||
}
|
||||
corruptMemory := []byte("\xffPHOTO")
|
||||
want := []row{
|
||||
{name: []byte("Alice"), photo: corruptMemory},
|
||||
{name: []byte("Bob"), photo: corruptMemory},
|
||||
{name: []byte("Chris"), photo: corruptMemory},
|
||||
}
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("mismatch.\n got: %#v\nwant: %#v", got, want)
|
||||
}
|
||||
|
||||
var photo RawBytes
|
||||
err = db.QueryRow("SELECT|people|photo|name=?", "Alice").Scan(&photo)
|
||||
if err == nil {
|
||||
t.Error("want error scanning into RawBytes from QueryRow")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRowsColumns(t *testing.T) {
|
||||
db := newTestDB(t, "people")
|
||||
defer closeDB(t, db)
|
||||
@ -3192,8 +3155,11 @@ func TestIssue18429(t *testing.T) {
|
||||
// reported.
|
||||
rows, _ := tx.QueryContext(ctx, "WAIT|"+qwait+"|SELECT|people|name|")
|
||||
if rows != nil {
|
||||
var name string
|
||||
// Call Next to test Issue 21117 and check for races.
|
||||
for rows.Next() {
|
||||
// Scan the buffer so it is read and checked for races.
|
||||
rows.Scan(&name)
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user