claude-code - 💡(How to fix) Fix [BUG] PR_SET_THP_DISABLE=1 is inherited into spawned children, breaking applications that rely on THP (devdax SIGBUS) [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#52366Fetched 2026-04-24 06:09:03
View on GitHub
Comments
0
Participants
1
Timeline
3
Reactions
0
Author
Participants
Timeline (top)
labeled ×3

Error Message

Error Messages/Logs

-> Bus error

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).

Code Example



---

/*
 * 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;
}

---

$ ./pr_thp_devdax_sigbus /dev/dax0.1

---

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

---

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

---

A (inherited):
  PR_GET_THP_DISABLE=1
  -> Bus error
B (prctl PR_SET_THP_DISABLE=0):
  PR_GET_THP_DISABLE=0
  -> ok
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • 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
  3. 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

extent analysis

TL;DR

The issue can be fixed by clearing the PR_SET_THP_DISABLE flag for child processes spawned by Claude Code's tools.

Guidance

  • Identify the source of the PR_SET_THP_DISABLE flag inheritance, which is the V8 call to prctl(PR_SET_THP_DISABLE, 1) on startup.
  • Modify the child process creation to clear the PR_SET_THP_DISABLE flag before execve, using prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0).
  • Document this behavior to inform users of affected workloads and provide a workaround.
  • Test the fix using the provided minimal reproducer code.

Example

The provided C code snippet demonstrates the issue and the fix:

prctl(PR_SET_THP_DISABLE, 0, 0, 0, 0); // Clear the flag

This line should be added to the child process creation code to prevent the PR_SET_THP_DISABLE flag from being inherited.

Notes

The fix may require modifications to the Claude Code's Bash tool or the V8 source code. The provided example code snippet only demonstrates the issue and the fix, and may not be directly applicable to the Claude Code's implementation.

Recommendation

Apply the workaround by clearing the PR_SET_THP_DISABLE flag for child processes spawned by Claude Code's tools, as this will prevent the silent inheritance of the flag and allow affected workloads to function correctly.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING