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/convert_test.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/database/sql/convert_test.go') diff --git a/src/database/sql/convert_test.go b/src/database/sql/convert_test.go index 6d09fa1eae..f94db8e5f8 100644 --- a/src/database/sql/convert_test.go +++ b/src/database/sql/convert_test.go @@ -354,9 +354,10 @@ func TestRawBytesAllocs(t *testing.T) { {"time", time.Unix(2, 5).UTC(), "1970-01-01T00:00:02.000000005Z"}, } - buf := make(RawBytes, 10) + var buf RawBytes + rows := &Rows{} test := func(name string, in any, want string) { - if err := convertAssign(&buf, in); err != nil { + if err := convertAssignRows(&buf, in, rows); err != nil { t.Fatalf("%s: convertAssign = %v", name, err) } match := len(buf) == len(want) @@ -375,6 +376,7 @@ func TestRawBytesAllocs(t *testing.T) { n := testing.AllocsPerRun(100, func() { for _, tt := range tests { + rows.raw = rows.raw[:0] test(tt.name, tt.in, tt.want) } }) @@ -383,7 +385,11 @@ func TestRawBytesAllocs(t *testing.T) { // and gc. With 32-bit words there are more convT2E allocs, and // with gccgo, only pointers currently go in interface data. // So only care on amd64 gc for now. - measureAllocs := runtime.GOARCH == "amd64" && runtime.Compiler == "gc" + measureAllocs := false + switch runtime.GOARCH { + case "amd64", "arm64": + measureAllocs = runtime.Compiler == "gc" + } if n > 0.5 && measureAllocs { t.Fatalf("allocs = %v; want 0", n) -- cgit v1.3