From 7c8dc3e817b8334bc36392fb007984966dd733ea Mon Sep 17 00:00:00 2001 From: Ed Savage Date: Tue, 21 Jul 2026 13:14:03 +1200 Subject: [PATCH 1/2] 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 --- bin/controller/Main.cc | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/bin/controller/Main.cc b/bin/controller/Main.cc index 61e43288c2..14096758c0 100644 --- a/bin/controller/Main.cc +++ b/bin/controller/Main.cc @@ -63,6 +63,36 @@ #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 was used to overwrite access@GOT and turn a +//! controller spawn into dlopen (elastic/security#12621). 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. + if (::prctl(PR_GET_DUMPABLE) != 0) { + LOG_ERROR(<< "process remains 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 +152,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. From 88ade68d3bd13cdb99d4e5a9b118eecd50c7fb8f Mon Sep 17 00:00:00 2001 From: Ed Savage Date: Tue, 21 Jul 2026 13:14:40 +1200 Subject: [PATCH 2/2] Add changelog for #3081 Co-authored-by: Cursor --- docs/changelog/3081.yaml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 docs/changelog/3081.yaml 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