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
15use super::c;
16use super::elf::*;
17use crate::ffi::CStr;
18use crate::utils::check_raw_pointer;
19use core::ffi::c_void;
20use core::mem::size_of;
21use core::ptr::{null, null_mut};
22
23pub(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.
43fn 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.
57fn 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
194impl 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]
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.
209    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.
254    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.
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.
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