shadow_rs/host/syscall/handler/
mman.rs1use std::os::unix::ffi::OsStrExt;
2use std::path::PathBuf;
3
4use linux_api::errno::Errno;
5use linux_api::fcntl::OFlag;
6use linux_api::mman::{MapFlags, ProtFlags};
7use shadow_shim_helper_rs::syscall_types::ForeignPtr;
8
9use crate::cshadow as c;
10use crate::host::descriptor::{CompatFile, FileState};
11use crate::host::memory_manager::AllocdMem;
12use crate::host::syscall::handler::{SyscallContext, SyscallHandler, ThreadContext};
13use crate::host::syscall::types::SyscallError;
14
15impl SyscallHandler {
16 log_syscall!(
17 brk,
18 std::ffi::c_int,
19 *const std::ffi::c_void,
20 );
21 pub fn brk(
22 ctx: &mut SyscallContext,
23 addr: ForeignPtr<u8>,
24 ) -> Result<ForeignPtr<u8>, SyscallError> {
25 let mut memory_manager = ctx.objs.process.memory_borrow_mut();
27 memory_manager.handle_brk(ctx.objs, addr)
28 }
29
30 log_syscall!(
37 mremap,
38 *const std::ffi::c_void,
39 *const std::ffi::c_void,
40 std::ffi::c_ulong,
41 std::ffi::c_ulong,
42 linux_api::mman::MRemapFlags,
43 *const std::ffi::c_void,
44 );
45 pub fn mremap(
46 ctx: &mut SyscallContext,
47 old_addr: std::ffi::c_ulong,
48 old_size: std::ffi::c_ulong,
49 new_size: std::ffi::c_ulong,
50 flags: std::ffi::c_ulong,
51 new_addr: std::ffi::c_ulong,
52 ) -> Result<ForeignPtr<u8>, SyscallError> {
53 let old_addr: usize = old_addr.try_into().unwrap();
54 let old_size: usize = old_size.try_into().unwrap();
55 let new_size: usize = new_size.try_into().unwrap();
56 let new_addr: usize = new_addr.try_into().unwrap();
57
58 if flags as u32 as u64 != flags {
61 warn_once_then_trace!("Ignoring truncated flags from mremap: {flags}");
62 }
63
64 let flags = flags as i32;
65
66 let old_addr = ForeignPtr::<()>::from(old_addr).cast::<u8>();
67 let new_addr = ForeignPtr::<()>::from(new_addr).cast::<u8>();
68
69 let mut memory_manager = ctx.objs.process.memory_borrow_mut();
71 memory_manager.handle_mremap(ctx.objs, old_addr, old_size, new_size, flags, new_addr)
72 }
73
74 log_syscall!(
79 munmap,
80 std::ffi::c_int,
81 *const std::ffi::c_void,
82 usize,
83 );
84 pub fn munmap(
85 ctx: &mut SyscallContext,
86 addr: std::ffi::c_ulong,
87 len: usize,
88 ) -> Result<(), SyscallError> {
89 let addr: usize = addr.try_into().unwrap();
90 let addr = ForeignPtr::<()>::from(addr).cast::<u8>();
91
92 let mut memory_manager = ctx.objs.process.memory_borrow_mut();
94 memory_manager.handle_munmap(ctx.objs, addr, len)
95 }
96
97 log_syscall!(
102 mprotect,
103 std::ffi::c_int,
104 *const std::ffi::c_void,
105 usize,
106 linux_api::mman::ProtFlags,
107 );
108 pub fn mprotect(
109 ctx: &mut SyscallContext,
110 addr: std::ffi::c_ulong,
111 len: usize,
112 prot: std::ffi::c_ulong,
113 ) -> Result<(), SyscallError> {
114 let addr: usize = addr.try_into().unwrap();
115 let addr = ForeignPtr::<()>::from(addr).cast::<u8>();
116
117 let Some(prot) = ProtFlags::from_bits(prot) else {
118 let unrecognized = ProtFlags::from_bits_retain(prot).difference(ProtFlags::all());
119 log_once_per_value_at_level!(
120 unrecognized,
121 ProtFlags,
122 log::Level::Warn,
123 log::Level::Debug,
124 "Unrecognized prot flag: {:#x}",
125 unrecognized.bits()
126 );
127 return Err(Errno::EINVAL.into());
128 };
129
130 let mut memory_manager = ctx.objs.process.memory_borrow_mut();
132 memory_manager.handle_mprotect(ctx.objs, addr, len, prot)
133 }
134
135 log_syscall!(
142 mmap,
143 *const std::ffi::c_void,
144 *const std::ffi::c_void,
145 usize,
146 linux_api::mman::ProtFlags,
147 linux_api::mman::MapFlags,
148 std::ffi::c_ulong,
149 std::ffi::c_ulong,
150 );
151 pub fn mmap(
152 ctx: &mut SyscallContext,
153 addr: std::ffi::c_ulong,
154 len: std::ffi::c_ulong,
155 prot: std::ffi::c_ulong,
156 flags: std::ffi::c_ulong,
157 fd: std::ffi::c_ulong,
158 offset: std::ffi::c_ulong,
159 ) -> Result<ForeignPtr<u8>, Errno> {
160 log::trace!("mmap called on fd {fd} for {len} bytes");
161
162 let addr: usize = addr.try_into().unwrap();
163 let addr = ForeignPtr::<()>::from(addr).cast::<u8>();
164
165 let len: usize = len.try_into().unwrap();
166
167 let offset = offset as i64;
168
169 let Some(prot) = ProtFlags::from_bits(prot) else {
170 let unrecognized = ProtFlags::from_bits_retain(prot).difference(ProtFlags::all());
171 log_once_per_value_at_level!(
172 unrecognized,
173 ProtFlags,
174 log::Level::Warn,
175 log::Level::Debug,
176 "Unrecognized prot flag: {:#x}",
177 unrecognized.bits()
178 );
179 return Err(Errno::EINVAL);
180 };
181 let Some(flags) = MapFlags::from_bits(flags) else {
182 let unrecognized = MapFlags::from_bits_retain(flags).difference(MapFlags::all());
183 log_once_per_value_at_level!(
184 unrecognized,
185 MapFlags,
186 log::Level::Warn,
187 log::Level::Debug,
188 "Unrecognized map flag: {:#x}",
189 unrecognized.bits()
190 );
191 return Err(Errno::EINVAL);
192 };
193
194 let required_flags =
196 MapFlags::MAP_PRIVATE | MapFlags::MAP_SHARED | MapFlags::MAP_SHARED_VALIDATE;
197
198 if len == 0 || !required_flags.intersects(flags) {
200 log::debug!("Invalid len ({len}), prot ({prot:?}), or flags ({flags:?})");
201 return Err(Errno::EINVAL);
202 }
203
204 if fd <= 2 && !flags.contains(MapFlags::MAP_ANONYMOUS) {
207 log::debug!("Invalid fd {fd} and MAP_ANONYMOUS is not set in flags {flags:?}");
208 return Err(Errno::EBADF);
209 }
210
211 let file = if flags.contains(MapFlags::MAP_ANONYMOUS) {
213 None
214 } else {
215 let file = {
216 let desc_table = ctx.objs.thread.descriptor_table_borrow(ctx.objs.host);
218 let desc = Self::get_descriptor(&desc_table, fd)?;
219
220 let CompatFile::Legacy(file) = desc.file() else {
221 return Err(Errno::EINVAL);
223 };
224
225 file.ptr()
226 };
227
228 assert!(!file.is_null());
229
230 if unsafe { c::legacyfile_getStatus(file) }.contains(FileState::CLOSED) {
231 log::warn!("File {file:p} (fd={fd}) is closed");
237 return Err(Errno::EBADF);
238 }
239
240 if unsafe { c::legacyfile_getType(file) } != c::_LegacyFileType_DT_FILE {
241 log::debug!("Descriptor exists for fd {fd}, but is not a regular file type");
242 return Err(Errno::EACCES);
243 }
244
245 Some(file as *mut c::RegularFile)
247 };
248
249 let plugin_fd = file.map(|file| Self::open_plugin_file(ctx.objs, fd, file));
252
253 let Ok(plugin_fd) = plugin_fd.transpose() else {
255 log::warn!("mmap on fd {fd} for {len} bytes failed");
256 return Err(Errno::EACCES);
257 };
258
259 let mut memory_manager = ctx.objs.process.memory_borrow_mut();
261 let mmap_result = memory_manager.do_mmap(
262 ctx.objs,
263 addr,
264 len,
265 prot,
266 flags,
267 plugin_fd.unwrap_or(-1),
268 offset,
269 );
270
271 log::trace!(
272 "Plugin-native mmap syscall at plugin addr {addr:p} with plugin fd {fd} for \
273 {len} bytes returned {mmap_result:?}"
274 );
275
276 if let Some(plugin_fd) = plugin_fd {
278 Self::close_plugin_file(ctx.objs, plugin_fd);
279 }
280
281 mmap_result
282 }
283
284 fn open_plugin_file(
285 ctx: &ThreadContext,
286 fd: std::ffi::c_ulong,
287 file: *mut c::RegularFile,
288 ) -> Result<i32, ()> {
289 assert!(!file.is_null());
290
291 log::trace!("Trying to open file {fd} in the plugin");
292
293 let file_type = unsafe { c::regularfile_getType(file) };
297 if file_type != c::_FileType_FILE_TYPE_REGULAR
298 && file_type != c::_FileType_FILE_TYPE_LOCALTIME
299 {
300 warn_once_then_debug!("Tried to mmap a non-regular non-localtime file");
301 return Err(());
302 }
303
304 let native_fd = unsafe { c::regularfile_getOSBackedFD(file) };
305
306 let Some(path) = Self::create_persistent_mmap_path(native_fd) else {
308 log::trace!("RegularFile {fd} has a NULL path");
309 return Err(());
310 };
311
312 let path_bytes = path.as_os_str().as_bytes();
313
314 let path_len = std::cmp::min(path_bytes.len(), libc::PATH_MAX as usize - 1);
319 assert!(path_len > 0);
320
321 let path_bytes = &path_bytes[..path_len];
322
323 log::trace!("Opening path '{}' in plugin", path.display());
324
325 let plugin_buffer = AllocdMem::<u8>::new(ctx, path_len + 1);
328
329 {
330 let mut mem = ctx.process.memory_borrow_mut();
331
332 if let Err(e) = mem.copy_to_ptr(plugin_buffer.ptr().slice(..path_len), path_bytes) {
334 log::warn!("Unable to write string to allocated buffer: {e}");
335 std::mem::drop(mem);
336 plugin_buffer.free(ctx);
337 return Err(());
338 }
339
340 if let Err(e) = mem.copy_to_ptr(plugin_buffer.ptr().slice(path_len..), &[0]) {
342 log::warn!("Unable to write NUL to allocated buffer: {e}");
343 std::mem::drop(mem);
344 plugin_buffer.free(ctx);
345 return Err(());
346 }
347 }
348
349 let creation_flags = OFlag::empty()
354 | OFlag::O_CLOEXEC
355 | OFlag::O_CREAT
356 | OFlag::O_DIRECTORY
357 | OFlag::O_EXCL
358 | OFlag::O_NOCTTY
359 | OFlag::O_NOFOLLOW
360 | OFlag::O_TMPFILE
361 | OFlag::O_TRUNC;
362
363 let native_flags = OFlag::from_bits_retain(unsafe {
365 libc::fcntl(c::regularfile_getOSBackedFD(file), libc::F_GETFL)
366 });
367
368 let mut flags = OFlag::from_bits_retain(unsafe { c::regularfile_getFlagsAtOpen(file) });
370 flags &= creation_flags.difference(OFlag::O_CLOEXEC);
372 flags |= native_flags.difference(OFlag::from_bits_retain(unsafe { c::SHADOW_FLAG_MASK }));
374 flags |= OFlag::from_bits_retain(unsafe { c::regularfile_getShadowFlags(file) });
376 flags -= OFlag::O_CREAT | OFlag::O_EXCL | OFlag::O_TMPFILE | OFlag::O_TRUNC;
378 flags -= OFlag::O_NOFOLLOW;
381
382 let mode = unsafe { c::regularfile_getModeAtOpen(file) };
383
384 let (process_ctx, thread) = ctx.split_thread();
386 let open_result = thread.native_open(
387 &process_ctx,
388 plugin_buffer.ptr().ptr(),
389 flags.bits() as i32,
390 mode as i32,
391 );
392
393 plugin_buffer.free(ctx);
394
395 let open_result = match open_result {
396 Ok(x) => x,
397 Err(e) => {
398 log::trace!(
399 "Failed to open path '{}' in plugin, error {e}",
400 path.display()
401 );
402 return Err(());
403 }
404 };
405
406 log::trace!(
407 "Successfully opened path '{}' in plugin, got plugin fd {open_result}",
408 path.display(),
409 );
410
411 Ok(open_result)
412 }
413
414 fn close_plugin_file(ctx: &ThreadContext, plugin_fd: i32) {
416 let (ctx, thread) = ctx.split_thread();
417 let result = thread.native_close(&ctx, plugin_fd);
418
419 if let Err(e) = result {
420 log::trace!("Failed to close file at fd {plugin_fd} in plugin, error {e}");
421 } else {
422 log::trace!("Successfully closed file at fd {plugin_fd} in plugin");
423 }
424 }
425
426 fn create_persistent_mmap_path(native_fd: std::ffi::c_int) -> Option<PathBuf> {
430 assert!(native_fd >= 0);
431
432 let pid_string = std::process::id().to_string();
444 let native_fd_string = native_fd.to_string();
445
446 let path: PathBuf = ["/proc", &pid_string, "fd", &native_fd_string]
449 .iter()
450 .collect();
451
452 if !path.exists() {
454 log::warn!(
455 "Unable to produce a persistent mmap path for file (linux file {native_fd})"
456 );
457 return None;
458 }
459
460 log::trace!(
461 "RegularFile (linux file {native_fd}) is persistent in procfs at {}",
462 path.display()
463 );
464
465 Some(path)
466 }
467}