Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8419ad9
Hardening: fix crashes across the runtime, bundler, dev server, shell…
Jarred-Sumner Oct 19, 2026
8cc3067
Fix the Windows build, two clippy errors and a mordant finding
Jarred-Sumner Oct 19, 2026
ac8b035
Fix the remaining clippy errors, an aliasing bug that miri reports, a…
Jarred-Sumner Oct 19, 2026
601626a
jsc: root_read_only asks whether a copy is needed before it roots, so…
Jarred-Sumner Oct 19, 2026
4c67718
test: restore the node:vm FinalizationRegistry test
Jarred-Sumner Oct 19, 2026
9200df9
node:vm: go back to the sandbox object for DONT_CONTEXTIFY contexts (…
Jarred-Sumner Oct 19, 2026
b5a1767
bake tests: edit only files that the dev server has bundled, since kq…
Jarred-Sumner Oct 19, 2026
f192cf6
test: fix three tests that fail in CI
Jarred-Sumner Oct 19, 2026
b37e6a9
test: skip the EMFILE FIFO test on macOS and the rename-based dev ser…
Jarred-Sumner Oct 19, 2026
afbbb7f
node:net: don't capture the closed handle in the closure that emits '…
Jarred-Sumner Oct 19, 2026
87987c6
Zero the stack under a microtask checkpoint without inline assembly
Jarred-Sumner Oct 19, 2026
b0adc1a
Use JSC's stack sanitizer before a microtask checkpoint
Jarred-Sumner Oct 19, 2026
fba2e00
Address review: one uv_async pass per task, cc() runs no user JS whil…
Jarred-Sumner Oct 19, 2026
51e1723
Zero only the frame of VM::drainMicrotasks() before a checkpoint, and…
Jarred-Sumner Oct 19, 2026
cd91e10
Remove the stack clear before a microtask checkpoint; open a handle s…
Jarred-Sumner Oct 19, 2026
b88bba8
bun:sqlite: a collected write on a closed connection doesn't interrup…
Jarred-Sumner Oct 19, 2026
7852174
test: say why the uv_queue_work V8 test is skipped on Windows
Jarred-Sumner Oct 19, 2026
c125996
dev server: answer 500 for a page whose stylesheet imports a file tha…
Jarred-Sumner Oct 19, 2026
976200f
dev server test: connect the client after the page request that repor…
Jarred-Sumner Oct 19, 2026
e216ca6
test: build long strings with Buffer.alloc instead of String.prototyp…
Jarred-Sumner Oct 19, 2026
7da2f83
Merge branch 'main' into claude/hardening
Jarred-Sumner Oct 20, 2026
6c00b85
[autofix.ci] apply automated fixes
autofix-ci[bot] Oct 20, 2026
d7dc120
Format a test
Jarred-Sumner Oct 20, 2026
0616357
Merge branch 'main' into claude/hardening
Jarred-Sumner Oct 20, 2026
cb1eedf
Merge branch 'main' into claude/hardening
Jarred-Sumner Oct 20, 2026
5326931
Merge branch 'main' into claude/hardening
Jarred-Sumner Oct 20, 2026
2079a5f
sourcemap: check the range of an embedded source file before slicing it
Jarred-Sumner Oct 20, 2026
587c6f5
test: count FinalizationRegistry cleanups only for the node:vm contex…
Jarred-Sumner Oct 20, 2026
f39b19b
test: count FinalizationRegistry cleanups only for the node:vm contex…
Jarred-Sumner Oct 20, 2026
5bf6cb2
napi: keep the JSC::Exception from napi_run_script and unwrap it in n…
Jarred-Sumner Oct 20, 2026
be8c3cc
Merge branch 'main' into claude/hardening
Jarred-Sumner Oct 20, 2026
f48d297
dev server: answer a waiting framework request through its RequestCon…
Jarred-Sumner Oct 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion docs/project/bindgen.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,8 @@ thread.
**A shared buffer** is borrowed like any other, as JSC's own `toBase64` and `indexOf` read one, and
another thread that writes it during the call changes what the body reads. `shared: "copied"` is
for a body that reads a byte again and counts on what it was, such as a parser that scans ahead
and comes back. JSC's `sort` copies for the same reason.
and comes back. JSC's `sort` copies for the same reason. With `string: "asIs"` the body calls
`JsBytes::get_or_copy_if_shared` instead.

The kind of the value is checked, and a string converted, in the order of the arguments. The
bytes of a buffer are read, copied or pinned after every argument is converted. It is not a
Expand Down
2 changes: 1 addition & 1 deletion docs/runtime/http/websockets.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ ws.terminate(); // no Close frame
- `0`, `1005` and `1006` send a Close frame with no code, so you can forward the code from a `close` handler to another socket.
- The reason is truncated to 123 bytes of UTF-8.

When the client violates the protocol, Bun sends a Close frame with `1002` (protocol error), `1007` (invalid UTF-8 or compressed data) or `1009` (message too big). The `close` handler receives `1006`, with a description of the problem as the reason.
When the client violates the protocol, Bun sends a Close frame with `1002` (protocol error), `1007` (invalid UTF-8 or compressed data) or `1009` (message too big). If a buffer for a message cannot be allocated, Bun sends `1011` and closes that connection only. The `close` handler receives `1006`, with a description of the problem as the reason.

`server.stop()` doesn't close open WebSockets. `server.stop(true)` closes each one with `1001`.

Expand Down
3 changes: 2 additions & 1 deletion packages/bun-types/ffi.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,8 @@ declare module "bun:ffi" {
*
* Once called, the library is no longer usable.
*
* Calling a function from a library that has been closed is undefined behavior.
* Calling a symbol from a closed {@link cc} library throws a `TypeError`.
* Calling a symbol from a closed {@link dlopen} or {@link linkSymbols} library is undefined behavior.
*/
close(): void;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/bun-types/jsc.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,8 @@ declare module "bun:jsc" {
* value, use {@link profile} instead.
*
* @param optionalDirectory A directory to write a text report of the hottest
* functions and bytecodes into when the process exits. Created if it does not exist.
* functions and bytecodes to when the process (or the calling worker) exits.
* The directory is created if it doesn't exist.
* @param sampleInterval How often to sample the stack, in microseconds. Defaults to 1000 (once a millisecond).
*/
function startSamplingProfiler(optionalDirectory?: string, sampleInterval?: number): void;
Expand Down
15 changes: 14 additions & 1 deletion packages/bun-usockets/src/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ void us_socket_group_deinit(struct us_socket_group_t *group) {
* closes it in finalize(), so closing+freeing it here turns that into a UAF
* (the original LSAN was only the *accepted* sockets, not the listener). */
void us_socket_group_close_all_ex(struct us_socket_group_t *group, int also_listeners) {
struct us_loop_t *loop = group->loop;
if (!loop) {
/* A Windows named-pipe Listener never initializes its group. */
return;
}

/* A close handler can tick the loop (waitForPromise). Bump tick_depth so
* that nested tick doesn't free the sockets closed here while this loop and
* Listener.stop still read them. */
loop->data.tick_depth++;

if (also_listeners) {
/* Listeners first — stops new sockets from being accepted into
* head_sockets while we're draining it. */
Expand Down Expand Up @@ -141,6 +152,8 @@ void us_socket_group_close_all_ex(struct us_socket_group_t *group, int also_list
}
US_ASSERT(group->low_prio_count == 0);
}

loop->data.tick_depth--;
}

void us_socket_group_close_all(struct us_socket_group_t *group) {
Expand Down Expand Up @@ -464,7 +477,7 @@ void us_listen_socket_close(struct us_listen_socket_t *ls) {
struct us_socket_group_t *group = ls->accept_group;
struct us_loop_t *loop = s->group->loop;
us_poll_stop((struct us_poll_t *) s, loop);
bsd_close_socket(us_poll_fd((struct us_poll_t *) s));
us_internal_poll_close_fd((struct us_poll_t *) s);

us_internal_listen_socket_ssl_free(ls);

Expand Down
22 changes: 1 addition & 21 deletions packages/bun-usockets/src/crypto/openssl.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ enum {
/* SNI tree leaf — stored as the void* user in sni_tree.cpp. */
struct sni_node_t {
SSL_CTX *ctx;
void *user;
};

static _Atomic long ssl_ctx_live = 0;
Expand All @@ -140,7 +139,6 @@ long us_ssl_ctx_live_count(void) {
* - us_ctx_ex_idx (SSL_CTX): packed reneg {limit:u32,window:u32}; its
* free_func also decrements ssl_ctx_live so the counter tracks ACTUAL
* destruction (refcount→0), not every SSL_CTX_free.
* - us_sni_ex_idx (SSL_CTX): per-domain userdata (uWS HttpRouter*).
* - us_ssl_rare_ex_idx (SSL): us_ssl_rare_t.
* - us_ssl_wrapper_ex_idx (SSL): the owner of an SSL that no us_socket_t drives.
*
Expand All @@ -151,7 +149,6 @@ long us_ssl_ctx_live_count(void) {
* internal-only, so use the platform primitive directly; root_certs.cpp does
* the same via std::call_once.) */
static int us_ctx_ex_idx = -1;
static int us_sni_ex_idx = -1;
static int us_ctx_cache_ex_idx = -1;
/* Marks an SSL_CTX whose verification store holds user-provided CAs (the
* ca/caFile options or a later addCACert): the per-socket client attach must
Expand Down Expand Up @@ -373,7 +370,6 @@ extern void bun_ssl_ctx_cache_on_free(void *parent, void *ptr, CRYPTO_EX_DATA *a

static void us_ex_idx_init(void) {
us_ctx_ex_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, us_ctx_ex_free);
us_sni_ex_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
us_ctx_cache_ex_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, bun_ssl_ctx_cache_on_free);
us_ctx_user_ca_ex_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
us_ctx_sni_policy_ex_idx = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
Expand Down Expand Up @@ -3394,7 +3390,7 @@ static int sni_cb(SSL *ssl, int *al, void *arg) {

void us_listen_socket_add_server_name(struct us_listen_socket_t *ls,
const char *hostname_pattern,
SSL_CTX *ctx, void *user) {
SSL_CTX *ctx) {
SSL_CTX *default_ctx = ls->ssl_ctx;
if (!default_ctx) return;

Expand All @@ -3408,22 +3404,11 @@ void us_listen_socket_add_server_name(struct us_listen_socket_t *ls,

struct sni_node_t *node = us_malloc(sizeof(struct sni_node_t));
node->ctx = ctx;
node->user = user;
SSL_CTX_up_ref(ctx);
/* Stash userdata on the SSL_CTX too so per-socket lookup via
* SSL_get_SSL_CTX works regardless of which ctx the SNI cb selected. */
us_ex_idx_ensure();
SSL_CTX_set_ex_data(ctx, us_sni_ex_idx, user);

sni_node_destructor(sni_add(names->tree, hostname_pattern, node));
}

void *us_listen_socket_find_server_name_userdata(struct us_listen_socket_t *ls,
const char *hostname_pattern) {
struct sni_node_t *node = us_server_names_find(ls->server_names, hostname_pattern);
return node ? node->user : NULL;
}

/* Returns the SSL_CTX registered for `hostname_pattern` via
* us_listen_socket_add_server_name, or NULL. Owned - the caller must release
* the reference. The on_server_name resolvers return owned references (the
Expand Down Expand Up @@ -3481,11 +3466,6 @@ void us_socket_on_server_name(struct us_socket_t *s) {
if (ctx) SSL_CTX_set_select_certificate_cb(ctx, us_select_cert_cb);
}

void *us_socket_server_name_userdata(struct us_socket_t *s) {
if (!s->ssl || !s_ssl(s) || us_sni_ex_idx < 0) return NULL;
return SSL_CTX_get_ex_data(SSL_get_SSL_CTX(s_ssl(s)), us_sni_ex_idx);
}

const char *us_internal_ssl_sni_servername(struct us_socket_t *s) {
if (!s->ssl || !s_ssl(s)) return NULL;
return SSL_get_servername(s_ssl(s), TLSEXT_NAMETYPE_host_name);
Expand Down
4 changes: 4 additions & 0 deletions packages/bun-usockets/src/eventing/epoll_kqueue.c
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,10 @@ void us_poll_stop(struct us_poll_t *p, struct us_loop_t *loop) {
us_internal_loop_update_pending_ready_polls(loop, p, 0, old_events, new_events);
}

void us_internal_poll_close_fd(struct us_poll_t *p) {
bsd_close_socket(us_poll_fd(p));
}

size_t us_internal_accept_poll_event(struct us_poll_t *p) {
#ifdef LIBUS_USE_EPOLL
int fd = us_poll_fd(p);
Expand Down
Loading
Loading