Skip to content

Commit 15ffb64

Browse files
committed
Refactor the code that does vDSO checks into a helper function.
Move all the safety checks out of `init_from_sysinfo_ehdr` and into a helper function. This makes `init_from_sysinfo_ehdr` less huge and clarifies the split between the safety checks and the actual vDSO parsing code.
1 parent 7139d45 commit 15ffb64

File tree

1 file changed

+103
-91
lines changed

1 file changed

+103
-91
lines changed

src/imp/linux_raw/vdso.rs

Lines changed: 103 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,8 @@ const fn make_pointer<T>(value: usize) -> Option<*const T> {
6767
///
6868
/// `base` must be a valid pointer to an ELF image in memory.
6969
unsafe fn init_from_sysinfo_ehdr(base: usize) -> Option<Vdso> {
70-
extern "C" {
71-
static __ehdr_start: c::c_void;
72-
}
70+
// Check that `base` is a valid pointer to the kernel-provided vDSO.
71+
let hdr = check_vdso_base(base)?;
7372

7473
let mut vdso = Vdso {
7574
load_addr: base,
@@ -85,94 +84,6 @@ unsafe fn init_from_sysinfo_ehdr(base: usize) -> Option<Vdso> {
8584
verdef: null(),
8685
};
8786

88-
// Check that we're not attempting to parse our own ELF image.
89-
let ehdr_start: *const c::c_void = &__ehdr_start;
90-
if base == (ehdr_start as usize) {
91-
return None;
92-
}
93-
94-
// Check that the vDSO is page-aligned and appropriately mapped.
95-
madvise(
96-
base as *mut c::c_void,
97-
size_of::<Elf_Ehdr>(),
98-
Advice::Normal,
99-
)
100-
.ok()?;
101-
102-
let hdr = &*make_pointer::<Elf_Ehdr>(base)?;
103-
104-
if hdr.e_ident[..SELFMAG] != ELFMAG {
105-
return None; // Wrong ELF magic
106-
}
107-
if hdr.e_ident[EI_CLASS] != ELFCLASS {
108-
return None; // Wrong ELF class
109-
}
110-
if hdr.e_ident[EI_DATA] != ELFDATA {
111-
return None; // Wrong ELF data
112-
}
113-
if !matches!(hdr.e_ident[EI_OSABI], ELFOSABI_SYSV | ELFOSABI_LINUX) {
114-
return None; // Unrecognized ELF OS ABI
115-
}
116-
if hdr.e_ident[EI_ABIVERSION] != ELFABIVERSION {
117-
return None; // Unrecognized ELF ABI version
118-
}
119-
if hdr.e_type != ET_DYN {
120-
return None; // Wrong ELF type
121-
}
122-
// Verify that the `e_machine` matches the architecture we're running as.
123-
// This helps catch cases where we're running under qemu.
124-
if hdr.e_machine != EM_CURRENT {
125-
return None; // Wrong machine type
126-
}
127-
128-
// If ELF is extended, we'll need to adjust.
129-
if hdr.e_ident[EI_VERSION] != EV_CURRENT
130-
|| hdr.e_ehsize as usize != size_of::<Elf_Ehdr>()
131-
|| hdr.e_phentsize as usize != size_of::<Elf_Phdr>()
132-
{
133-
return None;
134-
}
135-
// We don't currently support extra-large numbers of segments.
136-
if hdr.e_phnum == PN_XNUM {
137-
return None;
138-
}
139-
140-
// If `e_phoff` is zero, it's more likely that we're looking at memory that
141-
// has been zeroed than that the kernel has somehow aliased the `Ehdr` and
142-
// the `Phdr`.
143-
if hdr.e_phoff < size_of::<Elf_Ehdr>() {
144-
return None;
145-
}
146-
147-
// Check that the vDSO is not writable, since that would indicate that this
148-
// isn't the kernel vDSO. Here we're just using `clock_getres` just as an
149-
// arbitrary system call which writes to a buffer and fails with `EFAULT`
150-
// if the buffer is not writable.
151-
{
152-
use super::arch::choose::syscall2;
153-
use super::conv::{clockid_t, ret, void_star};
154-
use super::reg::nr;
155-
use crate::time::ClockId;
156-
use linux_raw_sys::general::__NR_clock_getres;
157-
if ret(syscall2(
158-
nr(__NR_clock_getres),
159-
clockid_t(ClockId::Monotonic),
160-
void_star(base as *mut c::c_void),
161-
)) != Err(io::Error::FAULT)
162-
{
163-
// We can't gracefully fail here because we would seem to have just
164-
// mutated some unknown memory.
165-
#[cfg(feature = "std")]
166-
{
167-
std::process::abort();
168-
}
169-
#[cfg(all(not(feature = "std"), feature = "rustc-dep-of-std"))]
170-
{
171-
core::intrinsics::abort();
172-
}
173-
}
174-
}
175-
17687
let pt = make_pointer::<Elf_Phdr>(base.checked_add(hdr.e_phoff)?)?;
17788
let mut dyn_: *const Elf_Dyn = null();
17889
let mut num_dyn = 0;
@@ -274,6 +185,107 @@ unsafe fn init_from_sysinfo_ehdr(base: usize) -> Option<Vdso> {
274185
Some(vdso)
275186
}
276187

188+
/// Check that `base` is a valid pointer to the kernel-provided vDSO.
189+
///
190+
/// `base` is some value we got from a `AT_SYSINFO_EHDR` aux record somewhere,
191+
/// which hopefully holds the value of the kernel-provided vDSO in memory. Do
192+
/// a series of checks to be as sure as we can that it's safe to use.
193+
unsafe fn check_vdso_base<'vdso>(base: usize) -> Option<&'vdso Elf_Ehdr> {
194+
extern "C" {
195+
static __ehdr_start: c::c_void;
196+
}
197+
198+
// Check that we're not attempting to parse our own ELF image.
199+
let ehdr_start: *const c::c_void = &__ehdr_start;
200+
if base == (ehdr_start as usize) {
201+
return None;
202+
}
203+
204+
// Check that the vDSO is page-aligned and appropriately mapped.
205+
madvise(
206+
base as *mut c::c_void,
207+
size_of::<Elf_Ehdr>(),
208+
Advice::Normal,
209+
)
210+
.ok()?;
211+
212+
let hdr = &*make_pointer::<Elf_Ehdr>(base)?;
213+
214+
if hdr.e_ident[..SELFMAG] != ELFMAG {
215+
return None; // Wrong ELF magic
216+
}
217+
if hdr.e_ident[EI_CLASS] != ELFCLASS {
218+
return None; // Wrong ELF class
219+
}
220+
if hdr.e_ident[EI_DATA] != ELFDATA {
221+
return None; // Wrong ELF data
222+
}
223+
if !matches!(hdr.e_ident[EI_OSABI], ELFOSABI_SYSV | ELFOSABI_LINUX) {
224+
return None; // Unrecognized ELF OS ABI
225+
}
226+
if hdr.e_ident[EI_ABIVERSION] != ELFABIVERSION {
227+
return None; // Unrecognized ELF ABI version
228+
}
229+
if hdr.e_type != ET_DYN {
230+
return None; // Wrong ELF type
231+
}
232+
// Verify that the `e_machine` matches the architecture we're running as.
233+
// This helps catch cases where we're running under qemu.
234+
if hdr.e_machine != EM_CURRENT {
235+
return None; // Wrong machine type
236+
}
237+
238+
// If ELF is extended, we'll need to adjust.
239+
if hdr.e_ident[EI_VERSION] != EV_CURRENT
240+
|| hdr.e_ehsize as usize != size_of::<Elf_Ehdr>()
241+
|| hdr.e_phentsize as usize != size_of::<Elf_Phdr>()
242+
{
243+
return None;
244+
}
245+
// We don't currently support extra-large numbers of segments.
246+
if hdr.e_phnum == PN_XNUM {
247+
return None;
248+
}
249+
250+
// If `e_phoff` is zero, it's more likely that we're looking at memory that
251+
// has been zeroed than that the kernel has somehow aliased the `Ehdr` and
252+
// the `Phdr`.
253+
if hdr.e_phoff < size_of::<Elf_Ehdr>() {
254+
return None;
255+
}
256+
257+
// Check that the vDSO is not writable, since that would indicate that this
258+
// isn't the kernel vDSO. Here we're just using `clock_getres` just as an
259+
// arbitrary system call which writes to a buffer and fails with `EFAULT`
260+
// if the buffer is not writable.
261+
{
262+
use super::arch::choose::syscall2;
263+
use super::conv::{clockid_t, ret, void_star};
264+
use super::reg::nr;
265+
use crate::time::ClockId;
266+
use linux_raw_sys::general::__NR_clock_getres;
267+
if ret(syscall2(
268+
nr(__NR_clock_getres),
269+
clockid_t(ClockId::Monotonic),
270+
void_star(base as *mut c::c_void),
271+
)) != Err(io::Error::FAULT)
272+
{
273+
// We can't gracefully fail here because we would seem to have just
274+
// mutated some unknown memory.
275+
#[cfg(feature = "std")]
276+
{
277+
std::process::abort();
278+
}
279+
#[cfg(all(not(feature = "std"), feature = "rustc-dep-of-std"))]
280+
{
281+
core::intrinsics::abort();
282+
}
283+
}
284+
}
285+
286+
Some(hdr)
287+
}
288+
277289
impl Vdso {
278290
/// Parse the vDSO.
279291
///

0 commit comments

Comments
 (0)