fix(client): align the error-queue control buffer

The MSG_ERRQUEUE control buffer was a plain [u8; N], which only
guarantees byte alignment. recvmsg's cmsgs() walks cmsghdr references
out of that storage and requires cmsghdr alignment, so this mirrors
the parent module's #[repr(align(8))] ControlBuffer instead of relying
on incidental stack alignment.
This commit is contained in:
Nils Andreas Svee committed 2026-08-15 15:08:30 +07:00
1 parent 97b094539f
commit 12b4816e14
1 file changed
+15 -2
@@ -131,6 +131,19 @@ pub(crate) fn classify(cmsgs: impl Iterator<Item = ControlMessageOwned>) -> Erro
}
}
/// Reusable per-receive control buffer. `recvmsg` control data must satisfy
/// `cmsghdr` alignment; a plain `[u8; N]` only guarantees byte alignment, so
/// this mirrors the parent module's `ControlBuffer` rather than reading
/// `cmsghdr`s out of unaligned storage.
#[repr(align(8))]
struct ErrorQueueControlBuffer([u8; ERROR_QUEUE_CONTROL_LEN]);
impl ErrorQueueControlBuffer {
fn new() -> Self {
Self([0; ERROR_QUEUE_CONTROL_LEN])
}
}
/// Nonblocking drain of a single record from `fd`'s `MSG_ERRQUEUE`.
///
/// Returns `Ok(None)` when the queue is empty (`EAGAIN`/`EWOULDBLOCK`).
@@ -140,12 +153,12 @@ pub(crate) fn classify(cmsgs: impl Iterator<Item = ControlMessageOwned>) -> Erro
pub(crate) fn try_recv_error_queue_record(fd: RawFd) -> io::Result<Option<ErrorQueueRecord>> {
let mut payload: [u8; 0] = [];
let mut iov = [IoSliceMut::new(&mut payload)];
let mut control = [0_u8; ERROR_QUEUE_CONTROL_LEN];
let mut control = ErrorQueueControlBuffer::new();
match recvmsg::<()>(
fd,
&mut iov,
Some(&mut control),
Some(&mut control.0),
MsgFlags::MSG_ERRQUEUE | MsgFlags::MSG_DONTWAIT,
) {
Ok(msg) => {