From fb44b377ebd2b610620f34c05ad2c4f02968682a Mon Sep 17 00:00:00 2001 From: Ed Savage Date: Fri, 24 Jul 2026 15:06:41 +1200 Subject: [PATCH] Mark ML controller non-dumpable before accepting commands (#3081) * Mark ML controller non-dumpable before accepting commands Same-UID peers in the shared ES/ml-cpp pod could write /proc//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//mem is not the concern. Co-authored-by: Cursor * Add changelog for #3081 Co-authored-by: Cursor * [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. Co-authored-by: Cursor * [ML] Remove public-to-private security-tracker references Co-authored-by: Cursor * [ML] Fix clang-format in controller Main.cc Co-authored-by: Cursor --------- Co-authored-by: Cursor (cherry picked from commit 9c46fa006df89a790fd3a312181b1f916c235d56) --- bin/controller/Main.cc | 42 ++++++++++++++++++++++++++++++++++++++++ docs/changelog/3081.yaml | 5 +++++ 2 files changed, 47 insertions(+) create mode 100644 docs/changelog/3081.yaml diff --git a/bin/controller/Main.cc b/bin/controller/Main.cc index 61e43288c2..9a863f2429 100644 --- a/bin/controller/Main.cc +++ b/bin/controller/Main.cc @@ -63,6 +63,41 @@ #include #include +#ifdef Linux +#include +#endif + +namespace { + +//! Prevent same-UID peers (e.g. a compromised pytorch_inference / JVM Attach +//! agent in the shared pod) from writing this process's memory via +//! /proc//mem. That path could be used to overwrite access@GOT and turn a +//! controller spawn into dlopen (privately reported security finding). Fail +//! closed: if we cannot establish the boundary, refuse to accept commands. +bool makeProcessNonDumpable() { +#ifdef Linux + if (::prctl(PR_SET_DUMPABLE, 0) != 0) { + LOG_ERROR(<< "prctl PR_SET_DUMPABLE(0) failed: " << std::strerror(errno)); + return false; + } + // Confirm the attribute stuck — a silent no-op would leave the process exposed. + const int dumpable{::prctl(PR_GET_DUMPABLE)}; + if (dumpable == -1) { + LOG_ERROR(<< "prctl PR_GET_DUMPABLE failed: " << std::strerror(errno)); + return false; + } + if (dumpable != 0) { + LOG_ERROR(<< "process remains dumpable (" << dumpable << ") after PR_SET_DUMPABLE(0)"); + return false; + } + return true; +#else + // /proc//mem same-UID writes are a Linux concern; no equivalent gate here. + return true; +#endif +} +} + int main(int argc, char** argv) { const std::string& defaultNamedPipePath{ml::core::CNamedPipeFactory::defaultPath()}; const std::string& progName{ml::core::CProgName::progName()}; @@ -122,6 +157,13 @@ int main(int argc, char** argv) { // statically links its own version library. LOG_INFO(<< ml::ver::CBuildInfo::fullInfo()); + // Harden against same-UID /proc//mem writes before accepting commands. + if (makeProcessNonDumpable() == false) { + LOG_FATAL(<< "Could not mark ML controller non-dumpable"); + cancellerThread.stop(); + return EXIT_FAILURE; + } + // Unlike other programs we DON'T reduce the process priority here, because // the controller is critical to the overall system. Also its resource // requirements should always be very low. diff --git a/docs/changelog/3081.yaml b/docs/changelog/3081.yaml new file mode 100644 index 0000000000..06926697d9 --- /dev/null +++ b/docs/changelog/3081.yaml @@ -0,0 +1,5 @@ +area: Machine Learning +issues: [] +pr: 3081 +summary: Mark ML controller non-dumpable before accepting commands +type: bug