diff options
| -rw-r--r-- | meta/recipes-devtools/go/go-1.17.13.inc | 125 | ||||
| -rw-r--r-- | meta/recipes-devtools/go/go-1.21/CVE-2025-47907-pre-0001.patch | 354 | ||||
| -rw-r--r-- | meta/recipes-devtools/go/go-1.21/CVE-2025-47907-pre-0002.patch | 232 | ||||
| -rw-r--r-- | meta/recipes-devtools/go/go-1.21/CVE-2025-47907.patch | 327 |
4 files changed, 977 insertions, 61 deletions
diff --git a/meta/recipes-devtools/go/go-1.17.13.inc b/meta/recipes-devtools/go/go-1.17.13.inc index b17853035b..2052f4adbc 100644 --- a/meta/recipes-devtools/go/go-1.17.13.inc +++ b/meta/recipes-devtools/go/go-1.17.13.inc | |||
| @@ -4,67 +4,70 @@ FILESEXTRAPATHS:prepend := "${FILE_DIRNAME}/go-1.21:${FILE_DIRNAME}/go-1.20:${FI | |||
| 4 | 4 | ||
| 5 | LIC_FILES_CHKSUM = "file://LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707" | 5 | LIC_FILES_CHKSUM = "file://LICENSE;md5=5d4950ecb7b26d2c5e4e7b4e0dd74707" |
| 6 | 6 | ||
| 7 | SRC_URI += "\ | 7 | SRC_URI = "https://golang.org/dl/go${PV}.src.tar.gz;name=main \ |
| 8 | file://0001-allow-CC-and-CXX-to-have-multiple-words.patch \ | 8 | file://0001-allow-CC-and-CXX-to-have-multiple-words.patch \ |
| 9 | file://0002-cmd-go-make-content-based-hash-generation-less-pedan.patch \ | 9 | file://0002-cmd-go-make-content-based-hash-generation-less-pedan.patch \ |
| 10 | file://0003-allow-GOTOOLDIR-to-be-overridden-in-the-environment.patch \ | 10 | file://0003-allow-GOTOOLDIR-to-be-overridden-in-the-environment.patch \ |
| 11 | file://0004-ld-add-soname-to-shareable-objects.patch \ | 11 | file://0004-ld-add-soname-to-shareable-objects.patch \ |
| 12 | file://0005-make.bash-override-CC-when-building-dist-and-go_boot.patch \ | 12 | file://0005-make.bash-override-CC-when-building-dist-and-go_boot.patch \ |
| 13 | file://0006-cmd-dist-separate-host-and-target-builds.patch \ | 13 | file://0006-cmd-dist-separate-host-and-target-builds.patch \ |
| 14 | file://0007-cmd-go-make-GOROOT-precious-by-default.patch \ | 14 | file://0007-cmd-go-make-GOROOT-precious-by-default.patch \ |
| 15 | file://0008-use-GOBUILDMODE-to-set-buildmode.patch \ | 15 | file://0008-use-GOBUILDMODE-to-set-buildmode.patch \ |
| 16 | file://0009-Revert-cmd-go-make-sure-CC-and-CXX-are-absolute.patch \ | 16 | file://0009-Revert-cmd-go-make-sure-CC-and-CXX-are-absolute.patch \ |
| 17 | file://0001-exec.go-do-not-write-linker-flags-into-buildids.patch \ | 17 | file://0001-exec.go-do-not-write-linker-flags-into-buildids.patch \ |
| 18 | file://0001-src-cmd-dist-buildgo.go-do-not-hardcode-host-compile.patch \ | 18 | file://0001-src-cmd-dist-buildgo.go-do-not-hardcode-host-compile.patch \ |
| 19 | file://0010-net-Fix-issue-with-DNS-not-being-updated.patch \ | 19 | file://0010-net-Fix-issue-with-DNS-not-being-updated.patch \ |
| 20 | file://CVE-2022-27664.patch \ | 20 | file://CVE-2022-27664.patch \ |
| 21 | file://0001-net-http-httputil-avoid-query-parameter-smuggling.patch \ | 21 | file://0001-net-http-httputil-avoid-query-parameter-smuggling.patch \ |
| 22 | file://CVE-2022-41715.patch \ | 22 | file://CVE-2022-41715.patch \ |
| 23 | file://CVE-2022-41717.patch \ | 23 | file://CVE-2022-41717.patch \ |
| 24 | file://CVE-2022-2879.patch \ | 24 | file://CVE-2022-2879.patch \ |
| 25 | file://CVE-2022-41720.patch \ | 25 | file://CVE-2022-41720.patch \ |
| 26 | file://CVE-2022-41723.patch \ | 26 | file://CVE-2022-41723.patch \ |
| 27 | file://cve-2022-41724.patch \ | 27 | file://cve-2022-41724.patch \ |
| 28 | file://add_godebug.patch \ | 28 | file://add_godebug.patch \ |
| 29 | file://cve-2022-41725.patch \ | 29 | file://cve-2022-41725.patch \ |
| 30 | file://CVE-2022-41722.patch \ | 30 | file://CVE-2022-41722.patch \ |
| 31 | file://CVE-2023-24537.patch \ | 31 | file://CVE-2023-24537.patch \ |
| 32 | file://CVE-2023-24534.patch \ | 32 | file://CVE-2023-24534.patch \ |
| 33 | file://CVE-2023-24538_1.patch \ | 33 | file://CVE-2023-24538_1.patch \ |
| 34 | file://CVE-2023-24538_2.patch \ | 34 | file://CVE-2023-24538_2.patch \ |
| 35 | file://CVE-2023-24540.patch \ | 35 | file://CVE-2023-24540.patch \ |
| 36 | file://CVE-2023-24539.patch \ | 36 | file://CVE-2023-24539.patch \ |
| 37 | file://CVE-2023-29404.patch \ | 37 | file://CVE-2023-29404.patch \ |
| 38 | file://CVE-2023-29405.patch \ | 38 | file://CVE-2023-29405.patch \ |
| 39 | file://CVE-2023-29402.patch \ | 39 | file://CVE-2023-29402.patch \ |
| 40 | file://CVE-2023-29400.patch \ | 40 | file://CVE-2023-29400.patch \ |
| 41 | file://CVE-2023-29406-1.patch \ | 41 | file://CVE-2023-29406-1.patch \ |
| 42 | file://CVE-2023-29406-2.patch \ | 42 | file://CVE-2023-29406-2.patch \ |
| 43 | file://CVE-2023-24536_1.patch \ | 43 | file://CVE-2023-24536_1.patch \ |
| 44 | file://CVE-2023-24536_2.patch \ | 44 | file://CVE-2023-24536_2.patch \ |
| 45 | file://CVE-2023-24536_3.patch \ | 45 | file://CVE-2023-24536_3.patch \ |
| 46 | file://CVE-2023-24531_1.patch \ | 46 | file://CVE-2023-24531_1.patch \ |
| 47 | file://CVE-2023-24531_2.patch \ | 47 | file://CVE-2023-24531_2.patch \ |
| 48 | file://CVE-2023-29409.patch \ | 48 | file://CVE-2023-29409.patch \ |
| 49 | file://CVE-2023-39319.patch \ | 49 | file://CVE-2023-39319.patch \ |
| 50 | file://CVE-2023-39318.patch \ | 50 | file://CVE-2023-39318.patch \ |
| 51 | file://CVE-2023-39326.patch \ | 51 | file://CVE-2023-39326.patch \ |
| 52 | file://CVE-2023-45285.patch \ | 52 | file://CVE-2023-45285.patch \ |
| 53 | file://CVE-2023-45287.patch \ | 53 | file://CVE-2023-45287.patch \ |
| 54 | file://CVE-2023-45289.patch \ | 54 | file://CVE-2023-45289.patch \ |
| 55 | file://CVE-2023-45290.patch \ | 55 | file://CVE-2023-45290.patch \ |
| 56 | file://CVE-2024-24784.patch \ | 56 | file://CVE-2024-24784.patch \ |
| 57 | file://CVE-2024-24785.patch \ | 57 | file://CVE-2024-24785.patch \ |
| 58 | file://CVE-2023-45288.patch \ | 58 | file://CVE-2023-45288.patch \ |
| 59 | file://CVE-2024-24789.patch \ | 59 | file://CVE-2024-24789.patch \ |
| 60 | file://CVE-2024-24791.patch \ | 60 | file://CVE-2024-24791.patch \ |
| 61 | file://CVE-2024-34155.patch \ | 61 | file://CVE-2024-34155.patch \ |
| 62 | file://CVE-2024-34156.patch \ | 62 | file://CVE-2024-34156.patch \ |
| 63 | file://CVE-2024-34158.patch \ | 63 | file://CVE-2024-34158.patch \ |
| 64 | file://CVE-2024-45336.patch \ | 64 | file://CVE-2024-45336.patch \ |
| 65 | file://CVE-2025-22871.patch \ | 65 | file://CVE-2025-22871.patch \ |
| 66 | file://CVE-2025-4673.patch \ | 66 | file://CVE-2025-4673.patch \ |
| 67 | " | 67 | file://CVE-2025-47907-pre-0001.patch \ |
| 68 | file://CVE-2025-47907-pre-0002.patch \ | ||
| 69 | file://CVE-2025-47907.patch \ | ||
| 70 | " | ||
| 68 | SRC_URI[main.sha256sum] = "a1a48b23afb206f95e7bbaa9b898d965f90826f6f1d1fc0c1d784ada0cd300fd" | 71 | SRC_URI[main.sha256sum] = "a1a48b23afb206f95e7bbaa9b898d965f90826f6f1d1fc0c1d784ada0cd300fd" |
| 69 | 72 | ||
| 70 | # Upstream don't believe it is a signifiant real world issue and will only | 73 | # Upstream don't believe it is a signifiant real world issue and will only |
diff --git a/meta/recipes-devtools/go/go-1.21/CVE-2025-47907-pre-0001.patch b/meta/recipes-devtools/go/go-1.21/CVE-2025-47907-pre-0001.patch new file mode 100644 index 0000000000..97e7539dc3 --- /dev/null +++ b/meta/recipes-devtools/go/go-1.21/CVE-2025-47907-pre-0001.patch | |||
| @@ -0,0 +1,354 @@ | |||
| 1 | From 298fe517a9333c05143a8a8e1f9d5499f0c6e59b Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Brad Fitzpatrick <bradfitz@golang.org> | ||
| 3 | Date: Tue, 23 May 2023 15:12:47 -0700 | ||
| 4 | Subject: [PATCH] database/sql: make RawBytes safely usable with contexts | ||
| 5 | |||
| 6 | sql.RawBytes was added the very first Go release, Go 1. Its docs | ||
| 7 | say: | ||
| 8 | |||
| 9 | > RawBytes is a byte slice that holds a reference to memory owned by | ||
| 10 | > the database itself. After a Scan into a RawBytes, the slice is only | ||
| 11 | > valid until the next call to Next, Scan, or Close. | ||
| 12 | |||
| 13 | That "only valid until the next call" bit was true at the time, | ||
| 14 | until contexts were added to database/sql in Go 1.8. | ||
| 15 | |||
| 16 | In the past ~dozen releases it's been unsafe to use QueryContext with | ||
| 17 | a context that might become Done to get an *sql.Rows that's scanning | ||
| 18 | into a RawBytes. The Scan can succeed, but then while the caller's | ||
| 19 | reading the memory, a database/sql-managed goroutine can see the | ||
| 20 | context becoming done and call Close on the database/sql/driver and | ||
| 21 | make the caller's view of the RawBytes memory no longer valid, | ||
| 22 | introducing races, crashes, or database corruption. See #60304 | ||
| 23 | and #53970 for details. | ||
| 24 | |||
| 25 | This change does the minimal surgery on database/sql to make it safe | ||
| 26 | again: Rows.Scan was already acquiring a mutex to check whether the | ||
| 27 | rows had been closed, so this change make Rows.Scan notice whether | ||
| 28 | *RawBytes was used and, if so, doesn't release the mutex on exit | ||
| 29 | before returning. That mean it's still locked while the user code | ||
| 30 | operates on the RawBytes memory and the concurrent context-watching | ||
| 31 | goroutine to close the database still runs, but if it fires, it then | ||
| 32 | gets blocked on the mutex until the next call to a Rows method (Next, | ||
| 33 | NextResultSet, Err, Close). | ||
| 34 | |||
| 35 | Updates #60304 | ||
| 36 | Updates #53970 (earlier one I'd missed) | ||
| 37 | |||
| 38 | Change-Id: Ie41c0c6f32c24887b2f53ec3686c2aab73a1bfff | ||
| 39 | Reviewed-on: https://go-review.googlesource.com/c/go/+/497675 | ||
| 40 | TryBot-Result: Gopher Robot <gobot@golang.org> | ||
| 41 | Reviewed-by: Ian Lance Taylor <iant@google.com> | ||
| 42 | Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> | ||
| 43 | Auto-Submit: Ian Lance Taylor <iant@google.com> | ||
| 44 | Reviewed-by: Russ Cox <rsc@golang.org> | ||
| 45 | |||
| 46 | CVE: CVE-2025-47907 | ||
| 47 | |||
| 48 | Upstream-Status: Backport [https://github.com/golang/go/commit/298fe517a9333c05143a8a8e1f9d5499f0c6e59b] | ||
| 49 | |||
| 50 | Signed-off-by: Praveen Kumar <praveen.kumar@windriver.com> | ||
| 51 | --- | ||
| 52 | src/database/sql/fakedb_test.go | 13 +++++- | ||
| 53 | src/database/sql/sql.go | 72 ++++++++++++++++++++++++++++++++- | ||
| 54 | src/database/sql/sql_test.go | 58 ++++++++++++++++++++++++++ | ||
| 55 | 3 files changed, 141 insertions(+), 2 deletions(-) | ||
| 56 | |||
| 57 | diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go | ||
| 58 | index 4b68f1c..33c57b9 100644 | ||
| 59 | --- a/src/database/sql/fakedb_test.go | ||
| 60 | +++ b/src/database/sql/fakedb_test.go | ||
| 61 | @@ -15,6 +15,7 @@ import ( | ||
| 62 | "strconv" | ||
| 63 | "strings" | ||
| 64 | "sync" | ||
| 65 | + "sync/atomic" | ||
| 66 | "testing" | ||
| 67 | "time" | ||
| 68 | ) | ||
| 69 | @@ -90,6 +91,8 @@ func (cc *fakeDriverCtx) OpenConnector(name string) (driver.Connector, error) { | ||
| 70 | type fakeDB struct { | ||
| 71 | name string | ||
| 72 | |||
| 73 | + useRawBytes atomic.Bool | ||
| 74 | + | ||
| 75 | mu sync.Mutex | ||
| 76 | tables map[string]*table | ||
| 77 | badConn bool | ||
| 78 | @@ -680,6 +683,8 @@ func (c *fakeConn) PrepareContext(ctx context.Context, query string) (driver.Stm | ||
| 79 | switch cmd { | ||
| 80 | case "WIPE": | ||
| 81 | // Nothing | ||
| 82 | + case "USE_RAWBYTES": | ||
| 83 | + c.db.useRawBytes.Store(true) | ||
| 84 | case "SELECT": | ||
| 85 | stmt, err = c.prepareSelect(stmt, parts) | ||
| 86 | case "CREATE": | ||
| 87 | @@ -783,6 +788,9 @@ func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (d | ||
| 88 | case "WIPE": | ||
| 89 | db.wipe() | ||
| 90 | return driver.ResultNoRows, nil | ||
| 91 | + case "USE_RAWBYTES": | ||
| 92 | + s.c.db.useRawBytes.Store(true) | ||
| 93 | + return driver.ResultNoRows, nil | ||
| 94 | case "CREATE": | ||
| 95 | if err := db.createTable(s.table, s.colName, s.colType); err != nil { | ||
| 96 | return nil, err | ||
| 97 | @@ -912,6 +920,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) ( | ||
| 98 | txStatus = "transaction" | ||
| 99 | } | ||
| 100 | cursor := &rowsCursor{ | ||
| 101 | + db: s.c.db, | ||
| 102 | parentMem: s.c, | ||
| 103 | posRow: -1, | ||
| 104 | rows: [][]*row{ | ||
| 105 | @@ -1008,6 +1017,7 @@ func (s *fakeStmt) QueryContext(ctx context.Context, args []driver.NamedValue) ( | ||
| 106 | } | ||
| 107 | |||
| 108 | cursor := &rowsCursor{ | ||
| 109 | + db: s.c.db, | ||
| 110 | parentMem: s.c, | ||
| 111 | posRow: -1, | ||
| 112 | rows: setMRows, | ||
| 113 | @@ -1050,6 +1060,7 @@ func (tx *fakeTx) Rollback() error { | ||
| 114 | } | ||
| 115 | |||
| 116 | type rowsCursor struct { | ||
| 117 | + db *fakeDB | ||
| 118 | parentMem memToucher | ||
| 119 | cols [][]string | ||
| 120 | colType [][]string | ||
| 121 | @@ -1121,7 +1132,7 @@ func (rc *rowsCursor) Next(dest []driver.Value) error { | ||
| 122 | // messing up conversions or doing them differently. | ||
| 123 | dest[i] = v | ||
| 124 | |||
| 125 | - if bs, ok := v.([]byte); ok { | ||
| 126 | + if bs, ok := v.([]byte); ok && !rc.db.useRawBytes.Load() { | ||
| 127 | if rc.bytesClone == nil { | ||
| 128 | rc.bytesClone = make(map[*byte][]byte) | ||
| 129 | } | ||
| 130 | diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go | ||
| 131 | index 68fb392..ef49e70 100644 | ||
| 132 | --- a/src/database/sql/sql.go | ||
| 133 | +++ b/src/database/sql/sql.go | ||
| 134 | @@ -2879,6 +2879,8 @@ type Rows struct { | ||
| 135 | cancel func() // called when Rows is closed, may be nil. | ||
| 136 | closeStmt *driverStmt // if non-nil, statement to Close on close | ||
| 137 | |||
| 138 | + contextDone atomic.Value // error that awaitDone saw; set before close attempt | ||
| 139 | + | ||
| 140 | // closemu prevents Rows from closing while there | ||
| 141 | // is an active streaming result. It is held for read during non-close operations | ||
| 142 | // and exclusively during close. | ||
| 143 | @@ -2891,6 +2893,15 @@ type Rows struct { | ||
| 144 | // lastcols is only used in Scan, Next, and NextResultSet which are expected | ||
| 145 | // not to be called concurrently. | ||
| 146 | lastcols []driver.Value | ||
| 147 | + | ||
| 148 | + // closemuScanHold is whether the previous call to Scan kept closemu RLock'ed | ||
| 149 | + // without unlocking it. It does that when the user passes a *RawBytes scan | ||
| 150 | + // target. In that case, we need to prevent awaitDone from closing the Rows | ||
| 151 | + // while the user's still using the memory. See go.dev/issue/60304. | ||
| 152 | + // | ||
| 153 | + // It is only used by Scan, Next, and NextResultSet which are expected | ||
| 154 | + // not to be called concurrently. | ||
| 155 | + closemuScanHold bool | ||
| 156 | } | ||
| 157 | |||
| 158 | // lasterrOrErrLocked returns either lasterr or the provided err. | ||
| 159 | @@ -2928,7 +2939,11 @@ func (rs *Rows) awaitDone(ctx, txctx context.Context) { | ||
| 160 | } | ||
| 161 | select { | ||
| 162 | case <-ctx.Done(): | ||
| 163 | + err := ctx.Err() | ||
| 164 | + rs.contextDone.Store(&err) | ||
| 165 | case <-txctxDone: | ||
| 166 | + err := txctx.Err() | ||
| 167 | + rs.contextDone.Store(&err) | ||
| 168 | } | ||
| 169 | rs.close(ctx.Err()) | ||
| 170 | } | ||
| 171 | @@ -2940,6 +2955,15 @@ func (rs *Rows) awaitDone(ctx, txctx context.Context) { | ||
| 172 | // | ||
| 173 | // Every call to Scan, even the first one, must be preceded by a call to Next. | ||
| 174 | func (rs *Rows) Next() bool { | ||
| 175 | + // If the user's calling Next, they're done with their previous row's Scan | ||
| 176 | + // results (any RawBytes memory), so we can release the read lock that would | ||
| 177 | + // be preventing awaitDone from calling close. | ||
| 178 | + rs.closemuRUnlockIfHeldByScan() | ||
| 179 | + | ||
| 180 | + if rs.contextDone.Load() != nil { | ||
| 181 | + return false | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | var doClose, ok bool | ||
| 185 | withLock(rs.closemu.RLocker(), func() { | ||
| 186 | doClose, ok = rs.nextLocked() | ||
| 187 | @@ -2994,6 +3018,11 @@ func (rs *Rows) nextLocked() (doClose, ok bool) { | ||
| 188 | // scanning. If there are further result sets they may not have rows in the result | ||
| 189 | // set. | ||
| 190 | func (rs *Rows) NextResultSet() bool { | ||
| 191 | + // If the user's calling NextResultSet, they're done with their previous | ||
| 192 | + // row's Scan results (any RawBytes memory), so we can release the read lock | ||
| 193 | + // that would be preventing awaitDone from calling close. | ||
| 194 | + rs.closemuRUnlockIfHeldByScan() | ||
| 195 | + | ||
| 196 | var doClose bool | ||
| 197 | defer func() { | ||
| 198 | if doClose { | ||
| 199 | @@ -3030,6 +3059,10 @@ func (rs *Rows) NextResultSet() bool { | ||
| 200 | // Err returns the error, if any, that was encountered during iteration. | ||
| 201 | // Err may be called after an explicit or implicit Close. | ||
| 202 | func (rs *Rows) Err() error { | ||
| 203 | + if errp := rs.contextDone.Load(); errp != nil { | ||
| 204 | + return *(errp.(*error)) | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | rs.closemu.RLock() | ||
| 208 | defer rs.closemu.RUnlock() | ||
| 209 | return rs.lasterrOrErrLocked(nil) | ||
| 210 | @@ -3223,6 +3256,11 @@ func rowsColumnInfoSetupConnLocked(rowsi driver.Rows) []*ColumnType { | ||
| 211 | // If any of the first arguments implementing Scanner returns an error, | ||
| 212 | // that error will be wrapped in the returned error | ||
| 213 | func (rs *Rows) Scan(dest ...interface{}) error { | ||
| 214 | + if rs.closemuScanHold { | ||
| 215 | + // This should only be possible if the user calls Scan twice in a row | ||
| 216 | + // without calling Next. | ||
| 217 | + return fmt.Errorf("sql: Scan called without calling Next (closemuScanHold)") | ||
| 218 | + } | ||
| 219 | rs.closemu.RLock() | ||
| 220 | |||
| 221 | if rs.lasterr != nil && rs.lasterr != io.EOF { | ||
| 222 | @@ -3234,23 +3272,50 @@ func (rs *Rows) Scan(dest ...interface{}) error { | ||
| 223 | rs.closemu.RUnlock() | ||
| 224 | return err | ||
| 225 | } | ||
| 226 | - rs.closemu.RUnlock() | ||
| 227 | + | ||
| 228 | + if scanArgsContainRawBytes(dest) { | ||
| 229 | + rs.closemuScanHold = true | ||
| 230 | + } else { | ||
| 231 | + rs.closemu.RUnlock() | ||
| 232 | + } | ||
| 233 | |||
| 234 | if rs.lastcols == nil { | ||
| 235 | + rs.closemuRUnlockIfHeldByScan() | ||
| 236 | return errors.New("sql: Scan called without calling Next") | ||
| 237 | } | ||
| 238 | if len(dest) != len(rs.lastcols) { | ||
| 239 | + rs.closemuRUnlockIfHeldByScan() | ||
| 240 | return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest)) | ||
| 241 | } | ||
| 242 | + | ||
| 243 | for i, sv := range rs.lastcols { | ||
| 244 | err := convertAssignRows(dest[i], sv, rs) | ||
| 245 | if err != nil { | ||
| 246 | + rs.closemuRUnlockIfHeldByScan() | ||
| 247 | return fmt.Errorf(`sql: Scan error on column index %d, name %q: %w`, i, rs.rowsi.Columns()[i], err) | ||
| 248 | } | ||
| 249 | } | ||
| 250 | return nil | ||
| 251 | } | ||
| 252 | |||
| 253 | +// closemuRUnlockIfHeldByScan releases any closemu.RLock held open by a previous | ||
| 254 | +// call to Scan with *RawBytes. | ||
| 255 | +func (rs *Rows) closemuRUnlockIfHeldByScan() { | ||
| 256 | + if rs.closemuScanHold { | ||
| 257 | + rs.closemuScanHold = false | ||
| 258 | + rs.closemu.RUnlock() | ||
| 259 | + } | ||
| 260 | +} | ||
| 261 | + | ||
| 262 | +func scanArgsContainRawBytes(args []interface{}) bool { | ||
| 263 | + for _, a := range args { | ||
| 264 | + if _, ok := a.(*RawBytes); ok { | ||
| 265 | + return true | ||
| 266 | + } | ||
| 267 | + } | ||
| 268 | + return false | ||
| 269 | +} | ||
| 270 | + | ||
| 271 | // rowsCloseHook returns a function so tests may install the | ||
| 272 | // hook through a test only mutex. | ||
| 273 | var rowsCloseHook = func() func(*Rows, *error) { return nil } | ||
| 274 | @@ -3260,6 +3325,11 @@ var rowsCloseHook = func() func(*Rows, *error) { return nil } | ||
| 275 | // the Rows are closed automatically and it will suffice to check the | ||
| 276 | // result of Err. Close is idempotent and does not affect the result of Err. | ||
| 277 | func (rs *Rows) Close() error { | ||
| 278 | + // If the user's calling Close, they're done with their previous row's Scan | ||
| 279 | + // results (any RawBytes memory), so we can release the read lock that would | ||
| 280 | + // be preventing awaitDone from calling the unexported close before we do so. | ||
| 281 | + rs.closemuRUnlockIfHeldByScan() | ||
| 282 | + | ||
| 283 | return rs.close(nil) | ||
| 284 | } | ||
| 285 | |||
| 286 | diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go | ||
| 287 | index f771dee..53b38d1 100644 | ||
| 288 | --- a/src/database/sql/sql_test.go | ||
| 289 | +++ b/src/database/sql/sql_test.go | ||
| 290 | @@ -4255,6 +4255,64 @@ func TestRowsScanProperlyWrapsErrors(t *testing.T) { | ||
| 291 | } | ||
| 292 | } | ||
| 293 | |||
| 294 | +// From go.dev/issue/60304 | ||
| 295 | +func TestContextCancelDuringRawBytesScan(t *testing.T) { | ||
| 296 | + db := newTestDB(t, "people") | ||
| 297 | + defer closeDB(t, db) | ||
| 298 | + | ||
| 299 | + if _, err := db.Exec("USE_RAWBYTES"); err != nil { | ||
| 300 | + t.Fatal(err) | ||
| 301 | + } | ||
| 302 | + | ||
| 303 | + ctx, cancel := context.WithCancel(context.Background()) | ||
| 304 | + defer cancel() | ||
| 305 | + | ||
| 306 | + r, err := db.QueryContext(ctx, "SELECT|people|name|") | ||
| 307 | + if err != nil { | ||
| 308 | + t.Fatal(err) | ||
| 309 | + } | ||
| 310 | + numRows := 0 | ||
| 311 | + var sink byte | ||
| 312 | + for r.Next() { | ||
| 313 | + numRows++ | ||
| 314 | + var s RawBytes | ||
| 315 | + err = r.Scan(&s) | ||
| 316 | + if !r.closemuScanHold { | ||
| 317 | + t.Errorf("expected closemu to be held") | ||
| 318 | + } | ||
| 319 | + if err != nil { | ||
| 320 | + t.Fatal(err) | ||
| 321 | + } | ||
| 322 | + t.Logf("read %q", s) | ||
| 323 | + if numRows == 2 { | ||
| 324 | + cancel() // invalidate the context, which used to call close asynchronously | ||
| 325 | + } | ||
| 326 | + for _, b := range s { // some operation reading from the raw memory | ||
| 327 | + sink += b | ||
| 328 | + } | ||
| 329 | + } | ||
| 330 | + if r.closemuScanHold { | ||
| 331 | + t.Errorf("closemu held; should not be") | ||
| 332 | + } | ||
| 333 | + | ||
| 334 | + // There are 3 rows. We canceled after reading 2 so we expect either | ||
| 335 | + // 2 or 3 depending on how the awaitDone goroutine schedules. | ||
| 336 | + switch numRows { | ||
| 337 | + case 0, 1: | ||
| 338 | + t.Errorf("got %d rows; want 2+", numRows) | ||
| 339 | + case 2: | ||
| 340 | + if err := r.Err(); err != context.Canceled { | ||
| 341 | + t.Errorf("unexpected error: %v (%T)", err, err) | ||
| 342 | + } | ||
| 343 | + default: | ||
| 344 | + // Made it to the end. This is rare, but fine. Permit it. | ||
| 345 | + } | ||
| 346 | + | ||
| 347 | + if err := r.Close(); err != nil { | ||
| 348 | + t.Fatal(err) | ||
| 349 | + } | ||
| 350 | +} | ||
| 351 | + | ||
| 352 | // badConn implements a bad driver.Conn, for TestBadDriver. | ||
| 353 | // The Exec method panics. | ||
| 354 | type badConn struct{} | ||
diff --git a/meta/recipes-devtools/go/go-1.21/CVE-2025-47907-pre-0002.patch b/meta/recipes-devtools/go/go-1.21/CVE-2025-47907-pre-0002.patch new file mode 100644 index 0000000000..fe0b6a4d9c --- /dev/null +++ b/meta/recipes-devtools/go/go-1.21/CVE-2025-47907-pre-0002.patch | |||
| @@ -0,0 +1,232 @@ | |||
| 1 | From c23579f031ecd09bf37c644723b33736dffa8b92 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Damien Neil <dneil@google.com> | ||
| 3 | Date: Tue, 23 Jan 2024 15:59:47 -0800 | ||
| 4 | Subject: [PATCH] database/sql: avoid clobbering driver-owned memory in | ||
| 5 | RawBytes | ||
| 6 | |||
| 7 | Depending on the query, a RawBytes can contain memory owned by the | ||
| 8 | driver or by database/sql: | ||
| 9 | |||
| 10 | If the driver provides the column as a []byte, | ||
| 11 | RawBytes aliases that []byte. | ||
| 12 | |||
| 13 | If the driver provides the column as any other type, | ||
| 14 | RawBytes contains memory allocated by database/sql. | ||
| 15 | Prior to this CL, Rows.Scan will reuse existing capacity in a | ||
| 16 | RawBytes to permit a single allocation to be reused across rows. | ||
| 17 | |||
| 18 | When a RawBytes is reused across queries, this can result | ||
| 19 | in database/sql writing to driver-owned memory. | ||
| 20 | |||
| 21 | Add a buffer to Rows to store RawBytes data, and reuse this | ||
| 22 | buffer across calls to Rows.Scan. | ||
| 23 | |||
| 24 | Fixes #65201 | ||
| 25 | |||
| 26 | Change-Id: Iac640174c7afa97eeb39496f47dec202501b2483 | ||
| 27 | Reviewed-on: https://go-review.googlesource.com/c/go/+/557917 | ||
| 28 | Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> | ||
| 29 | Reviewed-by: Roland Shoemaker <roland@golang.org> | ||
| 30 | LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> | ||
| 31 | |||
| 32 | CVE: CVE-2025-47907 | ||
| 33 | |||
| 34 | Upstream-Status: Backport [https://github.com/golang/go/commit/c23579f031ecd09bf37c644723b33736dffa8b92] | ||
| 35 | |||
| 36 | Signed-off-by: Praveen Kumar <praveen.kumar@windriver.com> | ||
| 37 | --- | ||
| 38 | src/database/sql/convert.go | 8 +++--- | ||
| 39 | src/database/sql/convert_test.go | 14 +++++++--- | ||
| 40 | src/database/sql/sql.go | 34 +++++++++++++++++++++++ | ||
| 41 | src/database/sql/sql_test.go | 47 ++++++++++++++++++++++++++++++++ | ||
| 42 | 4 files changed, 95 insertions(+), 8 deletions(-) | ||
| 43 | |||
| 44 | diff --git a/src/database/sql/convert.go b/src/database/sql/convert.go | ||
| 45 | index b966ef9..3a581f6 100644 | ||
| 46 | --- a/src/database/sql/convert.go | ||
| 47 | +++ b/src/database/sql/convert.go | ||
| 48 | @@ -237,7 +237,7 @@ func convertAssignRows(dest, src interface{}, rows *Rows) error { | ||
| 49 | if d == nil { | ||
| 50 | return errNilPtr | ||
| 51 | } | ||
| 52 | - *d = append((*d)[:0], s...) | ||
| 53 | + *d = rows.setrawbuf(append(rows.rawbuf(), s...)) | ||
| 54 | return nil | ||
| 55 | } | ||
| 56 | case []byte: | ||
| 57 | @@ -285,7 +285,7 @@ func convertAssignRows(dest, src interface{}, rows *Rows) error { | ||
| 58 | if d == nil { | ||
| 59 | return errNilPtr | ||
| 60 | } | ||
| 61 | - *d = s.AppendFormat((*d)[:0], time.RFC3339Nano) | ||
| 62 | + *d = rows.setrawbuf(s.AppendFormat(rows.rawbuf(), time.RFC3339Nano)) | ||
| 63 | return nil | ||
| 64 | } | ||
| 65 | case decimalDecompose: | ||
| 66 | @@ -366,8 +366,8 @@ func convertAssignRows(dest, src interface{}, rows *Rows) error { | ||
| 67 | } | ||
| 68 | case *RawBytes: | ||
| 69 | sv = reflect.ValueOf(src) | ||
| 70 | - if b, ok := asBytes([]byte(*d)[:0], sv); ok { | ||
| 71 | - *d = RawBytes(b) | ||
| 72 | + if b, ok := asBytes(rows.rawbuf(), sv); ok { | ||
| 73 | + *d = rows.setrawbuf(b) | ||
| 74 | return nil | ||
| 75 | } | ||
| 76 | case *bool: | ||
| 77 | diff --git a/src/database/sql/convert_test.go b/src/database/sql/convert_test.go | ||
| 78 | index 2668a5e..23a70bf 100644 | ||
| 79 | --- a/src/database/sql/convert_test.go | ||
| 80 | +++ b/src/database/sql/convert_test.go | ||
| 81 | @@ -357,9 +357,10 @@ func TestRawBytesAllocs(t *testing.T) { | ||
| 82 | {"time", time.Unix(2, 5).UTC(), "1970-01-01T00:00:02.000000005Z"}, | ||
| 83 | } | ||
| 84 | |||
| 85 | - buf := make(RawBytes, 10) | ||
| 86 | - test := func(name string, in interface{}, want string) { | ||
| 87 | - if err := convertAssign(&buf, in); err != nil { | ||
| 88 | + var buf RawBytes | ||
| 89 | + rows := &Rows{} | ||
| 90 | + test := func(name string, in interface{}, want string) { | ||
| 91 | + if err := convertAssignRows(&buf, in, rows); err != nil { | ||
| 92 | t.Fatalf("%s: convertAssign = %v", name, err) | ||
| 93 | } | ||
| 94 | match := len(buf) == len(want) | ||
| 95 | @@ -378,6 +379,7 @@ func TestRawBytesAllocs(t *testing.T) { | ||
| 96 | |||
| 97 | n := testing.AllocsPerRun(100, func() { | ||
| 98 | for _, tt := range tests { | ||
| 99 | + rows.raw = rows.raw[:0] | ||
| 100 | test(tt.name, tt.in, tt.want) | ||
| 101 | } | ||
| 102 | }) | ||
| 103 | @@ -386,7 +388,11 @@ func TestRawBytesAllocs(t *testing.T) { | ||
| 104 | // and gc. With 32-bit words there are more convT2E allocs, and | ||
| 105 | // with gccgo, only pointers currently go in interface data. | ||
| 106 | // So only care on amd64 gc for now. | ||
| 107 | - measureAllocs := runtime.GOARCH == "amd64" && runtime.Compiler == "gc" | ||
| 108 | + measureAllocs := false | ||
| 109 | + switch runtime.GOARCH { | ||
| 110 | + case "amd64", "arm64": | ||
| 111 | + measureAllocs = runtime.Compiler == "gc" | ||
| 112 | + } | ||
| 113 | |||
| 114 | if n > 0.5 && measureAllocs { | ||
| 115 | t.Fatalf("allocs = %v; want 0", n) | ||
| 116 | diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go | ||
| 117 | index ef49e70..e25447c 100644 | ||
| 118 | --- a/src/database/sql/sql.go | ||
| 119 | +++ b/src/database/sql/sql.go | ||
| 120 | @@ -2894,6 +2894,13 @@ type Rows struct { | ||
| 121 | // not to be called concurrently. | ||
| 122 | lastcols []driver.Value | ||
| 123 | |||
| 124 | + // raw is a buffer for RawBytes that persists between Scan calls. | ||
| 125 | + // This is used when the driver returns a mismatched type that requires | ||
| 126 | + // a cloning allocation. For example, if the driver returns a *string and | ||
| 127 | + // the user is scanning into a *RawBytes, we need to copy the string. | ||
| 128 | + // The raw buffer here lets us reuse the memory for that copy across Scan calls. | ||
| 129 | + raw []byte | ||
| 130 | + | ||
| 131 | // closemuScanHold is whether the previous call to Scan kept closemu RLock'ed | ||
| 132 | // without unlocking it. It does that when the user passes a *RawBytes scan | ||
| 133 | // target. In that case, we need to prevent awaitDone from closing the Rows | ||
| 134 | @@ -3068,6 +3075,32 @@ func (rs *Rows) Err() error { | ||
| 135 | return rs.lasterrOrErrLocked(nil) | ||
| 136 | } | ||
| 137 | |||
| 138 | +// rawbuf returns the buffer to append RawBytes values to. | ||
| 139 | +// This buffer is reused across calls to Rows.Scan. | ||
| 140 | +// | ||
| 141 | +// Usage: | ||
| 142 | +// | ||
| 143 | +// rawBytes = rows.setrawbuf(append(rows.rawbuf(), value...)) | ||
| 144 | +func (rs *Rows) rawbuf() []byte { | ||
| 145 | + if rs == nil { | ||
| 146 | + // convertAssignRows can take a nil *Rows; for simplicity handle it here | ||
| 147 | + return nil | ||
| 148 | + } | ||
| 149 | + return rs.raw | ||
| 150 | +} | ||
| 151 | + | ||
| 152 | +// setrawbuf updates the RawBytes buffer with the result of appending a new value to it. | ||
| 153 | +// It returns the new value. | ||
| 154 | +func (rs *Rows) setrawbuf(b []byte) RawBytes { | ||
| 155 | + if rs == nil { | ||
| 156 | + // convertAssignRows can take a nil *Rows; for simplicity handle it here | ||
| 157 | + return RawBytes(b) | ||
| 158 | + } | ||
| 159 | + off := len(rs.raw) | ||
| 160 | + rs.raw = b | ||
| 161 | + return RawBytes(rs.raw[off:]) | ||
| 162 | +} | ||
| 163 | + | ||
| 164 | var errRowsClosed = errors.New("sql: Rows are closed") | ||
| 165 | var errNoRows = errors.New("sql: no Rows available") | ||
| 166 | |||
| 167 | @@ -3275,6 +3308,7 @@ func (rs *Rows) Scan(dest ...interface{}) error { | ||
| 168 | |||
| 169 | if scanArgsContainRawBytes(dest) { | ||
| 170 | rs.closemuScanHold = true | ||
| 171 | + rs.raw = rs.raw[:0] | ||
| 172 | } else { | ||
| 173 | rs.closemu.RUnlock() | ||
| 174 | } | ||
| 175 | diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go | ||
| 176 | index 53b38d1..6aa9bf0 100644 | ||
| 177 | --- a/src/database/sql/sql_test.go | ||
| 178 | +++ b/src/database/sql/sql_test.go | ||
| 179 | @@ -4313,6 +4313,53 @@ func TestContextCancelDuringRawBytesScan(t *testing.T) { | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | +// Issue #65201. | ||
| 184 | +// | ||
| 185 | +// If a RawBytes is reused across multiple queries, | ||
| 186 | +// subsequent queries shouldn't overwrite driver-owned memory from previous queries. | ||
| 187 | +func TestRawBytesReuse(t *testing.T) { | ||
| 188 | + db := newTestDB(t, "people") | ||
| 189 | + defer closeDB(t, db) | ||
| 190 | + | ||
| 191 | + if _, err := db.Exec("USE_RAWBYTES"); err != nil { | ||
| 192 | + t.Fatal(err) | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + var raw RawBytes | ||
| 196 | + | ||
| 197 | + // The RawBytes in this query aliases driver-owned memory. | ||
| 198 | + rows, err := db.Query("SELECT|people|name|") | ||
| 199 | + if err != nil { | ||
| 200 | + t.Fatal(err) | ||
| 201 | + } | ||
| 202 | + rows.Next() | ||
| 203 | + rows.Scan(&raw) // now raw is pointing to driver-owned memory | ||
| 204 | + name1 := string(raw) | ||
| 205 | + rows.Close() | ||
| 206 | + | ||
| 207 | + // The RawBytes in this query does not alias driver-owned memory. | ||
| 208 | + rows, err = db.Query("SELECT|people|age|") | ||
| 209 | + if err != nil { | ||
| 210 | + t.Fatal(err) | ||
| 211 | + } | ||
| 212 | + rows.Next() | ||
| 213 | + rows.Scan(&raw) // this must not write to the driver-owned memory in raw | ||
| 214 | + rows.Close() | ||
| 215 | + | ||
| 216 | + // Repeat the first query. Nothing should have changed. | ||
| 217 | + rows, err = db.Query("SELECT|people|name|") | ||
| 218 | + if err != nil { | ||
| 219 | + t.Fatal(err) | ||
| 220 | + } | ||
| 221 | + rows.Next() | ||
| 222 | + rows.Scan(&raw) // raw points to driver-owned memory again | ||
| 223 | + name2 := string(raw) | ||
| 224 | + rows.Close() | ||
| 225 | + if name1 != name2 { | ||
| 226 | + t.Fatalf("Scan read name %q, want %q", name2, name1) | ||
| 227 | + } | ||
| 228 | +} | ||
| 229 | + | ||
| 230 | // badConn implements a bad driver.Conn, for TestBadDriver. | ||
| 231 | // The Exec method panics. | ||
| 232 | type badConn struct{} | ||
diff --git a/meta/recipes-devtools/go/go-1.21/CVE-2025-47907.patch b/meta/recipes-devtools/go/go-1.21/CVE-2025-47907.patch new file mode 100644 index 0000000000..b2af7df81b --- /dev/null +++ b/meta/recipes-devtools/go/go-1.21/CVE-2025-47907.patch | |||
| @@ -0,0 +1,327 @@ | |||
| 1 | From 8a924caaf348fdc366bab906424616b2974ad4e9 Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Damien Neil <dneil@google.com> | ||
| 3 | Date: Wed, 23 Jul 2025 14:26:54 -0700 | ||
| 4 | Subject: [PATCH] database/sql: avoid closing Rows while scan is in progress | ||
| 5 | |||
| 6 | A database/sql/driver.Rows can return database-owned data | ||
| 7 | from Rows.Next. The driver.Rows documentation doesn't explicitly | ||
| 8 | document the lifetime guarantees for this data, but a reasonable | ||
| 9 | expectation is that the caller of Next should only access it | ||
| 10 | until the next call to Rows.Close or Rows.Next. | ||
| 11 | |||
| 12 | Avoid violating that constraint when a query is cancelled while | ||
| 13 | a call to database/sql.Rows.Scan (note the difference between | ||
| 14 | the two different Rows types!) is in progress. We previously | ||
| 15 | took care to avoid closing a driver.Rows while the user has | ||
| 16 | access to driver-owned memory via a RawData, but we could still | ||
| 17 | close a driver.Rows while a Scan call was in the process of | ||
| 18 | reading previously-returned driver-owned data. | ||
| 19 | |||
| 20 | Update the fake DB used in database/sql tests to invalidate | ||
| 21 | returned data to help catch other places we might be | ||
| 22 | incorrectly retaining it. | ||
| 23 | |||
| 24 | Updates #74831 | ||
| 25 | Fixes #74832 | ||
| 26 | |||
| 27 | Change-Id: Ice45b5fad51b679c38e3e1d21ef39156b56d6037 | ||
| 28 | Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2540 | ||
| 29 | Reviewed-by: Roland Shoemaker <bracewell@google.com> | ||
| 30 | Reviewed-by: Neal Patel <nealpatel@google.com> | ||
| 31 | Reviewed-on: https://go-internal-review.googlesource.com/c/go/+/2601 | ||
| 32 | Reviewed-on: https://go-review.googlesource.com/c/go/+/693558 | ||
| 33 | TryBot-Bypass: Dmitri Shuralyov <dmitshur@golang.org> | ||
| 34 | Reviewed-by: Mark Freeman <markfreeman@google.com> | ||
| 35 | Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> | ||
| 36 | Auto-Submit: Dmitri Shuralyov <dmitshur@google.com> | ||
| 37 | |||
| 38 | CVE: CVE-2025-47907 | ||
| 39 | |||
| 40 | Upstream-Status: Backport [https://github.com/golang/go/commit/8a924caaf348fdc366bab906424616b2974ad4e9] | ||
| 41 | |||
| 42 | Signed-off-by: Praveen Kumar <praveen.kumar@windriver.com> | ||
| 43 | --- | ||
| 44 | src/database/sql/convert.go | 2 -- | ||
| 45 | src/database/sql/fakedb_test.go | 47 ++++++++++++-------------- | ||
| 46 | src/database/sql/sql.go | 26 +++++++------- | ||
| 47 | src/database/sql/sql_test.go | 60 ++++++++++++++++++++++++++++++--- | ||
| 48 | 4 files changed, 90 insertions(+), 45 deletions(-) | ||
| 49 | |||
| 50 | diff --git a/src/database/sql/convert.go b/src/database/sql/convert.go | ||
| 51 | index 3a581f6..5b0c6f0 100644 | ||
| 52 | --- a/src/database/sql/convert.go | ||
| 53 | +++ b/src/database/sql/convert.go | ||
| 54 | @@ -324,7 +324,6 @@ func convertAssignRows(dest, src interface{}, rows *Rows) error { | ||
| 55 | if rows == nil { | ||
| 56 | return errors.New("invalid context to convert cursor rows, missing parent *Rows") | ||
| 57 | } | ||
| 58 | - rows.closemu.Lock() | ||
| 59 | *d = Rows{ | ||
| 60 | dc: rows.dc, | ||
| 61 | releaseConn: func(error) {}, | ||
| 62 | @@ -340,7 +339,6 @@ func convertAssignRows(dest, src interface{}, rows *Rows) error { | ||
| 63 | parentCancel() | ||
| 64 | } | ||
| 65 | } | ||
| 66 | - rows.closemu.Unlock() | ||
| 67 | return nil | ||
| 68 | } | ||
| 69 | } | ||
| 70 | diff --git a/src/database/sql/fakedb_test.go b/src/database/sql/fakedb_test.go | ||
| 71 | index 33c57b9..9f3d517 100644 | ||
| 72 | --- a/src/database/sql/fakedb_test.go | ||
| 73 | +++ b/src/database/sql/fakedb_test.go | ||
| 74 | @@ -5,6 +5,7 @@ | ||
| 75 | package sql | ||
| 76 | |||
| 77 | import ( | ||
| 78 | + "bytes" | ||
| 79 | "context" | ||
| 80 | "database/sql/driver" | ||
| 81 | "errors" | ||
| 82 | @@ -15,7 +16,6 @@ import ( | ||
| 83 | "strconv" | ||
| 84 | "strings" | ||
| 85 | "sync" | ||
| 86 | - "sync/atomic" | ||
| 87 | "testing" | ||
| 88 | "time" | ||
| 89 | ) | ||
| 90 | @@ -91,8 +91,6 @@ func (cc *fakeDriverCtx) OpenConnector(name string) (driver.Connector, error) { | ||
| 91 | type fakeDB struct { | ||
| 92 | name string | ||
| 93 | |||
| 94 | - useRawBytes atomic.Bool | ||
| 95 | - | ||
| 96 | mu sync.Mutex | ||
| 97 | tables map[string]*table | ||
| 98 | badConn bool | ||
| 99 | @@ -683,8 +681,6 @@ func (c *fakeConn) PrepareContext(ctx context.Context, query string) (driver.Stm | ||
| 100 | switch cmd { | ||
| 101 | case "WIPE": | ||
| 102 | // Nothing | ||
| 103 | - case "USE_RAWBYTES": | ||
| 104 | - c.db.useRawBytes.Store(true) | ||
| 105 | case "SELECT": | ||
| 106 | stmt, err = c.prepareSelect(stmt, parts) | ||
| 107 | case "CREATE": | ||
| 108 | @@ -788,9 +784,6 @@ func (s *fakeStmt) ExecContext(ctx context.Context, args []driver.NamedValue) (d | ||
| 109 | case "WIPE": | ||
| 110 | db.wipe() | ||
| 111 | return driver.ResultNoRows, nil | ||
| 112 | - case "USE_RAWBYTES": | ||
| 113 | - s.c.db.useRawBytes.Store(true) | ||
| 114 | - return driver.ResultNoRows, nil | ||
| 115 | case "CREATE": | ||
| 116 | if err := db.createTable(s.table, s.colName, s.colType); err != nil { | ||
| 117 | return nil, err | ||
| 118 | @@ -1073,10 +1066,9 @@ type rowsCursor struct { | ||
| 119 | errPos int | ||
| 120 | err error | ||
| 121 | |||
| 122 | - // a clone of slices to give out to clients, indexed by the | ||
| 123 | - // original slice's first byte address. we clone them | ||
| 124 | - // just so we're able to corrupt them on close. | ||
| 125 | - bytesClone map[*byte][]byte | ||
| 126 | + // Data returned to clients. | ||
| 127 | + // We clone and stash it here so it can be invalidated by Close and Next. | ||
| 128 | + driverOwnedMemory [][]byte | ||
| 129 | |||
| 130 | // Every operation writes to line to enable the race detector | ||
| 131 | // check for data races. | ||
| 132 | @@ -1090,9 +1082,19 @@ func (rc *rowsCursor) touchMem() { | ||
| 133 | rc.line++ | ||
| 134 | } | ||
| 135 | |||
| 136 | +func (rc *rowsCursor) invalidateDriverOwnedMemory() { | ||
| 137 | + for _, buf := range rc.driverOwnedMemory { | ||
| 138 | + for i := range buf { | ||
| 139 | + buf[i] = 'x' | ||
| 140 | + } | ||
| 141 | + } | ||
| 142 | + rc.driverOwnedMemory = nil | ||
| 143 | +} | ||
| 144 | + | ||
| 145 | func (rc *rowsCursor) Close() error { | ||
| 146 | rc.touchMem() | ||
| 147 | rc.parentMem.touchMem() | ||
| 148 | + rc.invalidateDriverOwnedMemory() | ||
| 149 | rc.closed = true | ||
| 150 | return nil | ||
| 151 | } | ||
| 152 | @@ -1123,6 +1125,8 @@ func (rc *rowsCursor) Next(dest []driver.Value) error { | ||
| 153 | if rc.posRow >= len(rc.rows[rc.posSet]) { | ||
| 154 | return io.EOF // per interface spec | ||
| 155 | } | ||
| 156 | + // Corrupt any previously returned bytes. | ||
| 157 | + rc.invalidateDriverOwnedMemory() | ||
| 158 | for i, v := range rc.rows[rc.posSet][rc.posRow].cols { | ||
| 159 | // TODO(bradfitz): convert to subset types? naah, I | ||
| 160 | // think the subset types should only be input to | ||
| 161 | @@ -1130,20 +1134,13 @@ func (rc *rowsCursor) Next(dest []driver.Value) error { | ||
| 162 | // a wider range of types coming out of drivers. all | ||
| 163 | // for ease of drivers, and to prevent drivers from | ||
| 164 | // messing up conversions or doing them differently. | ||
| 165 | - dest[i] = v | ||
| 166 | - | ||
| 167 | - if bs, ok := v.([]byte); ok && !rc.db.useRawBytes.Load() { | ||
| 168 | - if rc.bytesClone == nil { | ||
| 169 | - rc.bytesClone = make(map[*byte][]byte) | ||
| 170 | - } | ||
| 171 | - clone, ok := rc.bytesClone[&bs[0]] | ||
| 172 | - if !ok { | ||
| 173 | - clone = make([]byte, len(bs)) | ||
| 174 | - copy(clone, bs) | ||
| 175 | - rc.bytesClone[&bs[0]] = clone | ||
| 176 | - } | ||
| 177 | - dest[i] = clone | ||
| 178 | + if bs, ok := v.([]byte); ok { | ||
| 179 | + // Clone []bytes and stash for later invalidation. | ||
| 180 | + bs = bytes.Clone(bs) | ||
| 181 | + rc.driverOwnedMemory = append(rc.driverOwnedMemory, bs) | ||
| 182 | + v = bs | ||
| 183 | } | ||
| 184 | + dest[i] = v | ||
| 185 | } | ||
| 186 | return nil | ||
| 187 | } | ||
| 188 | diff --git a/src/database/sql/sql.go b/src/database/sql/sql.go | ||
| 189 | index e25447c..a428e29 100644 | ||
| 190 | --- a/src/database/sql/sql.go | ||
| 191 | +++ b/src/database/sql/sql.go | ||
| 192 | @@ -3294,38 +3294,36 @@ func (rs *Rows) Scan(dest ...interface{}) error { | ||
| 193 | // without calling Next. | ||
| 194 | return fmt.Errorf("sql: Scan called without calling Next (closemuScanHold)") | ||
| 195 | } | ||
| 196 | + | ||
| 197 | rs.closemu.RLock() | ||
| 198 | + rs.raw = rs.raw[:0] | ||
| 199 | + err := rs.scanLocked(dest...) | ||
| 200 | + if err == nil && scanArgsContainRawBytes(dest) { | ||
| 201 | + rs.closemuScanHold = true | ||
| 202 | + } else { | ||
| 203 | + rs.closemu.RUnlock() | ||
| 204 | + } | ||
| 205 | + return err | ||
| 206 | +} | ||
| 207 | |||
| 208 | +func (rs *Rows) scanLocked(dest ...interface{}) error { | ||
| 209 | if rs.lasterr != nil && rs.lasterr != io.EOF { | ||
| 210 | - rs.closemu.RUnlock() | ||
| 211 | return rs.lasterr | ||
| 212 | } | ||
| 213 | if rs.closed { | ||
| 214 | - err := rs.lasterrOrErrLocked(errRowsClosed) | ||
| 215 | - rs.closemu.RUnlock() | ||
| 216 | - return err | ||
| 217 | - } | ||
| 218 | - | ||
| 219 | - if scanArgsContainRawBytes(dest) { | ||
| 220 | - rs.closemuScanHold = true | ||
| 221 | - rs.raw = rs.raw[:0] | ||
| 222 | - } else { | ||
| 223 | - rs.closemu.RUnlock() | ||
| 224 | + return rs.lasterrOrErrLocked(errRowsClosed) | ||
| 225 | } | ||
| 226 | |||
| 227 | if rs.lastcols == nil { | ||
| 228 | - rs.closemuRUnlockIfHeldByScan() | ||
| 229 | return errors.New("sql: Scan called without calling Next") | ||
| 230 | } | ||
| 231 | if len(dest) != len(rs.lastcols) { | ||
| 232 | - rs.closemuRUnlockIfHeldByScan() | ||
| 233 | return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest)) | ||
| 234 | } | ||
| 235 | |||
| 236 | for i, sv := range rs.lastcols { | ||
| 237 | err := convertAssignRows(dest[i], sv, rs) | ||
| 238 | if err != nil { | ||
| 239 | - rs.closemuRUnlockIfHeldByScan() | ||
| 240 | return fmt.Errorf(`sql: Scan error on column index %d, name %q: %w`, i, rs.rowsi.Columns()[i], err) | ||
| 241 | } | ||
| 242 | } | ||
| 243 | diff --git a/src/database/sql/sql_test.go b/src/database/sql/sql_test.go | ||
| 244 | index 6aa9bf0..6aec7ec 100644 | ||
| 245 | --- a/src/database/sql/sql_test.go | ||
| 246 | +++ b/src/database/sql/sql_test.go | ||
| 247 | @@ -5,6 +5,7 @@ | ||
| 248 | package sql | ||
| 249 | |||
| 250 | import ( | ||
| 251 | + "bytes" | ||
| 252 | "context" | ||
| 253 | "database/sql/driver" | ||
| 254 | "errors" | ||
| 255 | @@ -4321,10 +4322,6 @@ func TestRawBytesReuse(t *testing.T) { | ||
| 256 | db := newTestDB(t, "people") | ||
| 257 | defer closeDB(t, db) | ||
| 258 | |||
| 259 | - if _, err := db.Exec("USE_RAWBYTES"); err != nil { | ||
| 260 | - t.Fatal(err) | ||
| 261 | - } | ||
| 262 | - | ||
| 263 | var raw RawBytes | ||
| 264 | |||
| 265 | // The RawBytes in this query aliases driver-owned memory. | ||
| 266 | @@ -4469,6 +4466,61 @@ func TestTypedString(t *testing.T) { | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | +type testScanner struct { | ||
| 271 | + scanf func(src any) error | ||
| 272 | +} | ||
| 273 | + | ||
| 274 | +func (ts testScanner) Scan(src any) error { return ts.scanf(src) } | ||
| 275 | + | ||
| 276 | +func TestContextCancelDuringScan(t *testing.T) { | ||
| 277 | + db := newTestDB(t, "people") | ||
| 278 | + defer closeDB(t, db) | ||
| 279 | + | ||
| 280 | + ctx, cancel := context.WithCancel(context.Background()) | ||
| 281 | + defer cancel() | ||
| 282 | + | ||
| 283 | + scanStart := make(chan any) | ||
| 284 | + scanEnd := make(chan error) | ||
| 285 | + scanner := &testScanner{ | ||
| 286 | + scanf: func(src any) error { | ||
| 287 | + scanStart <- src | ||
| 288 | + return <-scanEnd | ||
| 289 | + }, | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + // Start a query, and pause it mid-scan. | ||
| 293 | + want := []byte("Alice") | ||
| 294 | + r, err := db.QueryContext(ctx, "SELECT|people|name|name=?", string(want)) | ||
| 295 | + if err != nil { | ||
| 296 | + t.Fatal(err) | ||
| 297 | + } | ||
| 298 | + if !r.Next() { | ||
| 299 | + t.Fatalf("r.Next() = false, want true") | ||
| 300 | + } | ||
| 301 | + go func() { | ||
| 302 | + r.Scan(scanner) | ||
| 303 | + }() | ||
| 304 | + got := <-scanStart | ||
| 305 | + defer close(scanEnd) | ||
| 306 | + gotBytes, ok := got.([]byte) | ||
| 307 | + if !ok { | ||
| 308 | + t.Fatalf("r.Scan returned %T, want []byte", got) | ||
| 309 | + } | ||
| 310 | + if !bytes.Equal(gotBytes, want) { | ||
| 311 | + t.Fatalf("before cancel: r.Scan returned %q, want %q", gotBytes, want) | ||
| 312 | + } | ||
| 313 | + | ||
| 314 | + // Cancel the query. | ||
| 315 | + // Sleep to give it a chance to finish canceling. | ||
| 316 | + cancel() | ||
| 317 | + time.Sleep(10 * time.Millisecond) | ||
| 318 | + | ||
| 319 | + // Cancelling the query should not have changed the result. | ||
| 320 | + if !bytes.Equal(gotBytes, want) { | ||
| 321 | + t.Fatalf("after cancel: r.Scan result is now %q, want %q", gotBytes, want) | ||
| 322 | + } | ||
| 323 | +} | ||
| 324 | + | ||
| 325 | func BenchmarkConcurrentDBExec(b *testing.B) { | ||
| 326 | b.ReportAllocs() | ||
| 327 | ct := new(concurrentDBExecTest) | ||
