Commit Graph

12 Commits

Author SHA1 Message Date
robobun
0dc136149d fix(napi): prevent finalizer crash during process exit (#21951)
## Summary

Fixes a critical segmentation fault crash occurring during NAPI
finalizer cleanup when finalizers trigger GC operations. This crash was
reported with `node-sqlite3` and other NAPI modules during process exit.

## Root Cause

The crash was caused by **iterator invalidation** in
`napi_env__::cleanup()`:

1. Range-based for loop iterates over `m_finalizers`
(std::unordered_set)
2. `boundFinalizer.call(this)` executes finalizer callbacks
3. Finalizers can trigger JavaScript execution and GC operations  
4. GC can add/remove entries from `m_finalizers` during iteration
5. **Iterator invalidation** → undefined behavior → segfault

## Solution

Added `JSC::DeferGCForAWhile deferGC(m_vm)` scope during entire
finalizer cleanup to prevent any GC operations from occurring during
iteration.

### Why This Approach?

- **Addresses root cause**: Prevents GC entirely during critical section
- **Simple & safe**: One-line RAII fix using existing JSC patterns  
- **Minimal impact**: Only affects process cleanup (not runtime
performance)
- **Proven pattern**: Already used elsewhere in Bun's codebase
- **Better than alternatives**: Cleaner than Node.js manual iterator
approach

## Testing

Added comprehensive test (`test_finalizer_iterator_invalidation.c`) that
reproduces the crash by:
- Creating finalizers that trigger GC operations
- Forcing JavaScript execution during finalization
- Allocating objects that can trigger more GC
- Calling process exit to trigger finalizer cleanup

**Before fix**: Segmentation fault  
**After fix**: Clean exit 

## Files Changed

- `src/bun.js/bindings/napi.h`: Core fix + include
- `test/napi/napi-app/test_finalizer_iterator_invalidation.c`: Test
addon
- `test/napi/napi-app/binding.gyp`: Build config for test addon
- `test/regression/issue/napi-finalizer-crash.test.ts`: Integration test

## Test Plan

- [x] Reproduces original crash without fix
- [x] Passes cleanly with fix applied  
- [x] Existing NAPI tests continue to pass
- [x] Manual testing with node-sqlite3 scenarios

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Kai Tamkun <kai@tamkun.io>
2025-08-18 16:48:48 -07:00
robobun
99c3824b31 fix(napi): Make cleanup hooks behavior match Node.js exactly (#21883)
# Fix NAPI cleanup hook behavior to match Node.js

This PR addresses critical differences in NAPI cleanup hook
implementation that cause crashes when native modules attempt to remove
cleanup hooks. The fixes ensure Bun's behavior matches Node.js exactly.

## Issues Fixed

Fixes #20835
Fixes #18827
Fixes #21392
Fixes #21682
Fixes #13253

All these issues show crashes related to NAPI cleanup hook management:
- #20835, #18827, #21392, #21682: Show "Attempted to remove a NAPI
environment cleanup hook that had never been added" crashes with
`napi_remove_env_cleanup_hook`
- #13253: Shows `napi_remove_async_cleanup_hook` crashes in the stack
trace during Vite dev server cleanup

## Key Behavioral Differences Addressed

### 1. Error Handling for Non-existent Hook Removal
- **Node.js**: Silently ignores removal of non-existent hooks (see
`node/src/cleanup_queue-inl.h:27-30`)
- **Bun Before**: Crashes with `NAPI_PERISH` error
- **Bun After**: Silently ignores, matching Node.js behavior

### 2. Duplicate Hook Prevention 
- **Node.js**: Uses `CHECK_EQ` which crashes in ALL builds when adding
duplicate hooks (see `node/src/cleanup_queue-inl.h:24`)
- **Bun Before**: Used debug-only assertions
- **Bun After**: Uses `NAPI_RELEASE_ASSERT` to crash in all builds,
matching Node.js

### 3. VM Termination Checks
- **Node.js**: No VM termination checks in cleanup hook APIs
- **Bun Before**: Had VM termination checks that could cause spurious
failures
- **Bun After**: Removed VM termination checks to match Node.js

### 4. Async Cleanup Hook Handle Validation
- **Node.js**: Validates handle is not NULL before processing
- **Bun Before**: Missing NULL handle validation 
- **Bun After**: Added proper NULL handle validation with
`napi_invalid_arg` return

## Execution Order Verified

Both Bun and Node.js execute cleanup hooks in LIFO order (Last In, First
Out) as expected.

## Additional Architectural Differences Identified

Two major architectural differences remain that affect compatibility but
don't cause crashes:

1. **Queue Architecture**: Node.js uses a single unified queue for all
cleanup hooks, while Bun uses separate queues for regular vs async
cleanup hooks
2. **Iteration Safety**: Different behavior when hooks are added/removed
during cleanup iteration

These will be addressed in future work as they require more extensive
architectural changes.

## Testing

- Added comprehensive test suite covering all cleanup hook scenarios
- Tests verify identical behavior between Bun and Node.js
- Includes edge cases like duplicate hooks, non-existent removal, and
execution order
- All tests pass with the current fixes

The changes ensure NAPI modules using cleanup hooks (like LMDB, native
Rust modules, etc.) work reliably without crashes.

---------

Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Kai Tamkun <kai@tamkun.io>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2025-08-15 21:08:53 -07:00
190n
03dfd7d96b Run callback passed to napi_module_register after dlopen returns instead of during call (#20478) 2025-07-24 11:46:56 -07:00
Ben Grant
1ebec90d6e Revert "Add test from #18287 (#19775)"
This reverts commit f1504c4265.
2025-05-20 12:22:01 -07:00
190n
f1504c4265 Add test from #18287 (#19775) 2025-05-20 11:56:30 -07:00
190n
cde668b54c Better edge case handling in napi_value<->String conversion (#18107) 2025-03-12 18:15:00 -07:00
190n
efabdcbe1f Start fixing bugs discovered by Node.js's Node-API tests (#14501)
Co-authored-by: Kai Tamkun <kai@tamkun.io>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
Co-authored-by: Ashcon Partovi <ashcon@partovi.net>
Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
Co-authored-by: 190n <190n@users.noreply.github.com>
2025-02-26 22:11:42 -08:00
190n
71eb1476db Fix crash when napi_register_module_v1 returns nullptr (#16816) 2025-01-27 20:13:22 -08:00
190n
4eae3a90e8 fix(napi): Make napi_wrap work on regular objects (#15622)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2024-12-16 15:54:39 -08:00
190n
282b92d6e1 Fix issues with NAPI tests (#13831)
Co-authored-by: 190n <190n@users.noreply.github.com>
2024-09-09 17:08:40 -07:00
Jarred Sumner
0248e3c2b7 Add NODE_API_EXPERIMENTAL_NOGC_ENV_OPT_OUT=1 (#9742)
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2024-03-30 14:03:52 -07:00
Olivier Guimbal
6e7014c91b Fix napi_get_value_string_utf8 to match node (#7175)
* fix napi_get_value_string_utf8 to match node
closes #6949

* [autofix.ci] apply automated fixes

* Update test/napi/napi.test.ts

Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>

* Update test/napi/napi.test.ts

Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
2023-11-17 05:27:58 -08:00