forked from tikv/pprof-rs
-
Notifications
You must be signed in to change notification settings - Fork 2
/
addr_validate.rs
122 lines (98 loc) · 3.03 KB
/
addr_validate.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use std::{cell::RefCell, mem::size_of};
use nix::{
errno::Errno,
unistd::{close, read, write},
};
thread_local! {
static MEM_VALIDATE_PIPE: RefCell<[i32; 2]> = RefCell::new([-1, -1]);
}
#[inline]
#[cfg(target_os = "linux")]
fn create_pipe() -> nix::Result<(i32, i32)> {
use nix::fcntl::OFlag;
use nix::unistd::pipe2;
pipe2(OFlag::O_CLOEXEC | OFlag::O_NONBLOCK)
}
#[inline]
#[cfg(target_os = "macos")]
fn create_pipe() -> nix::Result<(i32, i32)> {
use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag};
use nix::unistd::pipe;
use std::os::unix::io::RawFd;
fn set_flags(fd: RawFd) -> nix::Result<()> {
let mut flags = FdFlag::from_bits(fcntl(fd, FcntlArg::F_GETFD)?).unwrap();
flags |= FdFlag::FD_CLOEXEC;
fcntl(fd, FcntlArg::F_SETFD(flags))?;
let mut flags = OFlag::from_bits(fcntl(fd, FcntlArg::F_GETFL)?).unwrap();
flags |= OFlag::O_NONBLOCK;
fcntl(fd, FcntlArg::F_SETFL(flags))?;
Ok(())
}
let (read_fd, write_fd) = pipe()?;
set_flags(read_fd)?;
set_flags(write_fd)?;
Ok((read_fd, write_fd))
}
fn open_pipe() -> nix::Result<()> {
MEM_VALIDATE_PIPE.with(|pipes| {
let mut pipes = pipes.borrow_mut();
// ignore the result
let _ = close(pipes[0]);
let _ = close(pipes[1]);
let (read_fd, write_fd) = create_pipe()?;
pipes[0] = read_fd;
pipes[1] = write_fd;
Ok(())
})
}
pub fn validate(addr: *const libc::c_void) -> bool {
const CHECK_LENGTH: usize = 2 * size_of::<*const libc::c_void>() / size_of::<u8>();
// read data in the pipe
let valid_read = MEM_VALIDATE_PIPE.with(|pipes| {
let pipes = pipes.borrow();
loop {
let mut buf = [0u8; CHECK_LENGTH];
match read(pipes[0], &mut buf) {
Ok(bytes) => break bytes > 0,
Err(_err @ Errno::EINTR) => continue,
Err(_err @ Errno::EAGAIN) => break true,
Err(_) => break false,
}
}
});
if !valid_read && open_pipe().is_err() {
return false;
}
MEM_VALIDATE_PIPE.with(|pipes| {
let pipes = pipes.borrow();
loop {
let buf = unsafe { std::slice::from_raw_parts(addr as *const u8, CHECK_LENGTH) };
match write(pipes[1], buf) {
Ok(bytes) => break bytes > 0,
Err(_err @ Errno::EINTR) => continue,
Err(_) => break false,
}
}
})
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn validate_stack() {
let i = 0;
assert_eq!(validate(&i as *const _ as *const libc::c_void), true);
}
#[test]
fn validate_heap() {
let vec = vec![0; 1000];
for i in vec.iter() {
assert_eq!(validate(i as *const _ as *const libc::c_void), true);
}
}
#[test]
fn failed_validate() {
assert_eq!(validate(0 as *const libc::c_void), false);
assert_eq!(validate((-1 as i32) as usize as *const libc::c_void), false)
}
}