Skip to content

Commit 3db3984

Browse files
epilysstsquad
authored andcommitted
xen-ioctls: add SAFETY docs to unsafe {} blocks
Signed-off-by: Manos Pitsidianakis <[email protected]>
1 parent f2baf3b commit 3db3984

File tree

6 files changed

+308
-294
lines changed

6 files changed

+308
-294
lines changed

xen-ioctls/src/domctl/domctl.rs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,35 @@
1010

1111
use std::convert::TryFrom;
1212

13-
use libc::c_void;
14-
1513
use crate::{
1614
domctl::{types::*, xc_types::XcDominfo},
1715
private::*,
1816
};
1917

2018
fn do_domctl(xen_domctl: &mut XenDomctl) -> Result<(), std::io::Error> {
2119
let bouncebuffer = BounceBuffer::new(std::mem::size_of::<XenDomctl>())?;
22-
let vaddr = bouncebuffer.to_vaddr() as *mut XenDomctl;
23-
let mut privcmd_hypercall = PrivCmdHypercall {
20+
let vaddr = bouncebuffer.vaddr() as *mut XenDomctl;
21+
let mut privcmd = PrivCmdHypercall {
2422
op: __HYPERVISOR_DOMCTL,
2523
arg: [vaddr as u64, 0, 0, 0, 0],
2624
};
27-
/*
28-
* The expression "&mut privcmd_hypercall as *mut _" creates a reference
29-
* to privcmd_hypercall before casting it to a *mut c_void
30-
*/
31-
let privcmd_ptr: *mut c_void = &mut privcmd_hypercall as *mut _ as *mut c_void;
25+
// Write content of XenDomctl to the bounce buffer so that Xen knows what
26+
// we are asking for.
27+
// SAFETY: vaddr is a valid bounce buffer of XenDomctl size
28+
unsafe { vaddr.write(*xen_domctl) };
3229

30+
// SAFETY: we pass a PrivCmdHypercall sized value to an IOCTL_PRIVCMD_HYPERCALL
31+
// ioctl
3332
unsafe {
34-
// Write content of XenDomctl to the bounce buffer so that Xen knows what
35-
// we are asking for.
36-
vaddr.write(*xen_domctl);
37-
38-
do_ioctl(IOCTL_PRIVCMD_HYPERCALL(), privcmd_ptr).map(|_| {
39-
// Read back content from bounce buffer if no errors.
40-
*xen_domctl = vaddr.read();
41-
})
42-
}
33+
do_ioctl(
34+
IOCTL_PRIVCMD_HYPERCALL(),
35+
std::ptr::addr_of_mut!(privcmd).cast(),
36+
)?
37+
};
38+
// Read back content from bounce buffer if no errors.
39+
// SAFETY: vaddr is a valid bounce buffer of XenDomctl size
40+
*xen_domctl = unsafe { vaddr.read() };
41+
Ok(())
4342
}
4443

4544
pub fn xc_domain_info(first_domain: u16, max_domain: u32) -> Vec<XcDominfo> {
@@ -59,7 +58,10 @@ pub fn xc_domain_info(first_domain: u16, max_domain: u32) -> Vec<XcDominfo> {
5958

6059
match do_domctl(&mut domctl) {
6160
Ok(()) => {
62-
if let Ok(dominfo) = XcDominfo::try_from(unsafe { domctl.u.domaininfo }) {
61+
if let Ok(dominfo) = XcDominfo::try_from(
62+
// SAFETY: domctl was successful and the union is a XenDomctlPayload variant
63+
unsafe { domctl.u.domaininfo },
64+
) {
6365
vec.push(dominfo);
6466
}
6567
}

xen-ioctls/src/private.rs

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -120,50 +120,56 @@ impl BounceBuffer {
120120
.write(true)
121121
.open(HYPERCALL_BUFFER_FILE)?;
122122

123-
unsafe {
124-
// Setup a bounce buffer for Xen to use.
125-
let vaddr = mmap(
123+
// Setup a bounce buffer for Xen to use.
124+
// SAFETY: `fd is a valid HYPERCALL_BUFFER_FILE descriptor
125+
let vaddr = unsafe {
126+
mmap(
126127
core::ptr::null_mut::<c_void>(),
127128
bounce_buffer_size,
128129
PROT_READ | PROT_WRITE,
129130
MAP_SHARED,
130131
fd.as_raw_fd(),
131132
0,
132-
) as *mut u8;
133-
134-
// Function mmap() returns -1 in case of error. Casting to i16 or i64
135-
// yield the same result.
136-
if vaddr as i8 == -1 {
137-
return Err(Error::last_os_error());
138-
}
139-
140-
// Zero-out the memory we got from Xen. This will give us a clean slate and add
141-
// the pages in the EL1 and EL2 page tables. Otherwise the MMU
142-
// throws and exception and Xen will abort the transfer.
143-
vaddr.write_bytes(0, bounce_buffer_size);
144-
145-
Ok(BounceBuffer {
146-
vaddr: vaddr as *mut c_void,
147-
size: bounce_buffer_size,
148-
})
133+
)
134+
} as *mut u8;
135+
136+
// Function mmap() returns -1 in case of error. Casting to i16 or i64
137+
// yield the same result.
138+
if vaddr as i8 == -1 {
139+
return Err(Error::last_os_error());
149140
}
141+
142+
// Zero-out the memory we got from Xen. This will give us a clean slate and add
143+
// the pages in the EL1 and EL2 page tables. Otherwise the MMU
144+
// throws an exception and Xen will abort the transfer.
145+
// SAFETY: vaddr was mmapped and we checked mmap return value for errors.
146+
unsafe { vaddr.write_bytes(0, bounce_buffer_size) };
147+
148+
Ok(BounceBuffer {
149+
vaddr: vaddr as *mut c_void,
150+
size: bounce_buffer_size,
151+
})
150152
}
151153

152-
pub(crate) fn to_vaddr(&self) -> *mut c_void {
154+
pub(crate) fn vaddr(&self) -> *mut c_void {
153155
self.vaddr
154156
}
157+
158+
pub(crate) unsafe fn into_vec<T: Clone>(self, len: usize) -> Vec<T> {
159+
assert!(len * std::mem::size_of::<T>() < self.size);
160+
core::slice::from_raw_parts::<T>(self.vaddr.cast(), len).to_vec()
161+
}
155162
}
156163

157164
impl Drop for BounceBuffer {
158165
fn drop(&mut self) {
159-
unsafe {
160-
if munmap(self.vaddr, self.size) < 0 {
161-
println!(
162-
"Error {} unmapping vaddr: {:?}",
163-
Error::last_os_error(),
164-
self.vaddr
165-
);
166-
}
166+
// SAFETY: we allocated self.vaddr in Self::new
167+
if unsafe { munmap(self.vaddr, self.size) } < 0 {
168+
println!(
169+
"Error {} unmapping vaddr: {:?}",
170+
Error::last_os_error(),
171+
self.vaddr
172+
);
167173
}
168174
}
169175
}

xen-ioctls/src/sysctl/sysctl.rs

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@
88
* except according to those terms.
99
*/
1010

11-
use std::slice;
12-
13-
use libc::c_void;
14-
1511
#[cfg(target_arch = "aarch64")]
1612
use crate::aarch64::types::*;
1713
#[cfg(target_arch = "x86_64")]
@@ -20,27 +16,30 @@ use crate::{domctl::types::*, private::*, sysctl::types::*};
2016

2117
fn do_sysctl(xen_sysctl: &mut XenSysctl) -> Result<(), std::io::Error> {
2218
let bouncebuffer = BounceBuffer::new(std::mem::size_of::<XenSysctl>())?;
23-
let vaddr = bouncebuffer.to_vaddr() as *mut XenSysctl;
24-
let mut privcmd_hypercall = PrivCmdHypercall {
19+
let vaddr = bouncebuffer.vaddr() as *mut XenSysctl;
20+
let mut privcmd = PrivCmdHypercall {
2521
op: __HYPERVISOR_SYSCTL,
2622
arg: [vaddr as u64, 0, 0, 0, 0],
2723
};
28-
/*
29-
* The expression "&mut privcmd_hypercall as *mut _" creates a reference
30-
* to privcmd_hypercall before casting it to a *mut c_void
31-
*/
32-
let privcmd_ptr: *mut c_void = &mut privcmd_hypercall as *mut _ as *mut c_void;
3324

34-
unsafe {
35-
// Write content of XenSysctl to the bounce buffer so that Xen knows what
36-
// we are asking for.
37-
vaddr.write(*xen_sysctl);
25+
// Write content of XenSysctl to the bounce buffer so that Xen knows what
26+
// we are asking for.
27+
// SAFETY: vaddr points to valid memory allocated when we created the bounce
28+
// buffer
29+
unsafe { vaddr.write(*xen_sysctl) };
3830

39-
do_ioctl(IOCTL_PRIVCMD_HYPERCALL(), privcmd_ptr).map(|_| {
40-
// Read back content from bounce buffer if no errors.
41-
*xen_sysctl = vaddr.read();
42-
})
43-
}
31+
// SAFETY: we pass a PrivCmdHypercall value to an IOCTL_PRIVCMD_HYPERCALL ioctl
32+
unsafe {
33+
do_ioctl(
34+
IOCTL_PRIVCMD_HYPERCALL(),
35+
std::ptr::addr_of_mut!(privcmd).cast(),
36+
)?
37+
};
38+
// Read back content from bounce buffer if no errors.
39+
// SAFETY: the ioctl succeeded and vaddr was previously successfully allocated
40+
// by us
41+
*xen_sysctl = unsafe { vaddr.read() };
42+
Ok(())
4443
}
4544

4645
pub fn xc_physinfo() -> Result<XenSysctlPhysinfo, std::io::Error> {
@@ -52,7 +51,11 @@ pub fn xc_physinfo() -> Result<XenSysctlPhysinfo, std::io::Error> {
5251
},
5352
};
5453

55-
do_sysctl(&mut sysctl).map(|_| unsafe { sysctl.u.physinfo })
54+
do_sysctl(&mut sysctl)?;
55+
Ok(
56+
// SAFETY: sysctl was successful, and we initialized the union ourselves.
57+
unsafe { sysctl.u.physinfo },
58+
)
5659
}
5760

5861
pub fn xc_domain_getinfolist(
@@ -61,7 +64,6 @@ pub fn xc_domain_getinfolist(
6164
) -> Result<Vec<XenDomctlGetDomainInfo>, std::io::Error> {
6265
let bouncebuffer =
6366
BounceBuffer::new(std::mem::size_of::<XenDomctlGetDomainInfo>() * max_domain as usize)?;
64-
let vaddr = bouncebuffer.to_vaddr() as *mut XenDomctlGetDomainInfo;
6567

6668
let mut sysctl = XenSysctl {
6769
cmd: XEN_SYSCTL_getdomaininfolist,
@@ -70,13 +72,18 @@ pub fn xc_domain_getinfolist(
7072
domaininfolist: XenSysctlGetdomaininfolist {
7173
first_domain,
7274
max_domain,
73-
buffer: U64Aligned { v: vaddr as u64 },
75+
buffer: U64Aligned {
76+
v: bouncebuffer.vaddr() as u64,
77+
},
7478
num_domains: 0,
7579
},
7680
},
7781
};
7882

79-
do_sysctl(&mut sysctl).map(|_| unsafe {
80-
slice::from_raw_parts(vaddr, sysctl.u.domaininfolist.num_domains as usize).to_vec()
81-
})
83+
do_sysctl(&mut sysctl)?;
84+
Ok(
85+
// SAFETY: sysctl was successful, so bounce buffer must be populated with num_domains
86+
// elements
87+
unsafe { bouncebuffer.into_vec(sysctl.u.domaininfolist.num_domains as usize) },
88+
)
8289
}

0 commit comments

Comments
 (0)