]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ethtool: cmis: require exact CDB reply length
authorJakub Kicinski <kuba@kernel.org>
Fri, 22 May 2026 23:13:09 +0000 (16:13 -0700)
committerJakub Kicinski <kuba@kernel.org>
Tue, 26 May 2026 15:19:33 +0000 (08:19 -0700)
Malicious SFP module could respond with rpl_len longer than
what cmis_cdb_process_reply() expected, leading to OOB writes.
Malicious HW is a bit theoretical but some modules may just
be buggy and/or the reads may occasionally get corrupted,
so let's protect the kernel.

The existing check protects from short replies. We need to
protect from long ones, too. All callers that pass a non-zero
rpl_exp_len cast the reply payload to a fixed-layout struct
and read fields at fixed offsets, with no version negotiation
or short-reply handling:

  - cmis_cdb_validate_password()
  - cmis_cdb_module_features_get()
  - cmis_fw_update_fw_mng_features_get()

so let's assume that responses longer than expected do not
have to be handled gracefully here. Add a warning message
to make the debug easier in case my understanding is wrong...

Note that page_data->length (argument of kmalloc) comes from
last arg to ethtool_cmis_page_init() which is rpl_exp_len.

Note2 that AIs also like to point out overflows in args->req.payload
itself (which is a fixed-size 120 B buffer, on the stack),
but callers should be reading structs defined by the standard,
so protecting from requests for more data than max seem like
defensive programming.

Fixes: a39c84d79625 ("ethtool: cmis_cdb: Add a layer for supporting CDB commands")
Reviewed-by: Danielle Ratson <danieller@nvidia.com>
Link: https://patch.msgid.link/20260522231312.1710836-7-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ethtool/cmis_cdb.c

index 3670ca42dd403e2fbaec43e0ce760a6c2fec1b96..f3a53a984460996aadb4dd9d7d1c7b51814c87fd 100644 (file)
@@ -513,8 +513,13 @@ static int cmis_cdb_process_reply(struct net_device *dev,
        }
 
        rpl = (struct ethtool_cmis_cdb_rpl *)page_data->data;
-       if ((args->rpl_exp_len > rpl->hdr.rpl_len + rpl_hdr_len) ||
-           !rpl->hdr.rpl_chk_code) {
+       if (rpl->hdr.rpl_len != args->rpl_exp_len) {
+               netdev_warn(dev, "CDB reply length mismatch, expected %u got %u\n",
+                           args->rpl_exp_len, rpl->hdr.rpl_len);
+               err = -EIO;
+               goto out;
+       }
+       if (!rpl->hdr.rpl_chk_code) {
                err = -EIO;
                goto out;
        }