[BUG] PR_SET_THP_DISABLE=1 is inherited into spawned children, breaking applications that rely on THP (devdax SIGBUS)

Resolved 💬 1 comment Opened Apr 23, 2026 by nilpe Closed May 27, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Claude Code runs on Node.js, and V8 calls prctl(PR_SET_THP_DISABLE, 1) on startup to disable transparent huge pages for itself. That prctl flag is inherited across fork/execve, so every child process spawned from Claude Code (Bash tool, sub-agents, user binaries) also has THP disabled, whether or not it wants to.

This silently breaks any workload that actually requires PMD-sized faults.
The most visible failure mode is devdax mmap with align=2M: on such devices the kernel only implements a PMD fault path, so with PR_SET_THP_DISABLE=1 the very first access to the mapping raises SIGBUS.

Root cause

V8 disables THP for its own process on startup:

  • V8 source: src/base/platform/platform-linux.cc calls prctl(PR_SET_THP_DISABLE, 1, ...) to avoid khugepaged latency spikes during GC.
  • Linux semantics: per prctl(2), the PR_SET_THP_DISABLE flag is inherited by child processes created via fork(2) and preserved across execve(2).

Most user code has no reason to ever call that prctl and will simply misbehave:

  • devdax align=2M mappings: immediate SIGBUS on first touch.
  • Regular anonymous THP workloads: silent TLB-miss regressions (harder to notice, but same root cause).

Impact

  • Any code path under Claude Code that touches a 2 MiB-aligned devdax device crashes on first access (to be honest, Most user doesn't affect ).
  • Silent performance degradation for any THP-sensitive workload launched from Claude Code.
  • Hard to diagnose from the user side: the failing binary looks buggy, but strace shows a clean mmap followed by a SIGBUS, and the only hint is grep THP_enabled /proc/self/status returning 0.

What Should Happen?

Children spawned by Claude Code's tools should not silently inheritPR_SET_THP_DISABLE=1. Either:

  • child processes are started with the flag cleared, or
  • the user-facing Bash tool clears it before execve, or
  • at minimum, this is documented so affected workloads can work around it.

Error Messages/Logs

Steps to Reproduce

Minimal reproducer

/*
 * Devdax (align=2 MiB) SIGBUSes on the first fault when the process has
 * PR_SET_THP_DISABLE=1.  Flipping it back makes the same access work.
 *
 *   cc -O2 -Wall -o pr_thp_devdax_sigbus pr_thp_devdax_sigbus.c
 *   ./pr_thp_devdax_sigbus [/dev/dax0.1]
 */
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/prctl.h>
#include <sys/wait.h>
#include <unistd.h>

static void child(int restore, const char *path) {
    if (restore) prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0);
    printf("  PR_GET_THP_DISABLE=%d\n", prctl(PR_GET_THP_DISABLE, 0, 0, 0, 0));
    int fd = open(path, O_RDWR);
    void *p = mmap(NULL, 2UL << 20, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    *(volatile unsigned char *)p = 0xA5;
    _exit(0);
}

static void run(const char *tag, int restore, const char *path) {
    printf("%s:\n", tag); fflush(stdout);
    pid_t pid = fork();
    if (pid == 0) child(restore, path);
    int st; waitpid(pid, &st, 0);
    printf("  -> %s\n",
           WIFSIGNALED(st) ? strsignal(WTERMSIG(st)) : "ok");
}

int main(int argc, char **argv) {
    const char *path = argc > 1 ? argv[1] : "/dev/dax0.1";
    setlinebuf(stdout);
    run("A (inherited)",                  0, path);
    run("B (prctl PR_SET_THP_DISABLE=0)", 1, path);
    return 0;
}

Steps

  1. Build: cc -O2 -Wall -o pr_thp_devdax_sigbus pr_thp_devdax_sigbus.c
  2. Run it directly from a normal shell:

``
$ ./pr_thp_devdax_sigbus /dev/dax0.1
``

  1. Run it again from inside Claude Code's Bash tool on the same machine:

``
(claude) $ ./pr_thp_devdax_sigbus /dev/dax0.1
``

Observed

From a plain shell (both phases fine, as expected):

A (inherited):
  PR_GET_THP_DISABLE=0
  -> ok
B (prctl PR_SET_THP_DISABLE=0):
  PR_GET_THP_DISABLE=0
  -> ok

From inside Claude Code's Bash tool — phase A dies with SIGBUS:

A (inherited):
  PR_GET_THP_DISABLE=1
  -> Bus error
B (prctl PR_SET_THP_DISABLE=0):
  PR_GET_THP_DISABLE=0
  -> ok

Phase B always succeeds, which pins the cause to the inherited prctl flag rather than the device, the kernel, or the binary.

Claude Model

None

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

2.1.118

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

Other

Additional Information

_No response_

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗