vmi_os_linux/comps/
task_struct.rs

1use once_cell::unsync::OnceCell;
2use vmi_core::{
3    Architecture, Pa, Va, VmiDriver, VmiError, VmiOs, VmiState, VmiVa,
4    os::{ProcessId, ProcessObject, VmiOsImageArchitecture, VmiOsProcess},
5};
6
7use super::{LinuxFsStruct, LinuxMmStruct, LinuxPath, LinuxVmAreaStruct, macros::impl_offsets};
8use crate::{ArchAdapter, LinuxError, LinuxOs};
9
10/// A Linux task struct.
11///
12/// The `task_struct` is the process descriptor in the Linux kernel,
13/// representing a task (process or thread).
14///
15/// # Implementation Details
16///
17/// Corresponds to `task_struct`.
18pub struct LinuxTaskStruct<'a, Driver>
19where
20    Driver: VmiDriver,
21    Driver::Architecture: Architecture + ArchAdapter<Driver>,
22{
23    /// The VMI state.
24    vmi: VmiState<'a, Driver, LinuxOs<Driver>>,
25
26    /// The virtual address of the `task_struct` structure.
27    va: Va,
28
29    /// Cached flags.
30    flags: OnceCell<u32>,
31}
32
33impl<Driver> VmiVa for LinuxTaskStruct<'_, Driver>
34where
35    Driver: VmiDriver,
36    Driver::Architecture: Architecture + ArchAdapter<Driver>,
37{
38    fn va(&self) -> Va {
39        self.va
40    }
41}
42
43impl<'a, Driver> LinuxTaskStruct<'a, Driver>
44where
45    Driver: VmiDriver,
46    Driver::Architecture: Architecture + ArchAdapter<Driver>,
47{
48    impl_offsets!();
49
50    /// Creates a new Linux task struct.
51    pub fn new(vmi: VmiState<'a, Driver, LinuxOs<Driver>>, process: ProcessObject) -> Self {
52        Self {
53            vmi,
54            va: process.0,
55            flags: OnceCell::new(),
56        }
57    }
58
59    /// Returns the process flags.
60    ///
61    /// Process flags in Linux include information about the process state,
62    /// such as whether it's exiting, a kernel thread, etc.
63    ///
64    /// # Implementation Details
65    ///
66    /// Corresponds to `task_struct.flags`.
67    pub fn flags(&self) -> Result<u32, VmiError> {
68        self.flags
69            .get_or_try_init(|| {
70                let offsets = self.offsets();
71                let __task_struct = &offsets.task_struct;
72
73                self.vmi.read_u32(self.va + __task_struct.flags.offset())
74            })
75            .copied()
76    }
77
78    /// Returns the memory descriptor (`mm_struct`) of the user-mode process.
79    ///
80    /// The `mm_struct` contains the memory management information for a process.
81    /// Kernel threads don't have an `mm_struct` and return `None`.
82    ///
83    /// # Implementation Details
84    ///
85    /// Corresponds to `task_struct->mm`.
86    pub fn mm(&self) -> Result<Option<LinuxMmStruct<'a, Driver>>, VmiError> {
87        let offsets = self.offsets();
88        let __task_struct = &offsets.task_struct;
89
90        let mm = self
91            .vmi
92            .read_va_native(self.va + __task_struct.mm.offset())?;
93
94        if mm.is_null() {
95            return Ok(None);
96        }
97
98        Ok(Some(LinuxMmStruct::new(self.vmi, mm)))
99    }
100
101    /// Returns the active memory context (`mm_struct`) of the process.
102    ///
103    /// Used by kernel threads to reference the last used `mm_struct` before
104    /// entering kernel mode.
105    ///
106    /// If a kernel thread ([`mm()`] is `None`) needs memory access,
107    /// it temporarily borrows `active_mm` from the last scheduled user-space
108    /// process.
109    ///
110    /// When the kernel thread exits, the original `mm_struct` is restored.
111    ///
112    /// # Implementation Details
113    ///
114    /// Corresponds to `task_struct->active_mm`.
115    ///
116    /// [`mm()`]: Self::mm
117    pub fn active_mm(&self) -> Result<LinuxMmStruct<'a, Driver>, VmiError> {
118        let offsets = self.offsets();
119        let __task_struct = &offsets.task_struct;
120
121        let mm = self
122            .vmi
123            .read_va_native(self.va + __task_struct.active_mm.offset())?;
124
125        if mm.is_null() {
126            return Err(LinuxError::CorruptedStruct("task_struct->active_mm").into());
127        }
128
129        Ok(LinuxMmStruct::new(self.vmi, mm))
130    }
131
132    /// Returns the filesystem context (`fs_struct`) of the process.
133    ///
134    /// `fs_struct` contains:
135    ///   - [`root`]: The process’s root directory (used for chroot).
136    ///   - [`pwd`]: The current working directory.
137    ///
138    /// All threads in the same process share the same `fs_struct`, unless
139    /// explicitly changed.
140    ///
141    /// Kernel threads don't have an `fs_struct` and return `None`.
142    ///
143    /// # Implementation Details
144    ///
145    /// Corresponds to `task_struct->fs`.
146    ///
147    /// [`root`]: LinuxFsStruct::root
148    /// [`pwd`]: LinuxFsStruct::pwd
149    pub fn fs(&self) -> Result<Option<LinuxFsStruct<'a, Driver>>, VmiError> {
150        let offsets = self.offsets();
151        let __task_struct = &offsets.task_struct;
152        let __fs_struct = &offsets.fs_struct;
153
154        let fs = self
155            .vmi
156            .read_va_native(self.va + __task_struct.fs.offset())?;
157
158        if fs.is_null() {
159            return Ok(None);
160        }
161
162        Ok(Some(LinuxFsStruct::new(self.vmi, fs)))
163    }
164
165    /// Constructs the absolute path from a `path` structure.
166    ///
167    /// Takes into account the process's filesystem root when constructing the
168    /// absolute path.
169    ///
170    /// Returns the resolved path as a string if successful, or `None` if the path
171    /// could not be resolved (e.g., if the root is null).
172    ///
173    /// # Implementation Details
174    ///
175    /// Concatenates `task_struct->fs->root` with the `path` structure to construct
176    /// the absolute path.
177    pub fn d_path(&self, path: &LinuxPath<Driver>) -> Result<Option<String>, VmiError> {
178        let root = match self.fs()? {
179            Some(root) => root.root()?,
180            None => return Ok(None),
181        };
182
183        Ok(Some(LinuxOs::<Driver>::construct_path(
184            self.vmi, path, &root,
185        )?))
186    }
187
188    /// Returns the path of the executable image for a process.
189    ///
190    /// Returns the executable path as a string, or `None` for special processes
191    /// like kernel threads or those in the process of exiting.
192    ///
193    /// # Implementation Details
194    ///
195    /// Corresponds to `d_path(task->mm->exe_file->f_path)`.
196    pub fn image_path(&self) -> Result<Option<String>, VmiError> {
197        let flags = self.flags()?;
198
199        const PF_EXITING: u32 = 0x00000004; // getting shut down
200        const PF_KTHREAD: u32 = 0x00200000; // kernel thread
201
202        if flags & PF_KTHREAD != 0 {
203            return Ok(None);
204        }
205
206        if flags & PF_EXITING != 0 {
207            return Ok(None);
208        }
209
210        let mm = match self.mm()? {
211            Some(mm) => mm,
212            None => return Ok(None),
213        };
214
215        let exe_file = match mm.exe_file()? {
216            Some(exe_file) => exe_file,
217            None => return Ok(None),
218        };
219
220        self.d_path(&exe_file.path()?)
221    }
222}
223
224impl<'a, Driver> VmiOsProcess<'a, Driver> for LinuxTaskStruct<'a, Driver>
225where
226    Driver: VmiDriver,
227    Driver::Architecture: Architecture + ArchAdapter<Driver>,
228{
229    type Os = LinuxOs<Driver>;
230
231    fn id(&self) -> Result<ProcessId, VmiError> {
232        let offsets = self.offsets();
233        let __task_struct = &offsets.task_struct;
234
235        let result = self.vmi.read_u32(self.va + __task_struct.tgid.offset())?;
236
237        Ok(ProcessId(result))
238    }
239
240    fn object(&self) -> Result<ProcessObject, VmiError> {
241        Ok(ProcessObject(self.va))
242    }
243
244    fn name(&self) -> Result<String, VmiError> {
245        let task_struct_comm_offset = 0xBC0;
246
247        self.vmi.read_string(self.va + task_struct_comm_offset)
248    }
249
250    fn parent_id(&self) -> Result<ProcessId, VmiError> {
251        unimplemented!()
252    }
253
254    fn architecture(&self) -> Result<VmiOsImageArchitecture, VmiError> {
255        unimplemented!()
256    }
257
258    fn translation_root(&self) -> Result<Pa, VmiError> {
259        unimplemented!()
260    }
261
262    fn user_translation_root(&self) -> Result<Pa, VmiError> {
263        unimplemented!()
264    }
265
266    fn image_base(&self) -> Result<Va, VmiError> {
267        unimplemented!()
268    }
269
270    fn regions(
271        &self,
272    ) -> Result<
273        impl Iterator<Item = Result<<Self::Os as VmiOs<Driver>>::Region<'a>, VmiError>>,
274        VmiError,
275    > {
276        let mut result = Vec::new();
277
278        let mm = match self.mm()? {
279            Some(mm) => mm,
280            None => return Ok(result.into_iter()),
281        };
282
283        let mt = mm.mm_mt()?;
284        mt.enumerate(|node| {
285            println!("XXXNode: {}", node);
286            if !node.is_null() {
287                result.push(Ok(LinuxVmAreaStruct::new(self.vmi, node)));
288            }
289            true
290        })?;
291
292        Ok(result.into_iter())
293    }
294
295    fn find_region(
296        &self,
297        _address: Va,
298    ) -> Result<Option<<Self::Os as VmiOs<Driver>>::Region<'a>>, VmiError> {
299        unimplemented!()
300    }
301
302    fn threads(
303        &self,
304    ) -> Result<
305        impl Iterator<Item = Result<<Self::Os as VmiOs<Driver>>::Thread<'a>, VmiError>>,
306        VmiError,
307    > {
308        #[expect(unreachable_code)]
309        {
310            unimplemented!() as Result<std::iter::Empty<_>, VmiError>
311        }
312    }
313
314    fn is_valid_address(&self, _address: Va) -> Result<Option<bool>, VmiError> {
315        unimplemented!()
316    }
317}