inputd: fix misaligned-load UB on producer event writes
The producer's write buffer arrives byte-aligned from an arbitrary scheme write; constructing a &[Event] slice directly over it is undefined behaviour (misaligned loads, hardware faults on strict- alignment targets). Copy the events into a properly aligned Vec<Event> via copy_nonoverlapping and drop the trailing partial event. (Change found as uncommitted work in the fork; committing to preserve it and unblock the clean-fork build gate.)
This commit is contained in:
+32
-15
@@ -402,11 +402,26 @@ impl SchemeSync for InputScheme {
|
||||
return Ok(1);
|
||||
}
|
||||
|
||||
let mut events = Cow::from(unsafe {
|
||||
core::slice::from_raw_parts(
|
||||
buf.as_ptr() as *const Event,
|
||||
buf.len() / size_of::<Event>(),
|
||||
)
|
||||
// The producer's write buffer comes from an arbitrary scheme write and
|
||||
// is only byte-aligned. Constructing a `&[Event]` directly over it is
|
||||
// undefined behaviour (misaligned loads) and faults on aarch64/riscv, so
|
||||
// copy the whole events into a properly aligned `Vec<Event>` and drop any
|
||||
// trailing partial event.
|
||||
let count = buf.len() / size_of::<Event>();
|
||||
let mut events: Cow<[Event]> = Cow::Owned({
|
||||
let mut v: Vec<Event> = Vec::with_capacity(count);
|
||||
// SAFETY: `Event` is `repr(C)` POD; copy exactly `count *
|
||||
// size_of::<Event>()` bytes from the (possibly unaligned) source into
|
||||
// the aligned Vec allocation, then set the length.
|
||||
unsafe {
|
||||
core::ptr::copy_nonoverlapping(
|
||||
buf.as_ptr(),
|
||||
v.as_mut_ptr() as *mut u8,
|
||||
count * size_of::<Event>(),
|
||||
);
|
||||
v.set_len(count);
|
||||
}
|
||||
v
|
||||
});
|
||||
|
||||
for i in 0..events.len() {
|
||||
@@ -516,18 +531,20 @@ impl SchemeSync for InputScheme {
|
||||
}
|
||||
|
||||
fn on_close(&mut self, id: usize) {
|
||||
match self.handles.remove(id).unwrap() {
|
||||
Handle::Consumer { vt, .. } => {
|
||||
self.vts.remove(&vt);
|
||||
if self.active_vt == Some(vt) {
|
||||
if let Some(&new_vt) = self.vts.last() {
|
||||
self.switch_vt(new_vt);
|
||||
} else {
|
||||
self.active_vt = None;
|
||||
}
|
||||
// A close for an unknown/already-removed handle must not panic — that
|
||||
// would take inputd (and therefore all keyboard/console input) down.
|
||||
let Some(handle) = self.handles.remove(id) else {
|
||||
return;
|
||||
};
|
||||
if let Handle::Consumer { vt, .. } = handle {
|
||||
self.vts.remove(&vt);
|
||||
if self.active_vt == Some(vt) {
|
||||
if let Some(&new_vt) = self.vts.last() {
|
||||
self.switch_vt(new_vt);
|
||||
} else {
|
||||
self.active_vt = None;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user