Skip to content

Proposal: build official Node.js binaries for glibc-based Linux using PIE, enables ASLR, helps prevent RCE #66951

Open

Description

@lovell

What is the problem this feature will solve?

Almost all operating systems enable Address Space Layout Randomisation (ASLR) to help prevent memory-related vulnerabilities from reaching a state where Remote Code Execution (RCE) is possible.

To take full advantage of this protection, an executable must be compiled as a Position Independent Executable (PIE).

The current state of PIE in Node.js is as follows:

What is the feature you are proposing to solve the problem?

A previous discussion from May 2020 rejected use of PIE on Linux due to a expected performance penalty of 5-10% and, at that point in time, little evidence of RCE within the Node.js ecosystem.

Given many organisations already quite happily use Node.js with PIE/ASLR, be that on macOS, via Alpine Linux based containers or OS-provided packages, is it time to revisit this discussion for those left behind?

The performance impact of PIE is negligible on 64-bit ARM CPUs, which have seen increasing adoption since 2020 e.g. AWS Graviton.

Do the security benefits of PIE now outweigh the performance cost for those still running glibc-based Linux on x64 architecture CPUs?

By way of a recent example, had this change already been in place then it's likely CVE-2026-111497 would have been rated "High" rather than "Critical" severity.

What alternatives have you considered?

If there's still a desire to provide official binaries for a subset of platforms without PIE then perhaps offering a security-hardened alternative with PIE might help raise awareness.

Activity

  1. changed the title [-]Proposal: build official Node.js binaries for glibc-based Linux on x64 CPUs using PIE, enables ASLR, helps prevent RCE[/-] [+]Proposal: build official Node.js binaries for glibc-based Linux using PIE, enables ASLR, helps prevent RCE[/+] on Oct 26, 2026
  2. added
    tsc-agendaIssues and PRs to discuss during Technical Steering Committee meetings.
    on Oct 28, 2026
  3. mcollina commented on Oct 29, 2026

    @mcollina
    SponsorMember

    Note

    This comment was AI-generated (Claude Code); I reviewed the methodology and numbers before posting.

    I measured the cost of PIE on x64 Linux to give the TSC discussion some current data.

    Setup

    • Intel i7-7700 (Kaby Lake, 4C/8T), Ubuntu 24.04, gcc 13.3, Node.js main at 8587d71
    • Both binaries were built from the same tree with the same compiler:
      • PIE: default build. Ubuntu's gcc defaults to PIE, so most locally built Node.js binaries on Debian/Ubuntu are already PIE.
      • non-PIE: CFLAGS=-fno-pie CXXFLAGS=-fno-pie LDFLAGS=-no-pie make
    • I confirmed that the official v26.13.0 linux-x64 binary is ET_EXEC (non-PIE). It's built with RHEL 8 gcc-toolset-14, which doesn't default to PIE.
    • benchmark/compare.js --runs 15 --old node-nopie --new node-pie

    Startup and binary size

    non-PIE PIE
    node -e 0 (3 alternating rounds of perf stat -r 200) 22.8 ms 23.8 ms (+1.0 ms, +4.2%)
    page faults 2,960 3,344
    R_X86_64_RELATIVE relocations 0 113,341 (2.7 MB .rela.dyn)
    .text 60.1 MB 61.2 MB (+1.9%)
    file size 155.6 MB 158.9 MB

    Most of the startup cost comes from the dynamic loader applying the relative relocations. -z pack-relative-relocs (DT_RELR) would cut most of it, but it needs glibc >= 2.36 at runtime, which is above the current glibc 2.28 baseline.

    Steady state

    Negative means PIE is slower. Only results with at least 95% confidence are marked.

    benchmark PIE vs non-PIE
    string_decoder/string-decoder.js (5 encodings) −0.9% to −4.5% (* to ***)
    util/text-encoder.js encode, len 256–8192 −2.4% to −3.2% (* to ***)
    http/bench-parser*.js (llhttp) 0% to −2.6% (a few *, one ***)
    misc/structured-clone.js type=object −2.5% (**)
    buffers/buffer-tostring.js utf8 / hex / latin1 −2.3% / +1.0% / +1.5%
    streams/iter-creation.js −4.6% to +3.4%, mixed
    misc/object-property-bench.js (pure JIT control) no change
    zlib/deflate.js, crypto/hash-stream-throughput.js sha256, buffers/buffer-base64-encode.js no change
    streams/creation.js, util/inspect.js, url/whatwg-url-parse.js no change (within noise)
    http/simple.js (wrk, c=50, len=4/1024) +0.8% / +1.3%, not significant (±5%)

    Summary

    On x64 the cost is much lower than the 5–10% quoted in #33425. That figure comes mainly from 32-bit x86, where PIE costs a general-purpose register.

    • JIT-compiled JavaScript isn't affected. V8 already generates position-independent code, and the embedded builtins are PC-relative.
    • Code that is mostly C++ (string decoding/encoding, llhttp, the V8 serializer) is 0–3% slower in microbenchmarks. Some results also move in the positive direction by similar amounts, so part of this is code-layout noise.
    • End-to-end HTTP throughput showed no measurable difference.
    • The clearest cost is about 1 ms (+4%) of startup per process, which matters mostly for short-lived CLI invocations.

    Caveats: one machine, one compiler version (gcc 13 rather than the gcc 14 used for releases), and 15 runs per benchmark. It would be worth repeating the startup and http numbers on newer x64 hardware with the actual release toolchain.

  4. richardlau commented on Oct 29, 2026

    @richardlau
    Member
    • I confirmed that the official v26.13.0 linux-x64 binary is ET_EXEC (non-PIE). It's built with RHEL 8 gcc-toolset-14, which doesn't default to PIE.

    FWIW Node.js 26 is built on RHEL 8 with clang 21, not gcc-toolset-14.

    | linux-x64 | RHEL 8 with Clang 20.1 and gcc-toolset-14-libatomic-devel[^6] |

    (DT_RELR) would cut most of it, but it needs glibc >= 2.36 at runtime, which is above the current glibc 2.28 baseline.

    We've moved Node.js 27 onto RHEL 9, which is glibc 2.34 (still below 2.36).

  5. richardlau commented on Oct 29, 2026

    @richardlau
    Member

    In case I don't make the TSC meeting next week where this is on the agenda, I'm in favour of turning on PIE for all supported release lines.

  6. mcollina commented on Oct 29, 2026

    @mcollina
    SponsorMember

    It seems the reward is worth the cost.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestIssues requesting new Node.js features.tsc-agendaIssues and PRs to discuss during Technical Steering Committee meetings.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions