nodejs / nodejs/node

SEA: BlobDeserializer SIGSEGVs when fuse byte is set but no NODE_SEA_BLOB is present

未关闭 适合新手
#63,466 2 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

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

描述

Version

v26.1.0 (also reproduces on v25.6.0)

Platform

Linux arm64 (reproduced on Apple Silicon via Docker Desktop, but the SEGV is platform-independent)

Subsystem

sea

What steps will reproduce the bug?

Binaries where the postject fuse byte is set to 1 but NODE_SEA_BLOB cannot be located at runtime currently die with a NULL-deref SIGSEGV inside BlobDeserializer::ReadArithmetic, with no error message indicating the cause.

Take any Node binary, flip the fuse byte from 0 to 1 without injecting an actual SEA blob:

python3 -c "
sent = b'NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2'
with open('hello','rb') as f: buf = bytearray(f.read())
i = buf.find(sent)
buf[i + len(sent) + 1] = ord('1')
with open('hello','wb') as f: f.write(bytes(buf))
"
chmod +x hello
./hello --version    # → Segmentation fault, exit 139

This state arises naturally when postject is run against a host binary with no PT_NOTE program header — postject silently fails to inject the note but still flips the fuse byte. See https://github.com/nodejs/postject/issues/107 and https://github.com/nodejs/unofficial-builds/issues/200.

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

100% reproducible. Required condition: fuse byte set to 1 AND no NODE_SEA_BLOB discoverable via postject_find_resource().

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

A clear error indicating that the SEA fuse is set but no blob is present, rather than a bare SIGSEGV at startup. The current behavior makes it look like a crash in OpenSSL or libc (because the SIGILLs from OpenSSL's ARM crypto-extension probes show up first under gdb), when the actual cause is much earlier and recoverable.

What do you see instead?
Program received signal SIGSEGV, Segmentation fault.
#0  memcpy ()
#1  node::BlobDeserializer<...>::ReadArithmetic<unsigned int>()
#2  node::sea::FindSingleExecutableResource()
#3  node::sea::FixupArgsForSEA(int, char**)
#4  node::Start(int, char**)

postject_find_resource("NODE_SEA_BLOB", &size, ...) returns NULL, then BlobDeserializer::ReadArithmetic calls memcpy(dst, NULL, sizeof(uint32_t)) → SIGSEGV.

Additional information

Suggested fix in node::sea::FindSingleExecutableBlob() (src/node_sea_bin.cc) — guard the deserialization on the resource lookup:

const char* blob = static_cast<const char*>(
    postject_find_resource("NODE_SEA_BLOB", &size, ...));
if (blob == nullptr) {
    fprintf(stderr,
            "node: SEA fuse is set but no NODE_SEA_BLOB resource was found "
            "in this binary. The host binary may be missing a PT_NOTE program "
            "header (run `readelf -lW <binary> | grep NOTE` to check).\n");
    exit(static_cast<int>(node::ExitCode::kGenericUserError));
}

Either that or CHECK_NOT_NULL(blob) — anything that surfaces a cause rather than a bare SEGV.

Related:

贡献指南

打开贡献指南

从这里开始

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

调研方向

从 src/node_sea_bin.cc 中的 node::sea::FindSingleExecutableBlob() 开始,跟踪 BlobDeserializer 运行前 postject_find_resource("NODE_SEA_BLOB", ...) 的结果。使用提供的 fuse-flipping 命令复现,然后验证缺少 blob 时会产生清晰的启动错误并以非零状态退出,而不是发生 SIGSEGV。

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

评估

技术栈
cpp, linux, nodejs
领域
operating-systems
Issue 类型
缺陷
难度
2/5
预计耗时
1-3 小时
活跃度
活跃
描述清晰度
描述清楚
新手友好度
76/100

把新 issue 发到你的邮箱

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