Compare commits

...

3 Commits

Author SHA1 Message Date
Jarred Sumner
963b51c013 Merge branch 'main' into toaster/fix-dns-null-cell-resolve 2026-02-20 15:34:10 -08:00
autofix-ci[bot]
ac75b1306c [autofix.ci] apply automated fixes 2026-02-20 22:47:58 +00:00
Claude
b797e5495e fix(dns): reject promise instead of resolving with null cell on error
When DNS result-to-JS conversion (toJSResponse/toJSArray) fails, the
error was caught with `catch .zero`, and the resulting JSValue.zero
(all-zero bit pattern) was passed to promise.resolveTask(). On ARM64,
a zero-encoded JSValue passes isCell() but asCell() returns nullptr,
causing a null pointer member call crash in JSC's isGetterSetter().

Fix the four onComplete/onCompleteWithArray functions in the DNS
resolver to check for .zero and reject the promise with the pending
exception instead of resolving with a null cell value.

Crash reproduction:
```js
delete globalThis.Loader;
Bun.generateHeapSnapshot = console.profile = console.profileEnd = process.abort = () => {};
const v4 = new ArrayBuffer(1024, { maxByteLength: 1024 });
new DataView(v4);
new Date();
new SharedArrayBuffer(SharedArrayBuffer, SharedArrayBuffer);
Bun.CookieMap;
Bun.dns.reverse("127.0.0.1");
const v20 = {
    maxByteLength: 3960605680,
    maxByteLength: 1024,
    maxByteLength: 3960605680,
    ...Bun,
    key: "127.0.0.1",
    maxByteLength: 1024,
};
const v22 = new SharedArrayBuffer(64, v20);
("10")["substring"](253);
function f29() {
    return 55103n;
}
new Uint16Array(v22);
Bun.dns.lookup("example.invalid");
Bun.gc(true);
```

https://claude.ai/code/session_01RTdLNAqvERaT7vQSmESYiN
2026-02-20 22:46:00 +00:00
2 changed files with 25 additions and 5 deletions

View File

@@ -516,7 +516,12 @@ pub const CAresNameInfo = struct {
var promise = this.promise;
const globalThis = this.globalThis;
this.promise = .{};
promise.resolveTask(globalThis, result) catch {}; // TODO: properly propagate exception upwards
if (result == .zero) {
const exception = globalThis.tryTakeException() orelse .js_undefined;
promise.rejectTask(globalThis, exception) catch {};
} else {
promise.resolveTask(globalThis, result) catch {};
}
this.deinit();
}
@@ -932,7 +937,12 @@ pub const CAresReverse = struct {
var promise = this.promise;
const globalThis = this.globalThis;
this.promise = .{};
promise.resolveTask(globalThis, result) catch {}; // TODO: properly propagate exception upwards
if (result == .zero) {
const exception = globalThis.tryTakeException() orelse .js_undefined;
promise.rejectTask(globalThis, exception) catch {};
} else {
promise.resolveTask(globalThis, result) catch {};
}
if (this.resolver) |resolver| {
resolver.requestCompleted();
}
@@ -1013,7 +1023,12 @@ pub fn CAresLookup(comptime cares_type: type, comptime type_name: []const u8) ty
var promise = this.promise;
const globalThis = this.globalThis;
this.promise = .{};
promise.resolveTask(globalThis, result) catch {}; // TODO: properly propagate exception upwards
if (result == .zero) {
const exception = globalThis.tryTakeException() orelse .js_undefined;
promise.rejectTask(globalThis, exception) catch {};
} else {
promise.resolveTask(globalThis, result) catch {};
}
if (this.resolver) |resolver| {
resolver.requestCompleted();
}
@@ -1108,7 +1123,12 @@ pub const DNSLookup = struct {
var promise = this.promise;
this.promise = .{};
const globalThis = this.globalThis;
promise.resolveTask(globalThis, result) catch {}; // TODO: properly propagate exception upwards
if (result == .zero) {
const exception = globalThis.tryTakeException() orelse .js_undefined;
promise.rejectTask(globalThis, exception) catch {};
} else {
promise.resolveTask(globalThis, result) catch {};
}
if (this.resolver) |resolver| {
resolver.requestCompleted();
}

View File

@@ -17,7 +17,7 @@
"EXCEPTION_ASSERT(!scope.exception())": 0,
"JSValue.false": 0,
"JSValue.true": 0,
"TODO: properly propagate exception upwards": 114,
"TODO: properly propagate exception upwards": 110,
"alloc.ptr !=": 0,
"alloc.ptr ==": 0,
"allocator.ptr !=": 1,