Coverage Report

Created: 2026-02-14 07:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}