fix(agent): drop MemoryDenyWriteExecute — breaks Node.js V8 JIT

Claude Code is a Node app. V8 JIT requires W^X transitions via mprotect
with PROT_EXEC on JIT'd code pages. MemoryDenyWriteExecute kills the
process with SIGTRAP/abort during startup (~10ms in).

Without JIT we'd have to use --jitless mode, which destroys performance.
The other systemd hardening (ProtectSystem, ProtectHome,
InaccessiblePaths, NoNewPrivileges, capability drop, syscall filter,
PrivateTmp, etc.) still gives strong filesystem and privilege isolation.
The remaining shellcode-injection risk is theoretical — there is no
Bash/Write/Edit tool exposed for an attacker to chain into.

Also: MemoryLimit -> MemoryMax (deprecated unit form).
This commit is contained in:
Erik 2026-04-25 21:29:16 +02:00
parent 9d4c724b7f
commit 5cf052cedf

View file

@ -25,7 +25,7 @@ StandardOutput=journal
StandardError=journal
# ─── Resource caps ─────────────────────────────────────────────────
MemoryLimit=512M
MemoryMax=512M
CPUQuota=200%
TasksMax=128
@ -74,7 +74,12 @@ LockPersonality=true
RestrictRealtime=true
RestrictSUIDSGID=true
RemoveIPC=true
MemoryDenyWriteExecute=true
# MemoryDenyWriteExecute would break Node.js (V8 JIT requires W^X
# transitions via mprotect with PROT_EXEC on JITted code pages). Claude
# Code is a Node app, so omit this. Without JIT we'd lose all model
# performance. The other restrictions still prevent shellcode injection
# in practice (no Bash/Write tools, no shellcraft surface).
# MemoryDenyWriteExecute=true ← DO NOT enable; breaks Node V8 JIT
RestrictNamespaces=true
# ─── Network family restriction ────────────────────────────────────