1#[macro_use]
2extern crate nom;
3
4use std::{error, fmt, result};
5use std::io::Read;
6use std::ops::{Deref, DerefMut};
7use libc::pid_t;
8use std::fs::File;
9use std::path::PathBuf;
10
11
12pub type Result<T> = result::Result<T, Error>;
13
14#[derive(Debug)]
15pub enum Error {
16 InvalidInput,
17 IoError,
18}
19
20impl fmt::Display for Error {
21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 match *self {
23 Error::InvalidInput => write!(f, "Invalid input"),
24 Error::IoError => write!(f, "IO Error"),
25 }
26 }
27}
28
29impl error::Error for Error {
30 fn description(&self) -> &str {
31 match *self {
32 Error::InvalidInput => "Incorrect input data for memory mapping",
33 Error::IoError => "I/O error",
34 }
35 }
36
37 fn cause(&self) -> Option<&dyn error::Error> {
38 None
39 }
40}
41
42impl From<std::io::Error> for Error {
43 fn from(_: std::io::Error) -> Error {
44 Error::IoError
45 }
46}
47
48impl <'a>From<Error> for nom::Err<&'a str> {
49 fn from(_: Error) -> nom::Err<&'a str> {
50 nom::Err::Incomplete(nom::Needed::Unknown)
51 }
52}
53
54impl <T>From<nom::Err<T>> for Error {
55 fn from(_: nom::Err<T>) -> Error {
56 Error::InvalidInput
57 }
58}
59
60#[derive(PartialEq, Debug)]
62pub enum Privacy {
63 Shared,
65 Private,
67}
68
69#[derive(Debug)]
71pub struct Permissions {
72 pub readable: bool,
73 pub writable: bool,
74 pub executable: bool,
75 pub privacy: Privacy,
76}
77
78impl Permissions {
79 fn from_str(input: &str) -> Result<Self> {
80 if input.len() != 4 {
81 return Err(Error::InvalidInput)
82 }
83 let input = input.as_bytes();
84 let readable = input[0] == b'r';
85 let writable = input[1] == b'w';
86 let executable = input[2] == b'x';
87
88 let privacy = match input[3] {
89 b'p' => Privacy::Private,
90 b's' => Privacy::Shared,
91 _e => return Err(Error::InvalidInput),
92 };
93
94 Ok(Permissions {
95 readable: readable,
96 writable: writable,
97 executable: executable,
98 privacy: privacy,
99 })
100 }
101}
102
103#[derive(PartialEq, Debug)]
106pub enum Path {
107 MappedFile(String),
109 Stack,
111 ThreadStack(usize),
113 Vdso,
115 Heap,
117 Vvar,
119 Vsyscall,
121}
122
123impl From<&str> for Path {
124 fn from(input: &str) -> Self {
125 match input {
127 "[heap]" => Path::Heap,
128 "[stack]" => Path::Stack,
129 "[vdso]" => Path::Vdso,
130 "[vvar]" => Path::Vvar,
131 "[vsyscall]" => Path::Vvar,
132 s => Path::MappedFile(s.to_string())
133 }
134 }
135}
136
137
138#[derive(Debug)]
141pub struct Map {
142 pub base: usize,
144 pub ceiling: usize,
146 pub perms: Permissions,
148 pub offset: usize,
150 pub dev_major: usize,
152 pub dev_minor: usize,
154 pub inode: usize,
156 pub pathname: Path,
158}
159
160impl Map {
161 pub fn size_of_mapping(&self) -> usize {
163 self.ceiling - self.base
164 }
165
166 fn from_str(input: &str) -> Result<Map> {
167 let res = parse_map(input);
168
169 match res {
170 Ok(val) => Ok(val.1),
171 Err(_e) => Err(Error::InvalidInput),
172 }
173 }
174}
175
176named!(parse_map<&str, Map>,
177 do_parse!(
178 base: map_res!(take_until!("-"), |b| usize::from_str_radix(b, 16)) >>
179 take!(1) >>
180 ceiling: map_res!(take_until!(" "), |b| usize::from_str_radix(b, 16)) >>
181 take!(1) >>
182 perms: map_res!(take_until!(" "), |b| Permissions::from_str(b)) >>
183 take!(1) >>
184 offset: map_res!(take_until!(" "), |b| usize::from_str_radix(b, 16)) >>
185 take!(1) >>
186 dev_major: map_res!(take_until!(":"), |b| usize::from_str_radix(b, 16)) >>
187 take!(1) >>
188 dev_minor: map_res!(take_until!(" "), |b| usize::from_str_radix(b, 16)) >>
189 take!(1) >>
190 inode: map_res!(take_until!(" "), |b| usize::from_str_radix(b, 16)) >>
191 take!(1) >>
192 pathname: opt!(take_until!("\n")) >>
193 (Map {
194 base: base,
195 ceiling: ceiling,
196 perms: perms,
197 offset: offset,
198 dev_major: dev_major,
199 dev_minor: dev_minor,
200 inode: inode,
201 pathname: pathname.unwrap().trim().into(),
202 })
203 )
204);
205
206
207#[derive(Debug)]
209pub struct Mappings(Vec<Map>);
210
211impl Mappings {
212 pub fn from_pid(pid: pid_t) -> Result<Mappings> {
214 let path = format!("/proc/{}/maps", pid);
215 let mut file = File::open(path)?;
216 let mut input = String::new();
217 file.read_to_string(&mut input)?;
218
219 let mut res: Vec<Map> = Vec::new();
220 let mut iter: Vec<&str> = input.split("\n").collect();
221 iter.pop();
222 for s in iter {
223 let map = Map::from_str(&format!("{}\n", &s))?;
224 res.push(map);
225 }
226
227 Ok(Mappings(res))
228 }
229
230 pub fn from_path(path: &mut PathBuf) -> Result<Mappings> {
231 path.push("maps");
232 let mut file = File::open(path)?;
233 let mut input = String::new();
234 file.read_to_string(&mut input)?;
235
236 let mut res: Vec<Map> = Vec::new();
237 let mut iter: Vec<&str> = input.split("\n").collect();
238 iter.pop();
239 for s in iter {
240 let map = Map::from_str(&format!("{}\n", &s))?;
241 res.push(map);
242 }
243
244 Ok(Mappings(res))
245 }
246}
247
248impl Deref for Mappings {
249 type Target = Vec<Map>;
250
251 fn deref(&self) -> &Self::Target {
252 &self.0
253 }
254}
255
256impl DerefMut for Mappings {
257 fn deref_mut(&mut self) -> &mut Vec<Map> { &mut self.0 }
258}
259
260#[cfg(test)]
261mod tests {
262 use crate::*;
263
264 #[test]
266 fn test_parse_map() {
267 let input = "55e8d4153000-55e8d416f000 r-xp 00000000 08:02 9175073 /bin/dash\n";
268 let res = parse_map(input).unwrap().1;
269 println!("{:?}", res);
270 assert_eq!(res.base, 94458478931968);
271 assert_eq!(res.ceiling, 94458479046656);
272 assert_eq!(res.offset, 0);
273 assert_eq!(res.dev_major, 8);
274 assert_eq!(res.dev_minor, 2);
275 assert_eq!(res.inode, 152522867);
276 assert_eq!(res.pathname, Path::MappedFile("/bin/dash".to_string()));
277 }
278
279 #[test]
280 fn test_map_path_types() {
281 let input = "7fffdb68b000-7fffdb6ac000 rw-p 00000000 00:00 0 [stack]\n";
282 let res = Map::from_str(input).unwrap();
283 assert_eq!(res.pathname, Path::Stack);
284
285 let input = "7fffdb7a7000-7fffdb7aa000 r--p 00000000 00:00 0 [vvar]\n";
286 let res = Map::from_str(input).unwrap();
287 assert_eq!(res.pathname, Path::Vvar);
288
289 let input = "7fffdb7aa000-7fffdb7ac000 r-xp 00000000 00:00 0 [vdso]\n";
290 let res = Map::from_str(input).unwrap();
291 assert_eq!(res.pathname, Path::Vdso);
292 }
293
294 #[test]
295 fn test_map_from_str_invalid_inputs() {
296 let input = "7fffdb68b000-7fffdb6ac000 rw- 00000000 00:00 0 [stack]\n";
298 let res = Map::from_str(input);
299 assert!(res.is_err());
300
301 let input = "7fffdb7a7000-7fffdb7aa000 r--p 00000000 0000 0 [vvar]\n";
303 let res = Map::from_str(input);
304 assert!(res.is_err());
305
306 let input = "7fffdb7aa0007fffdb7ac000 r-xp 00000000 00:00 0 [vdso]\n";
308 let res = Map::from_str(input);
309 assert!(res.is_err());
310 }
311
312 #[test]
313 fn test_size_of_mapping() {
314 let input = "55e8d4153000-55e8d416f000 r-xp 00000000 08:02 9175073 /bin/dash\n";
315 let res = Map::from_str(input).unwrap();
316 assert_eq!(res.size_of_mapping(), 114688usize);
317 }
318
319 #[test]
320 fn test_map_perms() {
321 let input = "55e8d4153000-55e8d416f000 r-xp 00000000 08:02 9175073 /bin/dash\n";
322 let res = Map::from_str(input).unwrap();
323 println!("{:?}", res);
324 assert!(res.perms.readable);
325 assert!(!res.perms.writable);
326 assert!(res.perms.executable);
327 assert_eq!(res.perms.privacy, Privacy::Private);
328 }
329
330 #[test]
342 fn test_map_from_str() {
343 let mut file = File::open("tests/example.txt").unwrap();
344 let mut input = String::new();
345 file.read_to_string(&mut input).unwrap();
346
347 let mut iter: Vec<&str> = input.split("\n").collect();
348 iter.pop();
349 for s in iter {
350 let map = Map::from_str(&format!("{}\n", &s)).unwrap();
351 println!("{:?}", map);
352 }
353 }
354
355 #[test]
356 fn test_maps() {
357 use std::process::id;
358 let m = Mappings::from_pid(id() as pid_t);
359 assert!(m.is_ok());
360 println!("{:?}", m);
361 }
362}