1use std::io::BufRead;
2use std::{fmt, fs, io, num, path};
3
4use super::regex;
5
6use super::ProcessId;
7
8#[derive(Clone)]
10pub struct Permissions {
11 read: bool,
12 write: bool,
13 execute: bool,
14 shared: bool,
15}
16
17impl Permissions {
18 pub fn read(&self) -> bool {
19 self.read
20 }
21 pub fn write(&self) -> bool {
22 self.write
23 }
24 pub fn execute(&self) -> bool {
25 self.execute
26 }
27 pub fn shared(&self) -> bool {
29 self.shared
30 }
31 pub fn private(&self) -> bool {
33 !self.shared
34 }
35}
36
37impl fmt::Debug for Permissions {
38 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
39 let read = if self.read { 'r' } else { '-' };
40 let write = if self.write { 'w' } else { '-' };
41 let execute = if self.execute { 'x' } else { '-' };
42 let shared = if self.shared { 's' } else { 'p' };
43 write!(f, "{}{}{}{}", read, write, execute, shared)
44 }
45}
46
47#[derive(Clone, Debug)]
49pub struct MemoryRegion {
50 pub start_address: usize,
52 pub end_address: usize,
53 pub permissions: Permissions,
54 pub offset: usize,
56 pub dev_major: u32,
59 pub dev_minor: u32,
62 pub inode: Option<u64>,
64 pub pathname: Option<String>,
66}
67
68#[derive(Debug)]
69pub enum Error {
70 ParseFailed(String),
71 IoError(io::Error),
72}
73
74impl From<io::Error> for Error {
75 fn from(e: io::Error) -> Self {
76 Error::IoError(e)
77 }
78}
79
80impl From<num::ParseIntError> for Error {
81 fn from(err: num::ParseIntError) -> Self {
82 Error::ParseFailed(format!("{:?}", err))
83 }
84}
85
86pub struct MemoryRegionIter<R: io::Read> {
88 reader: io::BufReader<R>,
89 buf: String,
90 finished: bool,
91}
92
93impl<R: io::Read> MemoryRegionIter<R> {
94 fn parse_buf(&mut self) -> Result<MemoryRegion, Error> {
95 lazy_static! {
96 static ref RE: regex::Regex = regex::Regex::new(
97 "^([0-9a-f]+)-([0-9a-f]+) ([a-z-]{4}) ([0-9a-f]+) ([0-9a-f]+):([0-9a-f]+) (\\d+) +(.*)$")
98 .expect("regex is valid");
99 }
100
101 let trimmed = self.buf.trim_end_matches('\n');
102 let captures = RE
103 .captures(trimmed)
104 .ok_or_else(|| Error::ParseFailed(trimmed.to_string()))?;
105 let start_address = usize::from_str_radix(&captures[1], 16)?;
106 let end_address = usize::from_str_radix(&captures[2], 16)?;
107 let p_bytes = &captures[3].as_bytes();
108 let permissions = Permissions {
109 read: p_bytes[0] == b'r',
110 write: p_bytes[1] == b'w',
111 execute: p_bytes[2] == b'x',
112 shared: p_bytes[3] == b's',
113 };
114 let offset = usize::from_str_radix(&captures[4], 16)?;
115 let dev_major = u32::from_str_radix(&captures[5], 16)?;
116 let dev_minor = u32::from_str_radix(&captures[6], 16)?;
117 let inode = u64::from_str_radix(&captures[7], 10)?;
118 let inode = if inode == 0 { None } else { Some(inode) };
119 let pathname = &captures[8];
120 let pathname = if pathname.is_empty() {
121 None
122 } else {
123 Some(String::from(pathname))
124 };
125
126 Ok(MemoryRegion {
127 start_address,
128 end_address,
129 permissions,
130 offset,
131 dev_major,
132 dev_minor,
133 inode,
134 pathname,
135 })
136 }
137}
138
139impl<R: io::Read> Iterator for MemoryRegionIter<R> {
140 type Item = Result<MemoryRegion, Error>;
141
142 fn next(&mut self) -> Option<Self::Item> {
143 if self.finished {
144 return None;
145 }
146
147 self.buf.clear();
148
149 match self.reader.read_line(&mut self.buf) {
150 Ok(bytes_read) => {
151 if bytes_read == 0 {
152 self.finished = true;
153 None
154 } else {
155 Some(self.parse_buf())
156 }
157 }
158 Err(e) => {
159 self.finished = true;
162 Some(Err(Error::IoError(e)))
163 }
164 }
165 }
166}
167
168pub fn iter_mappings(pid: ProcessId) -> io::Result<MemoryRegionIter<fs::File>> {
172 let path = path::PathBuf::from(match pid {
173 ProcessId::SelfPid => String::from("/proc/self/maps"),
174 ProcessId::Num(n) => format!("/proc/{}/maps", n),
175 });
176
177 let reader = fs::File::open(path)?;
178
179 Ok(iter_mapping_reader(reader))
180}
181
182fn iter_mapping_reader<R: io::Read>(reader: R) -> MemoryRegionIter<R> {
183 let buf_reader = io::BufReader::new(reader);
184
185 MemoryRegionIter {
186 reader: buf_reader,
187 buf: String::new(),
188 finished: false,
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 extern crate libc;
195
196 use super::*;
197 use std::path;
198
199 #[test]
200 fn sample_maps_file() {
201 let path = path::Path::new("src/test-data/obexd-map.txt");
202 let reader = fs::File::open(path).unwrap();
203
204 let mappings = iter_mapping_reader(reader)
205 .map(|r| r.unwrap())
206 .collect::<Vec<MemoryRegion>>();
207
208 assert_eq!(80, mappings.len());
209
210 let first = &mappings[0];
211 assert_eq!(94858956906496, first.start_address);
212 assert_eq!(94858957352960, first.end_address);
213 assert!(first.permissions.read());
214 assert!(!first.permissions.write());
215 assert!(first.permissions.execute());
216 assert!(!first.permissions.shared());
217 assert!(first.permissions.private());
218 assert_eq!(0, first.offset);
219 assert_eq!(8, first.dev_major);
220 assert_eq!(3, first.dev_minor);
221 assert_eq!(Some(58219319), first.inode);
222 assert_eq!(
223 "/usr/libexec/bluetooth/obexd",
224 first.pathname.as_ref().unwrap()
225 );
226
227 let anon = &mappings[12];
228 assert_eq!(0, anon.offset);
229 assert_eq!(0, anon.dev_major);
230 assert_eq!(0, anon.dev_minor);
231 assert_eq!(None, anon.inode);
232 assert_eq!(None, anon.pathname.as_ref());
233
234 let libpthread = &mappings[50];
236 assert!(!libpthread.permissions.read());
237 assert!(!libpthread.permissions.write());
238 assert!(!libpthread.permissions.execute());
239 assert!(!libpthread.permissions.shared());
240 assert!(libpthread.permissions.private());
241 assert_eq!(106496, libpthread.offset);
242
243 let gconv = &mappings[71];
245 assert!(gconv.permissions.read());
246 assert!(!gconv.permissions.write());
247 assert!(!gconv.permissions.execute());
248 assert!(gconv.permissions.shared());
249 assert!(!gconv.permissions.private());
250 assert_eq!(3, gconv.dev_minor);
251 assert_eq!(Some(55705632), gconv.inode);
252
253 let last = &mappings[79];
254 assert_eq!(18446744073699065856, last.start_address);
255 assert_eq!(18446744073699069952, last.end_address);
256 assert!(last.permissions.read());
257 assert!(!last.permissions.write());
258 assert!(last.permissions.execute());
259 assert!(!last.permissions.shared());
260 assert!(last.permissions.private());
261 assert_eq!(0, last.offset);
262 assert_eq!(0, last.dev_major);
263 assert_eq!(0, last.dev_minor);
264 assert_eq!(None, last.inode);
265 assert_eq!("[vsyscall]", last.pathname.as_ref().unwrap());
266 }
267
268 #[test]
269 fn maps_file_with_hex_device_nodes() {
270 let path = path::Path::new("src/test-data/issue-1.txt");
271 let reader = fs::File::open(path).unwrap();
272 let mappings = iter_mapping_reader(reader)
273 .map(|r| r.unwrap())
274 .collect::<Vec<MemoryRegion>>();
275
276 assert_eq!(518, mappings.len());
277
278 let hex_dev_node_map = &mappings[0];
279
280 assert_eq!(0xFD, hex_dev_node_map.dev_major);
281 assert_eq!(0x00, hex_dev_node_map.dev_minor);
282 }
283
284 #[test]
285 fn read_self() {
286 assert_eq!(
288 1,
289 iter_mappings(ProcessId::SelfPid)
290 .unwrap()
291 .filter_map(|r| r.unwrap().pathname.clone())
292 .filter(|p| p == "[stack]")
293 .count()
294 )
295 }
296
297 #[test]
298 fn read_own_pid() {
299 let pid = unsafe { libc::getpid() as u32 };
300
301 assert_eq!(
303 1,
304 iter_mappings(ProcessId::Num(pid))
305 .unwrap()
306 .filter_map(|r| r.unwrap().pathname.clone())
307 .filter(|p| p == "[stack]")
308 .count()
309 )
310 }
311
312 #[test]
313 fn permissions_debug() {
314 let p1 = Permissions {
315 read: false,
316 write: false,
317 execute: false,
318 shared: false,
319 };
320
321 assert_eq!("---p", format!("{:?}", p1));
322
323 let p2 = Permissions {
324 read: true,
325 write: true,
326 execute: true,
327 shared: true,
328 };
329
330 assert_eq!("rwxs", format!("{:?}", p2));
331 }
332
333 #[test]
334 fn iteration_aborts_on_error() {
335 let path = path::Path::new("src/test-data/issue-1.txt");
336 let file_reader = fs::File::open(path).unwrap();
337 let error_reader = ErroringReader {
338 reader: file_reader,
339 bytes_until_error: 500,
340 };
341
342 let mappings = iter_mapping_reader(error_reader)
344 .filter_map(|r| r.ok())
345 .collect::<Vec<MemoryRegion>>();
346
347 assert_eq!(5, mappings.len());
349 }
350
351 struct ErroringReader<R: io::Read> {
352 reader: R,
353 bytes_until_error: usize,
354 }
355
356 impl<R: io::Read> io::Read for ErroringReader<R> {
357 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
358 if self.bytes_until_error == 0 {
359 return Err(io::Error::from(io::ErrorKind::Other));
360 }
361
362 let bytes = self.reader.read(buf)?;
363
364 let remaining_bytes = self.bytes_until_error.saturating_sub(bytes);
365
366 let res = if remaining_bytes == 0 {
367 Ok(self.bytes_until_error)
369 } else {
370 Ok(bytes)
371 };
372
373 self.bytes_until_error = remaining_bytes;
374
375 res
376 }
377 }
378}