SIGN IN SIGN UP
oven-sh / bun UNCLAIMED

Incredibly fast JavaScript runtime, bundler, test runner, and package manager – all in one

0 0 150 Rust

fix(node:module): don't register native helpers as their own constructors (#31393)

### What does this PR do?

Fixes a fuzzer-found crash (`ASSERTION FAILED: isCell()` in
`JSCJSValue.h`, `asCell()`) triggered by constructing
`process.mainModule._compile`:

```js
Reflect.construct(process.mainModule._compile, []); // ASSERTION FAILED: isCell()
```

`Module.runMain`, `Module._resolveFilename`, `module._compile` (in
`src/jsc/modules/NodeModuleModule.cpp`) and `performance.now` (in
`src/jsc/bindings/webcore/JSPerformance.cpp`) were created with their
call function also registered as the **native construct callback**. JSC
requires a native construct callback to return an object, but these
functions return `undefined`, a string, or a number — so `new fn()` /
`Reflect.construct(fn, …)` hit `asObject()` → `ASSERT(isCell())` in
debug builds and produce an invalid `JSObject*` in release builds.

### Fix

Create these functions without a native constructor (the
`JSFunction::create` default, `callHostFunctionAsConstructor`), so
constructing them throws a `TypeError` like other native helper
functions — matching `performance.now` behavior in browsers/Node, while
calling them is unchanged.

Related: #31386 fixes the same assertion fingerprint for `bun:test` mock
functions; this PR covers the remaining non-mock instances the fuzzer
reached.

### How did you verify your code works?

- The minimized repros (`Reflect.construct(module._compile, [])`, `new
process.mainModule._compile()`,
`Reflect.construct(Module._resolveFilename, ["fs"])`, `new
Module.runMain()`, `Reflect.construct(performance.now, [])`) assert on
current main (debug build) and now throw `TypeError` with this change.
- Added regression tests:
- `node-module-module > native module functions are not constructors`
(`test/js/node/module/node-module-module.test.js`)
- `performance.now is not a constructor`
(`test/js/web/timers/performance.test.js`)

Both fail on an unfixed build and pass with this change; the full files
(29 and 7 tests) pass with the debug build, including the existing tests
that call `module._compile` / `Module._resolveFilename`.
R
robobun committed
e262c99168b268b9c99f4d00f0c864be6cc38656
Parent: 984bc1e
Committed by GitHub <noreply@github.com> on 5/25/2026, 11:53:25 PM