pprof/
addr_validate.rs
1use std::{
2 mem::size_of,
3 sync::atomic::{AtomicI32, Ordering},
4};
5
6use nix::{
7 errno::Errno,
8 unistd::{close, read, write},
9};
10
11struct Pipes {
12 read_fd: AtomicI32,
13 write_fd: AtomicI32,
14}
15
16static MEM_VALIDATE_PIPE: Pipes = Pipes {
17 read_fd: AtomicI32::new(-1),
18 write_fd: AtomicI32::new(-1),
19};
20
21#[inline]
22#[cfg(any(target_os = "android", target_os = "linux"))]
23fn create_pipe() -> nix::Result<(i32, i32)> {
24 use nix::fcntl::OFlag;
25 use nix::unistd::pipe2;
26
27 pipe2(OFlag::O_CLOEXEC | OFlag::O_NONBLOCK)
28}
29
30#[inline]
31#[cfg(any(target_os = "macos", target_os = "freebsd"))]
32fn create_pipe() -> nix::Result<(i32, i32)> {
33 use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag};
34 use nix::unistd::pipe;
35 use std::os::unix::io::RawFd;
36
37 fn set_flags(fd: RawFd) -> nix::Result<()> {
38 let mut flags = FdFlag::from_bits(fcntl(fd, FcntlArg::F_GETFD)?).unwrap();
39 flags |= FdFlag::FD_CLOEXEC;
40 fcntl(fd, FcntlArg::F_SETFD(flags))?;
41 let mut flags = OFlag::from_bits(fcntl(fd, FcntlArg::F_GETFL)?).unwrap();
42 flags |= OFlag::O_NONBLOCK;
43 fcntl(fd, FcntlArg::F_SETFL(flags))?;
44 Ok(())
45 }
46
47 let (read_fd, write_fd) = pipe()?;
48 set_flags(read_fd)?;
49 set_flags(write_fd)?;
50 Ok((read_fd, write_fd))
51}
52
53fn open_pipe() -> nix::Result<()> {
54 let _ = close(MEM_VALIDATE_PIPE.read_fd.load(Ordering::SeqCst));
56 let _ = close(MEM_VALIDATE_PIPE.write_fd.load(Ordering::SeqCst));
57
58 let (read_fd, write_fd) = create_pipe()?;
59
60 MEM_VALIDATE_PIPE.read_fd.store(read_fd, Ordering::SeqCst);
61 MEM_VALIDATE_PIPE.write_fd.store(write_fd, Ordering::SeqCst);
62
63 Ok(())
64}
65
66pub fn validate(addr: *const libc::c_void) -> bool {
72 if addr.is_null() {
75 return false;
76 }
77
78 const CHECK_LENGTH: usize = 2 * size_of::<*const libc::c_void>() / size_of::<u8>();
79
80 let read_fd = MEM_VALIDATE_PIPE.read_fd.load(Ordering::SeqCst);
82 let valid_read = loop {
83 let mut buf = [0u8; CHECK_LENGTH];
84
85 match read(read_fd, &mut buf) {
86 Ok(bytes) => break bytes > 0,
87 Err(_err @ Errno::EINTR) => continue,
88 Err(_err @ Errno::EAGAIN) => break true,
89 Err(_) => break false,
90 }
91 };
92
93 if !valid_read && open_pipe().is_err() {
94 return false;
95 }
96
97 let write_fd = MEM_VALIDATE_PIPE.write_fd.load(Ordering::SeqCst);
98 loop {
99 let buf = unsafe { std::slice::from_raw_parts(addr as *const u8, CHECK_LENGTH) };
100
101 match write(write_fd, buf) {
102 Ok(bytes) => break bytes > 0,
103 Err(_err @ Errno::EINTR) => continue,
104 Err(_) => break false,
105 }
106 }
107}
108
109#[cfg(test)]
110mod test {
111 use super::*;
112
113 #[test]
114 fn validate_stack() {
115 let i = 0;
116
117 assert!(validate(&i as *const _ as *const libc::c_void));
118 }
119
120 #[test]
121 fn validate_heap() {
122 let vec = vec![0; 1000];
123
124 for i in vec.iter() {
125 assert!(validate(i as *const _ as *const libc::c_void));
126 }
127 }
128
129 #[test]
130 fn failed_validate() {
131 assert!(!validate(std::ptr::null::<libc::c_void>()));
132 assert!(!validate(-1_i32 as usize as *const libc::c_void))
133 }
134}