Skip to content

Commit 487cd87

Browse files
committed
Add process_vm_readv and process_vm_writev
1 parent 07e6c2f commit 487cd87

File tree

3 files changed

+126
-0
lines changed

3 files changed

+126
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
2424
and nix::Error::UnsupportedOperation}`
2525
([#614](https://github.com/nix-rust/nix/pull/614))
2626
- Added `cfmakeraw`, `cfsetspeed`, and `tcgetsid`. ([#527](https://github.com/nix-rust/nix/pull/527))
27+
- Added `nix::sys::uio::{process_vm_readv, process_vm_writev}` on Linux
28+
([#568](https://github.com/nix-rust/nix/pull/568))
2729

2830
### Changed
2931
- Changed `ioctl!(write ...)` to take argument by value instead as pointer.

src/sys/uio.rs

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#![allow(improper_ctypes)]
33

44
use {Errno, Result};
5+
use unistd::Pid;
56
use libc::{self, c_int, c_void, size_t, off_t};
67
use std::marker::PhantomData;
78
use std::os::unix::io::RawFd;
@@ -56,6 +57,84 @@ pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize>{
5657
Errno::result(res).map(|r| r as usize)
5758
}
5859

60+
/// A slice of memory in a remote process, starting at address `base`
61+
/// and consisting of `len` bytes.
62+
///
63+
/// This is the same underlying C structure as [`IoVec`](struct.IoVec.html),
64+
/// except that it refers to memory in some other process, and is
65+
/// therefore not represented in Rust by an actual slice as IoVec is. It
66+
/// is used with [`process_vm_readv`](fn.process_vm_readv.html)
67+
/// and [`process_vm_writev`](fn.process_vm_writev.html).
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: 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: 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: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,3 +190,48 @@ 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+
let (r, w) = pipe().unwrap();
205+
match fork() {
206+
Ok(Parent { child }) => {
207+
close(w).unwrap();
208+
let mut msg = vec![0u8; 32];
209+
let bytes_read = read(r, &mut msg).unwrap();
210+
msg.truncate(bytes_read);
211+
close(r).unwrap();
212+
213+
let ptr: usize = str::from_utf8(&msg).unwrap().parse().unwrap();
214+
let remote_iov = RemoteIoVec { base: ptr, len: 4 };
215+
let mut buf = vec![0u8; 4];
216+
217+
let ret = process_vm_readv(child,
218+
&[IoVec::from_mut_slice(&mut buf)],
219+
&[remote_iov]);
220+
221+
kill(child, SIGTERM).unwrap();
222+
waitpid(child, None).unwrap();
223+
224+
assert_eq!(Ok(4), ret);
225+
assert_eq!(&buf, b"test");
226+
},
227+
Ok(Child) => {
228+
close(r).unwrap();
229+
let s = String::from("test");
230+
let msg = format!("{}", s.as_bytes().as_ptr() as usize);
231+
write(w, msg.as_bytes()).unwrap();
232+
close(w).unwrap();
233+
pause().unwrap();
234+
},
235+
Err(_) => panic!("fork failed")
236+
}
237+
}

0 commit comments

Comments
 (0)