1use crate::proc_directory::ProcDirectory;
6use starnix_core::mm::PAGE_SIZE;
7use starnix_core::task::{CurrentTask, Task, TaskContainer};
8use starnix_core::vfs::fs_args::MountParams;
9use starnix_core::vfs::{
10 CacheMode, FileSystem, FileSystemHandle, FileSystemOps, FileSystemOptions, FsStr, FsString,
11};
12use starnix_types::vfs::default_statfs;
13use starnix_uapi::auth::{PTRACE_MODE_NOAUDIT, PTRACE_MODE_READ_FSCREDS};
14use starnix_uapi::errors::Errno;
15use starnix_uapi::mount_flags::FileSystemFlags;
16use starnix_uapi::{PROC_SUPER_MAGIC, error, gid_t, statfs};
17use std::fmt::Write;
18use std::sync::atomic::{AtomicU64, Ordering};
19
20#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
22#[repr(u8)]
23pub enum HidePid {
24 #[default]
25 Off = 0,
26 NoAccess = 1,
27 Invisible = 2,
28 Ptraceable = 4,
29}
30
31impl HidePid {
32 fn parse(s: &str) -> Result<Self, Errno> {
33 match s {
34 "0" | "off" => Ok(Self::Off),
35 "1" | "noaccess" => Ok(Self::NoAccess),
36 "2" | "invisible" => Ok(Self::Invisible),
37 "4" | "ptraceable" => Ok(Self::Ptraceable),
38 _ => error!(EINVAL),
39 }
40 }
41
42 fn from_u8(v: u8) -> Self {
43 match v {
44 1 => Self::NoAccess,
45 2 => Self::Invisible,
46 4 => Self::Ptraceable,
47 _ => Self::Off,
48 }
49 }
50
51 fn as_str(&self) -> Option<&'static str> {
52 match self {
53 Self::Off => None,
54 Self::NoAccess => Some("noaccess"),
55 Self::Invisible => Some("invisible"),
56 Self::Ptraceable => Some("ptraceable"),
57 }
58 }
59}
60
61#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
62struct ProcMountParams {
63 gid: Option<gid_t>,
64 hide_pid: Option<HidePid>,
65}
66
67pub fn proc_fs(
71 current_task: &CurrentTask,
72 options: FileSystemOptions,
73) -> Result<FileSystemHandle, Errno> {
74 ProcFs::new_fs(current_task, options)
75}
76
77#[derive(Debug, Default)]
79pub struct ProcFs {
80 mount_options: AtomicU64,
82}
83
84impl FileSystemOps for ProcFs {
85 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
86 Ok(default_statfs(PROC_SUPER_MAGIC))
87 }
88
89 fn name(&self) -> &'static FsStr {
90 "proc".into()
91 }
92
93 fn reconfigure(
94 &self,
95 fs: &FileSystem,
96 _current_task: &CurrentTask,
97 new_flags: FileSystemFlags,
98 params: &MountParams,
99 ) -> Result<(), Errno> {
100 self.apply_mount_params(params)?;
101 fs.options.flags.store(new_flags, Ordering::Relaxed);
102 Ok(())
103 }
104
105 fn show_options(&self, _fs: &FileSystem) -> Result<FsString, Errno> {
106 let (pid_gid, hide_pid) = self.load_mount_options();
107 let mut opts = String::new();
108 if pid_gid != 0 {
109 let _ = write!(&mut opts, ",gid={pid_gid}");
110 }
111 if let Some(hide_pid) = hide_pid.as_str() {
112 let _ = write!(&mut opts, ",hidepid={hide_pid}");
113 }
114 Ok(opts.into())
115 }
116
117 fn default_blksize(&self) -> Option<usize> {
118 Some(*PAGE_SIZE as usize)
119 }
120}
121
122impl ProcFs {
123 pub fn new_fs(
125 current_task: &CurrentTask,
126 options: FileSystemOptions,
127 ) -> Result<FileSystemHandle, Errno> {
128 let params = Self::parse_mount_params(&options.params)?;
129 let proc_fs = ProcFs::default();
130 proc_fs.set_mount_params(params);
131 let kernel = current_task.kernel();
132 let fs = FileSystem::new(kernel, CacheMode::Uncached, proc_fs, options)?;
133 let root_ino = fs.allocate_ino();
134 fs.create_root(root_ino, ProcDirectory::new(kernel, &fs));
135 Ok(fs)
136 }
137
138 fn pack_mount_options(pid_gid: gid_t, hide_pid: HidePid) -> u64 {
139 (pid_gid as u64) | ((hide_pid as u64) << 32)
140 }
141
142 fn unpack_mount_options(raw: u64) -> (gid_t, HidePid) {
143 let pid_gid = raw as gid_t;
144 let hide_pid = HidePid::from_u8((raw >> 32) as u8);
145 (pid_gid, hide_pid)
146 }
147
148 pub fn load_mount_options(&self) -> (gid_t, HidePid) {
149 Self::unpack_mount_options(self.mount_options.load(Ordering::Relaxed))
150 }
151
152 fn parse_mount_params(params: &MountParams) -> Result<ProcMountParams, Errno> {
153 let gid = params.get_as::<gid_t>(b"gid")?;
154 let hide_pid = params.get_with(b"hidepid", HidePid::parse)?;
155 Ok(ProcMountParams { gid, hide_pid })
156 }
157
158 fn set_mount_params(&self, params: ProcMountParams) {
159 let _ = self.mount_options.try_update(Ordering::Relaxed, Ordering::Relaxed, |raw| {
160 let (cur_gid, cur_hide_pid) = Self::unpack_mount_options(raw);
161 let new_gid = params.gid.unwrap_or(cur_gid);
162 let new_hide_pid = params.hide_pid.unwrap_or(cur_hide_pid);
163 Some(Self::pack_mount_options(new_gid, new_hide_pid))
164 });
165 }
166
167 fn apply_mount_params(&self, params: &MountParams) -> Result<(), Errno> {
168 let parsed = Self::parse_mount_params(params)?;
169 self.set_mount_params(parsed);
170 Ok(())
171 }
172
173 pub fn has_pid_permissions_with_opts(
174 &self,
175 current_task: &CurrentTask,
176 target_task: &Task,
177 hide_pid_min: HidePid,
178 pid_gid: gid_t,
179 hide_pid: HidePid,
180 ) -> bool {
181 if current_task.pid == target_task.pid {
182 return true;
183 }
184 if hide_pid != HidePid::Ptraceable {
185 if hide_pid < hide_pid_min {
186 return true;
187 }
188 let creds = current_task.current_creds();
189 if creds.fsgid == pid_gid || creds.groups.contains(&pid_gid) {
190 return true;
191 }
192 }
193 current_task
194 .check_ptrace_access_mode(PTRACE_MODE_READ_FSCREDS | PTRACE_MODE_NOAUDIT, target_task)
195 .is_ok()
196 }
197
198 pub fn has_pid_permissions(
199 &self,
200 current_task: &CurrentTask,
201 target_task: &Task,
202 hide_pid_min: HidePid,
203 ) -> bool {
204 let (pid_gid, hide_pid) = self.load_mount_options();
205 self.has_pid_permissions_with_opts(
206 current_task,
207 target_task,
208 hide_pid_min,
209 pid_gid,
210 hide_pid,
211 )
212 }
213
214 pub fn check_pid_access(
215 &self,
216 current_task: &CurrentTask,
217 target: &TaskContainer,
218 hide_pid_min: HidePid,
219 ) -> Result<(), Errno> {
220 let (pid_gid, hide_pid) = self.load_mount_options();
221 let Ok(target_task) = target.get_task() else {
222 if hide_pid >= HidePid::Invisible {
223 return error!(ENOENT);
224 }
225 return error!(ESRCH);
226 };
227 if !self.has_pid_permissions_with_opts(
228 current_task,
229 &target_task,
230 hide_pid_min,
231 pid_gid,
232 hide_pid,
233 ) {
234 if hide_pid >= HidePid::Invisible {
235 return error!(ENOENT);
236 }
237 return error!(EPERM);
238 }
239 Ok(())
240 }
241}