1#[cfg(test)]
10mod tests;
11
12use core::iter::FusedIterator;
13use core::mem::{offset_of, size_of};
14use std::path::{Path, PathBuf};
15
16use nom::bytes::complete::{tag as nom_tag, take as nom_take};
17use nom::combinator::peek as nom_peek;
18use nom::number::complete::{u32 as nom_u32, u8 as nom_u8};
19use nom::number::Endianness;
20use nom::sequence::{preceded as nom_preceded, terminated as nom_terminated};
21
22use crate::utils::{map_file, CStringTable, HashTableIter};
23use crate::{CacheProvider, Error, Result};
24
25static CACHE_FILE_PATH: &str = "/etc/ld.so.cache";
26
27static MAGIC: &[u8] = b"glibc-ld.so.cache1.1";
28
29#[repr(C)]
30struct Header {
31 magic: [u8; 20],
32 lib_count: u32,
33 string_table_size: u32,
34 flags: u8,
35 flags_padding: [u8; 3],
36 extension_offset: u32,
37 unused: [u32; 3],
38}
39
40#[repr(C)]
41struct Entry {
42 flags: u32,
43 key: u32,
44 value: u32,
45 os_version: u32,
46 hw_cap: u64,
47}
48
49#[derive(Debug)]
50struct ParsedHeader {
51 byte_order: Endianness,
52 lib_count: usize,
53 string_table_size: usize,
54}
55
56impl ParsedHeader {
57 fn parse(path: &Path, bytes: &[u8]) -> Result<Self> {
58 let (_input, flags) =
59 Self::parse_flags(bytes).map_err(|r| Error::from_nom_parse(r, bytes, path))?;
60
61 let byte_order = match flags & 0b11 {
62 0 => Endianness::Native,
63 1 => {
64 let r = nom::error::make_error(bytes, nom::error::ErrorKind::IsA);
65 return Err(Error::from_nom_parse(nom::Err::Error(r), bytes, path));
66 }
67 2 => Endianness::Little,
68 3 => Endianness::Big,
69 4..=u8::MAX => unreachable!(),
70 };
71
72 let (_input, fields) = Self::parse_fields(bytes, byte_order)
73 .map_err(|r| Error::from_nom_parse(r, bytes, path))?;
74
75 let result = Self {
76 byte_order,
77 lib_count: usize::try_from(fields.0)?,
78 string_table_size: usize::try_from(fields.1)?,
79 };
80
81 result.validate(path, bytes).map(|()| result)
82 }
83
84 fn parse_flags(bytes: &[u8]) -> nom::IResult<&[u8], u8> {
85 use nom::Parser;
86
87 nom_preceded(nom_take(offset_of!(Header, flags)), nom_u8).parse(bytes)
88 }
89
90 fn parse_fields(bytes: &[u8], byte_order: Endianness) -> nom::IResult<&[u8], (u32, u32)> {
91 use nom::Parser;
92
93 (
94 nom_preceded(nom_tag(MAGIC), nom_u32(byte_order)),
95 nom_terminated(
96 nom_u32(byte_order),
97 nom_take(size_of::<Header>().saturating_sub(offset_of!(Header, flags))),
98 ),
99 )
100 .parse(bytes)
101 }
102
103 fn validate(&self, path: &Path, bytes: &[u8]) -> Result<()> {
104 use nom::Parser;
105
106 let max_lib_count = bytes.len().saturating_sub(size_of::<Header>()) / size_of::<Entry>();
107
108 let max_string_table_size = bytes
109 .len()
110 .saturating_sub(size_of::<Header>())
111 .saturating_sub(self.lib_count.saturating_mul(size_of::<Entry>()));
112
113 if self.lib_count > max_lib_count || self.string_table_size > max_string_table_size {
114 let r = nom::error::make_error(bytes, nom::error::ErrorKind::TooLarge);
115 return Err(Error::from_nom_parse(nom::Err::Error(r), bytes, path));
116 }
117
118 let min_size = size_of::<Header>()
119 .saturating_add(size_of::<Entry>().saturating_mul(self.lib_count))
120 .saturating_add(self.string_table_size);
121
122 nom_peek(nom_take(min_size))
123 .parse(bytes)
124 .map(|_| ())
125 .map_err(|r| Error::from_nom_parse(r, bytes, path))
126 }
127}
128
129#[derive(Debug)]
135pub struct Cache {
136 path: PathBuf,
137 map: memmap2::Mmap,
138 byte_order: Endianness,
139 lib_count: usize,
140}
141
142impl Cache {
143 pub fn load_default() -> Result<Self> {
145 Self::load(CACHE_FILE_PATH)
146 }
147
148 pub fn load(path: impl AsRef<Path>) -> Result<Self> {
150 let path = path.as_ref();
151 let map = map_file(path)?;
152 let header = ParsedHeader::parse(path, &map)?;
153
154 Ok(Self {
155 path: path.into(),
156 map,
157 byte_order: header.byte_order,
158 lib_count: header.lib_count,
159 })
160 }
161
162 pub fn iter(&self) -> Result<impl FusedIterator<Item = Result<crate::Entry<'_>>> + '_> {
164 Ok(HashTableIter::<{ size_of::<Entry>() }, _>::new(
165 &self.map[self.hash_table_range()],
166 CStringTable::new(&self.map[self.string_table_range()], &self.path),
167 self.next_hash_table_entry_parser(),
168 ))
169 }
170
171 fn hash_table_range(&self) -> core::ops::Range<usize> {
172 let size = self.lib_count.saturating_mul(size_of::<Entry>());
173 size_of::<Header>()..size_of::<Header>().saturating_add(size)
174 }
175
176 fn string_table_range(&self) -> core::ops::Range<usize> {
177 0..self.map.len()
178 }
179
180 fn next_hash_table_entry_parser(
181 &self,
182 ) -> impl nom::Parser<&[u8], Output = (u32, u32), Error = nom::error::Error<&[u8]>> {
183 (
184 nom_preceded(nom_take(offset_of!(Entry, key)), nom_u32(self.byte_order)),
185 nom_terminated(
186 nom_u32(self.byte_order),
187 nom_take(size_of::<Entry>().saturating_sub(offset_of!(Entry, os_version))),
188 ),
189 )
190 }
191}
192
193impl CacheProvider for Cache {
194 fn entries_iter<'cache>(
195 &'cache self,
196 ) -> Result<Box<dyn FusedIterator<Item = Result<crate::Entry<'cache>>> + 'cache>> {
197 let iter = self.iter()?;
198 Ok(Box::new(iter))
199 }
200}