Skip to content

Commit cb9515f

Browse files
authored
Check for a missing DT_HASH section in the VDSO parser (#1254) (#1256)
* Check for a missing `DT_HASH` section in the VDSO parser (#1254) * Check for a missing `DT_HASH` section in the VDSO parser Fix a missing check for a null hash table pointer, which can happen if the vDSO lacks a `DT_HASH` entry. Also, incorporate the changes in the latest upstream version of thee reference parse_vdso.c code. * Fix type differences on s390x. * Add more tests. * Remove an over-aggressive check. * Fix the name of getcpu on riscv64. * Manually install libglib2.0-dev for the qemu build.
1 parent 228ef24 commit cb9515f

File tree

3 files changed

+67
-25
lines changed

3 files changed

+67
-25
lines changed

.github/workflows/main.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ jobs:
450450
run: |
451451
set -ex
452452
sudo apt-get update
453-
sudo apt-get install -y ${{ matrix.gcc_package }} ninja-build
453+
sudo apt-get install -y ${{ matrix.gcc_package }} ninja-build libglib2.0-dev
454454
upcase=$(echo ${{ matrix.target }} | awk '{ print toupper($0) }' | sed 's/-/_/g')
455455
echo CARGO_TARGET_${upcase}_LINKER=${{ matrix.gcc }} >> $GITHUB_ENV
456456
if: matrix.gcc_package != '' && matrix.os == 'ubuntu-latest'
@@ -598,7 +598,7 @@ jobs:
598598
run: |
599599
set -ex
600600
sudo apt-get update
601-
sudo apt-get install -y ${{ matrix.gcc_package }} ninja-build
601+
sudo apt-get install -y ${{ matrix.gcc_package }} ninja-build libglib2.0-dev
602602
upcase=$(echo ${{ matrix.target }} | awk '{ print toupper($0) }' | sed 's/-/_/g')
603603
echo CARGO_TARGET_${upcase}_LINKER=${{ matrix.gcc }} >> $GITHUB_ENV
604604
if: matrix.gcc_package != '' && matrix.os == 'ubuntu-latest'
@@ -703,7 +703,7 @@ jobs:
703703
run: |
704704
set -ex
705705
sudo apt-get update
706-
sudo apt-get install -y ${{ matrix.gcc_package }} ninja-build
706+
sudo apt-get install -y ${{ matrix.gcc_package }} ninja-build libglib2.0-dev
707707
upcase=$(echo ${{ matrix.target }} | awk '{ print toupper($0) }' | sed 's/-/_/g')
708708
echo CARGO_TARGET_${upcase}_LINKER=${{ matrix.gcc }} >> $GITHUB_ENV
709709
if: matrix.gcc_package != '' && matrix.os == 'ubuntu-latest'

src/backend/linux_raw/vdso.rs

+63-21
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Parse the Linux vDSO.
22
//!
33
//! The following code is transliterated from
4-
//! tools/testing/selftests/vDSO/parse_vdso.c in Linux 5.11, which is licensed
4+
//! tools/testing/selftests/vDSO/parse_vdso.c in Linux 6.12, which is licensed
55
//! with Creative Commons Zero License, version 1.0,
66
//! available at <https://creativecommons.org/publicdomain/zero/1.0/legalcode>
77
//!
@@ -20,6 +20,11 @@ use core::mem::size_of;
2020
use core::ptr::{null, null_mut};
2121
use linux_raw_sys::elf::*;
2222

23+
#[cfg(target_arch = "s390x")]
24+
type ElfHashEntry = u64;
25+
#[cfg(not(target_arch = "s390x"))]
26+
type ElfHashEntry = u32;
27+
2328
pub(super) struct Vdso {
2429
// Load information
2530
load_addr: *const Elf_Ehdr,
@@ -29,17 +34,19 @@ pub(super) struct Vdso {
2934
// Symbol table
3035
symtab: *const Elf_Sym,
3136
symstrings: *const u8,
32-
bucket: *const u32,
33-
chain: *const u32,
34-
nbucket: u32,
35-
//nchain: u32,
37+
bucket: *const ElfHashEntry,
38+
chain: *const ElfHashEntry,
39+
nbucket: ElfHashEntry,
40+
//nchain: ElfHashEntry,
3641

3742
// Version table
3843
versym: *const u16,
3944
verdef: *const Elf_Verdef,
4045
}
4146

42-
// Straight from the ELF specification.
47+
/// Straight from the ELF specification...and then tweaked slightly, in order to
48+
/// avoid a few clang warnings.
49+
/// (And then translated to Rust).
4350
fn elf_hash(name: &CStr) -> u32 {
4451
let mut h: u32 = 0;
4552
for b in name.to_bytes() {
@@ -91,11 +98,6 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
9198
let mut found_vaddr = false;
9299
for i in 0..hdr.e_phnum {
93100
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-
}
99101
if phdr.p_type == PT_LOAD && !found_vaddr {
100102
// The segment should be readable and executable, because it
101103
// contains the symbol table and the function bodies.
@@ -129,7 +131,7 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
129131
}
130132

131133
// Fish out the useful bits of the dynamic table.
132-
let mut hash: *const u32 = null();
134+
let mut hash: *const ElfHashEntry = null();
133135
vdso.symstrings = null();
134136
vdso.symtab = null();
135137
vdso.versym = null();
@@ -152,8 +154,10 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
152154
.as_ptr();
153155
}
154156
DT_HASH => {
155-
hash = check_raw_pointer::<u32>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
156-
.as_ptr();
157+
hash = check_raw_pointer::<ElfHashEntry>(
158+
vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _
159+
)?
160+
.as_ptr();
157161
}
158162
DT_VERSYM => {
159163
vdso.versym =
@@ -176,8 +180,12 @@ fn init_from_sysinfo_ehdr() -> Option<Vdso> {
176180
}
177181
i = i.checked_add(1)?;
178182
}
179-
// The upstream code checks `symstrings`, `symtab`, and `hash` for
180-
// null; here, `check_raw_pointer` has already done that.
183+
// `check_raw_pointer` will have checked these pointers for null,
184+
// however they could still be null if the expected dynamic table
185+
// entries are absent.
186+
if vdso.symstrings.is_null() || vdso.symtab.is_null() || hash.is_null() {
187+
return None; // Failed
188+
}
181189

182190
if vdso.verdef.is_null() {
183191
vdso.versym = null();
@@ -260,16 +268,20 @@ impl Vdso {
260268

261269
// SAFETY: The pointers in `self` must be valid.
262270
unsafe {
263-
let mut chain = *self.bucket.add((name_hash % self.nbucket) as usize);
271+
let mut chain = *self
272+
.bucket
273+
.add((ElfHashEntry::from(name_hash) % self.nbucket) as usize);
264274

265-
while chain != STN_UNDEF {
275+
while chain != ElfHashEntry::from(STN_UNDEF) {
266276
let sym = &*self.symtab.add(chain as usize);
267277

268278
// Check for a defined global or weak function w/ right name.
269279
//
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.
280+
// Accept `STT_NOTYPE` in addition to `STT_FUNC` for the symbol
281+
// type, for compatibility with some versions of Linux on
282+
// PowerPC64. See [this commit] in Linux for more background.
283+
//
284+
// [this commit]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/tools/testing/selftests/vDSO/parse_vdso.c?id=0161bd38c24312853ed5ae9a425a1c41c4ac674a
273285
if (ELF_ST_TYPE(sym.st_info) != STT_FUNC &&
274286
ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
275287
|| (ELF_ST_BIND(sym.st_info) != STB_GLOBAL
@@ -311,3 +323,33 @@ impl Vdso {
311323
self.base_plus(elf_addr.wrapping_add(self.pv_offset))
312324
}
313325
}
326+
327+
#[cfg(linux_raw)]
328+
#[test]
329+
#[ignore] // Until rustix is updated to the new vDSO format.
330+
fn test_vdso() {
331+
let vdso = Vdso::new().unwrap();
332+
assert!(!vdso.symtab.is_null());
333+
assert!(!vdso.symstrings.is_null());
334+
335+
#[cfg(target_arch = "x86_64")]
336+
let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime"));
337+
#[cfg(target_arch = "arm")]
338+
let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64"));
339+
#[cfg(target_arch = "aarch64")]
340+
let ptr = vdso.sym(cstr!("LINUX_2.6.39"), cstr!("__kernel_clock_gettime"));
341+
#[cfg(target_arch = "x86")]
342+
let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64"));
343+
#[cfg(target_arch = "riscv64")]
344+
let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_clock_gettime"));
345+
#[cfg(target_arch = "powerpc64")]
346+
let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_clock_gettime"));
347+
#[cfg(target_arch = "s390x")]
348+
let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_clock_gettime"));
349+
#[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
350+
let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64"));
351+
#[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))]
352+
let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime"));
353+
354+
assert!(!ptr.is_null());
355+
}

src/backend/linux_raw/vdso_wrappers.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ fn init() {
554554
#[cfg(target_arch = "x86")]
555555
let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_getcpu"));
556556
#[cfg(target_arch = "riscv64")]
557-
let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__kernel_getcpu"));
557+
let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_getcpu"));
558558
#[cfg(target_arch = "powerpc64")]
559559
let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_getcpu"));
560560

0 commit comments

Comments
 (0)