reverie-syscalls/src/args/mod.rs (419 lines of code) (raw):
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
//! Collection of type-safe syscall arguments. These are shared among
//! potentially many syscalls.
use core::fmt;
use std::{
ffi::{CString, OsString},
os::unix::ffi::OsStringExt,
path::PathBuf,
};
mod clone;
mod fcntl;
pub mod ioctl;
mod poll;
mod stat;
mod time;
use nix::{
sys::stat::{Mode, SFlag},
unistd::Pid,
};
use serde::{Deserialize, Serialize};
pub use clone::*;
pub use fcntl::FcntlCmd;
pub use poll::*;
pub use stat::*;
pub use time::*;
use crate::{Addr, AddrMut, Displayable, Errno, FromToRaw, MemoryAccess};
/// Helper trait for reading a specific value from an address.
pub trait ReadAddr {
/// The type of value returned by `read`.
type Target: Sized;
/// The error type returned by `read`.
type Error;
/// Reads the contents of the address and returns it.
fn read<M: MemoryAccess>(&self, memory: &M) -> Result<Self::Target, Self::Error>;
}
impl<'a, T> ReadAddr for Addr<'a, T>
where
T: Copy + Sized,
{
type Target = T;
type Error = Errno;
fn read<M: MemoryAccess>(&self, memory: &M) -> Result<Self::Target, Self::Error> {
memory.read_value(*self)
}
}
impl<'a, T> ReadAddr for AddrMut<'a, T>
where
T: Copy + Sized,
{
type Target = T;
type Error = Errno;
fn read<M: MemoryAccess>(&self, memory: &M) -> Result<Self::Target, Self::Error> {
memory.read_value(*self)
}
}
/// An array of pointers.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct CArrayPtr<'a, T>(Addr<'a, Option<T>>);
impl<'a, T> ReadAddr for CArrayPtr<'a, T>
where
T: Copy,
{
type Target = Vec<T>;
type Error = Errno;
fn read<M: MemoryAccess>(&self, memory: &M) -> Result<Self::Target, Self::Error> {
let mut v = Vec::new();
let mut r = memory.reader(self.0);
while let Some(addr) = r.read_value()? {
v.push(addr);
}
Ok(v)
}
}
impl<'a, T> FromToRaw for Option<CArrayPtr<'a, T>> {
fn from_raw(raw: u64) -> Self {
Option::<Addr<'a, Option<T>>>::from_raw(raw).map(CArrayPtr)
}
fn into_raw(self) -> u64 {
self.map(|p| p.0).into_raw()
}
}
impl<'a, T> Displayable for Option<CArrayPtr<'a, T>>
where
T: Copy + Displayable,
{
fn fmt<M: MemoryAccess>(
&self,
memory: &M,
outputs: bool,
f: &mut fmt::Formatter,
) -> fmt::Result {
match self {
None => f.write_str("NULL"),
Some(array) => match array.read(memory) {
Ok(v) => {
write!(f, "{} -> [", array.0)?;
let mut count = 0;
let mut iter = v.into_iter();
if let Some(item) = iter.next() {
item.fmt(memory, outputs, f)?;
count += 1;
}
for item in iter {
f.write_str(", ")?;
// Only print the first 32 arguments like strace does.
if count > 32 {
f.write_str("...")?;
break;
}
item.fmt(memory, outputs, f)?;
count += 1;
}
f.write_str("]")
}
Err(e) => write!(f, "{} -> <{}>", array.0, e),
},
}
}
}
/// A pointer to a `CString` that resides in the target address space.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct CStrPtr<'a>(Addr<'a, u8>);
impl<'a> CStrPtr<'a> {
/// Creates the `CStrPtr` from a raw pointer. Returns `None` if the given
/// pointer is NULL.
pub fn from_ptr(r: *const libc::c_char) -> Option<Self> {
Addr::from_ptr(r as *const u8).map(CStrPtr)
}
}
impl<'a> ReadAddr for CStrPtr<'a> {
type Target = CString;
type Error = Errno;
fn read<M: MemoryAccess>(&self, memory: &M) -> Result<Self::Target, Self::Error> {
memory.read_cstring(self.0)
}
}
impl<'a> FromToRaw for Option<CStrPtr<'a>> {
fn from_raw(raw: u64) -> Self {
Option::<Addr<'a, u8>>::from_raw(raw).map(CStrPtr)
}
fn into_raw(self) -> u64 {
self.map(|p| p.0).into_raw()
}
}
impl<'a> Displayable for CStrPtr<'a> {
fn fmt<M: MemoryAccess>(
&self,
memory: &M,
_outputs: bool,
f: &mut fmt::Formatter,
) -> fmt::Result {
match self.read(memory) {
Ok(s) => {
// Only display the first 64 bytes.
if s.as_bytes().len() > 64 {
let mut bytes = s.into_bytes();
bytes.truncate(64);
let s = unsafe { CString::from_vec_unchecked(bytes) };
write!(f, "{} -> {:?}...", self.0, s)
} else {
write!(f, "{} -> {:?}", self.0, s)
}
}
Err(e) => write!(f, "{} -> <{}>", self.0, e),
}
}
}
impl<'a> Displayable for Option<CStrPtr<'a>> {
fn fmt<M: MemoryAccess>(
&self,
memory: &M,
outputs: bool,
f: &mut fmt::Formatter,
) -> fmt::Result {
match self {
None => f.write_str("NULL"),
Some(addr) => Displayable::fmt(addr, memory, outputs, f),
}
}
}
/// A pointer to a `Path` that resides in the target address space.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct PathPtr<'a>(CStrPtr<'a>);
impl<'a> PathPtr<'a> {
/// Creates the `PathPtr` from a raw pointer. Returns `None` if the given
/// pointer is NULL.
pub fn from_ptr(r: *const libc::c_char) -> Option<Self> {
CStrPtr::from_ptr(r).map(PathPtr)
}
}
impl<'a> ReadAddr for PathPtr<'a> {
type Target = PathBuf;
type Error = Errno;
fn read<M: MemoryAccess>(&self, memory: &M) -> Result<Self::Target, Self::Error> {
let path = PathBuf::from(OsString::from_vec(self.0.read(memory)?.into_bytes()));
Ok(path)
}
}
impl<'a> FromToRaw for Option<PathPtr<'a>> {
fn from_raw(raw: u64) -> Self {
Option::<CStrPtr<'a>>::from_raw(raw).map(PathPtr)
}
fn into_raw(self) -> u64 {
self.map(|p| p.0).into_raw()
}
}
impl<'a> Displayable for Option<PathPtr<'a>> {
fn fmt<M: MemoryAccess>(
&self,
memory: &M,
_outputs: bool,
f: &mut fmt::Formatter,
) -> fmt::Result {
match self {
None => f.write_str("NULL"),
Some(addr) => match addr.read(memory) {
Ok(s) => write!(f, "{} -> {:?}", addr.0.0, s),
Err(e) => write!(f, "{} -> <{}>", addr.0.0, e),
},
}
}
}
/// A pointer to a `stat` buffer.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct StatPtr<'a>(pub AddrMut<'a, libc::stat>);
impl<'a> StatPtr<'a> {
/// Creates the `StatPtr` from a raw pointer. Returns `None` if the given
/// pointer is NULL.
pub fn from_ptr(r: *const libc::stat) -> Option<Self> {
AddrMut::from_ptr(r as *const libc::stat).map(StatPtr)
}
}
impl<'a> ReadAddr for StatPtr<'a> {
type Target = libc::stat;
type Error = Errno;
fn read<M: MemoryAccess>(&self, memory: &M) -> Result<Self::Target, Self::Error> {
memory.read_value(self.0)
}
}
impl<'a> FromToRaw for Option<StatPtr<'a>> {
fn from_raw(raw: u64) -> Self {
Option::<AddrMut<'a, libc::stat>>::from_raw(raw).map(StatPtr)
}
fn into_raw(self) -> u64 {
self.map(|p| p.0).into_raw()
}
}
impl<'a> Displayable for Option<StatPtr<'a>> {
fn fmt<M: MemoryAccess>(
&self,
memory: &M,
outputs: bool,
f: &mut fmt::Formatter,
) -> fmt::Result {
match self {
None => f.write_str("NULL"),
Some(addr) => {
if outputs {
match addr.read(memory) {
Ok(stat) => {
// Print st_mode the same way strace does.
let sflag = SFlag::from_bits_truncate(stat.st_mode);
let mode = Mode::from_bits_truncate(stat.st_mode);
write!(
f,
"{} -> {{st_mode={:?} | 0{:o}, st_size={}, ...}}",
addr.0, sflag, mode, stat.st_size
)
}
Err(e) => write!(f, "{} -> <{}>", addr.0, e),
}
} else {
// Just print the address when not displaying outputs.
fmt::Display::fmt(&addr.0, f)
}
}
}
}
}
/// A pointer to a `statx` buffer.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct StatxPtr<'a>(pub AddrMut<'a, libc::statx>);
impl<'a> StatxPtr<'a> {
/// Creates the `StatxPtr` from a raw pointer. Returns `None` if the given
/// pointer is NULL.
pub fn from_ptr(r: *const libc::statx) -> Option<Self> {
AddrMut::from_ptr(r as *const libc::statx).map(StatxPtr)
}
}
impl<'a> ReadAddr for StatxPtr<'a> {
type Target = libc::statx;
type Error = Errno;
fn read<M: MemoryAccess>(&self, memory: &M) -> Result<Self::Target, Self::Error> {
memory.read_value(self.0)
}
}
impl<'a> FromToRaw for Option<StatxPtr<'a>> {
fn from_raw(raw: u64) -> Self {
Option::<AddrMut<'a, libc::statx>>::from_raw(raw).map(StatxPtr)
}
fn into_raw(self) -> u64 {
self.map(|p| p.0).into_raw()
}
}
impl<'a> Displayable for Option<StatxPtr<'a>> {
fn fmt<M: MemoryAccess>(
&self,
memory: &M,
outputs: bool,
f: &mut fmt::Formatter,
) -> fmt::Result {
match self {
None => f.write_str("NULL"),
Some(addr) => {
if outputs {
match addr.read(memory) {
Ok(stat) => {
// Print mode the same way strace does.
let sflag = SFlag::from_bits_truncate(stat.stx_mode.into());
let mode = Mode::from_bits_truncate(stat.stx_mode.into());
write!(
f,
"{} -> {{st_mode={:?} | 0{:o}, st_size={}, ...}}",
addr.0, sflag, mode, stat.stx_size
)
}
Err(e) => write!(f, "{} -> <{}>", addr.0, e),
}
} else {
// Just print the address when not displaying outputs.
fmt::Display::fmt(&addr.0, f)
}
}
}
}
}
bitflags::bitflags! {
/// stx_mask from statx, see linux/stat.h
#[derive(Serialize, Deserialize)]
pub struct StatxMask: u32 {
/// has stx_type
const STATX_TYPE = 0x1;
/// has stx_mode
const STATX_MODE = 0x2;
/// has stx_nlink
const STATX_NLINK = 0x4;
/// has stx_uid
const STATX_UID = 0x8;
/// has stx_gid
const STATX_GID = 0x10;
/// has stx_atime
const STATX_ATIME = 0x20;
/// has stx_mtime
const STATX_MTIME = 0x40;
/// has stx_ctime
const STATX_CTIME = 0x80;
/// has stx_ino
const STATX_INO = 0x100;
/// has stx_size
const STATX_SIZE = 0x200;
/// has stx_blocks
const STATX_BLOCKS = 0x400;
/// compatible with `stat'.
const STATX_BASIC_STATS = 0x7ff;
/// has stx_btime
const STATX_BTIME = 0x800;
/// has stx_mnt_id
const STATX_MNT_ID = 0x1000;
/// reserved
const STATX_RESERVED = 0x80000000;
}
}
impl Default for StatxMask {
fn default() -> Self {
StatxMask::STATX_BASIC_STATS
}
}
impl FromToRaw for StatxMask {
fn from_raw(raw: u64) -> Self {
StatxMask::from_bits_truncate(raw as u32)
}
fn into_raw(self) -> u64 {
self.bits() as u64
}
}
impl Displayable for StatxMask {
fn fmt<M: MemoryAccess>(
&self,
_memory: &M,
_outputs: bool,
f: &mut fmt::Formatter,
) -> fmt::Result {
fmt::Display::fmt(&self.bits(), f)
}
}
command_enum! {
/// The argument pairs of `arch_prctl(2)`.
#[allow(missing_docs)]
pub enum ArchPrctlCmd<'a>: libc::c_int {
ARCH_SET_GS(u64) = 0x1001,
ARCH_SET_FS(u64) = 0x1002,
ARCH_GET_FS(Option<Addr<'a, libc::c_ulong>>) = 0x1003,
ARCH_GET_GS(Option<Addr<'a, libc::c_ulong>>) = 0x1004,
ARCH_GET_CPUID(Option<Addr<'a, libc::c_ulong>>) = 0x1011,
ARCH_SET_CPUID(u64) = 0x1012,
}
}
const_enum! {
/// Directives that tell `lseek` and `lseek64` what the offset is relative
/// to.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum Whence: i32 {
/// Specifies an offset relative to the start of the file.
SEEK_SET,
/// Specifies an offset relative to the current file location.
SEEK_CUR,
/// Specifies an offset relative to the end of the file.
SEEK_END,
/// Specifies an offset relative to the next location in the file
/// greater than or equal to offset that contains some data. If offset
/// points to some data, then the file offset is set to offset.
SEEK_DATA,
/// Specify an offset relative to the next hole in the file greater than
/// or equal to offset. If offset points into the middle of a hole, then
/// the file offset should be set to offset. If there is no hole past
/// offset, then the file offset should be adjusted to the end of the
/// file (i.e., there is an implicit hole at the end of any file).
SEEK_HOLE,
}
}
const_enum! {
/// A clock ID. See the definitions in `kernel/include/uapi/linux/time.h`.
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ClockId: i32 {
CLOCK_REALTIME,
CLOCK_MONOTONIC,
CLOCK_PROCESS_CPUTIME_ID,
CLOCK_THREAD_CPUTIME_ID,
CLOCK_MONOTONIC_RAW,
CLOCK_REALTIME_COARSE,
CLOCK_MONOTONIC_COARSE,
CLOCK_BOOTTIME,
CLOCK_REALTIME_ALARM,
CLOCK_BOOTTIME_ALARM,
}
}