SEA: BlobDeserializer SIGSEGVs when fuse byte is set but no NODE_SEA_BLOB is present
Personne n'a encore pris cette issue.
- Langage dominant
- JavaScript
- Étoiles
- 122k
- Forks
- 37.4k
- Merge moyen
- 4 j 3 h
- PR mergées (30 j)
- 272
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:
- https://github.com/nodejs/unofficial-builds/issues/200 — upstream cause (arm64-musl tarball with no PT_NOTE)
- https://github.com/nodejs/unofficial-builds/pull/233 — fix for that root cause
- https://github.com/nodejs/postject/issues/107 — companion postject issue (silent injection failure)
Guide de contribution
Ouvrir le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Piste de recherche
Commencez dans src/node_sea_bin.cc, au niveau de node::sea::FindSingleExecutableBlob(), et suivez le résultat de postject_find_resource("NODE_SEA_BLOB", ...) avant l’exécution de BlobDeserializer. Reproduisez le problème avec la commande fournie qui inverse le fuse, puis vérifiez que l’absence du blob produit une erreur de démarrage claire et une sortie non nulle au lieu d’un SIGSEGV.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Évaluation
- Stack technique
- cpp, linux, nodejs
- Domaine
- operating-systems
- Type d'issue
- Bug
- Difficulté
- 2/5
- Temps estimé
- 1-3 heures
- Activité
- Active
- Clarté
- Clairement spécifiée
- Accessibilité débutants
- 76/100