1 //! Parse the Linux vDSO.
2 //!
3 //! The following code is transliterated from
4 //! tools/testing/selftests/vDSO/parse_vdso.c in Linux 5.11, which is licensed
5 //! with Creative Commons Zero License, version 1.0,
6 //! available at <https://creativecommons.org/publicdomain/zero/1.0/legalcode>
7 //!
8 //! # Safety
9 //!
10 //! Parsing the vDSO involves a lot of raw pointer manipulation. This
11 //! implementation follows Linux's reference implementation, and adds several
12 //! additional safety checks.
13 #![allow(unsafe_code)]
14
15 use super::c;
16 use super::elf::*;
17 use crate::ffi::CStr;
18 use crate::utils::check_raw_pointer;
19 use core::ffi::c_void;
20 use core::mem::size_of;
21 use core::ptr::{null, null_mut};
22
23 pub(super) struct Vdso {
24 // Load information
25 load_addr: *const Elf_Ehdr,
26 load_end: *const c_void, // the end of the `PT_LOAD` segment
27 pv_offset: usize, // recorded paddr - recorded vaddr
28
29 // Symbol table
30 symtab: *const Elf_Sym,
31 symstrings: *const u8,
32 bucket: *const u32,
33 chain: *const u32,
34 nbucket: u32,
35 //nchain: u32,
36
37 // Version table
38 versym: *const u16,
39 verdef: *const Elf_Verdef,
40 }
41
42 // Straight from the ELF specification.
elf_hash(name: &CStr) -> u3243 fn elf_hash(name: &CStr) -> u32 {
44 let mut h: u32 = 0;
45 for b in name.to_bytes() {
46 h = (h << 4).wrapping_add(u32::from(*b));
47 let g = h & 0xf000_0000;
48 if g != 0 {
49 h ^= g >> 24;
50 }
51 h &= !g;
52 }
53 h
54 }
55
56 /// Create a `Vdso` value by parsing the vDSO at the `sysinfo_ehdr` address.
init_from_sysinfo_ehdr() -> Option<Vdso>57 fn init_from_sysinfo_ehdr() -> Option<Vdso> {
58 // Safety: the auxv initialization code does extensive checks to ensure
59 // that the value we get really is an `AT_SYSINFO_EHDR` value from the
60 // kernel.
61 unsafe {
62 let hdr = super::param::auxv::sysinfo_ehdr();
63
64 // If the platform doesn't provide a `AT_SYSINFO_EHDR`, we can't locate
65 // the vDSO.
66 if hdr.is_null() {
67 return None;
68 }
69
70 let mut vdso = Vdso {
71 load_addr: hdr,
72 load_end: hdr.cast(),
73 pv_offset: 0,
74 symtab: null(),
75 symstrings: null(),
76 bucket: null(),
77 chain: null(),
78 nbucket: 0,
79 //nchain: 0,
80 versym: null(),
81 verdef: null(),
82 };
83
84 let hdr = &*hdr;
85 let pt = check_raw_pointer::<Elf_Phdr>(vdso.base_plus(hdr.e_phoff)? as *mut _)?.as_ptr();
86 let mut dyn_: *const Elf_Dyn = null();
87 let mut num_dyn = 0;
88
89 // We need two things from the segment table: the load offset
90 // and the dynamic table.
91 let mut found_vaddr = false;
92 for i in 0..hdr.e_phnum {
93 let phdr = &*pt.add(i as usize);
94 if phdr.p_flags & PF_W != 0 {
95 // Don't trust any vDSO that claims to be loading writable
96 // segments into memory.
97 return None;
98 }
99 if phdr.p_type == PT_LOAD && !found_vaddr {
100 // The segment should be readable and executable, because it
101 // contains the symbol table and the function bodies.
102 if phdr.p_flags & (PF_R | PF_X) != (PF_R | PF_X) {
103 return None;
104 }
105 found_vaddr = true;
106 vdso.load_end = vdso.base_plus(phdr.p_offset.checked_add(phdr.p_memsz)?)?;
107 vdso.pv_offset = phdr.p_offset.wrapping_sub(phdr.p_vaddr);
108 } else if phdr.p_type == PT_DYNAMIC {
109 // If `p_offset` is zero, it's more likely that we're looking at memory
110 // that has been zeroed than that the kernel has somehow aliased the
111 // `Ehdr` and the `Elf_Dyn` array.
112 if phdr.p_offset < size_of::<Elf_Ehdr>() {
113 return None;
114 }
115
116 dyn_ = check_raw_pointer::<Elf_Dyn>(vdso.base_plus(phdr.p_offset)? as *mut _)?
117 .as_ptr();
118 num_dyn = phdr.p_memsz / size_of::<Elf_Dyn>();
119 } else if phdr.p_type == PT_INTERP || phdr.p_type == PT_GNU_RELRO {
120 // Don't trust any ELF image that has an "interpreter" or that uses
121 // RELRO, which is likely to be a user ELF image rather and not the
122 // kernel vDSO.
123 return None;
124 }
125 }
126
127 if !found_vaddr || dyn_.is_null() {
128 return None; // Failed
129 }
130
131 // Fish out the useful bits of the dynamic table.
132 let mut hash: *const u32 = null();
133 vdso.symstrings = null();
134 vdso.symtab = null();
135 vdso.versym = null();
136 vdso.verdef = null();
137 let mut i = 0;
138 loop {
139 if i == num_dyn {
140 return None;
141 }
142 let d = &*dyn_.add(i);
143 match d.d_tag {
144 DT_STRTAB => {
145 vdso.symstrings =
146 check_raw_pointer::<u8>(vdso.addr_from_elf(d.d_val)? as *mut _)?.as_ptr();
147 }
148 DT_SYMTAB => {
149 vdso.symtab =
150 check_raw_pointer::<Elf_Sym>(vdso.addr_from_elf(d.d_val)? as *mut _)?
151 .as_ptr();
152 }
153 DT_HASH => {
154 hash =
155 check_raw_pointer::<u32>(vdso.addr_from_elf(d.d_val)? as *mut _)?.as_ptr();
156 }
157 DT_VERSYM => {
158 vdso.versym =
159 check_raw_pointer::<u16>(vdso.addr_from_elf(d.d_val)? as *mut _)?.as_ptr();
160 }
161 DT_VERDEF => {
162 vdso.verdef =
163 check_raw_pointer::<Elf_Verdef>(vdso.addr_from_elf(d.d_val)? as *mut _)?
164 .as_ptr();
165 }
166 DT_SYMENT => {
167 if d.d_val != size_of::<Elf_Sym>() {
168 return None; // Failed
169 }
170 }
171 DT_NULL => break,
172 _ => {}
173 }
174 i = i.checked_add(1)?;
175 }
176 // The upstream code checks `symstrings`, `symtab`, and `hash` for null;
177 // here, `check_raw_pointer` has already done that.
178
179 if vdso.verdef.is_null() {
180 vdso.versym = null();
181 }
182
183 // Parse the hash table header.
184 vdso.nbucket = *hash.add(0);
185 //vdso.nchain = *hash.add(1);
186 vdso.bucket = hash.add(2);
187 vdso.chain = hash.add(vdso.nbucket as usize + 2);
188
189 // That's all we need.
190 Some(vdso)
191 }
192 }
193
194 impl Vdso {
195 /// Parse the vDSO.
196 ///
197 /// Returns `None` if the vDSO can't be located or if it doesn't conform
198 /// to our expectations.
199 #[inline]
new() -> Option<Self>200 pub(super) fn new() -> Option<Self> {
201 init_from_sysinfo_ehdr()
202 }
203
204 /// Check the version for a symbol.
205 ///
206 /// # Safety
207 ///
208 /// The raw pointers inside `self` must be valid.
match_version(&self, mut ver: u16, name: &CStr, hash: u32) -> bool209 unsafe fn match_version(&self, mut ver: u16, name: &CStr, hash: u32) -> bool {
210 // This is a helper function to check if the version indexed by
211 // ver matches name (which hashes to hash).
212 //
213 // The version definition table is a mess, and I don't know how
214 // to do this in better than linear time without allocating memory
215 // to build an index. I also don't know why the table has
216 // variable size entries in the first place.
217 //
218 // For added fun, I can't find a comprehensible specification of how
219 // to parse all the weird flags in the table.
220 //
221 // So I just parse the whole table every time.
222
223 // First step: find the version definition
224 ver &= 0x7fff; // Apparently bit 15 means "hidden"
225 let mut def = self.verdef;
226 loop {
227 if (*def).vd_version != VER_DEF_CURRENT {
228 return false; // Failed
229 }
230
231 if ((*def).vd_flags & VER_FLG_BASE) == 0 && ((*def).vd_ndx & 0x7fff) == ver {
232 break;
233 }
234
235 if (*def).vd_next == 0 {
236 return false; // No definition.
237 }
238
239 def = def
240 .cast::<u8>()
241 .add((*def).vd_next as usize)
242 .cast::<Elf_Verdef>();
243 }
244
245 // Now figure out whether it matches.
246 let aux = &*(def.cast::<u8>())
247 .add((*def).vd_aux as usize)
248 .cast::<Elf_Verdaux>();
249 (*def).vd_hash == hash
250 && (name == CStr::from_ptr(self.symstrings.add(aux.vda_name as usize).cast()))
251 }
252
253 /// Look up a symbol in the vDSO.
sym(&self, version: &CStr, name: &CStr) -> *mut c::c_void254 pub(super) fn sym(&self, version: &CStr, name: &CStr) -> *mut c::c_void {
255 let ver_hash = elf_hash(version);
256 let name_hash = elf_hash(name);
257
258 // Safety: The pointers in `self` must be valid.
259 unsafe {
260 let mut chain = *self.bucket.add((name_hash % self.nbucket) as usize);
261
262 while chain != STN_UNDEF {
263 let sym = &*self.symtab.add(chain as usize);
264
265 // Check for a defined global or weak function w/ right name.
266 //
267 // The reference parser in Linux's parse_vdso.c requires
268 // symbols to have type `STT_FUNC`, but on powerpc64, the vDSO
269 // uses `STT_NOTYPE`, so allow that too.
270 if (ELF_ST_TYPE(sym.st_info) != STT_FUNC &&
271 ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
272 || (ELF_ST_BIND(sym.st_info) != STB_GLOBAL
273 && ELF_ST_BIND(sym.st_info) != STB_WEAK)
274 || sym.st_shndx == SHN_UNDEF
275 || sym.st_shndx == SHN_ABS
276 || ELF_ST_VISIBILITY(sym.st_other) != STV_DEFAULT
277 || (name != CStr::from_ptr(self.symstrings.add(sym.st_name as usize).cast()))
278 // Check symbol version.
279 || (!self.versym.is_null()
280 && !self.match_version(*self.versym.add(chain as usize), version, ver_hash))
281 {
282 chain = *self.chain.add(chain as usize);
283 continue;
284 }
285
286 let sum = self.addr_from_elf(sym.st_value).unwrap();
287 assert!(
288 sum as usize >= self.load_addr as usize
289 && sum as usize <= self.load_end as usize
290 );
291 return sum as *mut c::c_void;
292 }
293 }
294
295 null_mut()
296 }
297
298 /// Add the given address to the vDSO base address.
base_plus(&self, offset: usize) -> Option<*const c_void>299 unsafe fn base_plus(&self, offset: usize) -> Option<*const c_void> {
300 // Check for overflow.
301 let _ = (self.load_addr as usize).checked_add(offset)?;
302 // Add the offset to the base.
303 Some(self.load_addr.cast::<u8>().add(offset).cast())
304 }
305
306 /// Translate an ELF-address-space address into a usable virtual address.
addr_from_elf(&self, elf_addr: usize) -> Option<*const c_void>307 unsafe fn addr_from_elf(&self, elf_addr: usize) -> Option<*const c_void> {
308 self.base_plus(elf_addr.wrapping_add(self.pv_offset))
309 }
310 }
311