diff --git a/meta/recipes-devtools/go/go-1.22.12.inc b/meta/recipes-devtools/go/go-1.22.12.inc index 12d7539017..d0ce333117 100644 --- a/meta/recipes-devtools/go/go-1.22.12.inc +++ b/meta/recipes-devtools/go/go-1.22.12.inc @@ -18,6 +18,8 @@ SRC_URI += "\ file://CVE-2025-22871.patch \ file://CVE-2025-4673.patch \ file://CVE-2025-4674.patch \ + file://CVE-2025-47907-pre.patch \ + file://CVE-2025-47907.patch \ " SRC_URI[main.sha256sum] = "012a7e1f37f362c0918c1dfa3334458ac2da1628c4b9cf4d9ca02db986e17d71" diff --git a/meta/recipes-devtools/go/go/CVE-2025-47907-pre.patch b/meta/recipes-devtools/go/go/CVE-2025-47907-pre.patch new file mode 100644 index 0000000000..dafa878e73 --- /dev/null +++ b/meta/recipes-devtools/go/go/CVE-2025-47907-pre.patch @@ -0,0 +1,233 @@ +From c23579f031ecd09bf37c644723b33736dffa8b92 Mon Sep 17 00:00:00 2001 +From: Damien Neil +Date: Tue, 23 Jan 2024 15:59:47 -0800 +Subject: [PATCH 1/2] 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 + +CVE: CVE-2025-47907 + +Upstream-Status: Backport [https://github.com/golang/go/commit/c23579f031ecd09bf37c644723b33736dffa8b92] + +Signed-off-by: Praveen Kumar +--- + src/database/sql/convert.go | 8 +++--- + src/database/sql/convert_test.go | 12 ++++++-- + src/database/sql/sql.go | 34 +++++++++++++++++++++++ + src/database/sql/sql_test.go | 47 ++++++++++++++++++++++++++++++++ + 4 files changed, 94 insertions(+), 7 deletions(-) + +diff --git a/src/database/sql/convert.go b/src/database/sql/convert.go +index cca5d15..999b8f1 100644 +--- a/src/database/sql/convert.go ++++ b/src/database/sql/convert.go +@@ -237,7 +237,7 @@ func convertAssignRows(dest, src any, rows *Rows) error { + if d == nil { + return errNilPtr + } +- *d = append((*d)[:0], s...) ++ *d = rows.setrawbuf(append(rows.rawbuf(), s...)) + return nil + } + case []byte: +@@ -285,7 +285,7 @@ func convertAssignRows(dest, src any, rows *Rows) error { + if d == nil { + return errNilPtr + } +- *d = s.AppendFormat((*d)[:0], time.RFC3339Nano) ++ *d = rows.setrawbuf(s.AppendFormat(rows.rawbuf(), time.RFC3339Nano)) + return nil + } + case decimalDecompose: +@@ -366,8 +366,8 @@ func convertAssignRows(dest, src any, rows *Rows) error { + } + case *RawBytes: + sv = reflect.ValueOf(src) +- if b, ok := asBytes([]byte(*d)[:0], sv); ok { +- *d = RawBytes(b) ++ if b, ok := asBytes(rows.rawbuf(), sv); ok { ++ *d = rows.setrawbuf(b) + return nil + } + case *bool: +diff --git a/src/database/sql/convert_test.go b/src/database/sql/convert_test.go +index 6d09fa1..f94db8e 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) +diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go +index 4f1197d..da0f52c 100644 +--- a/src/database/sql/sql.go ++++ b/src/database/sql/sql.go +@@ -2936,6 +2936,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 +@@ -3130,6 +3137,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") + +@@ -3337,6 +3370,7 @@ func (rs *Rows) Scan(dest ...any) error { + + if scanArgsContainRawBytes(dest) { + rs.closemuScanHold = true ++ rs.raw = rs.raw[:0] + } else { + rs.closemu.RUnlock() + } +diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go +index c38a348..c3b4822 100644 +--- a/src/database/sql/sql_test.go ++++ b/src/database/sql/sql_test.go +@@ -4531,6 +4531,53 @@ func TestNilErrorAfterClose(t *testing.T) { + } + } + ++// Issue #65201. ++// ++// If a RawBytes is reused across multiple queries, ++// subsequent queries shouldn't overwrite driver-owned memory from previous queries. ++func TestRawBytesReuse(t *testing.T) { ++ db := newTestDB(t, "people") ++ defer closeDB(t, db) ++ ++ if _, err := db.Exec("USE_RAWBYTES"); err != nil { ++ t.Fatal(err) ++ } ++ ++ var raw RawBytes ++ ++ // The RawBytes in this query aliases driver-owned memory. ++ rows, err := db.Query("SELECT|people|name|") ++ if err != nil { ++ t.Fatal(err) ++ } ++ rows.Next() ++ rows.Scan(&raw) // now raw is pointing to driver-owned memory ++ name1 := string(raw) ++ rows.Close() ++ ++ // The RawBytes in this query does not alias driver-owned memory. ++ rows, err = db.Query("SELECT|people|age|") ++ if err != nil { ++ t.Fatal(err) ++ } ++ rows.Next() ++ rows.Scan(&raw) // this must not write to the driver-owned memory in raw ++ rows.Close() ++ ++ // Repeat the first query. Nothing should have changed. ++ rows, err = db.Query("SELECT|people|name|") ++ if err != nil { ++ t.Fatal(err) ++ } ++ rows.Next() ++ rows.Scan(&raw) // raw points to driver-owned memory again ++ name2 := string(raw) ++ rows.Close() ++ if name1 != name2 { ++ t.Fatalf("Scan read name %q, want %q", name2, name1) ++ } ++} ++ + // badConn implements a bad driver.Conn, for TestBadDriver. + // The Exec method panics. + type badConn struct{} +-- +2.40.0 diff --git a/meta/recipes-devtools/go/go/CVE-2025-47907.patch b/meta/recipes-devtools/go/go/CVE-2025-47907.patch new file mode 100644 index 0000000000..a556c3dd68 --- /dev/null +++ b/meta/recipes-devtools/go/go/CVE-2025-47907.patch @@ -0,0 +1,328 @@ +From 8a924caaf348fdc366bab906424616b2974ad4e9 Mon Sep 17 00:00:00 2001 +From: Damien Neil +Date: Wed, 23 Jul 2025 14:26:54 -0700 +Subject: [PATCH 2/2] database/sql: avoid closing Rows while scan is in + progress + +A database/sql/driver.Rows can return database-owned data +from Rows.Next. The driver.Rows documentation doesn't explicitly +document the lifetime guarantees for this data, but a reasonable +expectation is that the caller of Next should only access it +until the next call to Rows.Close or Rows.Next. + +Avoid violating that constraint when a query is cancelled while +a call to database/sql.Rows.Scan (note the difference between +the two different Rows types!) is in progress. We previously +took care to avoid closing a driver.Rows while the user has +access to driver-owned memory via a RawData, but we could still +close a driver.Rows while a Scan call was in the process of +reading previously-returned driver-owned data. + +Update the fake DB used in database/sql tests to invalidate +returned data to help catch other places we might be +incorrectly retaining it. + +Updates #74831 +Fixes #74832 + +Change-Id: Ice45b5fad51b679c38e3e1d21ef39156b56d6037 +Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2540 +Reviewed-by: Roland Shoemaker +Reviewed-by: Neal Patel +Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2601 +Reviewed-on: https://go-review.googlesource.com/c/go/+/693558 +TryBot-Bypass: Dmitri Shuralyov +Reviewed-by: Mark Freeman +Reviewed-by: Dmitri Shuralyov +Auto-Submit: Dmitri Shuralyov + +CVE: CVE-2025-47907 + +Upstream-Status: Backport [https://github.com/golang/go/commit/8a924caaf348fdc366bab906424616b2974ad4e9] + +Signed-off-by: Praveen Kumar +--- + src/database/sql/convert.go | 2 -- + src/database/sql/fakedb_test.go | 47 ++++++++++++-------------- + src/database/sql/sql.go | 26 ++++++-------- + src/database/sql/sql_test.go | 60 ++++++++++++++++++++++++++++++--- + 4 files changed, 89 insertions(+), 46 deletions(-) + +diff --git a/src/database/sql/convert.go b/src/database/sql/convert.go +index 999b8f1..2869a3b 100644 +--- a/src/database/sql/convert.go ++++ b/src/database/sql/convert.go +@@ -324,7 +324,6 @@ func convertAssignRows(dest, src any, rows *Rows) error { + if rows == nil { + return errors.New("invalid context to convert cursor rows, missing parent *Rows") + } +- rows.closemu.Lock() + *d = Rows{ + dc: rows.dc, + releaseConn: func(error) {}, +@@ -340,7 +339,6 @@ func convertAssignRows(dest, src any, rows *Rows) error { + parentCancel() + } + } +- rows.closemu.Unlock() + return nil + } + } +diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go +index c6c3172..95c0fa3 100644 +--- a/src/database/sql/fakedb_test.go ++++ b/src/database/sql/fakedb_test.go +@@ -5,6 +5,7 @@ + package sql + + import ( ++ "bytes" + "context" + "database/sql/driver" + "errors" +@@ -15,7 +16,6 @@ import ( + "strconv" + "strings" + "sync" +- "sync/atomic" + "testing" + "time" + ) +@@ -91,8 +91,6 @@ func (cc *fakeDriverCtx) OpenConnector(name string) (driver.Connector, error) { + type fakeDB struct { + name string + +- useRawBytes atomic.Bool +- + mu sync.Mutex + tables map[string]*table + badConn bool +@@ -700,8 +698,6 @@ func (c *fakeConn) PrepareContext(ctx context.Context, query string) (driver.Stm + switch cmd { + case "WIPE": + // Nothing +- case "USE_RAWBYTES": +- c.db.useRawBytes.Store(true) + case "SELECT": + stmt, err = c.prepareSelect(stmt, parts) + case "CREATE": +@@ -805,9 +801,6 @@ func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (d + case "WIPE": + db.wipe() + return driver.ResultNoRows, nil +- case "USE_RAWBYTES": +- s.c.db.useRawBytes.Store(true) +- return driver.ResultNoRows, nil + case "CREATE": + if err := db.createTable(s.table, s.colName, s.colType); err != nil { + return nil, err +@@ -1090,10 +1083,9 @@ type rowsCursor struct { + errPos int + err error + +- // a clone of slices to give out to clients, indexed by the +- // original slice's first byte address. we clone them +- // just so we're able to corrupt them on close. +- bytesClone map[*byte][]byte ++ // Data returned to clients. ++ // We clone and stash it here so it can be invalidated by Close and Next. ++ driverOwnedMemory [][]byte + + // Every operation writes to line to enable the race detector + // check for data races. +@@ -1110,9 +1102,19 @@ func (rc *rowsCursor) touchMem() { + rc.line++ + } + ++func (rc *rowsCursor) invalidateDriverOwnedMemory() { ++ for _, buf := range rc.driverOwnedMemory { ++ for i := range buf { ++ buf[i] = 'x' ++ } ++ } ++ rc.driverOwnedMemory = nil ++} ++ + func (rc *rowsCursor) Close() error { + rc.touchMem() + rc.parentMem.touchMem() ++ rc.invalidateDriverOwnedMemory() + rc.closed = true + return rc.closeErr + } +@@ -1143,6 +1145,8 @@ func (rc *rowsCursor) Next(dest []driver.Value) error { + if rc.posRow >= len(rc.rows[rc.posSet]) { + return io.EOF // per interface spec + } ++ // Corrupt any previously returned bytes. ++ rc.invalidateDriverOwnedMemory() + for i, v := range rc.rows[rc.posSet][rc.posRow].cols { + // TODO(bradfitz): convert to subset types? naah, I + // think the subset types should only be input to +@@ -1150,20 +1154,13 @@ func (rc *rowsCursor) Next(dest []driver.Value) error { + // a wider range of types coming out of drivers. all + // for ease of drivers, and to prevent drivers from + // messing up conversions or doing them differently. +- dest[i] = v +- +- if bs, ok := v.([]byte); ok && !rc.db.useRawBytes.Load() { +- if rc.bytesClone == nil { +- rc.bytesClone = make(map[*byte][]byte) +- } +- clone, ok := rc.bytesClone[&bs[0]] +- if !ok { +- clone = make([]byte, len(bs)) +- copy(clone, bs) +- rc.bytesClone[&bs[0]] = clone +- } +- dest[i] = clone ++ if bs, ok := v.([]byte); ok { ++ // Clone []bytes and stash for later invalidation. ++ bs = bytes.Clone(bs) ++ rc.driverOwnedMemory = append(rc.driverOwnedMemory, bs) ++ v = bs + } ++ dest[i] = v + } + return nil + } +diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go +index da0f52c..41130d9 100644 +--- a/src/database/sql/sql.go ++++ b/src/database/sql/sql.go +@@ -3357,37 +3357,33 @@ func (rs *Rows) Scan(dest ...any) error { + return fmt.Errorf("sql: Scan called without calling Next (closemuScanHold)") + } + rs.closemu.RLock() +- +- if rs.lasterr != nil && rs.lasterr != io.EOF { ++ rs.raw = rs.raw[:0] ++ err := rs.scanLocked(dest...) ++ if err == nil && scanArgsContainRawBytes(dest) { ++ rs.closemuScanHold = true ++ } else { + rs.closemu.RUnlock() ++ } ++ return err ++} ++func (rs *Rows) scanLocked(dest ...any) error { ++ if rs.lasterr != nil && rs.lasterr != io.EOF { + return rs.lasterr + } + if rs.closed { +- err := rs.lasterrOrErrLocked(errRowsClosed) +- rs.closemu.RUnlock() +- return err +- } +- +- if scanArgsContainRawBytes(dest) { +- rs.closemuScanHold = true +- rs.raw = rs.raw[:0] +- } else { +- rs.closemu.RUnlock() ++ return rs.lasterrOrErrLocked(errRowsClosed) + } + + if rs.lastcols == nil { +- rs.closemuRUnlockIfHeldByScan() + return errors.New("sql: Scan called without calling Next") + } + if len(dest) != len(rs.lastcols) { +- rs.closemuRUnlockIfHeldByScan() + return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest)) + } + + for i, sv := range rs.lastcols { + err := convertAssignRows(dest[i], sv, rs) + if err != nil { +- rs.closemuRUnlockIfHeldByScan() + return fmt.Errorf(`sql: Scan error on column index %d, name %q: %w`, i, rs.rowsi.Columns()[i], err) + } + } +diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go +index c3b4822..ee65b1b 100644 +--- a/src/database/sql/sql_test.go ++++ b/src/database/sql/sql_test.go +@@ -5,6 +5,7 @@ + package sql + + import ( ++ "bytes" + "context" + "database/sql/driver" + "errors" +@@ -4411,10 +4412,6 @@ func testContextCancelDuringRawBytesScan(t *testing.T, mode string) { + db := newTestDB(t, "people") + defer closeDB(t, db) + +- if _, err := db.Exec("USE_RAWBYTES"); err != nil { +- t.Fatal(err) +- } +- + // cancel used to call close asynchronously. + // This test checks that it waits so as not to interfere with RawBytes. + ctx, cancel := context.WithCancel(context.Background()) +@@ -4506,6 +4503,61 @@ func TestContextCancelBetweenNextAndErr(t *testing.T) { + } + } + ++type testScanner struct { ++ scanf func(src any) error ++} ++ ++func (ts testScanner) Scan(src any) error { return ts.scanf(src) } ++ ++func TestContextCancelDuringScan(t *testing.T) { ++ db := newTestDB(t, "people") ++ defer closeDB(t, db) ++ ++ ctx, cancel := context.WithCancel(context.Background()) ++ defer cancel() ++ ++ scanStart := make(chan any) ++ scanEnd := make(chan error) ++ scanner := &testScanner{ ++ scanf: func(src any) error { ++ scanStart <- src ++ return <-scanEnd ++ }, ++ } ++ ++ // Start a query, and pause it mid-scan. ++ want := []byte("Alice") ++ r, err := db.QueryContext(ctx, "SELECT|people|name|name=?", string(want)) ++ if err != nil { ++ t.Fatal(err) ++ } ++ if !r.Next() { ++ t.Fatalf("r.Next() = false, want true") ++ } ++ go func() { ++ r.Scan(scanner) ++ }() ++ got := <-scanStart ++ defer close(scanEnd) ++ gotBytes, ok := got.([]byte) ++ if !ok { ++ t.Fatalf("r.Scan returned %T, want []byte", got) ++ } ++ if !bytes.Equal(gotBytes, want) { ++ t.Fatalf("before cancel: r.Scan returned %q, want %q", gotBytes, want) ++ } ++ ++ // Cancel the query. ++ // Sleep to give it a chance to finish canceling. ++ cancel() ++ time.Sleep(10 * time.Millisecond) ++ ++ // Cancelling the query should not have changed the result. ++ if !bytes.Equal(gotBytes, want) { ++ t.Fatalf("after cancel: r.Scan result is now %q, want %q", gotBytes, want) ++ } ++} ++ + func TestNilErrorAfterClose(t *testing.T) { + db := newTestDB(t, "people") + defer closeDB(t, db) +-- +2.40.0