1use libc;
2use std;
3use std::fs::File;
4use std::io::Read;
5use std::path::{Path, PathBuf};
6
7use MapRangeImpl;
8
9pub type Pid = libc::pid_t;
10
11#[derive(Debug, Clone, PartialEq)]
16pub struct MapRange {
17 range_start: usize,
18 range_end: usize,
19 pub offset: usize,
20 pub dev: String,
21 pub flags: String,
22 pub inode: usize,
23 pathname: Option<PathBuf>,
24}
25
26impl MapRangeImpl for MapRange {
27 fn size(&self) -> usize {
28 self.range_end - self.range_start
29 }
30 fn start(&self) -> usize {
31 self.range_start
32 }
33 fn filename(&self) -> Option<&Path> {
34 self.pathname.as_deref()
35 }
36 fn is_exec(&self) -> bool {
37 &self.flags[2..3] == "x"
38 }
39 fn is_write(&self) -> bool {
40 &self.flags[1..2] == "w"
41 }
42 fn is_read(&self) -> bool {
43 &self.flags[0..1] == "r"
44 }
45}
46
47pub fn get_process_maps(pid: Pid) -> std::io::Result<Vec<MapRange>> {
51 let maps_file = format!("/proc/{}/maps", pid);
53 let mut file = File::open(maps_file)?;
54
55 let metadata = file.metadata()?;
57 if metadata.len() > 0x10000000 {
58 return Err(std::io::Error::from_raw_os_error(libc::EFBIG));
59 }
60
61 let mut contents = String::new();
62 file.read_to_string(&mut contents)?;
63 parse_proc_maps(&contents)
64}
65
66fn parse_proc_maps(contents: &str) -> std::io::Result<Vec<MapRange>> {
67 let mut vec: Vec<MapRange> = Vec::new();
68 for line in contents.split("\n") {
69 let mut split = line.split_whitespace();
70 let range = match split.next() {
71 None => break,
72 Some(s) => s,
73 };
74
75 let mut range_split = range.split("-");
76 let range_start = match range_split.next() {
77 None => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
78 Some(s) => match usize::from_str_radix(s, 16) {
79 Err(_) => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
80 Ok(i) => i,
81 },
82 };
83 let range_end = match range_split.next() {
84 None => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
85 Some(s) => match usize::from_str_radix(s, 16) {
86 Err(_) => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
87 Ok(i) => i,
88 },
89 };
90 if range_split.next().is_some() || range_start >= range_end {
91 return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
92 }
93
94 let flags = match split.next() {
95 None => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
96 Some(s) if s.len() < 3 => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
97 Some(s) => s.to_string(),
98 };
99 let offset = match split.next() {
100 None => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
101 Some(s) => match usize::from_str_radix(s, 16) {
102 Err(_) => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
103 Ok(i) if i & 0xfff != 0 => {
105 return Err(std::io::Error::from_raw_os_error(libc::EINVAL));
106 }
107 Ok(i) => i,
108 },
109 };
110 let dev = match split.next() {
111 None => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
112 Some(s) => s.to_string(),
113 };
114 let inode = match split.next() {
115 None => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
116 Some(s) => match usize::from_str_radix(s, 10) {
117 Err(_) => return Err(std::io::Error::from_raw_os_error(libc::EINVAL)),
118 Ok(i) => i,
119 },
120 };
121 let pathname = match Some(split.collect::<Vec<&str>>().join(" ")).filter(|x| !x.is_empty())
122 {
123 Some(s) => Some(PathBuf::from(s)),
124 None => None,
125 };
126
127 vec.push(MapRange {
128 range_start,
129 range_end,
130 offset,
131 dev,
132 flags,
133 inode,
134 pathname,
135 });
136 }
137 Ok(vec)
138}
139
140#[test]
141fn test_parse_maps() {
142 let contents = include_str!("../ci/testdata/map.txt");
143 let vec = parse_proc_maps(contents).unwrap();
144 let expected = vec![
145 MapRange {
146 range_start: 0x00400000,
147 range_end: 0x00507000,
148 offset: 0,
149 dev: "00:14".to_string(),
150 flags: "r-xp".to_string(),
151 inode: 205736,
152 pathname: Some(PathBuf::from("/usr/bin/fish")),
153 },
154 MapRange {
155 range_start: 0x00708000,
156 range_end: 0x0070a000,
157 offset: 0,
158 dev: "00:00".to_string(),
159 flags: "rw-p".to_string(),
160 inode: 0,
161 pathname: None,
162 },
163 MapRange {
164 range_start: 0x0178c000,
165 range_end: 0x01849000,
166 offset: 0,
167 dev: "00:00".to_string(),
168 flags: "rw-p".to_string(),
169 inode: 0,
170 pathname: Some(PathBuf::from("[heap]")),
171 },
172 MapRange {
173 range_start: 0x7f438050,
174 range_end: 0x7f438060,
175 offset: 0,
176 dev: "fd:01".to_string(),
177 flags: "r--p".to_string(),
178 inode: 59034409,
179 pathname: Some(PathBuf::from(
180 "/usr/lib/x86_64-linux-gnu/libgmodule-2.0.so.0.4200.6 (deleted)",
181 )),
182 },
183 ];
184 assert_eq!(vec, expected);
185
186 assert_eq!(super::maps_contain_addr(0x00400000, &vec), true);
188 assert_eq!(super::maps_contain_addr(0x00300000, &vec), false);
189}
190
191#[test]
192fn test_contains_addr_range() {
193 let vec = vec![
194 MapRange {
195 range_start: 0x00400000,
196 range_end: 0x00500000,
197 offset: 0,
198 dev: "00:14".to_string(),
199 flags: "r-xp".to_string(),
200 inode: 205736,
201 pathname: Some(PathBuf::from("/usr/bin/fish")),
202 },
203 MapRange {
204 range_start: 0x00600000,
205 range_end: 0x00700000,
206 offset: 0,
207 dev: "00:14".to_string(),
208 flags: "r--p".to_string(),
209 inode: 205736,
210 pathname: Some(PathBuf::from("/usr/bin/fish")),
211 },
212 MapRange {
213 range_start: 0x00700000,
214 range_end: 0x00800000,
215 offset: 0,
216 dev: "00:14".to_string(),
217 flags: "r--p".to_string(),
218 inode: 205736,
219 pathname: Some(PathBuf::from("/usr/bin/fish")),
220 },
221 ];
222
223 assert_eq!(super::maps_contain_addr_range(0x00400000, 0x1, &vec), true);
224 assert_eq!(
225 super::maps_contain_addr_range(0x00400000, 0x100000, &vec),
226 true
227 );
228 assert_eq!(
229 super::maps_contain_addr_range(0x00500000 - 1, 1, &vec),
230 true
231 );
232 assert_eq!(
233 super::maps_contain_addr_range(0x00600000, 0x100001, &vec),
234 true
235 );
236 assert_eq!(
237 super::maps_contain_addr_range(0x00600000, 0x200000, &vec),
238 true
239 );
240
241 assert_eq!(
242 super::maps_contain_addr_range(0x00400000, 0x100001, &vec),
243 false
244 );
245 assert_eq!(
246 super::maps_contain_addr_range(0x00400000, usize::MAX, &vec),
247 false
248 );
249 assert_eq!(super::maps_contain_addr_range(0x00400000, 0, &vec), false);
250 assert_eq!(
251 super::maps_contain_addr_range(0x00400000, 0x00200000, &vec),
252 false
253 );
254 assert_eq!(
255 super::maps_contain_addr_range(0x00400000, 0x00200001, &vec),
256 false
257 );
258}