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) | ||