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