]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr: fix PyObject leak in the remote() pickle calls 69755/head
authorKefu Chai <k.chai@proxmox.com>
Fri, 26 Jun 2026 00:44:35 +0000 (08:44 +0800)
committerKefu Chai <k.chai@proxmox.com>
Fri, 26 Jun 2026 04:29:21 +0000 (12:29 +0800)
dispatch_remote() and ceph_dispatch_remote() invoke pickle.loads/dumps via
PyObject_CallMethodObjArgs(pmodule, PyUnicode_FromString("loads"), ...). The
method-name string created by PyUnicode_FromString is a new reference that
CallMethodObjArgs does not steal, and it was never released, so every cross
-module remote() call leaked three PyUnicode objects on the active mgr.

Use PyObject_CallMethod(pmodule, "loads", "(O)", arg), which takes the method
name as a C string and manages it internally, so there is nothing to leak.
The "(O)" format wraps the argument in a one-tuple so it reaches the callee as
a single object. A bare "O" passes the argument through directly, and when it
is a tuple (as remote()'s *args always is, including the empty tuple from a
no-argument call) PyObject_CallMethod unpacks it and calls dumps() with the
wrong arguments, e.g. dumps() with none.

Fixes: https://tracker.ceph.com/issues/77724
Signed-off-by: Kefu Chai <k.chai@proxmox.com>
src/mgr/ActivePyModule.cc
src/mgr/BaseMgrModule.cc

index a01233ac5dc7c0264f68d6c11a28c51b1a681062..24e91082278c5a19f8b8f3d8c3aef50751b4f848 100644 (file)
@@ -155,11 +155,7 @@ std::optional<std::vector<std::byte>> ActivePyModule::dispatch_remote(
 
   auto pmodule = py_module->pPickleModule;
   auto pickled_args_bytes = py_bytes_from_span(pickled_args);
-  auto args = PyObject_CallMethodObjArgs(
-    pmodule,
-    PyUnicode_FromString("loads"),
-    pickled_args_bytes,
-    nullptr);
+  auto args = PyObject_CallMethod(pmodule, "loads", "(O)", pickled_args_bytes);
   Py_DECREF(pickled_args_bytes);
   if (args == nullptr) {
     std::string caller = "ActivePyModule::dispatch_remote "s + method;
@@ -169,11 +165,7 @@ std::optional<std::vector<std::byte>> ActivePyModule::dispatch_remote(
   }
 
   auto pickled_kwargs_bytes = py_bytes_from_span(pickled_kwargs);
-  auto kwargs = PyObject_CallMethodObjArgs(
-    pmodule,
-    PyUnicode_FromString("loads"),
-    pickled_kwargs_bytes,
-    nullptr);
+  auto kwargs = PyObject_CallMethod(pmodule, "loads", "(O)", pickled_kwargs_bytes);
   Py_DECREF(pickled_kwargs_bytes);
   if (kwargs == nullptr) {
     std::string caller = "ActivePyModule::dispatch_remote "s + method;
@@ -208,11 +200,7 @@ std::optional<std::vector<std::byte>> ActivePyModule::dispatch_remote(
   }
   dout(20) << "Success calling '" << method << "'" << dendl;
 
-  auto pickled_ret = PyObject_CallMethodObjArgs(
-    pmodule,
-    PyUnicode_FromString("dumps"),
-    ret,
-    nullptr);
+  auto pickled_ret = PyObject_CallMethod(pmodule, "dumps", "(O)", ret);
   Py_DECREF(ret);
   if (pickled_ret == nullptr) {
     std::string caller = "ActivePyModule::dispatch_remote "s + method;
index 8e34159aec7eb01f239aaccecf7ef7211b7f8b9e..11459d87938aa360b9918ef60288c5e3a37844bd 100644 (file)
@@ -871,11 +871,7 @@ ceph_dispatch_remote(BaseMgrModule *self, PyObject *args)
   }
 
   auto pmodule = self->this_module->py_module->pPickleModule;
-  auto pickled_args = PyObject_CallMethodObjArgs(
-    pmodule,
-    PyUnicode_FromString("dumps"),
-    remote_args,
-    nullptr);
+  auto pickled_args = PyObject_CallMethod(pmodule, "dumps", "(O)", remote_args);
   if (pickled_args == nullptr) {
     std::string caller = "ceph_dispatch_remote "s + " " + method;
     std::string err = handle_pyerror(true, other_module, caller);
@@ -885,11 +881,7 @@ ceph_dispatch_remote(BaseMgrModule *self, PyObject *args)
   }
   std::span<std::byte const> pickled_args_span = py_bytes_as_span(pickled_args);
 
-  auto pickled_kwargs = PyObject_CallMethodObjArgs(
-    pmodule,
-    PyUnicode_FromString("dumps"),
-    remote_kwargs,
-    nullptr);
+  auto pickled_kwargs = PyObject_CallMethod(pmodule, "dumps", "(O)", remote_kwargs);
   if (pickled_kwargs == nullptr) {
     std::string caller = "ceph_dispatch_remote "s + " " + method;
     std::string err = handle_pyerror(true, other_module, caller);
@@ -940,11 +932,7 @@ ceph_dispatch_remote(BaseMgrModule *self, PyObject *args)
   }
 
   auto pickled_ret_bytes = py_bytes_from_vec(*maybe_pickled_ret);
-  auto ret = PyObject_CallMethodObjArgs(
-    pmodule,
-    PyUnicode_FromString("loads"),
-    pickled_ret_bytes,
-    nullptr);
+  auto ret = PyObject_CallMethod(pmodule, "loads", "(O)", pickled_ret_bytes);
   if (ret == nullptr) {
     std::string caller = "ceph_dispatch_remote "s + " " + method;
     std::string err = handle_pyerror(true, other_module, caller);