`put()` trusted an unchecked `as` cast from `usize` to `c_int`.
When the length exceeds `i32::MAX` that cast wraps around to a
negative value.
This ultimately resulted in a kernel panic when the reinterpreted
value via `__nla_reserve()` and `skb_put()` became enormous.
Validate payload and header both fit together in a `u16`, rejecting
any payload that wouldn't leave room for `NLA_HDRLEN`.
Signed-off-by: Sagar Taunk <redacted>
---
Changes Since V1:
Tried to make the diff smaller as pointed out by Alexandre Courbot.
Moreover, as pointed out by Sashiko,`nlattr` is stored in a 16-bit
field which houses both the header and the payload, so make the check
verify that there is enough space left for the header to fit with the
payload.
rust/kernel/net/netlink.rs | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/rust/kernel/net/netlink.rs b/rust/kernel/net/netlink.rs
index a2f4bd171dcf..667667346f96 100644
--- a/rust/kernel/net/netlink.rs
+++ b/rust/kernel/net/netlink.rs
@@ -90,9 +90,14 @@ fn put<T>(&mut self, attrtype: c_int, value: &T) -> Result
where
T: ?Sized + IntoBytes + Immutable,
{
+ let max_payload_len = u16::MAX as usize - size_of::<bindings::nlattr>();
+
let skb = self.skb.skb.as_ptr();
let len = size_of_val(value);
let ptr = core::ptr::from_ref(value).cast::<c_void>();
+ if len > max_payload_len {
+ return Err(EMSGSIZE);
+ }
// 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) })--
2.55.0