MongoDB 8 stirbt leise – wenn die CPU die Datenbank für einen Angreifer hält
wordpress@neckar.it

Kein Stack Trace. Kein dmesg-Eintrag. Nur ein Restart-Loop alle 29 Sekunden — weil AMD Zen 4/5 plus Kernel 7.0 MongoDB beim ersten Fiber-Switch den Stecker ziehen.
Der Restart-Loop, der keiner sein sollte
Neuer Rechner, neues Kernel-Update — Ubuntu 26.04 LTS mit Linux 7.0.0 auf einem Ryzen 9000. docker compose up, alles grün. Zehn Minuten später: der mongodb-Dev-Container steht bei restartCount: 314. Takt: alle 29 Sekunden ein Crash. Exit-Code 139 — also SIGSEGV.
Gleichzeitig fallen Testcontainer-basierte Integration-Tests um, immer mit demselben Muster: „MongoDB container is not running. exitCode=139“. Mal läuft der Test durch, mal nicht. Flaky, würde man sagen.
Nur: Das ist nicht flaky. Das ist reproduzierbar kaputt. Du siehst es nur nicht sofort, weil MongoDB leise stirbt. Kein Stack Trace im Log. Kein Eintrag in dmesg. Nichts. Der Prozess ist einfach weg.
Die CPU hält MongoDB für einen Angreifer
AMD Zen 4 und Zen 5 unterstützen CET Shadow Stacks — eine Hardware-Security-Funktion, die einen zweiten, CPU-verwalteten Stack mitführt. Bei jedem RET wird geprüft: Zeigen der normale Stack und der Shadow Stack auf dieselbe Rücksprungadresse? Wenn nicht — Process kill. Return-Oriented-Programming-Exploits laufen damit ins Leere.
Linux-Kernel ≥ 6.19 aktiviert das Feature per user_shstk standardmäßig für User-Space. Ubuntu 26.04 LTS bringt Kernel 7.0.0 mit. Sprich: Auf jedem halbwegs aktuellen AMD-Rechner ist Shadow Stack jetzt scharfgeschaltet.
Und jetzt kommt MongoDB 8 ins Spiel. MongoDB 8 nutzt intern Fibers — eine User-Space-Scheduling-Variante, die bei jedem Context-Switch den Stack-Pointer auf eine andere Speicheradresse umlegt (klassisch via Boost.Context / jump_fcontext). Für die CPU sieht das nicht nach Fiber aus. Das sieht nach Angriff aus. Der normale Stack zeigt woanders hin als der Shadow Stack. CPU killt Prozess.
Der fiese Teil: Die Stack-Pivots passieren erst, wenn die Hintergrund-Threads LogicalSessionCacheReap und LogicalSessionCacheRefresh zum ersten Mal laufen. Das ist nach ungefähr 30 Sekunden. Deshalb der 29-Sekunden-Takt im Restart-Loop. Deshalb auch kein Stack Trace — CET killt sauber per SIGSEGV, bevor MongoDB irgendwas loggen kann. Und dmesg sagt auch nichts, weil das Kill ein ganz normaler SegFault aus Kernel-Sicht ist.
MongoDB 6.0 und 7.0 haben ein anderes Scheduling-Modell und sind nicht betroffen. Exakt diese Eigenheit von MongoDB 8 trifft die neue Hardware-Security.
Der Fix: GLIBC_TUNABLES=glibc.cpu.hwcaps=-SHSTK als Environment-Variable. Damit deaktiviert glibc Shadow Stacks für den mongod-Prozess. Andere Prozesse im System behalten ihren Schutz.
Eine Zeile im Docker-Compose. Das war’s.
Warum Silent Kills so teuer sind
Zwischen „Container crasht“ und „Ich weiß, warum“ lagen mehrere Stunden. Nicht weil das Problem kompliziert ist — sondern weil es keine Spur hinterlässt. Exceptions landen im Log, OOM-Kills tauchen in dmesg auf, ein gewöhnlicher SegFault schreibt einen Stack Trace. Shadow-Stack-Kills machen nichts davon: Die CPU terminiert den Prozess, der Kernel sieht einen normalen SegFault, und niemand schreibt irgendwo hin, warum.
Die Standard-Werkzeuge — docker logs, journalctl, strace — zeigen nichts. Der Prozess war da, der Prozess ist weg, dazwischen liegt ein schwarzes Loch. Bis dir einfällt, nach dem Exit-Code in MongoDB-Bug-Datenbanken und obskuren Kernel-Trackern zu suchen. Für die CPU ist ein legitimer Fiber-Pivot nicht von einem Angriff zu unterscheiden — sie sieht den Kontrollfluss, nicht die Absicht.
Per-Container fixen, nicht per Host
Die naheliegende Reaktion auf so einen Bug: Host-Config anpassen. /etc/docker/daemon.json editieren, /etc/security/limits.conf hochdrehen, systemd-Unit ändern, neu starten. Klassisch. Funktioniert. Dokumentieren. Ab in die Admin-Runbook.
Nur: Das skaliert schlecht. Jeder Dev muss den Fix lokal einspielen, jeder neue Laptop auch, CI-Runner sowieso. Und wenn der Fix irgendwann nicht mehr nötig ist — weil MongoDB den Fiber-Code umschreibt oder Docker die Defaults hochsetzt —, muss man überall zurückrollen. Unsichtbarer Status pro Host. Neue Form von Shadow Tech Debt.
Bessere Lösung: Fix ins Docker-Compose, ab in den Branch, Merge Request durch. Zwei Dateien:
docker-compose.template.yml—GLIBC_TUNABLESfür den mongodb-ServiceSharedMongoContainer.kt— dasselbe für die Testcontainer-basierten Tests, perwithEnv()
Das war’s. Jeder, der git pull macht, hat den Fix. Neue Laptops — kein Setup. CI-Runner — Fix inklusive. Und wenn’s irgendwann nicht mehr nötig ist: zwei Dateien editieren, MR, fertig. Der Status liegt im Repo, nicht im Admin-Gedächtnis — reviewt, in Git Blame auffindbar, genauso leicht wieder rausnehmbar wie reingesetzt.
Was man davon mitnehmen kann
- Neue Hardware-Security-Features sind per Default aktiv. Shadow Stacks, Intel CET, Memory Tagging — das alles rollt still in aktuelle Kernel. Vorhandener Code, der clever mit Stack oder Speicher umgeht, kann plötzlich krachen. Fibers, JIT-Compiler, bestimmte Garbage Collectors. Wer schon länger am gleichen Rechner arbeitet, merkt das erst, wenn er auf ein neues Setup wechselt.
- Silent Kills brauchen andere Debug-Strategien. Standard-Logs helfen nicht. Hilft: Bug-Datenbanken nach dem Exit-Code durchsuchen,
strace -faufs Binary, Kernel-Logs mitjournalctl -kgegen den exakten Timestamp. Und: Eigene Reproduktion bauen, die schnell crasht. Ohne Repro bist du blind. - Workarounds gehören ins Repo. Nicht in die Host-Config. Jede Änderung, die außerhalb des Repos stattfindet, ist unsichtbar, lässt sich nicht reviewen und rollt nicht auf neue Rechner aus. Ein
GLIBC_TUNABLESim Docker-Compose ist reproduzierbar. EinGLIBC_TUNABLESin/etc/docker/daemon.jsonist eine Zeitbombe.
Fazit
Ein SIGSEGV, kein Stack Trace, ein Exit 139. Schuld: eine neue CPU-Security, die den Fiber-Switch von MongoDB 8 für einen Angriff hält. Reproduzierbar kaputt, per Ein-Zeiler lösbar — nur eben erst, wenn man versteht, was passiert.
Die technische Lektion ist trivial. Die eigentliche: Wenn etwas ohne Fehlermeldung um die Ohren fliegt, liegt das Problem fast nie da, wo du zuerst suchst. Und der Fix gehört ins Repo, nicht in die Host-Config — sonst produzierst du dir die nächste Form von Shadow Tech Debt selber.
Quellen:
- MongoDB BugID 3392546 — findbugzero.com
- Sky1-Linux/linux-sky1 Issue #15 — github.com
- Linux Kernel Documentation — x86_64/shstk.rst