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:
Red Bear OS
2026-07-19 12:27:50 +09:00
parent 0683c020a5
commit bcf8ea25a7
+32 -15
View File
@@ -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;
}
}
_ => {}
}
}
}