]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
common/options: fix heap-use-after-free by using never-destroyed static 66895/head
authorKefu Chai <k.chai@proxmox.com>
Tue, 13 Jan 2026 01:19:17 +0000 (09:19 +0800)
committerKefu Chai <k.chai@proxmox.com>
Tue, 13 Jan 2026 01:27:54 +0000 (09:27 +0800)
The config schema map was using string_view keys that pointed to the
name field of Option objects stored in the global ceph_options vector.
When the vector is destroyed during program exit, the Option objects
are freed, but background threads (like BlueStore::MempoolThread) may
still be accessing config options, causing use-after-free.

ASan reported:
  READ of size 19 at 0x503000047c80 thread T411
    #12 md_config_t::find_option(std::string_view) const config.cc:261
    #17 BlueStore::MempoolThread::entry() BlueStore.cc:5591

  0x503000047c80 is located 0 bytes inside of 20-byte region
  freed by thread T0 here:
    #7 Option::~Option() options.h:15
    #13 std::vector<Option>::~vector() stl_vector.h:730
    #14 __run_exit_handlers stdlib/exit.c:113

  previously allocated by thread T0 here:
    #7 Option::Option(Option const&) options.h:15
    #18 build_options() build_options.cc:44

Fix by converting ceph_options from a global variable to a function
get_ceph_options() that returns a reference to a static pointer that
is never destroyed. This ensures the Option objects remain valid for
the lifetime of the program, even during exit when background threads
may still be accessing them.

This preserves the memory efficiency of using string_view keys in the
schema map while fixing the lifetime issue.

Signed-off-by: Kefu Chai <k.chai@proxmox.com>
src/common/ceph_context.cc
src/common/config.cc
src/common/options.cc
src/common/options.h
src/crimson/admin/admin_socket.cc
src/mon/ConfigMonitor.cc

index afd22f865437e8226448f6c1f347ae50e5b9cea5..5bb0b4678b76353d35e1fa91b9071fe11d0844f2 100644 (file)
@@ -658,7 +658,7 @@ int CephContext::_do_command(
       } else {
         // Output all
         f->open_array_section("options");
-        for (const auto &option : ceph_options) {
+        for (const auto &option : get_ceph_options()) {
           f->dump_object("option", option);
         }
         f->close_section();
index f320fbd1cbccbd064a8d7c3ad86e83c403c96dae..52071dbbe1902f257259154aec64e8cb7dfdd88b 100644 (file)
@@ -137,7 +137,7 @@ md_config_t::md_config_t(ConfigValues& values,
 {
   // Load the compile-time list of Option into
   // a map so that we can resolve keys quickly.
-  for (const auto &i : ceph_options) {
+  for (const auto &i : get_ceph_options()) {
     if (schema.count(i.name)) {
       // We may be instantiated pre-logging so send 
       std::cerr << "Duplicate config key in schema: '" << i.name << "'"
index bbac26e261cd82fda562b3192f0ff3bbcc2eb3a1..28e4941af4ed867e42d97645bd958c3d214031aa 100644 (file)
@@ -338,4 +338,9 @@ void Option::print(ostream *out) const
   }
 }
 
-const std::vector<Option> ceph_options = build_options();
+const std::vector<Option>& get_ceph_options() {
+  // Use a static pointer that's never destroyed to avoid use-after-free
+  // when background threads access options during program exit
+  static const std::vector<Option>* options = new std::vector<Option>(build_options());
+  return *options;
+}
index f99433464bcdbc7541a19c8e87bb76ea90ea5445..c29d2e029fb4a454cb94cf72c73aed0668b86372 100644 (file)
@@ -434,4 +434,4 @@ constexpr unsigned long long operator""_T (unsigned long long n) {
   return n << 40;
 }
 
-extern const std::vector<Option> ceph_options;
+const std::vector<Option>& get_ceph_options();
index 5a676d9214c10088364831e8c768d2b780af0fa3..9db62af2bdf4da31755bdc37befb31240d15f0b2 100644 (file)
@@ -574,7 +574,7 @@ public:
     unique_ptr<Formatter> f{Formatter::create(format, "json-pretty", "json-pretty")};
     // Output all
     f->open_array_section("options");
-    for (const auto &option : ceph_options) {
+    for (const auto &option : get_ceph_options()) {
       f->dump_object("option", option);
     }
     f->close_section();
index 7abd0c6818e9a98ce59f6348de9e30c875986bac..9b1f6a8aab5afa906e6444688ca828af03c3978c 100644 (file)
@@ -219,7 +219,7 @@ bool ConfigMonitor::preprocess_command(MonOpRequestRef op)
     if (f) {
       f->open_array_section("options");
     }
-    for (auto& i : ceph_options) {
+    for (auto& i : get_ceph_options()) {
       if (f) {
        f->dump_string("option", i.name);
       } else {