Control: reassign -1 rust-rustix 0.38.37
Control: affects -1 src:rust-timerfd

On Sun, May 04, 2025 at 11:41:36PM +0200, Chris Hofstaedtler wrote:
>   process didn't exit successfully: `CARGO=/usr/bin/cargo 
> CARGO_MANIFEST_DIR=/build/reproducible-path/rust-timerfd-1.5.0 
> CARGO_MANIFEST_PATH=/build/reproducible-path/rust-timerfd-1.5.0/Cargo.toml 
> CARGO_PKG_AUTHORS='main() <m...@ehvag.de>' CARGO_PKG_DESCRIPTION='A rust 
> interface to the Linux kernel'\''s timerfd API' CARGO_PKG_HOMEPAGE='' 
> CARGO_PKG_LICENSE=MIT CARGO_PKG_LICENSE_FILE='' CARGO_PKG_NAME=timerfd 
> CARGO_PKG_README=README.md 
> CARGO_PKG_REPOSITORY='https://github.com/main--/rust-timerfd' 
> CARGO_PKG_RUST_VERSION='' CARGO_PKG_VERSION=1.5.0 CARGO_PKG_VERSION_MAJOR=1 
> CARGO_PKG_VERSION_MINOR=5 CARGO_PKG_VERSION_PATCH=0 CARGO_PKG_VERSION_PRE='' 
> LD_LIBRARY_PATH='/build/reproducible-path/rust-timerfd-1.5.0/target/aarch64-unknown-linux-gnu/debug/deps:/build/reproducible-path/rust-timerfd-1.5.0/target/aarch64-unknown-linux-gnu/debug:/usr/lib/rustlib/aarch64-unknown-linux-gnu/lib'
>  
> /build/reproducible-path/rust-timerfd-1.5.0/target/aarch64-unknown-linux-gnu/debug/deps/timerfd-782df7dd57033e04`
>  (signal: 11, SIGSEGV: invalid memory reference)

This is caused by a Linux vDSO change on aarch64, see:
https://github.com/torvalds/linux/commit/48f6430505c0b0498ee9020ce3cf9558b1caaaeb

Upstream fix in rust-rustix:
https://github.com/bytecodealliance/rustix/pull/1259

Attached is a backported patch that works for me.

Chris

Backport of https://github.com/bytecodealliance/rustix/pull/1259

Necessary on aarch64 with linux kernel > 6.11
https://github.com/torvalds/linux/commit/48f6430505c0b0498ee9020ce3cf9558b1caaaeb

Debian bug: #1104704

--- a/src/backend/linux_raw/process/syscalls.rs
+++ b/src/backend/linux_raw/process/syscalls.rs
@@ -41,7 +41,8 @@
     target_arch = "x86_64",
     target_arch = "x86",
     target_arch = "riscv64",
-    target_arch = "powerpc64"
+    target_arch = "powerpc64",
+    target_arch = "s390x"
 ))]
 pub(crate) use crate::backend::vdso_wrappers::sched_getcpu;
 
@@ -50,7 +51,8 @@
     target_arch = "x86_64",
     target_arch = "x86",
     target_arch = "riscv64",
-    target_arch = "powerpc64"
+    target_arch = "powerpc64",
+    target_arch = "s390x"
 )))]
 #[inline]
 pub(crate) fn sched_getcpu() -> usize {
--- a/src/backend/linux_raw/vdso.rs
+++ b/src/backend/linux_raw/vdso.rs
@@ -20,6 +20,11 @@
 use core::ptr::{null, null_mut};
 use linux_raw_sys::elf::*;
 
+#[cfg(target_arch = "s390x")]
+type ElfHashEntry = u64;
+#[cfg(not(target_arch = "s390x"))]
+type ElfHashEntry = u32;
+
 pub(super) struct Vdso {
     // Load information
     load_addr: *const Elf_Ehdr,
@@ -29,10 +34,11 @@
     // Symbol table
     symtab: *const Elf_Sym,
     symstrings: *const u8,
-    bucket: *const u32,
-    chain: *const u32,
-    nbucket: u32,
-    //nchain: u32,
+    gnu_hash: *const u32,
+    bucket: *const ElfHashEntry,
+    chain: *const ElfHashEntry,
+    nbucket: ElfHashEntry,
+    //nchain: ElfHashEntry,
 
     // Version table
     versym: *const u16,
@@ -53,6 +59,16 @@
     h
 }
 
+fn gnu_hash(name: &CStr) -> u32 {
+    let mut h: u32 = 5381;
+    for s in name.to_bytes() {
+        h = h
+            .wrapping_add(h.wrapping_mul(32))
+            .wrapping_add(u32::from(*s));
+    }
+    h
+}
+
 /// Create a `Vdso` value by parsing the vDSO at the `sysinfo_ehdr` address.
 fn init_from_sysinfo_ehdr() -> Option<Vdso> {
     // SAFETY: The auxv initialization code does extensive checks to ensure
@@ -73,7 +89,8 @@
             pv_offset: 0,
             symtab: null(),
             symstrings: null(),
-            bucket: null(),
+			gnu_hash: null(),
+			bucket: null(),
             chain: null(),
             nbucket: 0,
             //nchain: 0,
@@ -129,7 +146,7 @@
         }
 
         // Fish out the useful bits of the dynamic table.
-        let mut hash: *const u32 = null();
+        let mut hash: *const ElfHashEntry = null();
         vdso.symstrings = null();
         vdso.symtab = null();
         vdso.versym = null();
@@ -152,9 +169,14 @@
                             .as_ptr();
                 }
                 DT_HASH => {
-                    hash = check_raw_pointer::<u32>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
+                    hash = check_raw_pointer::<ElfHashEntry>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
                         .as_ptr();
                 }
+                DT_GNU_HASH => {
+                    vdso.gnu_hash =
+                        check_raw_pointer::<u32>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
+                            .as_ptr()
+                }
                 DT_VERSYM => {
                     vdso.versym =
                         check_raw_pointer::<u16>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
@@ -176,18 +198,37 @@
             }
             i = i.checked_add(1)?;
         }
-        // The upstream code checks `symstrings`, `symtab`, and `hash` for
-        // null; here, `check_raw_pointer` has already done that.
+
+        // `check_raw_pointer` will have checked these pointers for null,
+        // however they could still be null if the expected dynamic table
+        // entries are absent.
+        if vdso.symstrings.is_null()
+            || vdso.symtab.is_null()
+            || (hash.is_null() && vdso.gnu_hash.is_null())
+        {
+            return None; // Failed
+        }
 
         if vdso.verdef.is_null() {
             vdso.versym = null();
         }
 
         // Parse the hash table header.
-        vdso.nbucket = *hash.add(0);
-        //vdso.nchain = *hash.add(1);
-        vdso.bucket = hash.add(2);
-        vdso.chain = hash.add(vdso.nbucket as usize + 2);
+        if !vdso.gnu_hash.is_null() {
+            vdso.nbucket = ElfHashEntry::from(*vdso.gnu_hash);
+            // The bucket array is located after the header (4 uint32) and the bloom
+            // filter (size_t array of gnu_hash[2] elements).
+            vdso.bucket = vdso
+                .gnu_hash
+                .add(4)
+                .add(size_of::<c::size_t>() / 4 * *vdso.gnu_hash.add(2) as usize)
+                .cast();
+        } else {
+            vdso.nbucket = *hash.add(0);
+            //vdso.nchain = *hash.add(1);
+            vdso.bucket = hash.add(2);
+            vdso.chain = hash.add(vdso.nbucket as usize + 2);
+        }
 
         // That's all we need.
         Some(vdso)
@@ -253,6 +294,46 @@
             && (name == CStr::from_ptr(self.symstrings.add(aux.vda_name as usize).cast()))
     }
 
+    /// Check to see if the symbol is the one we're looking for.
+    ///
+    /// # Safety
+    ///
+    /// The raw pointers inside `self` must be valid.
+    unsafe fn check_sym(
+        &self,
+        sym: &Elf_Sym,
+        i: ElfHashEntry,
+        name: &CStr,
+        version: &CStr,
+        ver_hash: u32,
+    ) -> bool {
+        // Check for a defined global or weak function w/ right name.
+        //
+        // Accept `STT_NOTYPE` in addition to `STT_FUNC` for the symbol
+        // type, for compatibility with some versions of Linux on
+        // PowerPC64. See [this commit] in Linux for more background.
+        //
+        // [this commit]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/tools/testing/selftests/vDSO/parse_vdso.c?id=0161bd38c24312853ed5ae9a425a1c41c4ac674a
+        if ELF_ST_TYPE(sym.st_info) != STT_FUNC && ELF_ST_TYPE(sym.st_info) != STT_NOTYPE {
+            return false;
+        }
+        if ELF_ST_BIND(sym.st_info) != STB_GLOBAL && ELF_ST_BIND(sym.st_info) != STB_WEAK {
+            return false;
+        }
+        if name != CStr::from_ptr(self.symstrings.add(sym.st_name as usize).cast()) {
+            return false;
+        }
+
+        // Check symbol version.
+        if !self.versym.is_null()
+            && !self.match_version(*self.versym.add(i as usize), version, ver_hash)
+        {
+            return false;
+        }
+
+        true
+    }
+
     /// Look up a symbol in the vDSO.
     pub(super) fn sym(&self, version: &CStr, name: &CStr) -> *mut c::c_void {
         let ver_hash = elf_hash(version);
@@ -260,38 +341,64 @@
 
         // SAFETY: The pointers in `self` must be valid.
         unsafe {
-            let mut chain = *self.bucket.add((name_hash % self.nbucket) as usize);
+            if !self.gnu_hash.is_null() {
+                let mut h1: u32 = gnu_hash(name);
 
-            while chain != STN_UNDEF {
-                let sym = &*self.symtab.add(chain as usize);
-
-                // Check for a defined global or weak function w/ right name.
-                //
-                // The reference parser in Linux's parse_vdso.c requires
-                // symbols to have type `STT_FUNC`, but on powerpc64, the vDSO
-                // uses `STT_NOTYPE`, so allow that too.
-                if (ELF_ST_TYPE(sym.st_info) != STT_FUNC &&
-                        ELF_ST_TYPE(sym.st_info) != STT_NOTYPE)
-                    || (ELF_ST_BIND(sym.st_info) != STB_GLOBAL
-                        && ELF_ST_BIND(sym.st_info) != STB_WEAK)
-                    || sym.st_shndx == SHN_UNDEF
-                    || sym.st_shndx == SHN_ABS
-                    || ELF_ST_VISIBILITY(sym.st_other) != STV_DEFAULT
-                    || (name != CStr::from_ptr(self.symstrings.add(sym.st_name as usize).cast()))
-                    // Check symbol version.
-                    || (!self.versym.is_null()
-                        && !self.match_version(*self.versym.add(chain as usize), version, ver_hash))
-                {
-                    chain = *self.chain.add(chain as usize);
-                    continue;
-                }
-
-                let sum = self.addr_from_elf(sym.st_value).unwrap();
-                assert!(
-                    sum as usize >= self.load_addr as usize
-                        && sum as usize <= self.load_end as usize
-                );
-                return sum as *mut c::c_void;
+                // Changes to fix the pointer arithmetic on s390x: cast
+                // `self.bucket` to `*const u32` here, because even though
+                // s390x's `ElfHashEntry` is 64-bit for `DT_HASH` tables,
+                // it uses 32-bit entries for `DT_GNU_HASH` tables.
+                let mut i = *self
+                    .bucket
+                    .cast::<u32>()
+                    .add((ElfHashEntry::from(h1) % self.nbucket) as usize);
+                if i == 0 {
+                    return null_mut();
+                }
+                h1 |= 1;
+                // Changes to fix the pointer arithmetic on s390x: As above,
+                // cast `self.bucket` to `*const u32`.
+                let mut hashval = self
+                    .bucket
+                    .cast::<u32>()
+                    .add(self.nbucket as usize)
+                    .add((i - *self.gnu_hash.add(1)) as usize);
+                loop {
+                    let sym: &Elf_Sym = &*self.symtab.add(i as usize);
+                    let h2 = *hashval;
+                    hashval = hashval.add(1);
+                    if h1 == (h2 | 1)
+                        && self.check_sym(sym, ElfHashEntry::from(i), name, version, ver_hash)
+                    {
+                        let sum = self.addr_from_elf(sym.st_value).unwrap();
+                        assert!(
+                            sum as usize >= self.load_addr as usize
+                                && sum as usize <= self.load_end as usize
+                        );
+                        return sum as *mut c::c_void;
+                    }
+                    if (h2 & 1) != 0 {
+                        break;
+                    }
+                    i += 1;
+                }
+            } else {
+                let mut i = *self
+                    .bucket
+                    .add((ElfHashEntry::from(elf_hash(name)) % self.nbucket) as usize);
+                while i != 0 {
+                    let sym: &Elf_Sym = &*self.symtab.add(i as usize);
+                    if sym.st_shndx != SHN_UNDEF && self.check_sym(sym, i, name, version, ver_hash)
+                    {
+                        let sum = self.addr_from_elf(sym.st_value).unwrap();
+                        assert!(
+                            sum as usize >= self.load_addr as usize
+                                && sum as usize <= self.load_end as usize
+                        );
+                        return sum as *mut c::c_void;
+                    }
+                    i = *self.chain.add(i as usize);
+                }
             }
         }
 
--- a/src/backend/linux_raw/vdso_wrappers.rs
+++ b/src/backend/linux_raw/vdso_wrappers.rs
@@ -20,7 +20,8 @@
     target_arch = "x86_64",
     target_arch = "x86",
     target_arch = "riscv64",
-    target_arch = "powerpc64"
+    target_arch = "powerpc64",
+    target_arch = "s390x"
 ))]
 use core::ffi::c_void;
 use core::mem::transmute;
@@ -37,7 +38,8 @@
             target_arch = "x86_64",
             target_arch = "x86",
             target_arch = "riscv64",
-            target_arch = "powerpc64"
+            target_arch = "powerpc64",
+            target_arch = "s390x"
         )
     ),
     feature = "time"
@@ -117,7 +119,8 @@
     target_arch = "x86_64",
     target_arch = "x86",
     target_arch = "riscv64",
-    target_arch = "powerpc64"
+    target_arch = "powerpc64",
+    target_arch = "s390x"
 ))]
 #[inline]
 pub(crate) fn sched_getcpu() -> usize {
@@ -268,7 +271,8 @@
     target_arch = "x86_64",
     target_arch = "x86",
     target_arch = "riscv64",
-    target_arch = "powerpc64"
+    target_arch = "powerpc64",
+    target_arch = "s390x"
 ))]
 type GetcpuType = unsafe extern "C" fn(*mut u32, *mut u32, *mut c_void) -> c::c_int;
 
@@ -294,7 +298,8 @@
     target_arch = "x86_64",
     target_arch = "x86",
     target_arch = "riscv64",
-    target_arch = "powerpc64"
+    target_arch = "powerpc64",
+    target_arch = "s390x"
 ))]
 #[cold]
 fn init_getcpu() -> GetcpuType {
@@ -324,7 +329,8 @@
     target_arch = "x86_64",
     target_arch = "x86",
     target_arch = "riscv64",
-    target_arch = "powerpc64"
+    target_arch = "powerpc64",
+    target_arch = "s390x"
 ))]
 static mut GETCPU: AtomicPtr<Function> = AtomicPtr::new(null_mut());
 #[cfg(target_arch = "x86")]
@@ -393,7 +399,8 @@
     target_arch = "x86_64",
     target_arch = "x86",
     target_arch = "riscv64",
-    target_arch = "powerpc64"
+    target_arch = "powerpc64",
+    target_arch = "s390x"
 ))]
 unsafe extern "C" fn rustix_getcpu_via_syscall(
     cpu: *mut u32,
@@ -457,7 +464,8 @@
             target_arch = "x86_64",
             target_arch = "x86",
             target_arch = "riscv64",
-            target_arch = "powerpc64"
+            target_arch = "powerpc64",
+            target_arch = "s390x"
         ))]
         {
             GETCPU
@@ -559,11 +567,14 @@
             let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__kernel_getcpu"));
             #[cfg(target_arch = "powerpc64")]
             let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_getcpu"));
+            #[cfg(target_arch = "s390x")]
+            let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_getcpu"));
 
             #[cfg(any(
                 target_arch = "x86_64",
                 target_arch = "riscv64",
-                target_arch = "powerpc64"
+                target_arch = "powerpc64",
+                target_arch = "s390x"
             ))]
             let ok = true;
 

Reply via email to