/rust/registry/src/index.crates.io-1949cf8c6b5b557f/region-3.0.2/src/os/linux.rs
Line | Count | Source |
1 | | use crate::{Error, Protection, Region, Result}; |
2 | | use std::fs; |
3 | | |
4 | | pub struct QueryIter { |
5 | | proc_maps: String, |
6 | | upper_bound: usize, |
7 | | offset: usize, |
8 | | } |
9 | | |
10 | | impl QueryIter { |
11 | 0 | pub fn new(origin: *const (), size: usize) -> Result<Self> { |
12 | | // Do not use a buffered reader here to avoid multiple read(2) calls to the |
13 | | // proc file, ensuring a consistent snapshot of the virtual memory. |
14 | 0 | let proc_maps = fs::read_to_string("/proc/self/maps").map_err(Error::SystemCall)?; |
15 | | |
16 | 0 | Ok(Self { |
17 | 0 | proc_maps, |
18 | 0 | upper_bound: (origin as usize).saturating_add(size), |
19 | 0 | offset: 0, |
20 | 0 | }) |
21 | 0 | } |
22 | | |
23 | 0 | pub fn upper_bound(&self) -> usize { |
24 | 0 | self.upper_bound |
25 | 0 | } |
26 | | } |
27 | | |
28 | | impl Iterator for QueryIter { |
29 | | type Item = Result<Region>; |
30 | | |
31 | 0 | fn next(&mut self) -> Option<Self::Item> { |
32 | 0 | let (line, _) = self.proc_maps.get(self.offset..)?.split_once('\n')?; |
33 | 0 | self.offset += line.len() + 1; |
34 | | |
35 | 0 | Some(parse_procfs_line(line).ok_or_else(|| Error::ProcfsInput(line.to_string()))) |
36 | 0 | } |
37 | | } |
38 | | |
39 | | /// Parses flags from /proc/[pid]/maps (e.g 'r--p'). |
40 | 0 | fn parse_procfs_flags(protection: &str) -> (Protection, bool) { |
41 | | const MAPPINGS: &[Protection] = &[Protection::READ, Protection::WRITE, Protection::EXECUTE]; |
42 | | |
43 | 0 | let result = protection |
44 | 0 | .chars() |
45 | 0 | .zip(MAPPINGS.iter()) |
46 | 0 | .filter(|(c, _)| *c != '-') |
47 | 0 | .fold(Protection::NONE, |acc, (_, prot)| acc | *prot); |
48 | | |
49 | 0 | (result, protection.ends_with('s')) |
50 | 0 | } |
51 | | |
52 | | /// Parses a line from /proc/[pid]/maps. |
53 | 0 | fn parse_procfs_line(input: &str) -> Option<Region> { |
54 | 0 | let mut parts = input.split_whitespace(); |
55 | 0 | let mut memory = parts |
56 | 0 | .next()? |
57 | 0 | .split('-') |
58 | 0 | .filter_map(|value| usize::from_str_radix(value, 16).ok()); |
59 | 0 | let (lower, upper) = (memory.next()?, memory.next()?); |
60 | | |
61 | 0 | let flags = parts.next()?; |
62 | 0 | let (protection, shared) = parse_procfs_flags(flags); |
63 | | |
64 | 0 | Some(Region { |
65 | 0 | base: lower as *const _, |
66 | 0 | protection, |
67 | 0 | shared, |
68 | 0 | size: upper - lower, |
69 | 0 | ..Region::default() |
70 | 0 | }) |
71 | 0 | } |
72 | | |
73 | | #[cfg(test)] |
74 | | mod tests { |
75 | | use super::{parse_procfs_flags, parse_procfs_line}; |
76 | | use crate::Protection; |
77 | | |
78 | | #[test] |
79 | | fn procfs_flags_are_parsed() { |
80 | | let rwx = Protection::READ_WRITE_EXECUTE; |
81 | | |
82 | | assert_eq!(parse_procfs_flags("r--s"), (Protection::READ, true)); |
83 | | assert_eq!(parse_procfs_flags("rw-p"), (Protection::READ_WRITE, false)); |
84 | | assert_eq!(parse_procfs_flags("r-xs"), (Protection::READ_EXECUTE, true)); |
85 | | assert_eq!(parse_procfs_flags("rwxs"), (rwx, true)); |
86 | | assert_eq!(parse_procfs_flags("--xp"), (Protection::EXECUTE, false)); |
87 | | assert_eq!(parse_procfs_flags("-w-s"), (Protection::WRITE, true)); |
88 | | } |
89 | | |
90 | | #[test] |
91 | | fn procfs_regions_are_parsed() { |
92 | | let line = "00400000-00409000 r-xs 00000000 08:00 16088 /usr/bin/head"; |
93 | | let region = parse_procfs_line(line).unwrap(); |
94 | | |
95 | | assert_eq!(region.as_ptr(), 0x40_0000 as *mut ()); |
96 | | assert_eq!(region.protection(), Protection::READ_EXECUTE); |
97 | | assert_eq!(region.len(), 0x9000); |
98 | | assert!(!region.is_guarded()); |
99 | | assert!(region.is_shared()); |
100 | | } |
101 | | } |