account for deletion marker in /proc/pid/exe
authorPhilipp Gesang <philipp.gesang@intra2net.com>
Tue, 16 Nov 2021 09:45:28 +0000 (10:45 +0100)
committerThomas Jarosch <thomas.jarosch@intra2net.com>
Tue, 16 Nov 2021 15:03:41 +0000 (16:03 +0100)
The kernel appends a `` (deleted)`` suffix to the executable path
if the original binary has been unlinked since the process was
started. The ``pid_of()`` API needs to account for this when
matching process names.

src/daemonfunc.cpp

index 99f3be8..248a79b 100644 (file)
@@ -102,6 +102,40 @@ bool drop_root_privileges(const std::string &username,
     return true;
 }
 
+static const char *const EXE_DELETED_MARKER = " (deleted)";
+
+/** @brief      Remove the deletion marker from a binary name, if applicable.
+ *
+ * @param exe   Binary name as obtained from ``/proc/$PID/exe``.
+ *
+ * @return      The input binary name with a trailing `` (deleted)`` removed.
+ *
+ * From proc(5):
+ *
+ *     /proc/[pid]/exe
+ *            […] If the pathname has been unlinked, the symbolic link will
+ *            contain the string '(deleted)' appended to the original pathname.
+ *
+ * Which introduces an ambiguity if the path of the actual binary ends in
+ * `` (deleted)``.
+ */
+static inline std::string
+strip_deleted_marker (const std::string &exe)
+{
+    if (exe.size () <= sizeof (*EXE_DELETED_MARKER)) {
+        /* This binary name can’t possibly contain a deleted marker. */
+        return exe;
+    }
+
+    const size_t found = exe.rfind (EXE_DELETED_MARKER);
+    if (found == std::string::npos) {
+        /* Input does not contain the deleted marker. */
+        return exe;
+    }
+
+    return exe.substr (0, found);
+}
+
 /**
  * @brief determine the pids for a given program
  * @param[in] name name (or full path) of the binary
@@ -139,21 +173,22 @@ bool pid_of(const std::string& name, std::vector< pid_t >& result)
       if (!real_exe.empty())
       {
          // we got the path of the exe
-         if (real_exe == name)
+         if (real_exe == name || strip_deleted_marker (real_exe) == name)
          {
-               result.push_back( pid );
-               continue;
+                 result.push_back( pid );
+                 continue;
          }
-
+         
          std::string proc_stat= read_file( base_path + "/stat");
          if (proc_stat.empty()) continue; // process vanished
-
+         
          //TODO some more fuzz tests here?! (cmdline, stat(us))
-
-         if (basename(real_exe) == name)
+         
+         real_exe = basename (real_exe);
+         if (real_exe == name || strip_deleted_marker (real_exe) == name)
          {
-               fuzz2_result.push_back(pid);
-               continue;
+                 fuzz2_result.push_back(pid);
+                 continue;
          }
       }
       else