Skip to content

version_db->db should not be closed if there are prepared statements referencing it - #13082

Closed
FuPeiJiang wants to merge 2 commits into
oven-sh:mainfrom
FuPeiJiang:main
Closed

FuPeiJiang wants to merge 2 commits into
oven-sh:mainfrom
FuPeiJiang:main

Conversation

@FuPeiJiang

Copy link
Copy Markdown
Contributor

fixes Segmentation fault

import { Database } from "bun:sqlite";
const db = new Database(":memory:")
const stmt = db.prepare("select 1")
db.close()
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"

https://github.com/oven-sh/bun/blob/98f9e276b032a139ef341a69e7967824718390bb/src/bun.js/bindings/sqlite/JSSQLStatement.cpp

once you close the db, version_db->db will be nullptr

databases()[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 db is garbage collected:

https://github.com/oven-sh/bun/blob/98f9e276b032a139ef341a69e7967824718390bb/src/bun.js/bindings/sqlite/JSSQLStatement.cpp

databases()[index]->db = nullptr;
import { context } from 'esbuild' //"esbuild": "^0.20.2"
import { Database } from "bun:sqlite";

//I wasn't able to get `db` garbage collected without importing `esbuild` and using it
const ctx = await context({
    entryPoints: [],
    outdir: 'www/js',
    bundle: true,
    write: false,
    sourcemap: 'inline',
})

function get_stmt() {
    const db = new Database(":memory:")
    const stmt = db.prepare("select 1")
    return stmt
}
const stmt = get_stmt()

setTimeout(() => {
    console.log(stmt.run());
}, 2000);

my changes are to add a CHECK only to the .run method, and not the .all nor the .get method, since they seem to work even if the database is closed

for .run to keep working, sqlite3_total_changes() would have to be skipped, and .changes would have to always be 0, which would potentially break users' if statements depending on this


for the garbage collection part, I'm using reference counting on VersionSqlite3.. I don't know of a better solution yet,

@Jarred-Sumner

Jarred-Sumner commented Aug 5, 2024 •

Copy link
Copy Markdown
Collaborator

Thank you for this. We can make VersionedDb extend from WTF::ThreadSafeRefCounted and that would be more consistent with what we do elsewhere

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

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

@github-actions

This comment was marked as outdated.

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

@Electroid why isn't CI running the PR?

@FuPeiJiang

Copy link
Copy Markdown
Contributor Author

@Jarred-Sumner Thanks for the additional information on Bun.gc(true); and await Bun.sleep(100);

, this would have been really useful, let's hope I can use it one day,


make VersionedDb extend from WTF::ThreadSafeRefCounted

that would try to close the database in the destructor, but version_db, which is stored in the databases() array, is never actually destroyed, databases()[dbIndex] = nullptr isn't found, only databases()[dbIndex]->db = nullptr;, which clears the fields, without freeing the struct

the way this is implemented, the array/vector can theoretically grow in size, and all the version_db inside will never be freed,

JSSQLStatement on the other hand, will probably free its reference to version_db, when ~JSSQLStatement() is reached


why isn't CI running the PR?

I'm looking at the logs of https://github.com/oven-sh/bun/actions/runs/10240688351?pr=13082
and it seems FAILED: CMakeFiles/bun-debug.dir/src/bun.js/bindings/webcore/Worker.cpp.o has been hit with [clang-analyzer-unix.Malloc,-warnings-as-errors]
you can probably disable it by adding -clang-analyzer-unix.Malloc to CMakeLists.txt

if(BUN_TIDY_ONLY)
    find_program(CLANG_TIDY_EXE NAMES "clang-tidy")
    set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}" "-checks=-*,clang-analyzer-*,-clang-analyzer-webkit.UncountedLambdaCapturesChecker" "--fix" "--fix-errors" "--format-style=webkit" "--warnings-as-errors=*")
    set_target_properties(${bun} PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}")
endif()

becomes

if(BUN_TIDY_ONLY)
    find_program(CLANG_TIDY_EXE NAMES "clang-tidy")
    set(CLANG_TIDY_COMMAND "${CLANG_TIDY_EXE}" "-checks=-*,clang-analyzer-*,-clang-analyzer-webkit.UncountedLambdaCapturesChecker,-clang-analyzer-unix.Malloc" "--fix" "--fix-errors" "--format-style=webkit" "--warnings-as-errors=*")
    set_target_properties(${bun} PROPERTIES CXX_CLANG_TIDY "${CLANG_TIDY_COMMAND}")
endif()

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

Can you add a test?

@7heMech

7heMech commented Aug 5, 2024

Copy link
Copy Markdown

What's Bun.gc(true)?

@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

@7heMech schedules the garbage collector manually

Jarred-Sumner added a commit that referenced this pull request Aug 6, 2024
Co-authored-by: FuPeiJiang <42662615+FuPeiJiang@users.noreply.github.com>
@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

Thank you. Superseded by #13105

robobun added a commit that referenced this pull request Aug 1, 2026
…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.
Jarred-Sumner pushed a commit that referenced this pull request Sep 10, 2026
… 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>
Jarred-Sumner pushed a commit that referenced this pull request Sep 15, 2026
… 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 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants