Skip to content

Commit 37fa931

Browse files
committed
Add process_vm_readv and process_vm_writev
1 parent 59d6b3c commit 37fa931

File tree

3 files changed

+129
-0
lines changed

3 files changed

+129
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
2929
- On Linux and Android, added support for receiving `PTRACE_O_TRACESYSGOOD`
3030
events from `wait` and `waitpid` using `WaitStatus::PtraceSyscall`
3131
([#566](https://github.com/nix-rust/nix/pull/566)).
32+
- Added `nix::sys::uio::{process_vm_readv, process_vm_writev}` on Linux
33+
([#568](https://github.com/nix-rust/nix/pull/568))
3234

3335
### Changed
3436
- Changed `ioctl!(write ...)` into `ioctl!(write_ptr ...)` and `ioctl!(write_int ..)` variants

src/sys/uio.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,85 @@ pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize>{
5656
Errno::result(res).map(|r| r as usize)
5757
}
5858

59+
/// A slice of memory in a remote process, starting at address `base`
60+
/// and consisting of `len` bytes.
61+
///
62+
/// This is the same underlying C structure as [`IoVec`](struct.IoVec.html),
63+
/// except that it refers to memory in some other process, and is
64+
/// therefore not represented in Rust by an actual slice as IoVec is. It
65+
/// is used with [`process_vm_readv`](fn.process_vm_readv.html)
66+
/// and [`process_vm_writev`](fn.process_vm_writev.html).
67+
#[cfg(target_os = "linux")]
68+
#[repr(C)]
69+
pub struct RemoteIoVec {
70+
/// The starting address of this slice (`iov_base`).
71+
pub base: usize,
72+
/// The number of bytes in this slice (`iov_len`).
73+
pub len: usize,
74+
}
75+
76+
/// Write data directly to another process's virtual memory
77+
/// (see [`process_vm_writev`(2)]).
78+
///
79+
/// `local_iov` is a list of [`IoVec`]s containing the data to be written,
80+
/// and `remote_iov` is a list of [`RemoteIoVec`]s identifying where the
81+
/// data should be written in the target process. On success, returns the
82+
/// number of bytes written, which will always be a whole
83+
/// number of remote_iov chunks.
84+
///
85+
/// This requires the same permissions as debugging the process using
86+
/// [ptrace]: you must either be a privileged process (with
87+
/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
88+
/// target process and the OS must have unprivileged debugging enabled.
89+
///
90+
/// This function is only available on Linux.
91+
///
92+
/// [`process_vm_writev`(2)]: http://man7.org/linux/man-pages/man2/process_vm_writev.2.html
93+
/// [ptrace]: ../ptrace/index.html
94+
/// [`IoVec`]: struct.IoVec.html
95+
/// [`RemoteIoVec`]: struct.RemoteIoVec.html
96+
#[cfg(target_os = "linux")]
97+
pub fn process_vm_writev(pid: ::unistd::Pid, local_iov: &[IoVec<&[u8]>], remote_iov: &[RemoteIoVec]) -> Result<usize> {
98+
let res = unsafe {
99+
libc::process_vm_writev(pid.into(),
100+
local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong,
101+
remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0)
102+
};
103+
104+
Errno::result(res).map(|r| r as usize)
105+
}
106+
107+
/// Read data directly from another process's virtual memory
108+
/// (see [`process_vm_readv`(2)]).
109+
///
110+
/// `local_iov` is a list of [`IoVec`]s containing the buffer to copy
111+
/// data into, and `remote_iov` is a list of [`RemoteIoVec`]s identifying
112+
/// where the source data is in the target process. On success,
113+
/// returns the number of bytes written, which will always be a whole
114+
/// number of remote_iov chunks.
115+
///
116+
/// This requires the same permissions as debugging the process using
117+
/// [ptrace]: you must either be a privileged process (with
118+
/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
119+
/// target process and the OS must have unprivileged debugging enabled.
120+
///
121+
/// This function is only available on Linux.
122+
///
123+
/// [`process_vm_readv`(2)]: http://man7.org/linux/man-pages/man2/process_vm_readv.2.html
124+
/// [ptrace]: ../ptrace/index.html
125+
/// [`IoVec`]: struct.IoVec.html
126+
/// [`RemoteIoVec`]: struct.RemoteIoVec.html
127+
#[cfg(any(target_os = "linux"))]
128+
pub fn process_vm_readv(pid: ::unistd::Pid, local_iov: &[IoVec<&mut [u8]>], remote_iov: &[RemoteIoVec]) -> Result<usize> {
129+
let res = unsafe {
130+
libc::process_vm_readv(pid.into(),
131+
local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong,
132+
remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0)
133+
};
134+
135+
Errno::result(res).map(|r| r as usize)
136+
}
137+
59138
#[repr(C)]
60139
pub struct IoVec<T>(libc::iovec, PhantomData<T>);
61140

test/sys/test_uio.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,51 @@ fn test_preadv() {
190190
let all = buffers.concat();
191191
assert_eq!(all, expected);
192192
}
193+
194+
#[test]
195+
#[cfg(target_os = "linux")]
196+
// FIXME: qemu-user doesn't implement process_vm_readv/writev on most arches
197+
#[cfg_attr(not(any(target_arch = "x86", target_arch = "x86_64")), ignore)]
198+
fn test_process_vm_readv() {
199+
use nix::unistd::ForkResult::*;
200+
use nix::sys::signal::*;
201+
use nix::sys::wait::*;
202+
use std::str;
203+
204+
#[allow(unused_variables)]
205+
let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
206+
207+
let (r, w) = pipe().unwrap();
208+
match fork() {
209+
Ok(Parent { child }) => {
210+
close(w).unwrap();
211+
let mut msg = vec![0u8; 32];
212+
let bytes_read = read(r, &mut msg).unwrap();
213+
msg.truncate(bytes_read);
214+
close(r).unwrap();
215+
216+
let ptr: usize = str::from_utf8(&msg).unwrap().parse().unwrap();
217+
let remote_iov = RemoteIoVec { base: ptr, len: 4 };
218+
let mut buf = vec![0u8; 4];
219+
220+
let ret = process_vm_readv(child,
221+
&[IoVec::from_mut_slice(&mut buf)],
222+
&[remote_iov]);
223+
224+
kill(child, SIGTERM).unwrap();
225+
waitpid(child, None).unwrap();
226+
227+
assert_eq!(Ok(4), ret);
228+
assert_eq!(&buf, b"test");
229+
},
230+
Ok(Child) => {
231+
let _ = close(r);
232+
let s = String::from("test");
233+
let msg = format!("{}", s.as_bytes().as_ptr() as usize);
234+
let _ = write(w, msg.as_bytes());
235+
let _ = close(w);
236+
loop { let _ = pause(); }
237+
},
238+
Err(_) => panic!("fork failed")
239+
}
240+
}

0 commit comments

Comments
 (0)