nodejs / nodejs/node

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

オープン 初心者向け
#63,466 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

stale
主要言語
JavaScript
スター
122k
フォーク
37.3k
平均マージ
4日 2時間
マージ済み PR(30日)
283

説明

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. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

src/node_sea_bin.cc の node::sea::FindSingleExecutableBlob() から開始し、BlobDeserializer が実行される前に postject_find_resource("NODE_SEA_BLOB", ...) の結果を追跡します。提供されている fuse-flipping コマンドで再現し、blob がない場合に SIGSEGV ではなく、明確な起動エラーが発生して終了コードが 0 以外になることを確認します。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
cpp, linux, nodejs
領域
operating-systems
issue の種類
バグ
難易度
2/5
見積もり時間
1〜3時間
活発さ
活発
明瞭さ
明確に書かれている
初心者へのやさしさ
76/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。