Skip to content

Commit 0236c9a

Browse files
committed
Backport: Fix target pidinfo usage, centralize procargs parsing with KERN_ARGMAX, and defer task-port acquisition
1 parent 95d297e commit 0236c9a

3 files changed

Lines changed: 147 additions & 75 deletions

File tree

src/macos/mem.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,23 @@ impl ProcessVirtualMemory {
3636
pid: info.pid,
3737
})
3838
}
39+
40+
pub fn new_unavailable(pid: u32) -> Self {
41+
Self {
42+
port: MACH_PORT_NULL,
43+
pid,
44+
}
45+
}
46+
47+
pub(crate) fn ensure_port(&mut self) -> Result<mach_port_t> {
48+
// Acquire task port lazily so callers that only need process metadata can still work
49+
// when task_for_pid is restricted.
50+
if self.port == MACH_PORT_NULL {
51+
self.port = get_task(self.pid)?;
52+
}
53+
54+
Ok(self.port)
55+
}
3956
}
4057

4158
// Helper trait for `process_rw` to be generic.
@@ -113,9 +130,11 @@ impl ProcessVirtualMemory {
113130
mut out_fail,
114131
}: MemOps<CTup3<Address, Address, T>, CTup2<Address, T>>,
115132
) -> Result<()> {
133+
let port = self.ensure_port()?;
134+
116135
for CTup3(addr, meta_addr, buf) in inp {
117136
let written =
118-
unsafe { T::do_rw(self.port, buf.as_ptr() as _, addr.to_umem() as _, buf.len()) }
137+
unsafe { T::do_rw(port, buf.as_ptr() as _, addr.to_umem() as _, buf.len()) }
119138
.unwrap_or(0);
120139

121140
let (succeed, fail) = buf.split_at(written as _);

src/macos/mod.rs

Lines changed: 121 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use memflow::os::process::*;
22
use memflow::prelude::v1::*;
33

4-
use libc::{c_int, sysctl, CTL_KERN, KERN_PROCARGS2};
4+
use libc::{c_int, size_t, sysctl, CTL_KERN, KERN_ARGMAX, KERN_PROCARGS2};
55

66
use libc::{sysconf, _SC_PAGESIZE};
77
use libproc::{
@@ -28,6 +28,107 @@ use mem::ProcessVirtualMemory;
2828
pub mod process;
2929
pub use process::MacProcess;
3030

31+
#[derive(Clone, Debug, Default)]
32+
pub(super) struct ProcArgs {
33+
pub exec_path: String,
34+
pub argv: Vec<String>,
35+
}
36+
37+
fn argmax() -> usize {
38+
let mut mib: [c_int; 2] = [CTL_KERN, KERN_ARGMAX];
39+
let mut value: c_int = 0;
40+
let mut len = core::mem::size_of::<c_int>() as size_t;
41+
42+
let ret = unsafe {
43+
sysctl(
44+
mib.as_mut_ptr(),
45+
mib.len() as _,
46+
(&mut value as *mut c_int).cast(),
47+
&mut len,
48+
core::ptr::null_mut(),
49+
0,
50+
)
51+
};
52+
53+
if ret == 0 && value > 0 {
54+
value as usize
55+
} else {
56+
4096
57+
}
58+
}
59+
60+
pub(super) fn read_procargs2(pid: Pid) -> Result<Vec<u8>> {
61+
let mut scratch = vec![0u8; argmax()];
62+
63+
let mut mib: [c_int; 3] = [CTL_KERN, KERN_PROCARGS2, pid as _];
64+
let mut len = scratch.len() as size_t;
65+
66+
let ret = unsafe {
67+
sysctl(
68+
mib.as_mut_ptr(),
69+
mib.len() as _,
70+
scratch.as_mut_ptr().cast(),
71+
&mut len,
72+
core::ptr::null_mut(),
73+
0,
74+
)
75+
};
76+
77+
if ret != 0 || len < 4 {
78+
return Err(Error(ErrorOrigin::OsLayer, ErrorKind::Unknown));
79+
}
80+
81+
scratch.truncate(len);
82+
Ok(scratch)
83+
}
84+
85+
pub(super) fn parse_procargs2(data: &[u8]) -> Result<ProcArgs> {
86+
if data.len() < 4 {
87+
return Err(Error(ErrorOrigin::OsLayer, ErrorKind::Unknown));
88+
}
89+
90+
let mut argc_buf = [0u8; 4];
91+
argc_buf.copy_from_slice(&data[..4]);
92+
let argc = u32::from_ne_bytes(argc_buf) as usize;
93+
let buf = &data[4..];
94+
95+
let mut idx = 0usize;
96+
while idx < buf.len() && buf[idx] != 0 {
97+
idx += 1;
98+
}
99+
if idx == buf.len() {
100+
return Err(Error(ErrorOrigin::OsLayer, ErrorKind::Unknown));
101+
}
102+
103+
let exec_path = String::from_utf8_lossy(&buf[..idx]).into_owned();
104+
105+
while idx < buf.len() && buf[idx] == 0 {
106+
idx += 1;
107+
}
108+
109+
let mut argv = Vec::new();
110+
for _ in 0..argc {
111+
if idx >= buf.len() {
112+
break;
113+
}
114+
115+
let start = idx;
116+
while idx < buf.len() && buf[idx] != 0 {
117+
idx += 1;
118+
}
119+
120+
if idx > start {
121+
argv.push(String::from_utf8_lossy(&buf[start..idx]).into_owned());
122+
}
123+
124+
if idx < buf.len() {
125+
idx += 1;
126+
}
127+
}
128+
129+
Ok(ProcArgs { exec_path, argv })
130+
}
131+
31132
fn get_arch() -> ArchitectureIdent {
32133
static ARCH: OnceLock<ArchitectureIdent> = OnceLock::new();
33134

@@ -45,7 +146,6 @@ fn get_arch() -> ArchitectureIdent {
45146

46147
pub struct MacOs {
47148
info: OsInfo,
48-
scratch: Box<[u8]>,
49149
//cached_modules: Vec<KernelModule>,
50150
}
51151

@@ -59,7 +159,6 @@ impl Clone for MacOs {
59159
fn clone(&self) -> Self {
60160
Self {
61161
info: self.info.clone(),
62-
scratch: self.scratch.clone(),
63162
//cached_modules: vec![],
64163
}
65164
}
@@ -75,8 +174,6 @@ impl Default for MacOs {
75174

76175
Self {
77176
info,
78-
// TODO: call KERN_ARGMAX to figure out the actual value.
79-
scratch: vec![0; 4096].into_boxed_slice(),
80177
//cached_modules: vec![],
81178
}
82179
}
@@ -109,79 +206,34 @@ impl Os for MacOs {
109206
}
110207

111208
fn process_info_by_pid(&mut self, pid: Pid) -> Result<ProcessInfo> {
112-
let us = std::process::id();
113-
114209
let bsd_info =
115-
lp::proc_pid::pidinfo::<lp::bsd_info::BSDInfo>(us as _, pid as _).map_err(|e| {
210+
lp::proc_pid::pidinfo::<lp::bsd_info::BSDInfo>(pid as _, 0).map_err(|e| {
116211
error!("bsd_info: {e}");
117212
Error(ErrorOrigin::OsLayer, ErrorKind::Unknown)
118213
})?;
119214

120215
// We could use lp::proc_pid::pidpath for path, but we already get it from procargs2
121216
let (path, command_line): (ReprCString, ReprCString) = {
122-
let mut name: [c_int; 3] = [CTL_KERN, KERN_PROCARGS2, pid as _];
123-
let mut len = self.scratch.len() - 4;
124-
let ret = unsafe {
125-
sysctl(
126-
name.as_mut_ptr(),
127-
name.len() as _,
128-
self.scratch.as_mut_ptr().cast(),
129-
&mut len,
130-
core::ptr::null_mut(),
131-
0,
132-
)
133-
};
134-
135-
if ret != 0 {
136-
len = 0;
137-
}
138-
139-
// We skip the first arg, because that is the executable path.
140-
let mut num_args = u32::from_ne_bytes(self.scratch[..4].try_into().unwrap()) + 1;
141-
142-
let buf = &mut self.scratch[4..(4 + len)];
143-
144-
let mut start_idx = 0;
145-
let mut start_idx_stripped = 0;
146-
let mut idx = 0;
147-
148-
for (i, b) in buf.iter_mut().enumerate() {
149-
if num_args == 0 {
150-
break;
217+
let fallback_path = bsd_info
218+
.pbi_comm
219+
.iter()
220+
.copied()
221+
.map(|b| b as u8)
222+
.take_while(|b| *b != 0)
223+
.collect::<Vec<u8>>();
224+
let fallback_path = String::from_utf8_lossy(&fallback_path).into_owned();
225+
226+
match read_procargs2(pid).and_then(|d| parse_procargs2(&d)) {
227+
Ok(parsed) => {
228+
let path = if parsed.exec_path.is_empty() {
229+
fallback_path.as_str()
230+
} else {
231+
parsed.exec_path.as_str()
232+
};
233+
(path.into(), parsed.argv.join(" ").into())
151234
}
152-
153-
if *b == 0 {
154-
*b = b' ';
155-
num_args -= 1;
156-
if start_idx == 0 {
157-
start_idx = i + 1;
158-
start_idx_stripped = i + 1;
159-
} else if start_idx_stripped == i {
160-
num_args += 1;
161-
start_idx_stripped = i + 1;
162-
}
163-
}
164-
165-
idx = i;
235+
Err(_) => (fallback_path.into(), "".into()),
166236
}
167-
168-
let path = if start_idx == 0 {
169-
let b = bsd_info.pbi_comm.split(|v| *v == 0).next().unwrap_or(&[]);
170-
unsafe { &*(b as *const [_] as *const [u8]) }
171-
} else {
172-
&buf[..(start_idx - 1)]
173-
};
174-
175-
(
176-
std::str::from_utf8(path).unwrap_or_default().into(),
177-
std::str::from_utf8(if start_idx_stripped <= idx {
178-
&buf[start_idx_stripped..idx]
179-
} else {
180-
&[]
181-
})
182-
.unwrap_or_default()
183-
.into(),
184-
)
185237
};
186238
let name = path.split(&['/', '\\'][..]).last().unwrap().into();
187239

src/macos/process.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,10 +118,10 @@ impl Clone for MacProcess {
118118
impl MacProcess {
119119
pub fn try_new(info: ProcessInfo) -> Result<Self> {
120120
Ok(Self {
121-
virt_mem: ProcessVirtualMemory::try_new(&info).map_err(|e| {
122-
log::error!("Unable to get port");
123-
e
124-
})?,
121+
virt_mem: ProcessVirtualMemory::try_new(&info).unwrap_or_else(|e| {
122+
log::warn!("Unable to get task port for pid {}: {e:?}", info.pid);
123+
ProcessVirtualMemory::new_unavailable(info.pid)
124+
}),
125125
info,
126126
cached_maps: vec![],
127127
cached_module_maps: vec![],
@@ -135,9 +135,10 @@ impl MacProcess {
135135

136136
let mut count =
137137
(core::mem::size_of::<task_dyld_info>() / core::mem::size_of::<natural_t>()) as _;
138+
let port = self.virt_mem.ensure_port()?;
138139
let ret = unsafe {
139140
task_info(
140-
self.virt_mem.port,
141+
port,
141142
TASK_DYLD_INFO,
142143
&mut info as *mut task_dyld_info as *mut _,
143144
&mut count,

0 commit comments

Comments
 (0)