Skip to content

Commit 94c29b2

Browse files
committed
Merge remote-tracking branch 'origin/main' into farm/951ebbb3/serve-stream-resolve-socket-uaf
2 parents f1c4c20 + 990be52 commit 94c29b2

28 files changed

Lines changed: 810 additions & 66 deletions

packages/bun-usockets/src/crypto/openssl.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1306,6 +1306,7 @@ void us_internal_ssl_attach(struct us_socket_t *s, SSL_CTX *ctx,
13061306
s->ssl_fatal_error = 0;
13071307
s->ssl_raw_tap = 0;
13081308
s->ssl_shutdown_after_spill = 0;
1309+
s->ssl_close_after_spill = 0;
13091310
s->ssl_in_use = 0;
13101311
s->ssl_pending_detach = 0;
13111312
s->ssl_pending_close_code = 0;
@@ -1549,17 +1550,30 @@ static int ssl_handle_shutdown(struct us_socket_t *s, int force_fast_shutdown) {
15491550
}
15501551

15511552
struct us_socket_t *us_internal_ssl_close(struct us_socket_t *s, int code, void *reason) {
1552-
ssl_release_spill(s->group->loop, s);
15531553
if (s->ssl && s->ssl_in_use) {
15541554
/* A JS callback running from inside SSL_do_handshake/SSL_read (ALPN, SNI,
15551555
* keylog, ...) destroyed this socket. Reaching ssl_set_loop_data /
15561556
* SSL_do_handshake here would re-enter BoringSSL on the same SSL* while
15571557
* the outer ssl_run_handshake is still on the stack; defer to the SSL
1558-
* driver's epilogue (the same protocol close_raw and ssl_detach honor). */
1558+
* driver's epilogue (the same protocol close_raw and ssl_detach honor),
1559+
* releasing the spill now so the re-issued close cannot itself defer. */
1560+
ssl_release_spill(s->group->loop, s);
15591561
s->ssl_pending_detach = 1;
15601562
s->ssl_pending_close_code = (unsigned char) code;
15611563
return s;
15621564
}
1565+
/* node's `_handle.close()` (FAST_SHUTDOWN, no reason) must not cut off spilled
1566+
* ciphertext already reported as written: SSL sealed it, so it can only be
1567+
* delivered, never re-sent. Mirror ssl_shutdown_after_spill; defer at most once. */
1568+
if (code == LIBUS_SOCKET_CLOSE_CODE_FAST_SHUTDOWN && !reason
1569+
&& !s->ssl_close_after_spill && !s->ssl_fatal_error && !us_socket_is_closed(s)) {
1570+
struct loop_ssl_data *loop_ssl_data = (struct loop_ssl_data *)s->group->loop->data.ssl_data;
1571+
if (loop_ssl_data && !ssl_drain_spill(loop_ssl_data, s)) {
1572+
s->ssl_close_after_spill = 1;
1573+
return s;
1574+
}
1575+
}
1576+
ssl_release_spill(s->group->loop, s);
15631577
/* SEMI_SOCKET never connected — SSL was attached eagerly on the fast-path
15641578
* connect, but no bytes were ever exchanged. Firing on_handshake(0) here
15651579
* lands in JS after onConnectError already tore down `this`/its handlers. */
@@ -1722,6 +1736,10 @@ struct us_socket_t *us_internal_ssl_on_writable(struct us_socket_t *s) {
17221736
us_internal_ssl_shutdown(s);
17231737
if (ssl_gone(s)) return s;
17241738
}
1739+
if (s->ssl_close_after_spill) {
1740+
s->ssl_close_after_spill = 0;
1741+
return us_internal_ssl_close(s, LIBUS_SOCKET_CLOSE_CODE_FAST_SHUTDOWN, NULL);
1742+
}
17251743
}
17261744
ssl_update_handshake(s);
17271745
if (ssl_gone(s)) return s;

packages/bun-usockets/src/internal/internal.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ struct us_socket_t {
271271
* spilled (see ssl_flush_write_batch); the shutdown re-runs once the
272272
* spill drains so those records are not cut off by our FIN/close_notify. */
273273
unsigned char ssl_shutdown_after_spill : 1;
274+
/* Same as ssl_shutdown_after_spill but for us_internal_ssl_close: the
275+
* close re-runs from the writable event once the spill drains. */
276+
unsigned char ssl_close_after_spill : 1;
274277
/* Set while SSL_do_handshake/SSL_read is on the stack: JS run from inside
275278
* those calls (ALPN/SNI/keylog callbacks) may destroy the socket, and the
276279
* SSL must not be freed under BoringSSL's feet - the detach is deferred to

src/jsc/bindings/bindings.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4047,6 +4047,7 @@ void JSC__JSValue__putToPropertyKey(JSC::EncodedJSValue JSValue0, JSC::JSGlobalO
40474047
auto pkey = key.toPropertyKey(arg1);
40484048
RETURN_IF_EXCEPTION(scope, );
40494049
object->putDirectMayBeIndex(arg1, pkey, value);
4050+
RETURN_IF_EXCEPTION(scope, );
40504051
}
40514052

40524053
extern "C" [[ZIG_EXPORT(check_slow)]] void JSC__JSValue__putMayBeIndex(JSC::EncodedJSValue target, JSC::JSGlobalObject* globalObject, const BunString* key, JSC::EncodedJSValue value)

src/jsc/bindings/node/crypto/JSCipherPrototype.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,12 @@ JSC_DEFINE_HOST_FUNCTION(jsCipherSetAAD, (JSC::JSGlobalObject * globalObject, JS
351351
return ERR::OUT_OF_RANGE(scope, globalObject, "buffer is too big"_s, 0, INT_MAX, jsNumber(aadbuf->byteLength()));
352352
}
353353

354+
// Passing a NULL output buffer to EVP_CipherUpdate is only valid for AEAD
355+
// modes; for any other mode it writes the ciphertext through the NULL pointer.
356+
if (!cipher->m_ctx || !cipher->isAuthenticatedMode()) {
357+
return ERR::CRYPTO_INVALID_STATE(scope, globalObject, "setAAD"_s);
358+
}
359+
354360
MarkPopErrorOnReturn popError;
355361

356362
int32_t outlen;

src/jsc/bindings/webcore/AbortSignal.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ JSValue AbortSignal::jsReason(JSC::JSGlobalObject& globalObject)
122122
if (m_commonReason != CommonAbortReason::None) {
123123
existingValue = toJS(&globalObject, m_commonReason);
124124
m_commonReason = CommonAbortReason::None;
125-
m_reason.setWeakly(existingValue);
125+
m_reason.set(globalObject.vm(), wrapper(), existingValue);
126126
}
127127
}
128128

@@ -159,10 +159,11 @@ void AbortSignal::markAborted(JSC::JSValue reason)
159159
applyFlags(static_cast<uint8_t>(AbortSignalFlags::Aborted) | static_cast<uint8_t>(AbortSignalFlags::IsFiringEventListeners));
160160
m_sourceSignals.clear();
161161

162-
// FIXME: This code is wrong: we should emit a write-barrier. Otherwise, GC can collect it.
163-
// https://bugs.webkit.org/show_bug.cgi?id=236353
164162
ASSERT(reason);
165-
m_reason.setWeakly(reason);
163+
if (auto* context = scriptExecutionContext())
164+
m_reason.set(context->vm(), wrapper(), reason);
165+
else
166+
m_reason.setWeakly(reason);
166167

167168
cancelTimer();
168169
}

src/jsc/bindings/webcore/JSAbortController.cpp

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,31 @@ void JSAbortController::visitChildrenImpl(JSCell* cell, Visitor& visitor)
242242
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
243243
Base::visitChildren(thisObject, visitor);
244244
addWebCoreOpaqueRoot(visitor, thisObject->wrapped().opaqueRoot());
245-
thisObject->wrapped().signal().reason().visit(visitor);
245+
thisObject->visitAdditionalChildrenInGCThread(visitor);
246246
}
247247

248248
DEFINE_VISIT_CHILDREN(JSAbortController);
249249

250+
template<typename Visitor>
251+
void JSAbortController::visitOutputConstraints(JSCell* cell, Visitor& visitor)
252+
{
253+
auto* thisObject = uncheckedDowncast<JSAbortController>(cell);
254+
ASSERT_GC_OBJECT_INHERITS(thisObject, info());
255+
Base::visitOutputConstraints(thisObject, visitor);
256+
thisObject->visitAdditionalChildrenInGCThread(visitor);
257+
}
258+
259+
template void JSAbortController::visitOutputConstraints(JSCell*, AbstractSlotVisitor&);
260+
template void JSAbortController::visitOutputConstraints(JSCell*, SlotVisitor&);
261+
262+
template<typename Visitor>
263+
void JSAbortController::visitAdditionalChildrenInGCThread(Visitor& visitor)
264+
{
265+
wrapped().signal().reason().visit(visitor);
266+
}
267+
268+
DEFINE_VISIT_ADDITIONAL_CHILDREN_IN_GC_THREAD(JSAbortController);
269+
250270
void JSAbortController::analyzeHeap(JSCell* cell, HeapAnalyzer& analyzer)
251271
{
252272
auto* thisObject = uncheckedDowncast<JSAbortController>(cell);

src/jsc/bindings/webcore/JSAbortController.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ class JSAbortController : public JSDOMWrapper<AbortController> {
5858
}
5959
static JSC::GCClient::IsoSubspace* subspaceForImpl(JSC::VM& vm);
6060
DECLARE_VISIT_CHILDREN;
61+
template<typename Visitor> void visitAdditionalChildrenInGCThread(Visitor&);
6162

63+
template<typename Visitor> static void visitOutputConstraints(JSCell*, Visitor&);
6264
static void analyzeHeap(JSCell*, JSC::HeapAnalyzer&);
6365

6466
protected:

src/jsc/bindings/webcore/JSValueInWrappedObject.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ inline void JSValueInWrappedObject::setWeakly(JSC::JSValue value)
9898
inline void JSValueInWrappedObject::set(JSC::VM& vm, const JSC::JSCell* owner, JSC::JSValue value)
9999
{
100100
setWeakly(value);
101-
vm.writeBarrier(owner, value);
101+
if (owner)
102+
vm.writeBarrier(owner, value);
102103
}
103104

104105
inline void JSValueInWrappedObject::clear()

src/runtime/api.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,21 @@ pub(crate) fn with_text_format_source<R>(
259259
_str_hold.slice()
260260
};
261261

262+
// Every parser reached from here records source positions as an `i32`
263+
// (`ast::Loc` via `usize2loc` for JSONC/TOML, JSON5's token locs, YAML's
264+
// `Pos`), so an input those offsets cannot represent panics inside the
265+
// lexer instead of reporting an error. Reject it before parsing.
266+
if bytes.len() > i32::MAX as usize {
267+
return Err(global.throw_range_error(
268+
bytes.len() as i64,
269+
bun_jsc::RangeErrorOptions {
270+
field_name: b"input.byteLength",
271+
max: i64::from(i32::MAX),
272+
..Default::default()
273+
},
274+
));
275+
}
276+
262277
let mut log = bun_ast::Log::init();
263278
let source = bun_ast::Source::init_path_string(path, bytes);
264279

src/runtime/node/node_fs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2426,7 +2426,7 @@ mod _async_tasks {
24262426
<$T as ReaddirEntry>::destroy_entry(item);
24272427
}
24282428
{
2429-
let _lock = self.pending_err_mutex.lock();
2429+
let _lock = self.pending_err_mutex.lock_guard();
24302430
if self.pending_err.is_none() {
24312431
let err_path: &[u8] = if !err.path.is_empty() {
24322432
&err.path[..]

0 commit comments

Comments
 (0)