]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ethtool: rss: Fix hfunc and input_xfrm parsing on big endian
authorGal Pressman <gal@nvidia.com>
Mon, 6 Jul 2026 05:50:17 +0000 (08:50 +0300)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 9 Jul 2026 09:38:30 +0000 (11:38 +0200)
ETHTOOL_A_RSS_HFUNC and ETHTOOL_A_RSS_INPUT_XFRM are NLA_U32 attributes,
but ethnl_rss_set() and ethnl_rss_create_doit() parse them with
ethnl_update_u8(), which reads a single byte.

On little endian this happens to read the least significant byte and
works as long as the value fits in a byte. On big endian it reads the
most significant byte, so the requested value is parsed incorrectly.

The destination fields in struct ethtool_rxfh_param are u8, so the
attribute can't be read directly with ethnl_update_u32().
Cap the hfunc policy at U8_MAX so an out of range value is rejected
instead of being silently truncated into the u8 field, and add
ethnl_update_u8_u32() to read the full u32 and narrow it into the u8
destination.

Fixes: 82ae67cbc423 ("ethtool: rss: support setting hfunc via Netlink")
Fixes: d3e2c7bab124 ("ethtool: rss: support setting input-xfrm via Netlink")
Fixes: a166ab7816c5 ("ethtool: rss: support creating contexts via Netlink")
Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
Reviewed-by: Nimrod Oren <noren@nvidia.com>
Signed-off-by: Gal Pressman <gal@nvidia.com>
Link: https://patch.msgid.link/20260706055017.3355806-1-gal@nvidia.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
net/ethtool/netlink.h
net/ethtool/rss.c

index 4ca2eca2e94b1458720a715953690f1bbfbfd342..3e969a070f9fe4aa2f56579d0ed708c80db6d826 100644 (file)
@@ -114,6 +114,34 @@ static inline void ethnl_update_u8(u8 *dst, const struct nlattr *attr,
        *mod = true;
 }
 
+/**
+ * ethnl_update_u8_u32() - update u8 value from an NLA_U32 attribute
+ * @dst:  value to update
+ * @attr: netlink attribute with new value or null
+ * @mod:  pointer to bool for modification tracking
+ *
+ * Some attributes are NLA_U32 on the wire but are stored in a u8. Read the
+ * full 32-bit value from NLA_U32 netlink attribute @attr and narrow it into
+ * the u8 pointed to by @dst; do nothing if @attr is null.
+ * Bool pointed to by @mod is set to true if this function changed the value
+ * of *dst, otherwise it is left as is.
+ */
+static inline void ethnl_update_u8_u32(u8 *dst, const struct nlattr *attr,
+                                      bool *mod)
+{
+       u32 val;
+
+       if (!attr)
+               return;
+       val = nla_get_u32(attr);
+       DEBUG_NET_WARN_ON_ONCE(val > U8_MAX);
+       if (*dst == val)
+               return;
+
+       *dst = val;
+       *mod = true;
+}
+
 /**
  * ethnl_update_bool32() - update u32 used as bool from NLA_U8 attribute
  * @dst:  value to update
index d8adc78e3775a0394543f5881f592e6a6ee95b4e..d4a1a4724b67537576776e014cc4e6e5fff6fb97 100644 (file)
@@ -570,7 +570,7 @@ static const struct nla_policy ethnl_rss_flows_policy[] = {
 const struct nla_policy ethnl_rss_set_policy[ETHTOOL_A_RSS_FLOW_HASH + 1] = {
        [ETHTOOL_A_RSS_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
        [ETHTOOL_A_RSS_CONTEXT] = { .type = NLA_U32, },
-       [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_MIN(NLA_U32, 1),
+       [ETHTOOL_A_RSS_HFUNC] = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX),
        [ETHTOOL_A_RSS_INDIR] = { .type = NLA_BINARY, },
        [ETHTOOL_A_RSS_HKEY] = NLA_POLICY_MIN(NLA_BINARY, 1),
        [ETHTOOL_A_RSS_INPUT_XFRM] =
@@ -851,7 +851,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
        indir_mod = !!tb[ETHTOOL_A_RSS_INDIR];
 
        rxfh.hfunc = data.hfunc;
-       ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
+       ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
        if (rxfh.hfunc == data.hfunc)
                rxfh.hfunc = ETH_RSS_HASH_NO_CHANGE;
 
@@ -860,7 +860,8 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
                goto exit_free_indir;
 
        rxfh.input_xfrm = data.input_xfrm;
-       ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
+       ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM],
+                           &mod);
        xfrm_sym = rxfh.input_xfrm || data.input_xfrm;
        if (rxfh.input_xfrm == data.input_xfrm)
                rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
@@ -934,7 +935,7 @@ const struct ethnl_request_ops ethnl_rss_request_ops = {
 const struct nla_policy ethnl_rss_create_policy[ETHTOOL_A_RSS_INPUT_XFRM + 1] = {
        [ETHTOOL_A_RSS_HEADER]  = NLA_POLICY_NESTED(ethnl_header_policy),
        [ETHTOOL_A_RSS_CONTEXT] = NLA_POLICY_MIN(NLA_U32, 1),
-       [ETHTOOL_A_RSS_HFUNC]   = NLA_POLICY_MIN(NLA_U32, 1),
+       [ETHTOOL_A_RSS_HFUNC]   = NLA_POLICY_RANGE(NLA_U32, 1, U8_MAX),
        [ETHTOOL_A_RSS_INDIR]   = NLA_POLICY_MIN(NLA_BINARY, 1),
        [ETHTOOL_A_RSS_HKEY]    = NLA_POLICY_MIN(NLA_BINARY, 1),
        [ETHTOOL_A_RSS_INPUT_XFRM] =
@@ -1048,14 +1049,15 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
                goto exit_clean_data;
        indir_user_size = ret;
 
-       ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
+       ethnl_update_u8_u32(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
 
        ret = rss_set_prep_hkey(dev, info, &data, &rxfh, &mod);
        if (ret)
                goto exit_free_indir;
 
        rxfh.input_xfrm = RXH_XFRM_NO_CHANGE;
-       ethnl_update_u8(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM], &mod);
+       ethnl_update_u8_u32(&rxfh.input_xfrm, tb[ETHTOOL_A_RSS_INPUT_XFRM],
+                           &mod);
 
        ctx = ethtool_rxfh_ctx_alloc(ops, data.indir_size, data.hkey_size);
        if (!ctx) {