Skip to content

Commit b6250dc

Browse files
alyssaisblitz
authored andcommitted
hypervisor: kvm: aarch64: fix get_device_attr() UB
DeviceFd::get_device_attr should be marked as unsafe, because it allows writing to an arbitrary address. I have opened a kvm-ioctls PR[1] to fix this. The hypervisor crate was using the function unsafely by passing it addresses of immutable variables. I noticed this because an optimisation change[2] in Rust 1.80.0 caused the kvm::aarch64::gic::tests::test_get_set_icc_regs test to start failing when built in release mode. To fix this, I've broken up the _access functions into _set and _get variants, with the _get variant using a pointer to a mutable variable. This has the side effect of making these functions a bit nicer to use, because the caller now has no need to use references at all, for either getting or setting. [1]: rust-vmm/kvm#273 [2]: rust-lang/rust@d2d24e3 Signed-off-by: Alyssa Ross <hi@alyssa.is> (cherry picked from commit 02f146f)
1 parent b495d2a commit b6250dc

File tree

4 files changed

+144
-128
lines changed

4 files changed

+144
-128
lines changed

hypervisor/src/kvm/aarch64/gic/dist_regs.rs

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -77,46 +77,61 @@ static VGIC_DIST_REGS: &[DistReg] = &[
7777
VGIC_DIST_REG!(GICD_IPRIORITYR, 8, 0),
7878
];
7979

80-
fn dist_attr_access(gic: &DeviceFd, offset: u32, val: &u32, set: bool) -> Result<()> {
81-
let mut gic_dist_attr = kvm_device_attr {
80+
fn dist_attr_set(gic: &DeviceFd, offset: u32, val: u32) -> Result<()> {
81+
let gic_dist_attr = kvm_device_attr {
8282
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
8383
attr: offset as u64,
84-
addr: val as *const u32 as u64,
84+
addr: &val as *const u32 as u64,
8585
flags: 0,
8686
};
87-
if set {
88-
gic.set_device_attr(&gic_dist_attr).map_err(|e| {
89-
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
90-
})?;
91-
} else {
92-
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
93-
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
94-
})?;
95-
}
87+
88+
gic.set_device_attr(&gic_dist_attr).map_err(|e| {
89+
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
90+
})?;
91+
9692
Ok(())
9793
}
9894

95+
fn dist_attr_get(gic: &DeviceFd, offset: u32) -> Result<u32> {
96+
let mut val = 0;
97+
98+
let mut gic_dist_attr = kvm_device_attr {
99+
group: KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
100+
attr: offset as u64,
101+
addr: &mut val as *mut u32 as u64,
102+
flags: 0,
103+
};
104+
105+
// get_device_attr should be marked as unsafe, and will be in future.
106+
// SAFETY: gic_dist_attr.addr is safe to write to.
107+
gic.get_device_attr(&mut gic_dist_attr).map_err(|e| {
108+
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
109+
})?;
110+
111+
Ok(val)
112+
}
113+
99114
/// Get the distributor control register.
100115
pub fn read_ctlr(gic: &DeviceFd) -> Result<u32> {
101-
let val: u32 = 0;
102-
dist_attr_access(gic, GICD_CTLR, &val, false)?;
103-
Ok(val)
116+
dist_attr_get(gic, GICD_CTLR)
104117
}
105118

106119
/// Set the distributor control register.
107120
pub fn write_ctlr(gic: &DeviceFd, val: u32) -> Result<()> {
108-
dist_attr_access(gic, GICD_CTLR, &val, true)
121+
dist_attr_set(gic, GICD_CTLR, val)
109122
}
110123

111124
fn get_interrupts_num(gic: &DeviceFd) -> Result<u32> {
112-
let num_irq = 0;
125+
let mut num_irq = 0;
113126

114127
let mut nr_irqs_attr = kvm_device_attr {
115128
group: KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
116129
attr: 0,
117-
addr: &num_irq as *const u32 as u64,
130+
addr: &mut num_irq as *mut u32 as u64,
118131
flags: 0,
119132
};
133+
// get_device_attr should be marked as unsafe, and will be in future.
134+
// SAFETY: nr_irqs_attr.addr is safe to write to.
120135
gic.get_device_attr(&mut nr_irqs_attr).map_err(|e| {
121136
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
122137
})?;
@@ -158,8 +173,7 @@ pub fn set_dist_regs(gic: &DeviceFd, state: &[u32]) -> Result<()> {
158173
let end = compute_reg_len(gic, dreg, base)?;
159174

160175
while base < end {
161-
let val = state[idx];
162-
dist_attr_access(gic, base, &val, true)?;
176+
dist_attr_set(gic, base, state[idx])?;
163177
idx += 1;
164178
base += REG_SIZE as u32;
165179
}
@@ -175,9 +189,7 @@ pub fn get_dist_regs(gic: &DeviceFd) -> Result<Vec<u32>> {
175189
let end = compute_reg_len(gic, dreg, base)?;
176190

177191
while base < end {
178-
let val: u32 = 0;
179-
dist_attr_access(gic, base, &val, false)?;
180-
state.push(val);
192+
state.push(dist_attr_get(gic, base)?);
181193
base += REG_SIZE as u32;
182194
}
183195
}

hypervisor/src/kvm/aarch64/gic/icc_regs.rs

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -79,25 +79,40 @@ static VGIC_ICC_REGS: &[u64] = &[
7979
SYS_ICC_AP1R3_EL1,
8080
];
8181

82-
fn icc_attr_access(gic: &DeviceFd, offset: u64, typer: u64, val: &u32, set: bool) -> Result<()> {
83-
let mut gic_icc_attr = kvm_device_attr {
82+
fn icc_attr_set(gic: &DeviceFd, offset: u64, typer: u64, val: u32) -> Result<()> {
83+
let gic_icc_attr = kvm_device_attr {
8484
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
8585
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
86-
addr: val as *const u32 as u64,
86+
addr: &val as *const u32 as u64,
8787
flags: 0,
8888
};
89-
if set {
90-
gic.set_device_attr(&gic_icc_attr).map_err(|e| {
91-
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
92-
})?;
93-
} else {
94-
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
95-
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
96-
})?;
97-
}
89+
90+
gic.set_device_attr(&gic_icc_attr).map_err(|e| {
91+
Error::SetDeviceAttribute(HypervisorDeviceError::SetDeviceAttribute(e.into()))
92+
})?;
93+
9894
Ok(())
9995
}
10096

97+
fn icc_attr_get(gic: &DeviceFd, offset: u64, typer: u64) -> Result<u32> {
98+
let mut val = 0;
99+
100+
let mut gic_icc_attr = kvm_device_attr {
101+
group: KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS,
102+
attr: ((typer & KVM_DEV_ARM_VGIC_V3_MPIDR_MASK) | offset), // this needs the mpidr
103+
addr: &mut val as *mut u32 as u64,
104+
flags: 0,
105+
};
106+
107+
// get_device_attr should be marked as unsafe, and will be in future.
108+
// SAFETY: gic_icc_attr.addr is safe to write to.
109+
gic.get_device_attr(&mut gic_icc_attr).map_err(|e| {
110+
Error::GetDeviceAttribute(HypervisorDeviceError::GetDeviceAttribute(e.into()))
111+
})?;
112+
113+
Ok(val)
114+
}
115+
101116
/// Get ICC registers.
102117
pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
103118
let mut state: Vec<u32> = Vec::new();
@@ -107,10 +122,9 @@ pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
107122
for ix in gicr_typer {
108123
let i = *ix;
109124
for icc_offset in VGIC_ICC_REGS {
110-
let val = 0;
111125
if *icc_offset == SYS_ICC_CTLR_EL1 {
112126
// calculate priority bits by reading the ctrl_el1 register.
113-
icc_attr_access(gic, *icc_offset, i, &val, false)?;
127+
let val = icc_attr_get(gic, *icc_offset, i)?;
114128
// The priority bits are found in the ICC_CTLR_EL1 register (bits from 10:8).
115129
// See page 194 from https://static.docs.arm.com/ihi0069/c/IHI0069C_gic_
116130
// architecture_specification.pdf.
@@ -130,21 +144,18 @@ pub fn get_icc_regs(gic: &DeviceFd, gicr_typer: &[u64]) -> Result<Vec<u32>> {
130144
// 7 bits of priority.
131145
else if *icc_offset == SYS_ICC_AP0R1_EL1 || *icc_offset == SYS_ICC_AP1R1_EL1 {
132146
if num_priority_bits >= 6 {
133-
icc_attr_access(gic, *icc_offset, i, &val, false)?;
134-
state.push(val);
147+
state.push(icc_attr_get(gic, *icc_offset, i)?);
135148
}
136149
} else if *icc_offset == SYS_ICC_AP0R2_EL1
137150
|| *icc_offset == SYS_ICC_AP0R3_EL1
138151
|| *icc_offset == SYS_ICC_AP1R2_EL1
139152
|| *icc_offset == SYS_ICC_AP1R3_EL1
140153
{
141154
if num_priority_bits == 7 {
142-
icc_attr_access(gic, *icc_offset, i, &val, false)?;
143-
state.push(val);
155+
state.push(icc_attr_get(gic, *icc_offset, i)?);
144156
}
145157
} else {
146-
icc_attr_access(gic, *icc_offset, i, &val, false)?;
147-
state.push(val);
158+
state.push(icc_attr_get(gic, *icc_offset, i)?);
148159
}
149160
}
150161
}
@@ -165,7 +176,7 @@ pub fn set_icc_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result
165176
}
166177
if *icc_offset == SYS_ICC_AP0R1_EL1 || *icc_offset == SYS_ICC_AP1R1_EL1 {
167178
if num_priority_bits >= 6 {
168-
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
179+
icc_attr_set(gic, *icc_offset, i, state[idx])?;
169180
idx += 1;
170181
}
171182
continue;
@@ -176,12 +187,12 @@ pub fn set_icc_regs(gic: &DeviceFd, gicr_typer: &[u64], state: &[u32]) -> Result
176187
|| *icc_offset == SYS_ICC_AP1R3_EL1
177188
{
178189
if num_priority_bits == 7 {
179-
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
190+
icc_attr_set(gic, *icc_offset, i, state[idx])?;
180191
idx += 1;
181192
}
182193
continue;
183194
}
184-
icc_attr_access(gic, *icc_offset, i, &state[idx], true)?;
195+
icc_attr_set(gic, *icc_offset, i, state[idx])?;
185196
idx += 1;
186197
}
187198
}

0 commit comments

Comments
 (0)