nodejs / nodejs/node

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

Open Beginner friendly
#63,466 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

stale
Dominant language
JavaScript
Stars
122k
Forks
37.3k
Avg merge
4d 2h
Merged PRs (30d)
283

Description

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:

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/node_sea_bin.cc at node::sea::FindSingleExecutableBlob() and trace the postject_find_resource("NODE_SEA_BLOB", ...) result before BlobDeserializer runs. Reproduce with the supplied fuse-flipping command, then verify that a missing blob produces a clear startup error and nonzero exit instead of a SIGSEGV.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, linux, nodejs
Domain
operating-systems
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.