Skip to content
Prev Previous commit
Next Next commit
Add debug_assert for thread state in start_the_world
  • Loading branch information
youknowone committed Mar 8, 2026
commit cfd7f011aacfd7b0dcf9f14e4668285bc07699fe
4 changes: 2 additions & 2 deletions crates/vm/src/stdlib/posix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -935,14 +935,14 @@ pub mod module {
Ok(n) => n,
Err(_) => return 0,
};
let line = match std::str::from_utf8(&buf[..n]) {
let line = match core::str::from_utf8(&buf[..n]) {
Ok(s) => s,
Err(_) => return 0,
};
if let Some(field) = line.split_whitespace().nth(19) {
return field.parse::<isize>().unwrap_or(0);
}
return 0;
0
}
Comment on lines +926 to +946
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Linux: /proc/self/stat parsing is fragile when comm contains spaces.

The comm field (field 2 in /proc/self/stat) is enclosed in parentheses and can contain spaces, parentheses, or newlines. Using split_whitespace().nth(19) will yield incorrect results if the process name contains spaces.

For example, a process named "my app" produces:

123 (my app) S 456 ...

split_whitespace() splits this into ["123", "(my", "app)", "S", ...], making nth(19) point to the wrong field.

CPython handles this by finding the last ) to locate the end of the comm field, then counting fields from there.

🛡️ Suggested fix to handle comm field correctly
         #[cfg(target_os = "linux")]
         {
             use std::io::Read as _;
             let mut file = match std::fs::File::open("/proc/self/stat") {
                 Ok(f) => f,
                 Err(_) => return 0,
             };
             let mut buf = [0u8; 160];
             let n = match file.read(&mut buf) {
                 Ok(n) => n,
                 Err(_) => return 0,
             };
             let line = match core::str::from_utf8(&buf[..n]) {
                 Ok(s) => s,
                 Err(_) => return 0,
             };
-            if let Some(field) = line.split_whitespace().nth(19) {
-                return field.parse::<isize>().unwrap_or(0);
+            // Find end of comm field (last ')') to handle spaces in process name
+            let after_comm = match line.rfind(')') {
+                Some(idx) => &line[idx + 1..],
+                None => return 0,
+            };
+            // Field 20 (num_threads) is the 17th field after comm (fields 3-19 are 17 fields)
+            if let Some(field) = after_comm.split_whitespace().nth(17) {
+                return field.parse::<isize>().unwrap_or(0);
             }
             0
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/vm/src/stdlib/posix.rs` around lines 926 - 946, The current parsing of
/proc/self/stat is fragile because split_whitespace() will break when the comm
field (inside parentheses) contains spaces; instead, after reading the file and
converting to string, find the index of the last ')' in the line, take the
substring after that character, then split_whitespace() on that remainder and
parse the desired field (the same field you previously accessed with nth(19))
from the resultant iterator (preserve the existing return behavior using
parse::<isize>().unwrap_or(0)); replace the direct
line.split_whitespace().nth(19) usage with this "find last ')', take substring
after it, then split" approach to reliably locate the field even when comm
contains spaces.

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
{
Expand Down
7 changes: 6 additions & 1 deletion crates/vm/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,12 @@ impl StopTheWorldState {
continue;
}
slot.stop_requested.store(false, Ordering::Release);
if slot.state.load(Ordering::Relaxed) == THREAD_SUSPENDED {
let state = slot.state.load(Ordering::Relaxed);
debug_assert!(
state == THREAD_SUSPENDED,
"non-requester thread not suspended at start-the-world: id={id} state={state}"
);
if state == THREAD_SUSPENDED {
slot.state.store(THREAD_DETACHED, Ordering::Release);
slot.thread.unpark();
}
Expand Down