Skip to content

Commit 2f2b242

Browse files
xuejun-xjjiangliu
authored andcommitted
bugfix: passthrough: refect CFileHandle struct
Previously, CFileHandle uses a *mut libc::c_char to transfer data between user space and kernel space. The system call "name_to_handle_at" will return the data with "copy_to_user". This may cause a bug because the memory layout of CFileHandle fields may be noncontinuous with the dynamically-sized array's. Therefore, the "copy_to_user" may destroy the stack. This is reproduced on aarch64 only when using "opt-level=3" to compile. This commit refectors the CFileHandle struct with FarmStruct trait to ensure the memory layout to be continuous. The trait enables struct to own a dynamically-sized array at the end of the struct like zero-array in C language. We refector the related implementation so that "copy_to_user" won't write outside the CFileHandle and destroy the user stack. Besides, add some units and fix clippy warnings. Signed-off-by: xuejun-xj <[email protected]>
1 parent 6788a74 commit 2f2b242

File tree

2 files changed

+144
-77
lines changed

2 files changed

+144
-77
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ vm-memory = { version = "0.9", features = ["backend-mmap", "backend-bitmap"] }
4949
default = ["fusedev"]
5050
async-io = ["async-trait", "tokio-uring", "tokio/fs", "tokio/net", "tokio/sync", "tokio/rt", "tokio/macros", "io-uring"]
5151
fusedev = ["vmm-sys-util", "caps", "core-foundation-sys"]
52-
virtiofs = ["virtio-queue", "caps"]
52+
virtiofs = ["virtio-queue", "caps", "vmm-sys-util"]
5353
vhost-user-fs = ["virtiofs", "vhost", "caps"]
5454

5555
[package.metadata.docs.rs]

src/passthrough/file_handle.rs

Lines changed: 143 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -9,34 +9,81 @@ use std::fmt::{Debug, Formatter};
99
use std::fs::File;
1010
use std::io;
1111
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
12-
use std::ptr;
1312
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
1413

14+
use vmm_sys_util::{
15+
fam::{FamStruct, FamStructWrapper},
16+
generate_fam_struct_impl,
17+
};
18+
1519
use crate::passthrough::PassthroughFs;
1620

1721
/// An arbitrary maximum size for CFileHandle::f_handle.
1822
///
1923
/// According to Linux ABI, struct file_handle has a flexible array member 'f_handle', but it's
2024
/// hard-coded here for simplicity.
21-
//pub const MAX_HANDLE_SZ: usize = 128;
25+
pub const MAX_HANDLE_SIZE: usize = 128;
26+
27+
/// Dynamically allocated array.
28+
#[derive(Default)]
29+
#[repr(C)]
30+
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
31+
impl<T> __IncompleteArrayField<T> {
32+
#[inline]
33+
pub unsafe fn as_ptr(&self) -> *const T {
34+
self as *const __IncompleteArrayField<T> as *const T
35+
}
36+
#[inline]
37+
pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
38+
self as *mut __IncompleteArrayField<T> as *mut T
39+
}
40+
#[inline]
41+
pub unsafe fn as_slice(&self, len: usize) -> &[T] {
42+
::std::slice::from_raw_parts(self.as_ptr(), len)
43+
}
44+
#[inline]
45+
pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] {
46+
::std::slice::from_raw_parts_mut(self.as_mut_ptr(), len)
47+
}
48+
}
2249

23-
#[derive(Clone, Copy)]
50+
/// The structure to transfer file_handle struct between user space and kernel space.
51+
/// ```c
52+
/// struct file_handle {
53+
/// __u32 handle_bytes;
54+
/// int handle_type;
55+
/// /* file identifier */
56+
/// unsigned char f_handle[];
57+
/// }
58+
/// ```
59+
#[derive(Default)]
2460
#[repr(C)]
61+
pub struct CFileHandleInner {
62+
pub handle_bytes: libc::c_uint,
63+
pub handle_type: libc::c_int,
64+
pub f_handle: __IncompleteArrayField<libc::c_char>,
65+
}
66+
67+
generate_fam_struct_impl!(
68+
CFileHandleInner,
69+
libc::c_char,
70+
f_handle,
71+
libc::c_uint,
72+
handle_bytes,
73+
MAX_HANDLE_SIZE
74+
);
75+
76+
type CFileHandleWrapper = FamStructWrapper<CFileHandleInner>;
77+
78+
#[derive(Clone)]
2579
struct CFileHandle {
26-
// Size of f_handle [in, out]
27-
handle_bytes: libc::c_uint,
28-
// Handle type [out]
29-
handle_type: libc::c_int,
30-
// File identifier (sized by caller) [out]
31-
f_handle: *mut libc::c_char,
80+
pub wrapper: CFileHandleWrapper,
3281
}
3382

3483
impl CFileHandle {
35-
fn new() -> Self {
84+
fn new(size: usize) -> Self {
3685
CFileHandle {
37-
handle_bytes: 0,
38-
handle_type: 0,
39-
f_handle: ptr::null_mut(),
86+
wrapper: CFileHandleWrapper::new(size).unwrap(),
4087
}
4188
}
4289
}
@@ -47,14 +94,24 @@ unsafe impl Sync for CFileHandle {}
4794

4895
impl Ord for CFileHandle {
4996
fn cmp(&self, other: &Self) -> Ordering {
50-
if self.handle_bytes != other.handle_bytes {
51-
return self.handle_bytes.cmp(&other.handle_bytes);
97+
let s_fh = self.wrapper.as_fam_struct_ref();
98+
let o_fh = other.wrapper.as_fam_struct_ref();
99+
if s_fh.handle_bytes != o_fh.handle_bytes {
100+
return s_fh.handle_bytes.cmp(&o_fh.handle_bytes);
52101
}
53-
if self.handle_type != other.handle_type {
54-
return self.handle_type.cmp(&other.handle_type);
102+
let length = s_fh.handle_bytes as usize;
103+
if s_fh.handle_type != o_fh.handle_type {
104+
return s_fh.handle_type.cmp(&o_fh.handle_type);
105+
}
106+
unsafe {
107+
if s_fh.f_handle.as_ptr() != o_fh.f_handle.as_ptr() {
108+
return s_fh
109+
.f_handle
110+
.as_slice(length)
111+
.cmp(o_fh.f_handle.as_slice(length));
112+
}
55113
}
56114

57-
// f_handle is left to be compared by FileHandle's buf.
58115
Ordering::Equal
59116
}
60117
}
@@ -75,10 +132,11 @@ impl Eq for CFileHandle {}
75132

76133
impl Debug for CFileHandle {
77134
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
135+
let fh = self.wrapper.as_fam_struct_ref();
78136
write!(
79137
f,
80138
"File handle: type {}, len {}",
81-
self.handle_type, self.handle_bytes
139+
fh.handle_type, fh.handle_bytes
82140
)
83141
}
84142
}
@@ -88,15 +146,13 @@ impl Debug for CFileHandle {
88146
pub struct FileHandle {
89147
pub(crate) mnt_id: u64,
90148
handle: CFileHandle,
91-
// internal buffer for handle.f_handle
92-
buf: Vec<libc::c_char>,
93149
}
94150

95151
extern "C" {
96152
fn name_to_handle_at(
97153
dirfd: libc::c_int,
98154
pathname: *const libc::c_char,
99-
file_handle: *mut CFileHandle,
155+
file_handle: *mut CFileHandleInner,
100156
mount_id: *mut libc::c_int,
101157
flags: libc::c_int,
102158
) -> libc::c_int;
@@ -105,7 +161,7 @@ extern "C" {
105161
// not to change it, so we can declare it `const`.
106162
fn open_by_handle_at(
107163
mount_fd: libc::c_int,
108-
file_handle: *const CFileHandle,
164+
file_handle: *const CFileHandleInner,
109165
flags: libc::c_int,
110166
) -> libc::c_int;
111167
}
@@ -114,7 +170,7 @@ impl FileHandle {
114170
/// Create a file handle for the given file.
115171
pub fn from_name_at(dir_fd: RawFd, path: &CStr) -> io::Result<Self> {
116172
let mut mount_id: libc::c_int = 0;
117-
let mut c_fh = CFileHandle::new();
173+
let mut c_fh = CFileHandle::new(0);
118174

119175
// Per name_to_handle_at(2), the caller can discover the required size
120176
// for the file_handle structure by making a call in which
@@ -126,7 +182,7 @@ impl FileHandle {
126182
name_to_handle_at(
127183
dir_fd,
128184
path.as_ptr(),
129-
&mut c_fh,
185+
c_fh.wrapper.as_mut_fam_struct_ptr(),
130186
&mut mount_id,
131187
libc::AT_EMPTY_PATH,
132188
)
@@ -141,18 +197,14 @@ impl FileHandle {
141197
return Err(io::Error::from(io::ErrorKind::InvalidData));
142198
}
143199

144-
let needed = c_fh.handle_bytes as usize;
145-
let mut buf = vec![0; needed];
146-
147-
// get a unsafe pointer, FileHandle takes care of freeing the memory
148-
// that 'f_handle' points to.
149-
c_fh.f_handle = buf.as_mut_ptr();
200+
let needed = c_fh.wrapper.as_fam_struct_ref().handle_bytes as usize;
201+
let mut c_fh = CFileHandle::new(needed);
150202

151203
let ret = unsafe {
152204
name_to_handle_at(
153205
dir_fd,
154206
path.as_ptr(),
155-
&mut c_fh,
207+
c_fh.wrapper.as_mut_fam_struct_ptr(),
156208
&mut mount_id,
157209
libc::AT_EMPTY_PATH,
158210
)
@@ -161,7 +213,6 @@ impl FileHandle {
161213
Ok(FileHandle {
162214
mnt_id: mount_id as u64,
163215
handle: c_fh,
164-
buf,
165216
})
166217
} else {
167218
let e = io::Error::last_os_error();
@@ -200,7 +251,13 @@ impl FileHandle {
200251
/// `mount_fd` must be an open non-`O_PATH` file descriptor for an inode on the same mount as
201252
/// the file to be opened, i.e. the mount given by `self.mnt_id`.
202253
fn open(&self, mount_fd: &impl AsRawFd, flags: libc::c_int) -> io::Result<File> {
203-
let ret = unsafe { open_by_handle_at(mount_fd.as_raw_fd(), &self.handle, flags) };
254+
let ret = unsafe {
255+
open_by_handle_at(
256+
mount_fd.as_raw_fd(),
257+
self.handle.wrapper.as_fam_struct_ptr(),
258+
flags,
259+
)
260+
};
204261
if ret >= 0 {
205262
// Safe because `open_by_handle_at()` guarantees this is a valid fd
206263
let file = unsafe { File::from_raw_fd(ret) };
@@ -330,82 +387,92 @@ impl MountFds {
330387
mod tests {
331388
use super::*;
332389

390+
fn generate_c_file_handle(
391+
handle_bytes: usize,
392+
handle_type: libc::c_int,
393+
buf: Vec<libc::c_char>,
394+
) -> CFileHandle {
395+
let mut wrapper = CFileHandle::new(handle_bytes);
396+
let fh = wrapper.wrapper.as_mut_fam_struct();
397+
fh.handle_type = handle_type;
398+
unsafe {
399+
fh.f_handle
400+
.as_mut_slice(handle_bytes)
401+
.copy_from_slice(buf.as_slice());
402+
}
403+
404+
wrapper
405+
}
406+
333407
#[test]
334408
fn test_file_handle_derives() {
335-
let mut buf1 = vec![0; 128];
336-
let h1 = CFileHandle {
337-
handle_bytes: 128,
338-
handle_type: 3,
339-
f_handle: buf1.as_mut_ptr(),
340-
};
409+
let h1 = generate_c_file_handle(128, 3, vec![0; 128]);
341410
let mut fh1 = FileHandle {
342411
mnt_id: 0,
343412
handle: h1,
344-
buf: buf1,
345413
};
346414

347-
let mut buf2 = vec![0; 129];
348-
let h2 = CFileHandle {
349-
handle_bytes: 129,
350-
handle_type: 3,
351-
f_handle: buf2.as_mut_ptr(),
352-
};
415+
let h2 = generate_c_file_handle(127, 3, vec![0; 127]);
353416
let fh2 = FileHandle {
354417
mnt_id: 0,
355418
handle: h2,
356-
buf: buf2,
357419
};
358420

359-
let mut buf3 = vec![0; 128];
360-
let h3 = CFileHandle {
361-
handle_bytes: 128,
362-
handle_type: 4,
363-
f_handle: buf3.as_mut_ptr(),
364-
};
421+
let h3 = generate_c_file_handle(128, 4, vec![0; 128]);
365422
let fh3 = FileHandle {
366423
mnt_id: 0,
367424
handle: h3,
368-
buf: buf3,
369425
};
370426

371-
let mut buf4 = vec![1; 128];
372-
let h4 = CFileHandle {
373-
handle_bytes: 128,
374-
handle_type: 3,
375-
f_handle: buf4.as_mut_ptr(),
376-
};
427+
let h4 = generate_c_file_handle(128, 3, vec![1; 128]);
377428
let fh4 = FileHandle {
378429
mnt_id: 0,
379430
handle: h4,
380-
buf: buf4,
381431
};
382432

383-
let mut buf5 = vec![0; 128];
384-
let h5 = CFileHandle {
385-
handle_bytes: 128,
386-
handle_type: 3,
387-
f_handle: buf5.as_mut_ptr(),
388-
};
433+
let h5 = generate_c_file_handle(128, 3, vec![0; 128]);
389434
let mut fh5 = FileHandle {
390435
mnt_id: 0,
391436
handle: h5,
392-
buf: buf5,
393437
};
394438

395-
assert!(fh1 < fh2);
439+
assert!(fh1 > fh2);
396440
assert!(fh1 != fh2);
397441
assert!(fh1 < fh3);
398442
assert!(fh1 != fh3);
399443
assert!(fh1 < fh4);
400444
assert!(fh1 != fh4);
401-
402445
assert!(fh1 == fh5);
403-
fh1.buf[0] = 1;
404-
fh1.handle.f_handle = fh1.buf.as_mut_ptr();
405-
assert!(fh1 > fh5);
406446

407-
fh5.buf[0] = 1;
408-
fh5.handle.f_handle = fh5.buf.as_mut_ptr();
447+
unsafe {
448+
fh1.handle
449+
.wrapper
450+
.as_mut_fam_struct()
451+
.f_handle
452+
.as_mut_slice(128)[0] = 1;
453+
}
454+
assert!(fh1 > fh5);
455+
unsafe {
456+
fh5.handle
457+
.wrapper
458+
.as_mut_fam_struct()
459+
.f_handle
460+
.as_mut_slice(128)[0] = 1;
461+
}
409462
assert!(fh1 == fh5);
410463
}
464+
465+
#[test]
466+
fn test_c_file_handle_wrapper() {
467+
let buf = (0..=127).collect::<Vec<libc::c_char>>();
468+
let mut wrapper = generate_c_file_handle(MAX_HANDLE_SIZE, 3, buf.clone());
469+
let fh = wrapper.wrapper.as_mut_fam_struct();
470+
471+
assert_eq!(fh.handle_bytes as usize, MAX_HANDLE_SIZE);
472+
assert_eq!(fh.handle_type, 3);
473+
assert_eq!(
474+
unsafe { fh.f_handle.as_slice(MAX_HANDLE_SIZE) },
475+
buf.as_slice(),
476+
);
477+
}
411478
}

0 commit comments

Comments
 (0)