Mark ML controller non-dumpable before accepting commands#3081
Conversation
Same-UID peers in the shared ES/ml-cpp pod could write /proc/<controller-pid>/mem and overwrite access@GOT so a spawn's access(path, X_OK) became dlopen(path, RTLD_LAZY) (elastic/security#12621). Call prctl(PR_SET_DUMPABLE, 0) after logging is up and before opening command pipes; verify with PR_GET_DUMPABLE and fail closed if the boundary cannot be established. No-op on non-Linux platforms where /proc/<pid>/mem is not the concern. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
Pinging @elastic/ml-core (Team:ML) |
There was a problem hiding this comment.
Pull request overview
Hardens the ML controller on Linux by making the process non-dumpable before it opens command/output pipes, mitigating same-UID /proc/<pid>/mem write attacks described in elastic/security#12621.
Changes:
- Add a Linux-only
prctl(PR_SET_DUMPABLE, 0)+ verification step, and fail-closed if it cannot be applied. - Call the hardening step after logging is reconfigured and before accepting commands (before opening command/output pipes).
- Add a changelog entry for the security hardening fix.
Reviewed changes
Copilot reviewed 1 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| docs/changelog/3081.yaml | Documents the security hardening change in the changelog. |
| bin/controller/Main.cc | Applies Linux PR_SET_DUMPABLE hardening before opening controller command/output pipes. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Test plan verificationAll three items checked off. Item 1 is confirmed by CI; items 2–3 were verified against the real controller binary from this PR's CI build ( 1 & CI — controller starts and processes commands normally
2 — non-dumpable after start (Linux)This kernel's The controller is caught after 3 — fail-closed (mocked)
Both failure paths refuse to accept commands (fail closed); the normal path continues. |
|
buildkite run_pytorch_tests |
valeriy42
left a comment
There was a problem hiding this comment.
LGTM. Minor suggestion to improve diagnostic
Distinguish a PR_GET_DUMPABLE syscall failure (-1) from the process still being dumpable, and log the actual value, per review feedback on elastic#3081. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
💚 All backports created successfully
Questions ?Please refer to the Backport tool documentation and see the Github Action logs for details |
) * Mark ML controller non-dumpable before accepting commands Same-UID peers in the shared ES/ml-cpp pod could write /proc/<controller-pid>/mem and overwrite access@GOT so a spawn's access(path, X_OK) became dlopen(path, RTLD_LAZY) (elastic/security#12621). Call prctl(PR_SET_DUMPABLE, 0) after logging is up and before opening command pipes; verify with PR_GET_DUMPABLE and fail closed if the boundary cannot be established. No-op on non-Linux platforms where /proc/<pid>/mem is not the concern. * Add changelog for #3081 * [ML] Improve PR_GET_DUMPABLE diagnostic in controller non-dumpable guard Distinguish a PR_GET_DUMPABLE syscall failure (-1) from the process still being dumpable, and log the actual value, per review feedback on #3081. * [ML] Remove public-to-private security-tracker references * [ML] Fix clang-format in controller Main.cc --------- (cherry picked from commit 9c46fa0) Co-authored-by: Ed Savage <ed.savage@elastic.co> Co-authored-by: Cursor <cursoragent@cursor.com>
) * Mark ML controller non-dumpable before accepting commands Same-UID peers in the shared ES/ml-cpp pod could write /proc/<controller-pid>/mem and overwrite access@GOT so a spawn's access(path, X_OK) became dlopen(path, RTLD_LAZY) (elastic/security#12621). Call prctl(PR_SET_DUMPABLE, 0) after logging is up and before opening command pipes; verify with PR_GET_DUMPABLE and fail closed if the boundary cannot be established. No-op on non-Linux platforms where /proc/<pid>/mem is not the concern. * Add changelog for #3081 * [ML] Improve PR_GET_DUMPABLE diagnostic in controller non-dumpable guard Distinguish a PR_GET_DUMPABLE syscall failure (-1) from the process still being dumpable, and log the actual value, per review feedback on #3081. * [ML] Remove public-to-private security-tracker references * [ML] Fix clang-format in controller Main.cc --------- (cherry picked from commit 9c46fa0) Co-authored-by: Ed Savage <ed.savage@elastic.co> Co-authored-by: Cursor <cursoragent@cursor.com>
) * Mark ML controller non-dumpable before accepting commands Same-UID peers in the shared ES/ml-cpp pod could write /proc/<controller-pid>/mem and overwrite access@GOT so a spawn's access(path, X_OK) became dlopen(path, RTLD_LAZY) (elastic/security#12621). Call prctl(PR_SET_DUMPABLE, 0) after logging is up and before opening command pipes; verify with PR_GET_DUMPABLE and fail closed if the boundary cannot be established. No-op on non-Linux platforms where /proc/<pid>/mem is not the concern. * Add changelog for #3081 * [ML] Improve PR_GET_DUMPABLE diagnostic in controller non-dumpable guard Distinguish a PR_GET_DUMPABLE syscall failure (-1) from the process still being dumpable, and log the actual value, per review feedback on #3081. * [ML] Remove public-to-private security-tracker references * [ML] Fix clang-format in controller Main.cc --------- (cherry picked from commit 9c46fa0) Co-authored-by: Ed Savage <ed.savage@elastic.co> Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
Follow-up to a privately reported security finding (details tracked internally): the ML controller shared UID
1000with the JVM /pytorch_inferenceand remained dumpable, so a compromised peer could write/proc/<controller-pid>/mem, overwriteaccess@GOT, and turnaccess(path, X_OK)intodlopen(path, RTLD_LAZY)(X_OK == RTLD_LAZY == 1) when handling a spawn command — loading attacker code under the controller's laxer RuntimeDefault-only seccomp.Fix
After logging is reconfigured and before opening command/output pipes (i.e. before accepting commands):
prctl(PR_SET_DUMPABLE, 0)prctl(PR_GET_DUMPABLE) == 0LOG_FATAL+EXIT_FAILURE) if either step failsNo-op on non-Linux platforms (
/proc/<pid>/memsame-UID writes are the Linux concern).Test plan
/proc/<controller-pid>/statusshowsDumpable: 0(or equivalent).PR_SET_DUMPABLEcannot be applied (manual / mocked).Related: #3078 (
__setstate__pre-load), #3080 (seccomp arch/ABI check).