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 crate::ffi::CStr;
17 use crate::utils::check_raw_pointer;
18 use core::ffi::c_void;
19 use core::mem::size_of;
20 use core::ptr::{null, null_mut};
21 use linux_raw_sys::elf::*;
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
110                 // at memory that has been zeroed than that the kernel has
111                 // somehow aliased the `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
121                 // that uses RELRO, which is likely to be a user ELF image
122                 // rather and not the 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_un.d_ptr)? as *mut _)?
147                             .as_ptr();
148                 }
149                 DT_SYMTAB => {
150                     vdso.symtab =
151                         check_raw_pointer::<Elf_Sym>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
152                             .as_ptr();
153                 }
154                 DT_HASH => {
155                     hash = check_raw_pointer::<u32>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
156                         .as_ptr();
157                 }
158                 DT_VERSYM => {
159                     vdso.versym =
160                         check_raw_pointer::<u16>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
161                             .as_ptr();
162                 }
163                 DT_VERDEF => {
164                     vdso.verdef = check_raw_pointer::<Elf_Verdef>(
165                         vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _,
166                     )?
167                     .as_ptr();
168                 }
169                 DT_SYMENT => {
170                     if d.d_un.d_ptr != size_of::<Elf_Sym>() {
171                         return None; // Failed
172                     }
173                 }
174                 DT_NULL => break,
175                 _ => {}
176             }
177             i = i.checked_add(1)?;
178         }
179         // The upstream code checks `symstrings`, `symtab`, and `hash` for
180         // null; here, `check_raw_pointer` has already done that.
181 
182         if vdso.verdef.is_null() {
183             vdso.versym = null();
184         }
185 
186         // Parse the hash table header.
187         vdso.nbucket = *hash.add(0);
188         //vdso.nchain = *hash.add(1);
189         vdso.bucket = hash.add(2);
190         vdso.chain = hash.add(vdso.nbucket as usize + 2);
191 
192         // That's all we need.
193         Some(vdso)
194     }
195 }
196 
197 impl Vdso {
198     /// Parse the vDSO.
199     ///
200     /// Returns `None` if the vDSO can't be located or if it doesn't conform to
201     /// our expectations.
202     #[inline]
new() -> Option<Self>203     pub(super) fn new() -> Option<Self> {
204         init_from_sysinfo_ehdr()
205     }
206 
207     /// Check the version for a symbol.
208     ///
209     /// # Safety
210     ///
211     /// The raw pointers inside `self` must be valid.
match_version(&self, mut ver: u16, name: &CStr, hash: u32) -> bool212     unsafe fn match_version(&self, mut ver: u16, name: &CStr, hash: u32) -> bool {
213         // This is a helper function to check if the version indexed by
214         // ver matches name (which hashes to hash).
215         //
216         // The version definition table is a mess, and I don't know how
217         // to do this in better than linear time without allocating memory
218         // to build an index. I also don't know why the table has
219         // variable size entries in the first place.
220         //
221         // For added fun, I can't find a comprehensible specification of how
222         // to parse all the weird flags in the table.
223         //
224         // So I just parse the whole table every time.
225 
226         // First step: find the version definition
227         ver &= 0x7fff; // Apparently bit 15 means "hidden"
228         let mut def = self.verdef;
229         loop {
230             if (*def).vd_version != VER_DEF_CURRENT {
231                 return false; // Failed
232             }
233 
234             if ((*def).vd_flags & VER_FLG_BASE) == 0 && ((*def).vd_ndx & 0x7fff) == ver {
235                 break;
236             }
237 
238             if (*def).vd_next == 0 {
239                 return false; // No definition.
240             }
241 
242             def = def
243                 .cast::<u8>()
244                 .add((*def).vd_next as usize)
245                 .cast::<Elf_Verdef>();
246         }
247 
248         // Now figure out whether it matches.
249         let aux = &*(def.cast::<u8>())
250             .add((*def).vd_aux as usize)
251             .cast::<Elf_Verdaux>();
252         (*def).vd_hash == hash
253             && (name == CStr::from_ptr(self.symstrings.add(aux.vda_name as usize).cast()))
254     }
255 
256     /// Look up a symbol in the vDSO.
sym(&self, version: &CStr, name: &CStr) -> *mut c::c_void257     pub(super) fn sym(&self, version: &CStr, name: &CStr) -> *mut c::c_void {
258         let ver_hash = elf_hash(version);
259         let name_hash = elf_hash(name);
260 
261         // SAFETY: The pointers in `self` must be valid.
262         unsafe {
263             let mut chain = *self.bucket.add((name_hash % self.nbucket) as usize);
264 
265             while chain != STN_UNDEF {
266                 let sym = &*self.symtab.add(chain as usize);
267 
268                 // Check for a defined global or weak function w/ right name.
269                 //
270                 // The reference parser in Linux's parse_vdso.c requires
271                 // symbols to have type `STT_FUNC`, but on powerpc64, the vDSO
272                 // uses `STT_NOTYPE`, so allow that too.
273                 if (ELF_ST_TYPE(sym.st_info) != STT_FUNC &&
274                         ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
275                     || (ELF_ST_BIND(sym.st_info) != STB_GLOBAL
276                         && ELF_ST_BIND(sym.st_info) != STB_WEAK)
277                     || sym.st_shndx == SHN_UNDEF
278                     || sym.st_shndx == SHN_ABS
279                     || ELF_ST_VISIBILITY(sym.st_other) != STV_DEFAULT
280                     || (name != CStr::from_ptr(self.symstrings.add(sym.st_name as usize).cast()))
281                     // Check symbol version.
282                     || (!self.versym.is_null()
283                         && !self.match_version(*self.versym.add(chain as usize), version, ver_hash))
284                 {
285                     chain = *self.chain.add(chain as usize);
286                     continue;
287                 }
288 
289                 let sum = self.addr_from_elf(sym.st_value).unwrap();
290                 assert!(
291                     sum as usize >= self.load_addr as usize
292                         && sum as usize <= self.load_end as usize
293                 );
294                 return sum as *mut c::c_void;
295             }
296         }
297 
298         null_mut()
299     }
300 
301     /// Add the given address to the vDSO base address.
base_plus(&self, offset: usize) -> Option<*const c_void>302     unsafe fn base_plus(&self, offset: usize) -> Option<*const c_void> {
303         // Check for overflow.
304         let _ = (self.load_addr as usize).checked_add(offset)?;
305         // Add the offset to the base.
306         Some(self.load_addr.cast::<u8>().add(offset).cast())
307     }
308 
309     /// Translate an ELF-address-space address into a usable virtual address.
addr_from_elf(&self, elf_addr: usize) -> Option<*const c_void>310     unsafe fn addr_from_elf(&self, elf_addr: usize) -> Option<*const c_void> {
311         self.base_plus(elf_addr.wrapping_add(self.pv_offset))
312     }
313 }
314