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 --- 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(&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::(); - // 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::(); + // 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