[PATCH] rust: net: netlink: Migrate to zerocopy's `IntoBytes`

From: Sagar Taunk

Date: Sat Sep 12 2026 - 02:29:10 EST


Replace the kernel's own `transmute::FromBytes` and `AsBytes` traits
with their zerocopy equivalents. Specifically, this updates
`GenlMsg::put` to rely on `IntoBytes` for converting attributes into
byte slices.

Also, add `Immutable` trait bound on `GenlMsg::put` as zerocopy
splits the `no interior mutability` guarantee that `AsBytes` bundled
together.

Moreover, shashiko pointed out `put()` trusted an unchecked `as` cast
from `usize` to `c_int` for the attribute length. A length larger than
`i32::MAX` wraps to a negative value in that cast, which can pass
`nla_put()`'s own signed `skb_tailroom()` check and then be reinterpreted
as an enormous unsigned length via `__nla_reserve()`/`skb_put()`, leading
to a kernel panic via `skb_over_panic()`. Validate the length with
`c_int::try_from()` and reject it with `EMSGSIZE` instead.

Link: https://github.com/Rust-for-Linux/linux/issues/1241
Signed-off-by: Sagar Taunk <sagartaunk@xxxxxxxxx>
---
rust/kernel/net/netlink.rs | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/rust/kernel/net/netlink.rs b/rust/kernel/net/netlink.rs
index 22ef3dde36fa..f929f63b32c1 100644
--- a/rust/kernel/net/netlink.rs
+++ b/rust/kernel/net/netlink.rs
@@ -12,11 +12,12 @@
alloc::{self, AllocError},
error::to_result,
prelude::*,
- transmute::AsBytes,
types::Opaque,
ThisModule,
};

+use zerocopy::{Immutable, IntoBytes};
+
use core::{
mem::ManuallyDrop,
ptr::NonNull, //
@@ -84,14 +85,22 @@ impl GenlMsg {
#[inline]
fn put<T>(&mut self, attrtype: c_int, value: &T) -> Result
where
- T: ?Sized + AsBytes,
+ T: ?Sized + IntoBytes + Immutable,
{
let skb = self.skb.skb.as_ptr();
- let len = size_of_val(value);
- let ptr = core::ptr::from_ref(value).cast::<c_void>();
- // SAFETY: `skb` is valid by `NetlinkSkBuff` type invariants, and the provided value is
- // readable and initialized for its `size_of` bytes.
- to_result(unsafe { bindings::nla_put(skb, attrtype, len as c_int, ptr) })
+ let bytes = value.as_bytes();
+ // `nla_put()` takes attrlen as a plain `c_int`. If `bytes.len()`
+ // doesn't fit, an `as` cast would wrap around a negative value.
+ // Which then, would feed a huge unsigned length to `__nla_reserve()`
+ // and `skb_put()` causing it to panic via `skb_over_panic()`. So,
+ // the following check will reject it instead.
+ let len = c_int::try_from(bytes.len()).map_err(|_| EMSGSIZE)?;
+ let ptr = bytes.as_ptr().cast::<c_void>();
+ // SAFETY: `skb` is valid as per `NetlinkSkBuff` type invariants.
+ // `bytes` is a valid Rust slice, so `ptr` is readable for `len`
+ // bytes, and `T: Immutable` guarantees nothing can mutate `*value`
+ // while `nla_put()` copies it.
+ to_result(unsafe { bindings::nla_put(skb, attrtype, len, ptr) })
}

/// Puts a `u32` attribute into the message.
--
2.55.0