Skip to content

postject silently succeeds when injecting into an ELF with no PT_NOTE program header #107

Description

@awbx

On ELF binaries that have no PT_NOTE program header (e.g. those built with a toolchain that doesn't pass --build-id to ld), postject currently:

  1. Prints 💉 Injection done! and exits 0
  2. Doesn't actually modify the binary (file size and content are byte-identical before/after)
  3. Produces a binary where postject_find_resource() returns NULL at runtime

This makes downstream failures (SEGV in BlobDeserializer, "resource not found" errors in user code) look unrelated to postject and was the single biggest time-sink in diagnosing nodejs/unofficial-builds#200.

Reproduction

Using a musl-cross-make-built aarch64-linux-musl-gcc (or any toolchain that doesn't pass --build-id by default):

// test.c
#include <stdio.h>
#define POSTJECT_SENTINEL_FUSE "NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2"
#include "postject-api.h"
int main(void) {
    size_t size; const void* p = postject_find_resource("FOO", &size, NULL);
    return p == NULL ? 2 : 0;
}
gcc test.c -o test                                  # no --build-id by default on this toolchain
readelf -lW test | grep '^[[:space:]]*NOTE'         # (no output — no PT_NOTE)

ls -la test                                         # 8848 bytes
npx postject test FOO some.txt \
  --sentinel-fuse NODE_SEA_FUSE_fce680ab2cc467b6e072b8b5df1996b2
# → 💉 Injection done!  (exit 0)
ls -la test                                         # 8848 bytes  (unchanged!)
./test                                              # exit 2 (find_resource returned NULL)

Same source built with -Wl,--build-id=sha1 → postject grows the binary (8848 → 67408 bytes) and ./test returns 0.

Suggested fix

In inject_into_elf (src/postject.cpp), if the parsed ELF has no PT_NOTE segment, either:

  • Return InjectResult::kError and surface a clear error from the JS wrapper (e.g. "target has no PT_NOTE program header — rebuild with -Wl,--build-id or equivalent"), or
  • Have LIEF synthesize a PT_NOTE segment so the injection actually succeeds

Either is better than the current "exit 0, do nothing, produce broken downstream binary" behavior.

Context

Hit while diagnosing nodejs/unofficial-builds#200 (unofficial-builds arm64-musl tarball ships a binary with no PT_NOTE). Root-cause fix for that at nodejs/unofficial-builds#233. The silent-no-op behavior here was load-bearing in masking the actual problem during diagnosis.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions