File: vdso-check-for-missing-dt-hash.patch

package info (click to toggle)
rust-rustix 0.38.37-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,176 kB
  • sloc: makefile: 2
file content (199 lines) | stat: -rw-r--r-- 7,835 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
From 83e769b98e8e820cd1f58385a7f975b39d2087d6 Mon Sep 17 00:00:00 2001
From: Dan Gohman <dev@sunfishcode.online>
Date: Tue, 7 Jan 2025 12:02:11 -0800
Subject: [PATCH] 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.

FG: replace DT_GNU_HASH constant with value, can be removed once linux-raw-sys
>= 0.6.6 is available in Debian
Signed-off-by: Fabian Grünbichler <debian@fabian.gruenbichler.email>

---
 src/backend/linux_raw/vdso.rs          | 84 +++++++++++++++++++-------
 src/backend/linux_raw/vdso_wrappers.rs |  2 +-
 2 files changed, 64 insertions(+), 22 deletions(-)

--- a/src/backend/linux_raw/vdso.rs
+++ b/src/backend/linux_raw/vdso.rs
@@ -1,7 +1,7 @@
 //! Parse the Linux vDSO.
 //!
 //! The following code is transliterated from
-//! tools/testing/selftests/vDSO/parse_vdso.c in Linux 5.11, which is licensed
+//! tools/testing/selftests/vDSO/parse_vdso.c in Linux 6.12, which is licensed
 //! with Creative Commons Zero License, version 1.0,
 //! available at <https://creativecommons.org/publicdomain/zero/1.0/legalcode>
 //!
@@ -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,17 +34,19 @@
     // Symbol table
     symtab: *const Elf_Sym,
     symstrings: *const u8,
-    bucket: *const u32,
-    chain: *const u32,
-    nbucket: u32,
-    //nchain: u32,
+    bucket: *const ElfHashEntry,
+    chain: *const ElfHashEntry,
+    nbucket: ElfHashEntry,
+    //nchain: ElfHashEntry,
 
     // Version table
     versym: *const u16,
     verdef: *const Elf_Verdef,
 }
 
-// Straight from the ELF specification.
+/// Straight from the ELF specification...and then tweaked slightly, in order to
+/// avoid a few clang warnings.
+/// (And then translated to Rust).
 fn elf_hash(name: &CStr) -> u32 {
     let mut h: u32 = 0;
     for b in name.to_bytes() {
@@ -91,11 +98,6 @@
         let mut found_vaddr = false;
         for i in 0..hdr.e_phnum {
             let phdr = &*pt.add(i as usize);
-            if phdr.p_flags & PF_W != 0 {
-                // Don't trust any vDSO that claims to be loading writable
-                // segments into memory.
-                return None;
-            }
             if phdr.p_type == PT_LOAD && !found_vaddr {
                 // The segment should be readable and executable, because it
                 // contains the symbol table and the function bodies.
@@ -129,7 +131,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,8 +154,10 @@
                             .as_ptr();
                 }
                 DT_HASH => {
-                    hash = check_raw_pointer::<u32>(vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _)?
-                        .as_ptr();
+                    hash = check_raw_pointer::<ElfHashEntry>(
+                        vdso.addr_from_elf(d.d_un.d_ptr)? as *mut _
+                    )?
+                    .as_ptr();
                 }
                 DT_VERSYM => {
                     vdso.versym =
@@ -176,8 +180,12 @@
             }
             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() {
+            return None; // Failed
+        }
 
         if vdso.verdef.is_null() {
             vdso.versym = null();
@@ -260,16 +268,20 @@
 
         // SAFETY: The pointers in `self` must be valid.
         unsafe {
-            let mut chain = *self.bucket.add((name_hash % self.nbucket) as usize);
+            let mut chain = *self
+                .bucket
+                .add((ElfHashEntry::from(name_hash) % self.nbucket) as usize);
 
-            while chain != STN_UNDEF {
+            while chain != ElfHashEntry::from(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.
+                // 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)
                     || (ELF_ST_BIND(sym.st_info) != STB_GLOBAL
@@ -311,3 +323,33 @@
         self.base_plus(elf_addr.wrapping_add(self.pv_offset))
     }
 }
+
+#[cfg(linux_raw)]
+#[test]
+#[ignore] // Until rustix is updated to the new vDSO format.
+fn test_vdso() {
+    let vdso = Vdso::new().unwrap();
+    assert!(!vdso.symtab.is_null());
+    assert!(!vdso.symstrings.is_null());
+
+    #[cfg(target_arch = "x86_64")]
+    let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime"));
+    #[cfg(target_arch = "arm")]
+    let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64"));
+    #[cfg(target_arch = "aarch64")]
+    let ptr = vdso.sym(cstr!("LINUX_2.6.39"), cstr!("__kernel_clock_gettime"));
+    #[cfg(target_arch = "x86")]
+    let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64"));
+    #[cfg(target_arch = "riscv64")]
+    let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_clock_gettime"));
+    #[cfg(target_arch = "powerpc64")]
+    let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_clock_gettime"));
+    #[cfg(target_arch = "s390x")]
+    let ptr = vdso.sym(cstr!("LINUX_2.6.29"), cstr!("__kernel_clock_gettime"));
+    #[cfg(any(target_arch = "mips", target_arch = "mips32r6"))]
+    let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime64"));
+    #[cfg(any(target_arch = "mips64", target_arch = "mips64r6"))]
+    let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_clock_gettime"));
+
+    assert!(!ptr.is_null());
+}
--- a/src/backend/linux_raw/vdso_wrappers.rs
+++ b/src/backend/linux_raw/vdso_wrappers.rs
@@ -556,7 +556,7 @@
             #[cfg(target_arch = "x86")]
             let ptr = vdso.sym(cstr!("LINUX_2.6"), cstr!("__vdso_getcpu"));
             #[cfg(target_arch = "riscv64")]
-            let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__kernel_getcpu"));
+            let ptr = vdso.sym(cstr!("LINUX_4.15"), cstr!("__vdso_getcpu"));
             #[cfg(target_arch = "powerpc64")]
             let ptr = vdso.sym(cstr!("LINUX_2.6.15"), cstr!("__kernel_getcpu"));