Repository navigation
version_db->db should not be closed if there are prepared statements referencing it - #13082
FuPeiJiang wants to merge 2 commits into
Conversation
…ared statements referencing it
|
Thank you for this. We can make |
|
Confirming I'm able to reproduce this on Windows with the following snippet: import { Database } from "bun:sqlite";
async function run() {
const stmt = (() => {
const db = new Database(":memory:");
let stmt = db.prepare("select 1");
db.close();
return stmt;
})();
Bun.gc(true);
await Bun.sleep(100);
Bun.gc(true);
console.log(stmt.all()); //succeeds
console.log(stmt.get()); //succeeds
console.log(stmt.run()); //Segmentation fault!! , should be: "Database has closed" or "Cannot use a closed database"
}
run(); |
This comment was marked as outdated.
This comment was marked as outdated.
|
@Electroid why isn't CI running the PR? |
|
@Jarred-Sumner Thanks for the additional information on , this would have been really useful, let's hope I can use it one day,
that would try to close the database in the destructor, but the way this is implemented, the array/vector can theoretically grow in size, and all the
I'm looking at the logs of https://github.com/oven-sh/bun/actions/runs/10240688351?pr=13082 becomes |
|
Can you add a test? |
|
What's |
|
@7heMech schedules the garbage collector manually |
Co-authored-by: FuPeiJiang <42662615+FuPeiJiang@users.noreply.github.com>
|
Thank you. Superseded by #13105 |
…on path A result-row push can run user code that closes the database, which now finalizes the statement; match the all()/values() loops and return on the pending exception without touching the stmt. Assert the rejections in the #13082 test, which close() finalizing statements made vacuous.
… values instead of truncating (#42219) ### Problem - `bun:sqlite` (and `Bun.SQL` with `adapter: "sqlite"`) binds a TypedArray with a detached ArrayBuffer as SQL NULL. A live `new Uint8Array(0)` binds as a zero-length BLOB, so a `NOT NULL` column rejects only the first. - Cause: `rebindValue()` (`src/jsc/bindings/sqlite/JSSQLStatement.cpp:945`) passed `buffer->vector()`, which is null for a detached view, to `sqlite3_bind_blob()`, where a null pointer means `sqlite3_bind_null()`. - The same calls narrowed `size_t` lengths to `int`. At 2^31 bytes the length turned negative, which SQLite reads as NUL-terminated: a 2 GiB `Uint8Array` bound as an empty TEXT value. ### Fix - Pass a non-null sentinel (`""`) when the data pointer is null, as `node:sqlite` already does in `NodeSqlite.cpp`. Length 0 copies nothing and binds a zero-length BLOB. This matches Node and the other `Bun.SQL` adapters. - Bind text and blobs through `sqlite3_bind_text64()` / `sqlite3_bind_blob64()`. They take a `sqlite3_uint64` length and return `SQLITE_TOOBIG` instead of truncating. - Verified: three new tests in `test/js/bun/sqlite/sqlite.test.js` and `test/js/sql/sqlite-sql.test.ts` fail on bun 1.4.3 and pass here. The other sqlite suites pass (list in Notes). - Self-reviewed: 1 concern raised, 1 addressed (the text binds shared the `int` narrowing). Bare `ArrayBuffer` arguments, which Node accepts, are out of scope. ### Background - A detached ArrayBuffer gave its memory away (`transfer()`, `postMessage`). In JSC its views then have length 0 and a null data pointer. A live buffer is never null, even at length 0. - `sqlite3.h`: a NULL data pointer to `sqlite3_bind_blob()` "is the same as sqlite3_bind_null()". A negative length is undefined behavior. - `SQLITE_TRANSIENT` makes SQLite copy the bytes at bind time, so the sentinel's contents never matter. <details><summary>Notes</summary> - Repro on bun 1.4.3: ```js import { Database } from "bun:sqlite"; const db = new Database(":memory:"); db.run("create table t (id int, data blob)"); const ins = db.prepare("insert into t values (?, ?)"); ins.run(1, new Uint8Array(0)); const d = new Uint8Array([1, 2, 3]); d.buffer.transfer(); ins.run(2, d); ins.run(3, new Uint8Array(2 ** 31)); console.log(db.query("select id, typeof(data) ty, length(data) len from t").all()); // [{ id: 1, ty: "blob", len: 0 }, { id: 2, ty: "null", len: null }, { id: 3, ty: "text", len: 0 }] ``` - The alternative for the detached case was to throw a TypeError (WebIDL style). A zero-length BLOB matches `node:sqlite`, the other `Bun.SQL` adapters, and what a live empty view already does, so it is the less surprising choice. `deserialize()` and `fileControl()` still throw for a detached buffer because they need its contents. - `rebindValue()` is the only place `bun:sqlite` binds a parameter value. Positional, array, and named (`$name`) bindings all go through it. - `sqlite3_bind_text64(..., SQLITE_UTF16)` is what `sqlite3_bind_text16()` calls internally (native byte order, length rounded down to even), and `sqlite3_bind_text64(..., SQLITE_UTF8)` is what `sqlite3_bind_text()` calls. Below 2^31 bytes nothing changes. Both `*64` symbols are already in `lazy_sqlite3.h` for the macOS system-SQLite path, because `node:sqlite` uses them. - A UTF-16 string of 2^30 characters (2^31 bytes) hit the same narrowing through `sqlite3_bind_text16()` and bound as its prefix up to the first NUL. It is the same API change, but a test would need a real 2 GiB string, so only the blob case has one. - The 2 GiB blob test runs in a subprocess and never writes the buffer, so the allocation is address space only (RSS about 330 MB in a debug build, 17 ms to bind and fail). If the allocation itself fails the child prints `SKIP`, the same pattern as the oversized-input tests in `web-crypto.test.ts` and `md-edge-cases.test.ts`. - Suites run with the debug build: `test/js/bun/sqlite/sqlite.test.js`, `column-types.test.js`, `sql-timezone.test.js`, `test/js/sql/sqlite-sql.test.ts`, `sqlite-url-parsing.test.ts`, and the detached-buffer cases in `test/js/node/sqlite/node-sqlite.test.ts`. All pass except `#13082` in `sqlite.test.js`, which times out in a debug ASAN build on `main` too and is unrelated. </details> <!-- robobun:evidence:begin --> --- **no test proof** · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/js/bun/sqlite/sqlite.test.js <!-- robobun:evidence:end --> --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
… UTF-8 (#42798) ### Problem - A non-ASCII string of 2^30 Latin-1 characters or more aborts the process: `panic(main thread): abort() called`, exit 134, also inside `try` / `catch`. Each API that reads it through `Bun::UTF8View` does this, for example `x509.checkHost("\u00e9".repeat(2 ** 30))`. Fuzzing found it, with no user report. - `UTF8View` (`src/jsc/bindings/BunString.h:18`) called `utf8()`, which is `tryGetUTF8()` plus `RELEASE_ASSERT`. It had no failure channel. ### Fix - `UTF8View` has no public constructor. `UTF8View::tryCreate()` returns `std::nullopt` when `tryGetUTF8()` fails, and an overload throws `RangeError: Out of memory`, as #42202 and #42308 do. - An 8-bit ASCII string is still borrowed, never copied. Each user passes a pointer and a length. A `const char*` consumer always needs a copy, and that case is not in this PR (Notes). - These throw now: `new X509Certificate(string)`, `hkdf` / `hkdfSync` (`salt`, `info`), `bun:sqlite` SQL text and string parameters, `new Bun.CookieMap(string)`. `checkHost`, `checkEmail`, `require.resolve.paths` and `Module._resolveLookupPaths` only look the string up, so they answer "no match". Removed `keyFromString`, `passphraseFromBufferSource`, `keyFromPublicString` (no caller). - Verified: `test/js/bun/util/utf8-conversion-limit.test.ts`. The unfixed build aborts at the first case. One row shows that an ASCII string is not copied. ### Background - A JSC string is Latin-1 (8-bit) or UTF-16, with no NUL terminator. `UTF8View` borrows an 8-bit all-ASCII string and converts any other string with WTF. - WTF reserves 2 bytes per Latin-1 character and a buffer holds at most 2^31 - 1 bytes, so 2^30 Latin-1 characters fail. A 16-bit string fails at a UTF-8 form of 2^31 bytes. - JSC throws `RangeError: Out of memory` for a string that it cannot create. <details><summary>Notes</summary> **Repro, on release 1.4.3 canary (09bb546, linux x64).** Each line aborts on its own with exit 134, at 1.1 GB RSS in about 0.6 s. ```js const crypto = require("node:crypto"); const { Database } = require("bun:sqlite"); const long = "\u00e9".repeat(2 ** 30); // Latin-1, not ASCII const x509 = new crypto.X509Certificate(pem); const db = new Database(":memory:"); x509.checkHost(long); x509.checkEmail(long); new crypto.X509Certificate(long); crypto.hkdfSync("sha256", "key", long, "info", 8); // salt crypto.hkdfSync("sha256", "key", "salt", long, 8); // info crypto.hkdf("sha256", "key", long, "info", 8, () => {}); require.resolve.paths(long); require("node:module")._resolveLookupPaths(long); db.run(long); db.prepare(long); db.prepare("SELECT length(?)").get(long); new Bun.CookieMap("a=%41" + long); // the value is converted only when the header has a "%" ``` An ASCII string of the same length is clean in all of them, because `UTF8View` borrows it. A 16-bit string aborts too: 715,827,883 x U+0800 has a UTF-8 form of 2^31 + 1 bytes. With this PR all of these cases throw or answer "no match" for that string as well. One character below the limit (2^30 - 1 x U+00E9) still converts: `checkHost` returns `undefined` after 8 s on release. **Every `UTF8View` user, and what it does when the conversion fails** | User | Now | | --- | --- | | `new X509Certificate(string)` (`JSX509Certificate.cpp`) | throws | | `X509Certificate#checkHost`, `#checkEmail` (`JSX509CertificatePrototype.cpp`) | return `undefined` | | `hkdf`, `hkdfSync`: `salt`, `info` (`CryptoHkdf.cpp`) | throws | | `Database#run`, `#exec`, `#prepare`, `#query`: SQL text (`JSSQLStatement.cpp`) | throws | | `bun:sqlite` string parameter (`JSSQLStatement.cpp` `rebindValue`) | throws | | `new Bun.CookieMap(string)`, `request.cookies` (`CookieMap.cpp`) | `Exception { OutOfMemoryError }`, which both callers throw | | `require.resolve.paths` (`JSCommonJSModule.cpp`), `Module._resolveLookupPaths` (`NodeModuleModule.cpp`) | the request is not a builtin, continue | | `decodeURIComponentSIMD` (`bun:internal-for-testing`) | throws | | `X509Certificate#validToDate`, `#validFromDate` | return `undefined`. The string is the 24 characters that `ASN1_TIME_print` writes, so this cannot happen. #42363 removes both uses. | | native `WebSocket` `onMessage` (`WebSocket.cpp`, used by the Chrome backend of `Bun.WebView`) | drops the message. The client fails a message longer than `MAX_RECEIVE_MESSAGE_LENGTH` first (128 MiB, `src/http_jsc/websocket_client.rs:57`, checked at `:813`). A compressed message has the same bound (`MAX_DECOMPRESSED_SIZE`, `websocket_client/WebSocketDeflate.rs:64`). So this cannot happen. | | replay of queued Chrome commands over the pipe (`ChromeBackend.cpp`) | rejects every pending promise of the transport | | `keyFromString`, `passphraseFromBufferSource`, `keyFromPublicString` | removed, no caller | Two of these did not use `UTF8View` before. The `bun:sqlite` parameter binding had its own copy of the same dispatch (borrow ASCII, else `utf8()`). `Module._resolveLookupPaths` is the twin of `require.resolve.paths` and called `utf8()` for the same builtin check. **Not in this PR.** These call `String::utf8()` or `StringView::utf8()` directly, not `UTF8View`. They abort for a string of 2^30 characters, ASCII included. | Parameter | Site | Owner | | --- | --- | --- | | `X509Certificate#checkIP(ip)` | `jsX509CertificateProtoFuncCheckIP` | #42780 (throws `ERR_INVALID_ARG_VALUE`, as for the same string one character shorter) | | `hkdf(digest)` and the other digest, cipher and curve names | `ncrypto.cpp` | #42780 | | `hkdf(ikm)` as a string of 2^31 UTF-8 bytes | `CryptoHkdf.cpp` `prepareKey` | #42187 | | `process.execve` | `BunProcess.cpp` | #42308 | | `Set-Cookie` header write | `CookieMap.cpp:20` | #42237 | | `bun:sqlite` `new Database(filename)`, `serialize(name)`, `loadExtension(path, entryPoint)`, `fileControl(name)`, `setCustomSQLite(path)` | `JSSQLStatement.cpp:1208`, `1377`, `1428`, `1430`, `1803`, `1955` | follow-up, with the 20 sites of `node:sqlite` (`NodeSqlite.cpp`) | | `Bun.Cookie` `expires` string | `Cookie.cpp:124`, `JSCookie.cpp:62` | follow-up | | `WebSocket` close reason, protocols | `WebSocket.cpp` | follow-up | | Chrome backend pipe command | `ChromeBackend.h:211` | follow-up | Most of them pass a `const char*` to a C API (`sqlite3_open_v2`, `sqlite3_serialize`, `sqlite3_load_extension`, `X509_check_ip_asc`, `EVP_get_digestbyname`). A WTF string has no NUL terminator (`StringImpl::allocationSize` is `tailOffset + length * sizeof(T)`), so that case always needs a copy, and the borrowed branch of `UTF8View` cannot serve it. It stays on `tryGetUTF8()`. `src/jsc` and `src/runtime` have about 100 `utf8()` call sites. Each needs a look: some pass a pointer and a length and can move to `UTF8View`, which also saves the copy of an ASCII string. That is a separate change. **Related open PRs** - #42187 edits `copyBufferOrString` in `CryptoHkdf.cpp` and keeps the `UTF8View utf8(view);` line. The PR that merges second takes the `tryCreate` form, a 3 line rebase. - #42363 rewrites `validToDate` / `validFromDate` and drops their `UTF8View`. - #33312 adds 7 new `Bun::UTF8View(...)` constructor calls (clipboard). After this PR they do not compile until they use `tryCreate`. That is the purpose of the private constructor: a new user cannot skip the failure case. An unchecked `tryCreate(s)->span()` still compiles. **Test.** The length is what is under test, so the child holds a string of 1 GiB, and a second one while the cookie header is joined (2.5 GB peak). One child runs the 14 cases and takes 6 to 10 s in a debug ASAN build. About 5 s of that is the last row, where the unoptimized build scans 1 GiB for a non-ASCII byte. The test skips below 8 GiB of total memory and carries a 30 s ceiling, like `error-message-string-length-limit.test.ts`. A second test runs the same APIs with `"café"` and `"café 😀"` to check that a string that converts still gives the same result. **The ASCII row.** `tryGetUTF8()` refuses every 8-bit string of 2^30 characters before it reads one (`StringImpl.h` `tryGetUTF8ForCharacters`: `isValidCapacityForVector<char8_t>(characters.size() * 2)`). So a `UTF8View` that copied an ASCII string of that length reports `Out of memory`. The row binds `"q".repeat(2 ** 30)` as a `bun:sqlite` parameter. The bundled SQLite answers `string or blob too big` (its limit is 1e9 bytes). The system SQLite that macOS loads takes up to 2 GiB and returns the length. The row accepts these two answers and no other: each shows that SQLite got the string's buffer. The same mechanism is why `Module._resolveLookupPaths("q".repeat(2 ** 30))` aborts on `main`: it called `request.utf8()` to pass a pointer and a length. It borrows now. **Also ran (debug ASAN build):** `x509.test.ts`, `x509-subclass.test.ts`, the vendored `test-crypto-x509.js`, `test-crypto-hkdf.js`, `test-crypto-sign-verify.js`, `test-crypto-key-objects.js`, `test-require-resolve.js`, `hkdf-callback-null.test.ts`, `hkdf-zero-length.test.ts`, `sqlite.test.js`, the four cookie suites, `decodeURIComponentSIMD.test.ts`, `node-module-module.test.js`, `resolve.test.ts`, `webview-chrome-pipe.test.ts`, `webview-chrome-disconnect.test.ts`. The child script also ran with `BUN_JSC_validateExceptionChecks=1`, with no report. In `sqlite.test.js`, `#13082` times out on this machine's debug build (about 6 s against the 5 s limit), with and without this change. **Self-review: 6 concerns raised, 4 addressed.** - Addressed: `checkHost` / `checkEmail` threw. They return `undefined` now. The rule that they and `checkIP` in #42780 follow: a call reports what it reports for the same string one character shorter. That string is a name that matches no certificate. - Addressed: the same 4 line throw block appeared 7 times. It is the `tryCreate(globalObject, scope, view)` overload now. - Addressed: the `bun:sqlite` parameter binding still aborted, through its own copy of the dispatch. - Addressed: the description named API surfaces as done. It names parameters now, and the table above lists what is left, with an owner for each row. - Rejected: convert the six other `utf8()` calls of `JSSQLStatement.cpp` here, through a new `Bun::tryUTF8`. They are direct `utf8()` callers that need a NUL-terminated copy. They belong with the `node:sqlite` sites in one change for that mechanism. - Rejected: bound the conversion by `Bun::maxVectorSize` so that a test can reach the failure with a small string. It would test a branch that production never takes, and a crash fix needs the crashing input as its fixture. **Review.** The review asked that the two cases stay apart: a `const char*` consumer (always a copy) and a pointer plus length consumer (borrow an ASCII string). `UTF8View` is the second case only, before and after this PR. Since the review: the comment on `UTF8View` says so, the fields are `m_borrowed` and `m_converted`, and the ASCII row above guards the borrow. </details> <!-- robobun:evidence:begin --> --- **[human-review]** gate passed · iteration 0 · 16 files touched <details><summary>fails on main (without fix)</summary> ```console ASAN without fix: 1 FAILED $ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" "test/js/bun/util/utf8-conversion-limit.test.ts" bun test v1.4.3 (09bb546) test/js/bun/util/utf8-conversion-limit.test.ts: 69 | env: bunEnv, 70 | stdout: "pipe", 71 | stderr: "pipe", 72 | }); 73 | const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); 74 | expect({ stdout: stdout.trim().split("\n"), stderr, exitCode }).toEqual({ ^ error: expect(received).toEqual(expected) { - "exitCode": 0, - "stderr": "", + "exitCode": 134, + "stderr": + "ASSERTION FAILED: expectedString + vendor/WebKit/Source/WTF/wtf/text/StringView.cpp(121) : CString WTF::StringView::utf8(ConversionMode) const + no stacktrace available" + , "stdout": [ - "X509Certificate#checkHost: returned undefined", - "X509Certificate#checkEmail: returned undefined", - "new X509Certificate: RangeError: Out of memory", - "hkdfSync salt: RangeError: Out of memory", - "hkdfSync info: RangeEr ... (truncated) release without fix: 1 FAILED bun test v1.4.3-canary.1 (09bb546) test/js/bun/util/utf8-conversion-limit.test.ts: 69 | env: bunEnv, 70 | stdout: "pipe", 71 | stderr: "pipe", 72 | }); 73 | const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); 74 | expect({ stdout: stdout.trim().split("\n"), stderr, exitCode }).toEqual({ ^ error: expect(received).toEqual(expected) { - "exitCode": 0, - "stderr": "", + "exitCode": 134, + "stderr": + "============================================================ + Bun Canary v1.4.3-canary.1 (09bb546) Linux x64 + Linux Kernel v7.0.0 | glibc v2.41 + CPU: sse42 popcnt avx avx2 avx512 + Args: "/workspace/bun/build/release/bun" "-e" "\n import { decodeURIComponentSIMD } from \"bun:internal-for-testing\";\n import { Database } from \"bun:sqlite\";\n import crypto from \"node:crypto\";\n import Module"... + Features: Bun.stderr(2) bunfig jsc tsconfig + Builtins: "bun:internal-for-testing" "bun:main" "bun:sqlite" "node:crypto" "node:module" + + Elapsed: 740ms | User: 198ms | Sys: 543ms + RSS: 1.12 ... (truncated) ``` </details> <details><summary>passes on PR (with fix)</summary> ```console ASAN with fix: all passed $ BUN_DEBUG_QUIET_LOGS=1 bun scripts/build.ts --profile=debug --quiet test "--reporter=junit" "--reporter-outfile=/tmp/pr_gate.xml" "test/js/bun/util/utf8-conversion-limit.test.ts" bun test v1.4.3 (09bb546) test/js/bun/util/utf8-conversion-limit.test.ts: (pass) a string whose UTF-8 form does not fit in a buffer is an error instead of an abort [3129.31ms] (pass) a short Latin-1 string converts as before [70.38ms] (pass) a short 16-bit string converts as before [10.22ms] 3 pass 0 fail 3 expect() calls Ran 3 tests across 1 file. [5.80s] __F:0:S:0 release with fix: all passed $ bun scripts/build.ts --profile=release [configured] bun-profile → bun (stripped) target linux-x64-gnu build type Release build dir ./build/release revision 40af339 features lto, baseline 23 deps, 131 codegen, 1176 objects in 698ms ninja: Entering directory `/workspace/bun/build/release' [1/1254] mkdir codegen [2/1254] mkdir stamps [3/1254] mkdir pch [4/1254] mkdir obj [5/1254] install /workspace/bun bun install v1.4.3-canary.1 (09bb546) Checked 22 installs across 61 packages (no changes) [11.00ms] [6/1254] install /workspace/bun/packages/bun-error bun install v1.4.3-canary.1 (09bb546) Checked 1 install across 2 packages (no changes) [4.00ms] [7/1254] gen ErrorCode+*.h [8/1254] install /workspace/bun/src/node-fallbacks bun install v1.4.3-canary.1 (09bb546) Checked 111 installs across 104 packages (no changes) [6.00ms] [9/1254] gen .bind.ts → GeneratedBindings.cpp [10/1254] fetch picohttpparser [picohttpparser] up to date [11/1254] fetch zlib [zlib] up to date [12/1254] gen bindgenv2 [13/1254] gen node-fallbacks/react-refresh.js Bundled 1 module in 5ms react-refresh.js 4.81 KB (entry point) [14/1254] fetch libjpeg ... (truncated) ``` </details> <details><summary>diff hotspot</summary> ``` src/jsc/bindings/BunString.cpp | 8 ++ src/jsc/bindings/BunString.h | 52 ++++----- src/jsc/bindings/CookieMap.cpp | 6 +- src/jsc/bindings/JSCommonJSModule.cpp | 6 +- src/jsc/bindings/JSX509Certificate.cpp | 9 +- src/jsc/bindings/JSX509CertificatePrototype.cpp | 26 +++-- src/jsc/bindings/decodeURIComponentSIMD.cpp | 5 +- src/jsc/bindings/node/crypto/CryptoHkdf.cpp | 5 +- src/jsc/bindings/node/crypto/CryptoUtil.cpp | 76 -------------- src/jsc/bindings/node/crypto/CryptoUtil.h | 2 - src/jsc/bindings/node/crypto/JSVerify.cpp | 36 ------- src/jsc/bindings/sqlite/JSSQLStatement.cpp | 22 ++-- src/jsc/bindings/webcore/WebSocket.cpp | 5 +- src/jsc/modules/NodeModuleModule.cpp | 10 +- src/runtime/webview/ChromeBackend.cpp | 8 +- test/js/bun/util/utf8-conversion-limit.test.ts | 134 ++++++++++++++++++++++++ 16 files changed, 233 insertions(+), 177 deletions(-) ``` </details> **gate history** · 1 passed · 0 rejected · iteration 0 <details><summary>evidence per changed file</summary> ``` file reads edits tests src/jsc/bindings/BunString.cpp 0 0 13 src/jsc/bindings/BunString.h 3 2 13 src/jsc/bindings/CookieMap.cpp 0 0 13 src/jsc/bindings/JSCommonJSModule.cpp 0 0 13 src/jsc/bindings/JSX509Certificate.cpp 0 0 13 src/jsc/bindings/JSX509CertificatePrototype.cpp 0 0 13 src/jsc/bindings/decodeURIComponentSIMD.cpp 0 0 13 src/jsc/bindings/node/crypto/CryptoHkdf.cpp 0 0 13 src/jsc/bindings/node/crypto/CryptoUtil.cpp 0 0 13 src/jsc/bindings/node/crypto/CryptoUtil.h 0 0 13 src/jsc/bindings/node/crypto/JSVerify.cpp 0 0 13 src/jsc/bindings/sqlite/JSSQLStatement.cpp 0 0 13 src/jsc/bindings/webcore/WebSocket.cpp 0 0 13 src/jsc/modules/NodeModuleModule.cpp 0 0 13 src/runtime/webview/ChromeBackend.cpp 0 0 13 test/js/bun/util/utf8-conversion-limit.test.ts 1 1 13 ``` </details> <!-- robobun:evidence:end -->
fixes Segmentation fault
https://github.com/oven-sh/bun/blob/98f9e276b032a139ef341a69e7967824718390bb/src/bun.js/bindings/sqlite/JSSQLStatement.cpp
once you close the db,
version_db->dbwill benullptrdatabases()[dbIndex]->db = nullptr;stmt.run()int total_changes_before = sqlite3_total_changes(castedThis->version_db->db);https://github.com/oven-sh/bun/blob/98f9e276b032a139ef341a69e7967824718390bb/src/bun.js/bindings/sqlite/sqlite3.c
return db->nTotalChange;this also happens when
dbis garbage collected:https://github.com/oven-sh/bun/blob/98f9e276b032a139ef341a69e7967824718390bb/src/bun.js/bindings/sqlite/JSSQLStatement.cpp
databases()[index]->db = nullptr;my changes are to add a CHECK only to the
.runmethod, and not the.allnor the.getmethod, since they seem to work even if the database is closedfor
.runto keep working,sqlite3_total_changes()would have to be skipped, and.changeswould have to always be0, which would potentially break users' if statements depending on thisfor the garbage collection part, I'm using reference counting on
VersionSqlite3.. I don't know of a better solution yet,