llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Raphael Isemann (Teemperor)

<details>
<summary>Changes</summary>

Our current FindProcessImpl has a TOCTOU bug where we first query the buffer 
size we should provide via sysctl and then later pass a buffer with that size 
to be filled. If the list of processes grows larger than the buffer we pass, 
then our current implementation fails by returning an empty list of processes. 
This race only happens rarely in practice as we pad the buffer size with 10 
additional entries to account for some process growth.

This patch replaces this logic by a backoff loop that retries fetching the 
process list if our buffer is too small (sysctl tells us if this is the case by 
setting ENOMEM). This new implementation can only fail in the last iteration if 
the system spawns about 130k processes between the two sysctl calls which 
should be practically impossible.

This should fix the actual root-cause for the random failures in 
TestSimulator.py

---
Full diff: https://github.com/llvm/llvm-project/pull/204109.diff


1 Files Affected:

- (modified) lldb/source/Host/macosx/objcxx/Host.mm (+44-18) 


``````````diff
diff --git a/lldb/source/Host/macosx/objcxx/Host.mm 
b/lldb/source/Host/macosx/objcxx/Host.mm
index febbbfb0a384c..22702db8f51fe 100644
--- a/lldb/source/Host/macosx/objcxx/Host.mm
+++ b/lldb/source/Host/macosx/objcxx/Host.mm
@@ -39,6 +39,7 @@
 #include "llvm/TargetParser/Host.h"
 
 #include <asl.h>
+#include <cerrno>
 #include <crt_externs.h>
 #include <cstdio>
 #include <cstdlib>
@@ -684,34 +685,59 @@ static bool 
GetMacOSXProcessUserAndGroup(ProcessInstanceInfo &process_info) {
   return false;
 }
 
-uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
-                                 ProcessInstanceInfoList &process_infos) {
-  std::vector<struct kinfo_proc> kinfos;
-
+/// Fetches the list of all processes. Returns true on success.
+[[nodiscard]] static bool
+GetProcessList(std::vector<struct kinfo_proc> &kinfos) {
   int mib[3] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL};
 
-  size_t pid_data_size = 0;
-  if (::sysctl(mib, 3, nullptr, &pid_data_size, nullptr, 0) != 0)
-    return 0;
+  // This is an inherently racy API. We have to first query the size for our
+  // buffer and then pass back to sysctl. If more processes spawn between the
+  // size query and the actual call to fetch, then sysctl returns ENOMEM.
+  // We keep retrying until we get a passing result.
+  for (int attempt = 1; attempt < 1024; ++attempt) {
+    // Fetch the buffer size sysctl would return.
+    size_t current_pid_size = 0;
+    if (::sysctl(mib, 3, nullptr, &current_pid_size, nullptr, 0) != 0)
+      return false;
 
-  // Add a few extra in case a few more show up
-  const size_t estimated_pid_count =
-      (pid_data_size / sizeof(struct kinfo_proc)) + 10;
+    // We expect some process growth. Start small and keep increasing the
+    // expected growth each attempt until we can get the sysctl call to
+    // succeed.
+    const size_t expected_growth = attempt * 128 * sizeof(struct kinfo_proc);
+    const size_t estimated_pid_size = current_pid_size + expected_growth;
 
-  kinfos.resize(estimated_pid_count);
-  pid_data_size = kinfos.size() * sizeof(struct kinfo_proc);
+    // Allocate the buffer sysctl will copy the process list into.
+    kinfos.resize(estimated_pid_size / sizeof(struct kinfo_proc));
 
-  if (::sysctl(mib, 3, &kinfos[0], &pid_data_size, nullptr, 0) != 0)
-    return 0;
+    // Fetch the actual process list and let syctl adjust actual_pid_size.
+    size_t actual_pid_size = kinfos.size() * sizeof(struct kinfo_proc);
+    if (::sysctl(mib, 3, &kinfos[0], &actual_pid_size, nullptr, 0) == 0) {
+      // Adjust the buffer to the actual number of processes returned.
+      kinfos.resize(actual_pid_size / sizeof(struct kinfo_proc));
+      return true;
+    }
 
-  const size_t actual_pid_count = (pid_data_size / sizeof(struct kinfo_proc));
+    // Errno is set to ENOMEM if our estimated_pid_size is too small.
+    if (errno != ENOMEM)
+      return false;
+  }
+
+  return false;
+}
+
+uint32_t Host::FindProcessesImpl(const ProcessInstanceInfoMatch &match_info,
+                                 ProcessInstanceInfoList &process_infos) {
+  std::vector<struct kinfo_proc> kinfos;
+  if (!GetProcessList(kinfos)) {
+    Log *log(GetLog(LLDBLog::Host | LLDBLog::Process));
+    LLDB_LOG(log, "error: Failed to read process list.");
+    return 0;
+  }
 
   bool all_users = match_info.GetMatchAllUsers();
   const lldb::pid_t our_pid = getpid();
   const uid_t our_uid = getuid();
-  for (size_t i = 0; i < actual_pid_count; i++) {
-    const struct kinfo_proc &kinfo = kinfos[i];
-
+  for (const struct kinfo_proc &kinfo : kinfos) {
     bool kinfo_user_matches = false;
     if (all_users)
       kinfo_user_matches = true;

``````````

</details>


https://github.com/llvm/llvm-project/pull/204109
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to