From: Jakub Kicinski <kuba@kernel.org>
To: rkannoth@marvell.com
Cc: Jakub Kicinski <kuba@kernel.org>,
linux-kernel@vger.kernel.org, chuck.lever@oracle.com,
jiri@resnulli.us, netdev@vger.kernel.org,
donald.hunter@gmail.com, saeedm@nvidia.com, sgoutham@marvell.com,
edumazet@google.com, davem@davemloft.net, matttbe@kernel.org,
horms@kernel.org, cjubran@nvidia.com, pabeni@redhat.com,
shshitrit@nvidia.com
Subject: Re: [v4,net-next,5/5] octeontx2-af: npc: Support for custom KPU profile from filesystem
Date: Mon, 16 Mar 2026 20:32:15 -0700 [thread overview]
Message-ID: <20260317033214.646969-2-kuba@kernel.org> (raw)
In-Reply-To: <20260312022754.2029595-6-rkannoth@marvell.com>
This is an AI-generated review of your patch. The human sending this
email says "review on patch 3 looks legit, so sending this along
even tho not so sure about this one".
For local reproduction steps see https://netdev-ai.bots.linux.dev/ai-local.html
---
octeontx2-af: npc: Support for custom KPU profile from filesystem
This commit provides a mechanism to load custom KPU (Key Parse Unit)
profiles from the filesystem at module load time, avoiding the need to
flash updated firmware on deployed devices.
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
> index 8dad272fd069..c8c09a91b13f 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
[ ... ]
> @@ -666,7 +672,8 @@ void npc_cn20k_parser_profile_init(struct rvu *rvu, int blkaddr)
> num_pkinds = min_t(int, hw->npc_pkinds, num_pkinds);
>
> for (idx = 0; idx < num_pkinds; idx++)
> - npc_config_kpmaction(rvu, blkaddr, &rvu->kpu.ikpu[idx],
> + npc_config_kpmaction(rvu, blkaddr,
> + npc_get_ikpu_nth_entry(rvu, idx),
> 0, idx, true);
When a filesystem-loaded profile is used on cn20k (from_fs=true),
npc_get_ikpu_nth_entry() returns &profile->ikpu2[idx]. However, ikpu2
is never allocated for the cn20k path.
The allocation of ikpu2 happens inside npc_apply_custom_kpu() in the
from_fs block, but that block is never reached for cn20k because
npc_apply_custom_kpu() returns early via npc_cn20k_apply_custom_kpu()
before reaching the ikpu2 allocation:
static int npc_apply_custom_kpu(struct rvu *rvu,
struct npc_kpu_profile_adapter *profile,
bool from_fs, int *fw_kpus)
{
...
if (is_cn20k(rvu->pdev))
return npc_cn20k_apply_custom_kpu(rvu, profile);
if (from_fs) {
profile->ikpu2 = devm_kcalloc(rvu->dev, 1,
sizeof(ikpu_action_entries),
GFP_KERNEL);
...
}
...
}
However, npc_load_kpu_profile() unconditionally sets profile->from_fs =
true after npc_apply_custom_kpu() returns success.
Does this code dereference a NULL-offset pointer? The resulting
&NULL[idx] is passed to npc_config_kpmaction() which dereferences it
(kpuaction->errlev), which would cause a kernel crash.
This same issue affects cam2 and action2 via npc_get_kpu_cam_nth_entry()
and npc_get_kpu_action_nth_entry().
>
> /* Program KPM CAM and Action profiles */
[ ... ]
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
> index 352dc5f1d5b9..e348cdd375eb 100644
> --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
> +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc.c
[ ... ]
> @@ -1731,32 +1837,89 @@ static int npc_apply_custom_kpu(struct rvu *rvu,
> return -EINVAL;
> }
>
> + *fw_kpus = fw->kpus;
> +
> + sfw = devm_kcalloc(rvu->dev, 1, sizeof(*sfw), GFP_KERNEL);
> + if (!sfw)
> + return -ENOMEM;
> +
> + memcpy(sfw, fw, sizeof(*sfw));
> +
> profile->custom = 1;
> - profile->name = fw->name;
> + profile->name = sfw->name;
> profile->version = le64_to_cpu(fw->version);
> - profile->mcam_kex_prfl.mkex = &fw->mkex;
> - profile->lt_def = &fw->lt_def;
> + profile->mcam_kex_prfl.mkex = &sfw->mkex;
> + profile->lt_def = &sfw->lt_def;
> +
> + /* Binary blob contains ikpu actions entries at start of data[0] */
> + if (from_fs) {
> + profile->ikpu2 = devm_kcalloc(rvu->dev, 1,
> + sizeof(ikpu_action_entries),
> + GFP_KERNEL);
> + if (!profile->ikpu2)
> + return -ENOMEM;
> +
> + action = (struct npc_kpu_profile_action *)(fw->data + offset);
> +
> + if (rvu->kpu_fwdata_sz < hdr_sz + sizeof(ikpu_action_entries))
> + return -ENOMEM;
> +
> + memcpy((void *)profile->ikpu2, action, sizeof(ikpu_action_entries));
> + offset += sizeof(ikpu_action_entries);
> + }
> +
> for (kpu = 0; kpu < fw->kpus; kpu++) {
> fw_kpu = (struct npc_kpu_fwdata *)(fw->data + offset);
> - if (fw_kpu->entries > KPU_MAX_CST_ENT)
> - dev_warn(rvu->dev,
> - "Too many custom entries on KPU%d: %d > %d\n",
> - kpu, fw_kpu->entries, KPU_MAX_CST_ENT);
> - entries = min(fw_kpu->entries, KPU_MAX_CST_ENT);
> - cam = (struct npc_kpu_profile_cam *)fw_kpu->data;
> - offset += sizeof(*fw_kpu) + fw_kpu->entries * sizeof(*cam);
> + if (!from_fs) {
> + if (fw_kpu->entries > KPU_MAX_CST_ENT)
> + dev_warn(rvu->dev,
> + "Too many custom entries on KPU%d: %d > %d\n",
> + kpu, fw_kpu->entries, KPU_MAX_CST_ENT);
> + entries = min(fw_kpu->entries, KPU_MAX_CST_ENT);
> + cam = (struct npc_kpu_profile_cam *)fw_kpu->data;
> + offset += sizeof(*fw_kpu) + fw_kpu->entries * sizeof(*cam);
> + action = (struct npc_kpu_profile_action *)(fw->data + offset);
> + offset += fw_kpu->entries * sizeof(*action);
> + if (rvu->kpu_fwdata_sz < hdr_sz + offset) {
> + dev_warn(rvu->dev,
> + "Profile size mismatch on KPU%i parsing.\n",
> + kpu + 1);
> + return -EINVAL;
> + }
> + for (entry = 0; entry < entries; entry++) {
> + profile->kpu[kpu].cam[entry] = cam[entry];
> + profile->kpu[kpu].action[entry] = action[entry];
> + }
> + continue;
> + }
> + entries = fw_kpu->entries;
> + dev_info(rvu->dev,
> + "Loading %u entries on KPU%d\n", entries, kpu);
> +
> + cam2 = (struct npc_kpu_profile_cam2 *)fw_kpu->data;
> + offset += sizeof(*fw_kpu) + fw_kpu->entries * sizeof(*cam2);
> action = (struct npc_kpu_profile_action *)(fw->data + offset);
> offset += fw_kpu->entries * sizeof(*action);
> if (rvu->kpu_fwdata_sz < hdr_sz + offset) {
> dev_warn(rvu->dev,
> - "Profile size mismatch on KPU%i parsing.\n",
> + "profile size mismatch on kpu%i parsing.\n",
> kpu + 1);
> return -EINVAL;
> }
> +
> + profile->kpu[kpu].cam_entries2 = entries;
> + profile->kpu[kpu].action_entries2 = entries;
> + ret = npc_alloc_kpu_cam2_n_action2(rvu, kpu, entries);
> + if (ret) {
> + dev_warn(rvu->dev,
> + "profile entry allocation failed for kpu=%d for %d entries\n",
> + kpu, entries);
> + return -EINVAL;
> + }
> +
> for (entry = 0; entry < entries; entry++) {
> - profile->kpu[kpu].cam[entry] = cam[entry];
> - profile->kpu[kpu].action[entry] = action[entry];
> + profile->kpu[kpu].cam2[entry] = cam2[entry];
> + profile->kpu[kpu].action2[entry] = action[entry];
> }
> }
This is a concern raised by Simon Horman during review that was
acknowledged but not addressed in v4.
The loop variable 'entry' is declared as u16 in npc_apply_custom_kpu():
u16 kpu, entry;
while 'entries' is an int:
entries = fw_kpu->entries;
In the from_fs path, 'entries' is read directly from the firmware blob
without bounds checking. If a corrupt or malicious firmware file
specifies fw_kpu->entries > 65535, the u16 variable wraps around at
65536:
for (entry = 0; entry < entries; entry++)
When entry reaches 65535, incrementing it produces 0, which is still
less than entries, creating an infinite loop that hangs the kernel.
Should the code cap entries in the from_fs path or use an int-sized loop
variable? The author acknowledged this concern with 'ACK' in v3 review
discussion, and Simon emphasized that "the kernel should not rely on
filesystem data being well-formed."
>
> return 0;
[ ... ]
> diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c b/drivers/net/ethernet/marvell/octeontx2/af/cn20k/npc.c
> @@ -521,13 +521,17 @@ npc_program_single_kpm_profile(struct rvu *rvu, int blkaddr,
[ ... ]
> @@ -536,16 +540,18 @@ npc_program_single_kpm_profile(struct rvu *rvu, int blkaddr,
> max_entries = rvu->hw->npc_kpu_entries / 2;
> entry = start_entry;
> /* Program CAM match entries for previous kpm extracted data */
> - num_entries = min_t(int, profile->cam_entries, max_entries);
> + num_entries = min_t(int, num_cam_entries, max_entries);
> for (idx = 0; entry < num_entries + start_entry; entry++, idx++)
> - npc_config_kpmcam(rvu, blkaddr, &profile->cam[idx],
> + npc_config_kpmcam(rvu, blkaddr,
> + npc_get_kpu_cam_nth_entry(rvu, profile, idx),
> kpm, entry);
>
> entry = start_entry;
> /* Program this kpm's actions */
> - num_entries = min_t(int, profile->action_entries, max_entries);
> + num_entries = min_t(int, num_action_entries, max_entries);
> for (idx = 0; entry < num_entries + start_entry; entry++, idx++)
> - npc_config_kpmaction(rvu, blkaddr, &profile->action[idx],
> + npc_config_kpmaction(rvu, blkaddr,
> + npc_get_kpu_action_nth_entry(rvu, profile, idx),
> kpm, entry, false);
> }
>
> @@ -615,6 +621,12 @@ npc_program_kpm_profile(struct rvu *rvu, int blkaddr,
> const struct npc_kpu_profile *profile1,
> const struct npc_kpu_profile *profile2)
> {
[ ... ]
> + /* Program the first KPM profile */
> + npc_program_single_kpm_profile(rvu, blkaddr, idx,
> + 0, profile1);
> +
> + /* Program the second KPM profile */
> + start_entry = profile->cam_entries;
> + npc_program_single_kpm_profile(rvu, blkaddr, idx,
> + start_entry, profile2);
> +
> + total_cam_entries = profile->cam_entries + profile2->cam_entries;
> + total_action_entries = profile->action_entries + profile2->action_entries;
> + npc_enable_kpm_entry(rvu, blkaddr, idx, 0,
> + total_cam_entries, total_action_entries);
> +
> + val = profile->cam_entries;
> + rvu_write64(rvu, blkaddr, NPC_AF_KPMX_PASS2_OFFSET(idx), val);
> }
Looking at npc_program_kpm_profile(), it uses profile->cam_entries
directly in four places instead of calling npc_get_num_kpu_cam_entries().
When profile->from_fs is true (filesystem-loaded KPU profile), the
accessor returns cam_entries2, while the raw field cam_entries retains
its default value from npc_prepare_default_kpu().
Does this cause the start_entry for the second profile to be based on
the wrong count, leading profile2 entries to overlap or be offset
incorrectly? Would total_cam_entries and total_action_entries passed to
npc_enable_kpm_entry() be wrong, enabling the wrong set of entries? And
would NPC_AF_KPMX_PASS2_OFFSET be set incorrectly?
The callee npc_program_single_kpm_profile() was correctly updated to use
accessors, but npc_program_kpm_profile() was not.
prev parent reply other threads:[~2026-03-17 3:33 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-12 2:27 [PATCH v4 net-next 0/5] octeontx2-af: npc: Enhancements Ratheesh Kannoth
2026-03-12 2:27 ` [PATCH v4 net-next 1/5] octeontx2-af: npc: cn20k: debugfs enhancements Ratheesh Kannoth
2026-03-12 2:27 ` [PATCH v4 net-next 2/5] devlink: Implement devlink param multi attribute nested data values Ratheesh Kannoth
2026-03-17 3:36 ` Jakub Kicinski
2026-03-17 13:22 ` Jiri Pirko
2026-03-18 2:31 ` Ratheesh Kannoth
2026-03-12 2:27 ` [PATCH v4 net-next 3/5] octeontx2-af: npc: cn20k: add subbank search order control Ratheesh Kannoth
2026-03-17 3:32 ` [v4,net-next,3/5] " Jakub Kicinski
2026-03-12 2:27 ` [PATCH v4 net-next 4/5] octeontx2-af: npc: cn20k: dynamically allocate and free default MCAM entries Ratheesh Kannoth
2026-03-12 2:27 ` [PATCH v4 net-next 5/5] octeontx2-af: npc: Support for custom KPU profile from filesystem Ratheesh Kannoth
2026-03-17 3:32 ` Jakub Kicinski [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260317033214.646969-2-kuba@kernel.org \
--to=kuba@kernel.org \
--cc=chuck.lever@oracle.com \
--cc=cjubran@nvidia.com \
--cc=davem@davemloft.net \
--cc=donald.hunter@gmail.com \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=jiri@resnulli.us \
--cc=linux-kernel@vger.kernel.org \
--cc=matttbe@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=rkannoth@marvell.com \
--cc=saeedm@nvidia.com \
--cc=sgoutham@marvell.com \
--cc=shshitrit@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox