nodejs / nodejs/node

module: compile cache leaks temporary files when persistence fails

未关闭
#65,473 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

主要语言
JavaScript
星标
122k
派生
37.4k
平均合并
4 天 3 小时
30 天内合并 PR
272

描述

Version

v24.15.0

Platform
Linux pike 7.0.0-29-generic #29-Ubuntu SMP PREEMPT_DYNAMIC Fri Jul 17 20:52:35 UTC 2026 x86_64 GNU/Linux
Subsystem

module compile cache

What steps will reproduce the bug?

This uses RLIMIT_FSIZE=0 to make mkstemp() succeed and the subsequent write fail. It requires no third-party dependencies or elevated privileges.

tmp="$(mktemp -d)"
mkdir -p "$tmp/cache"
printf '%s\n' 'module.exports = 42;' >"$tmp/fixture.cjs"

(
  cd "$tmp"
  ulimit -f 0
  trap '' XFSZ
  NODE_COMPILE_CACHE="$tmp/cache" node -e "require('./fixture.cjs')"
)

find "$tmp/cache" -type f -printf '%P %s bytes\n'

Example output:

v24.15.0-x64-cf738c9d-1000/712488c2.LuhsmB 0 bytes
v24.15.0-x64-cf738c9d-1000/8e9c99c2.hwlSas 0 bytes
v24.15.0-x64-cf738c9d-1000/9fb526e9.wPP9zO 0 bytes

The Node process exits with status 0.

How often does it reproduce? Is there a required condition?

Every run on the Linux system above. The required condition is that creation of the temporary cache file succeeds but a later write, close, or rename fails. EDQUOT, ENOSPC, and EFBIG are representative failure modes.

What is the expected behavior? Why is that the expected behavior?

The compile cache should continue to fail gracefully, but it should close the temporary file descriptor and unlink the temporary file whenever persistence does not complete. A failed cache write is disposable internal state and should not consume additional filesystem space or inodes.

What do you see instead?

Each failed entry leaves its random-suffix temporary file in the compile-cache directory. On the write-error path, its file descriptor also remains open until process teardown.

In a real EDQUOT incident, one Node process left 3,424 zero-byte compile-cache temporary files in one minute. This amplified a block-quota failure into substantial inode consumption.

Additional information

Current main creates the temporary file, then immediately continues on write, close, or rename errors without unlinking it:

https://github.com/nodejs/node/blob/9d94b6548a8c3c18e5985ce2aef84b2bd8ea9ce0/src/compile_cache.cc#L478-L516

The same control flow is present in v24.15.0.

A possible fix is an RAII cleanup guard established immediately after successful uv_fs_mkstemp() that:

  1. closes the descriptor if it is still open;
  2. unlinks the temporary path unless the rename succeeded;
  3. is disarmed only after successful rename;
  4. preserves the original persistence error.

A regression test can use the same RLIMIT_FSIZE technique on supported POSIX platforms and assert that no random-suffix temporary files remain.

贡献指南

打开贡献指南

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

调研方向

从 src/compile_cache.cc 第 478-516 行附近开始,运行提供的 RLIMIT_FSIZE 复现,以跟踪临时文件处理在 write、close 和 rename 失败时的行为。在受支持的 POSIX 平台上添加回归测试,验证持久化失败不会留下带随机后缀的临时文件。完成标准是:失败的缓存写入会关闭描述符、删除临时路径,并保持缓存失败时的正常处理。

由索引模型根据 Issue 内容生成。

评估

技术栈
cpp, javascript, nodejs
领域
backend, performance, testing
Issue 类型
缺陷
难度
4/5
预计耗时
3-5 天
活跃度
活跃
描述清晰度
描述清楚
新手友好度
74/100

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。