Netdev List
 help / color / mirror / Atom feed
From: Yishai Hadas <yishaih@nvidia.com>
To: <netdev-bot+sashiko@kernel.org>
Cc: <jgg@ziepe.ca>, <leon@kernel.org>, <linux-rdma@vger.kernel.org>,
	<netdev@vger.kernel.org>, <saeedm@nvidia.com>,
	<tariqt@nvidia.com>, <mbloch@nvidia.com>, <kuba@kernel.org>,
	<pabeni@redhat.com>, <edumazet@kernel.org>, <davem@davemloft.net>,
	<andrew+netdev@lunn.ch>, <dtatulea@nvidia.com>,
	<moshe@nvidia.com>, <maorg@nvidia.com>
Subject: Re: [PATCH rdma-next 03/23] net/mlx5: Add capability filtering in adversarial mode
Date: Mon, 9 Nov 2026 10:27:23 +0200	[thread overview]
Message-ID: <02149fa6-3ded-4a32-a0fe-986c5c35e8bd@nvidia.com> (raw)
In-Reply-To: <179408308320.994134.5973332884050170035@kernel.org>

On 07/11/2026 22:24, netdev-bot+sashiko@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 7 potential
> issue(s) to consider.
> 
> Critical: 0 · High: 3 · Medium: 3 · Low: 1
> 
> If you believe none of the issues are legitimate, please reply to each
> issue of Medium severity or higher and explain why the AI is wrong.
> If any of them are legitimate and you plan to respin, please let patchwork
> know by including "pw-bot: cr" as a separate line at the end of your reply
> (one such reply per series is enough).
> 
> - [High] The adversarial-mode filter copies cmd_hca_cap.max_tc from
>    firmware with MLX5_HCA_CAP_ALLOW and never clamps or checks it…
> - [High] net/mlx5: unchecked minimum EQ caps cause divide-by-zero in
>    alloc_cpu_rmap()
> - [High] net/mlx5: unclamped log_max_flow_counter_bulk causes OOB heap
>    read
> - [Medium] The commit message says the patch will "trust only a subset of
>    capabilities" and will "Start off by clearing all firmware…
> - [Medium] In adversarial mode, dev->caps.hca[MLX5_CAP_GENERAL]->cur no
>    longer reflects firmware's current configuration: -…
> - [Medium] mlx5_adversarial_filter_general_2() checks
>    log_min_mkey_entity_size only against (max_mkey_log_entity_size_mtt ?:…
> - [Low] The num_ports clamp uses the literal 8 as its upper bound, and
>    the comment says the bound comes from MLX5_MAX_PORTS (`/* 0 is…
> 
> --
> 
> net/mlx5: Add capability filtering in adversarial mode
> 
> This adds mlx5_store_caps(). In adversarial mode it replaces the raw
> memcpy of firmware HCA capabilities with per-field allow-list filtering
> for the general, general_2 and ethernet offload capability types. A few
> numeric capabilities are clamped or range-checked, and a failed check
> aborts driver load. The remaining capability types are either passed
> through whole or zeroed.
> 
>> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/caps_filter.c b/drivers/net/ethernet/mellanox/mlx5/core/caps_filter.c
>> new file mode 100644
>> index 0000000000000..67b2603397450
>> --- /dev/null
>> +++ b/drivers/net/ethernet/mellanox/mlx5/core/caps_filter.c
>> @@ -0,0 +1,381 @@
> 
> [ ... ]
> 
>> +static int mlx5_adversarial_filter_general(struct mlx5_core_dev *dev, void *dst,
>> +					   void *src)
>> +{
> 
> [ ... ]
> 
>> +	MLX5_HCA_CAP_ALLOW(cmd_hca_cap, port_type);
>> +	/* 0 is invalid, 8 is for MLX5_MAX_PORTS */
>> +	MLX5_HCA_CAP_CLAMP(cmd_hca_cap, num_ports, 1, 8);
> 
> [Severity: Low]
> This isn't a bug, but the comment says the bound comes from MLX5_MAX_PORTS.
> Could the clamp use MLX5_MAX_PORTS directly instead of the literal 8? The
> two values agree today.
> 
>> +	MLX5_HCA_CAP_ALLOW(cmd_hca_cap, log_max_msg);
>> +	MLX5_HCA_CAP_ALLOW(cmd_hca_cap, max_tc);
> 
> [Severity: High]
> Should max_tc be clamped or checked here too? It is a 4 bit field, so
> firmware can report up to 15. mlx5_max_tc() turns it into a loop bound:
> 
> drivers/net/ethernet/mellanox/mlx5/core/port.c:mlx5_max_tc() {
> 	u8 num_tc = MLX5_CAP_GEN(mdev, max_tc) ? : 8;
> 
> 	return num_tc - 1;
> }
> 
> That can return up to 14, but the callers assume at most 7 and use it to
> index 8-entry arrays.
> 
> This path runs at netdev init with no user action. It is gated only by
> the qos and ets caps, and both are allow-listed above:
> 

This capability is not used by RDMA.
Currently, netdev doesn't handle adversarial flows.


> [ ... ]
> 
>> +static int mlx5_adversarial_filter_general_2(struct mlx5_core_dev *dev, void *dst,
>> +					     void *src)
>> +{
> 
> [ ... ]
> 
>> +	val = MLX5_GET(cmd_hca_cap_2, dst, max_mkey_log_entity_size_mtt) ?: 31;
>> +	err = MLX5_HCA_CAP_CHECK(cmd_hca_cap_2, log_min_mkey_entity_size, 0,
>> +				 val);
>> +	if (err)
>> +		return err;
>> +
>> +	/* The fixed buffer min is don't-care when its valid bit is clear. */
>> +	if (!MLX5_GET(cmd_hca_cap_2, dst,
>> +		      min_mkey_log_entity_size_fixed_buffer_valid))
>> +		return 0;
> 
> [Severity: Medium]
> log_min_mkey_entity_size is only compared against the MTT max here. Is
> that enough for the code that consumes it?
> 
> In mlx5_ib, get_min_log_entity_size_cap() uses log_min_mkey_entity_size
> as the KSM minimum exactly when the fixed buffer valid bit is clear. The
> KSM maximum comes from max_mkey_log_entity_size_fixed_buffer. This
> combination passes the filter:
> 
>    max_mkey_log_entity_size_mtt = 0
>    log_min_mkey_entity_size = 20
>    max_mkey_log_entity_size_fixed_buffer = 12
>    min_mkey_log_entity_size_fixed_buffer_valid = 0
> 
> mlx5_umem_mkc_find_best_pgsz() then builds GENMASK_ULL(12, 20). That mask
> is empty, so KSM mkey registration fails.
> 
> There is a second case. The filter accepts log_min_mkey_entity_size up to
> 31, and mlx5e_mpwrq_min_page_shift() uses it directly. For a regular RQ,
> mlx5e_mpwrq_page_shift() hits this whenever the value is above PAGE_SHIFT:
> 
> 	if (WARN_ON_ONCE(!xsk && req_page_shift < min_page_shift))
> 
> Should the filter also bound log_min_mkey_entity_size by
> max_mkey_log_entity_size_fixed_buffer and by PAGE_SHIFT?
> 

The PAGE_SHIFT bound will be added in V1. The KSM case only makes the 
registration fail.

> [ ... ]
> 
>> +	switch (cap_type) {
>> +	case MLX5_CAP_GENERAL:
>> +		return mlx5_adversarial_filter_general(dev, dst, src);
>> +	case MLX5_CAP_GENERAL_2:
>> +		return mlx5_adversarial_filter_general_2(dev, dst, src);
>> +	case MLX5_CAP_ETHERNET_OFFLOADS:
>> +		return mlx5_adversarial_filter_per_protocol(dev, dst, src);
>> +
>> +	case MLX5_CAP_ATOMIC:
>> +	case MLX5_CAP_ROCE:
>> +	case MLX5_CAP_FLOW_TABLE:
>> +	case MLX5_CAP_DEV_EVENT:
>> +		memcpy(dst, src, MLX5_UN_SZ_BYTES(hca_cap_union));
>> +		break;
> 
> [Severity: Medium]
> The commit message says:
> 
>    When in adversarial mode, trust only a subset of capabilities
>    and do some additional validations.
> 
>    Start off by clearing all firmware capabilities and enabling
>    them one by one.
> 
> Is the whole-structure pass-through for MLX5_CAP_ATOMIC, MLX5_CAP_ROCE,
> MLX5_CAP_FLOW_TABLE and MLX5_CAP_DEV_EVENT intended? MLX5_CAP_FLOW_TABLE
> in particular carries numeric limits that the steering code relies on.
> 
> There is a related case. mlx5_adversarial_filter_general() allow-lists
> sbcam_reg, qcam_reg, mcam_reg and pcam_reg. In mlx5_query_hca_caps() those
> bits gate mlx5_get_pcam_reg(), mlx5_get_mcam_access_reg_group() and
> mlx5_get_qcam_reg(). These store firmware data straight into
> dev->caps.pcam, dev->caps.mcam and dev->caps.qcam without going through
> mlx5_store_caps().
> 
> If this is deliberate, could the commit message say which capability
> areas are still trusted wholesale?

Yes, it is deliberate. This series filters the caps that the RDMA paths 
use as sizes and limits.

> 
>> diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c
>> index f9e10464a71b5..e76a4486b5a31 100644
>> --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c
>> +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c
>> @@ -325,12 +325,12 @@ int mlx5_core_get_caps_mode(struct mlx5_core_dev *dev, enum mlx5_cap_type cap_ty
>>   
>>   	switch (cap_mode) {
>>   	case HCA_CAP_OPMOD_GET_MAX:
>> -		memcpy(dev->caps.hca[cap_type]->max, hca_caps,
>> -		       MLX5_UN_SZ_BYTES(hca_cap_union));
>> +		err = mlx5_store_caps(dev, cap_type,
>> +				      dev->caps.hca[cap_type]->max, hca_caps);
>>   		break;
>>   	case HCA_CAP_OPMOD_GET_CUR:
>> -		memcpy(dev->caps.hca[cap_type]->cur, hca_caps,
>> -		       MLX5_UN_SZ_BYTES(hca_cap_union));
>> +		err = mlx5_store_caps(dev, cap_type,
>> +				      dev->caps.hca[cap_type]->cur, hca_caps);
>>   		break;
> 
> [Severity: Medium]
> In adversarial mode, dev->caps.hca[MLX5_CAP_GENERAL]->cur now holds the
> filtered image:
> 
> - it is zeroed, and only allow-listed fields are copied back
> - num_of_uars_per_page is forced
> - num_ports, num_lag_ports, num_vhca_ports and log_max_rqt_size are
>    clamped
> 
> handle_hca_cap() still uses ->cur as the template for SET_HCA_CAP:
> 
> drivers/net/ethernet/mellanox/mlx5/core/main.c:handle_hca_cap() {
>      ...
> 	set_hca_cap = MLX5_ADDR_OF(set_hca_cap_in, set_ctx,
> 				   capability);
> 	memcpy(set_hca_cap, dev->caps.hca[MLX5_CAP_GENERAL]->cur,
> 	       MLX5_ST_SZ_BYTES(cmd_hca_cap));
>      ...
> }
> 
> It then writes the whole structure back with set_caps().
> 
> Does this program every firmware-writable field to 0 if it is not on the
> allow-list and handle_hca_cap() does not set it explicitly? Are the forced
> and clamped values also written back to the device?
> 
> If so, adversarial mode changes the device configuration, not only what
> the driver trusts, and the commit message doesn't mention it. Is that
> intended, or should handle_hca_cap() start from the unfiltered firmware
> values?

Right, in adversarial mode SET_HCA_CAP starts from the filtered values.
The default mode is not affected.

> [Severity: High]
> Should log_max_flow_counter_bulk be clamped like log_max_rqt_size?
> It is an 8 bit field, so firmware can report up to 255.

This capability is not used by RDMA. Currently, netdev doesn't handle 
adversarial flows.

> [Severity: High]
> Should the EQ count be range checked as well? log_max_eq and
> max_num_eqs are allow-listed here, and max_num_eqs_24b is allow-listed
> in mlx5_adversarial_filter_general_2(). Nothing makes sure the device
> reports more EQs than the driver reserves for async EQs.
> 
> mlx5_max_eq_cap_get() returns max_num_eqs_24b if it is set, otherwise
> max_num_eqs, otherwise 1 << log_max_eq. get_num_eqs() then takes the
> minimum of two values:
> 
> - the IRQ table's completion vector count
> - max_dev_eqs - MLX5_MAX_ASYNC_EQS
> 
> MLX5_MAX_ASYNC_EQS is 3 or 4, depending on
> CONFIG_INFINIBAND_ON_DEMAND_PAGING.
> 
> Suppose firmware reports exactly MLX5_MAX_ASYNC_EQS, for example
> max_num_eqs = 4 with ODP enabled. Then eq_table->max_comp_eqs becomes
> 0, and probe continues into:
> 
> mlx5_load()
>    mlx5_eq_table_create()
>      alloc_rmap()
>        alloc_irq_cpu_rmap(eq_table->max_comp_eqs)
>          alloc_cpu_rmap(0, GFP_KERNEL)
> 
> alloc_cpu_rmap() fills the near[] table with:
> 
> 	rmap->near[cpu].index = cpu % size;
> 
> Doesn't that divide by zero during driver load? This path is taken
> with CONFIG_RFS_ACCEL on non-SF devices and needs no user action.
> 
> If firmware reports fewer EQs than MLX5_MAX_ASYNC_EQS, max_comp_eqs
> goes negative instead. alloc_cpu_rmap() rejects that value as too
> large. But when the rmap is not allocated, what do
> mlx5_comp_vectors_max() and the completion EQ code make of a negative
> count?
> 
> With trusted firmware this predates the patch. The three fields come
> from two different cap types, so one MLX5_HCA_CAP_CHECK() in a single
> filter function can't cover them all. Would it be simpler to reject
> max_dev_eqs <= MLX5_MAX_ASYNC_EQS in get_num_eqs() or
> mlx5_eq_table_create()? Or is there a good place in the filter for
> this check?
> 

OK, it will be fixed as part of V1.

Yishai

  reply	other threads:[~2026-11-09  8:28 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 16:39 [PATCH rdma-next 00/23] mlx5: Adversarial mode for confidential computing guests Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 01/23] net/mlx5: Add mlx5_trigger_health_work_force() Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-08 16:17     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 02/23] net/mlx5: Derive adversarial mode from CC guest memory encryption Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-03 16:39 ` [PATCH rdma-next 03/23] net/mlx5: Add capability filtering in adversarial mode Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-09  8:27     ` Yishai Hadas [this message]
2026-11-03 16:39 ` [PATCH rdma-next 04/23] net/mlx5: Add macros for reading log-based caps Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-03 16:39 ` [PATCH rdma-next 05/23] RDMA/mlx5: Reject QP transport types not enabled by FW capability Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-03 16:39 ` [PATCH rdma-next 06/23] RDMA/mlx5: Require IB_QPT_DRIVER for the DC vendor QP types Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-09  8:37     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 07/23] RDMA/mlx5: Derive mlx5_ib_adversarial_mode global from mlx5_core adversarial API Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-03 16:39 ` [PATCH rdma-next 08/23] RDMA/core: Suppress kverbs_provider for adversarial_mode devices Yishai Hadas
2026-11-03 19:03   ` Jacob Moroni
2026-11-03 19:07     ` Jason Gunthorpe
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-03 16:39 ` [PATCH rdma-next 09/23] RDMA/mlx5: Remove duplicate mlx5_ib_umr_context struct Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 10/23] RDMA/mlx5: Introduce per-CQ poll_one hook in mlx5_ib_cq Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-03 16:39 ` [PATCH rdma-next 11/23] RDMA/mlx5: Add shadow FIFO infrastructure for UMR completions Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-09  8:50     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 12/23] RDMA/mlx5: Verify UMR CQ polling against a hostile NIC Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-09  9:38     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 13/23] RDMA/mlx5: Keep the UMR QP failed once a forged CQE is detected Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-09  9:48     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 14/23] RDMA/mlx5: Block generic CQ poll path in adversarial mode Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-03 16:39 ` [PATCH rdma-next 15/23] RDMA/core: Set the CQ comp_handler after its poll context is set up Yishai Hadas
2026-11-07 20:24   ` netdev-bot+sashiko
2026-11-09 10:03     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 16/23] RDMA/mlx5: Ignore completion events for a CQ without a handler Yishai Hadas
2026-11-07 20:25   ` netdev-bot+sashiko
2026-11-09 10:09     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 17/23] RDMA/mlx5: Validate EQE port and GSI port before indexing devr.ports[] Yishai Hadas
2026-11-07 20:25   ` netdev-bot+sashiko
2026-11-09 10:18     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 18/23] RDMA/mlx5: Validate firmware RQ/SQ state before sqrq_trans[] lookup Yishai Hadas
2026-11-07 20:25   ` netdev-bot+sashiko
2026-11-09 10:24     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 19/23] RDMA/mlx5: Clamp log_max_cq_sz shift in CQ size checks Yishai Hadas
2026-11-07 20:25   ` netdev-bot+sashiko
2026-11-09 10:27     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 20/23] RDMA/mlx5: Clamp log_max_* shifts in QP and WQ limit checks Yishai Hadas
2026-11-07 20:25   ` netdev-bot+sashiko
2026-11-09 10:30     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 21/23] RDMA/mlx5: Clamp log_max_* shifts in query_device and ucontext paths Yishai Hadas
2026-11-07 20:25   ` netdev-bot+sashiko
2026-11-09 10:44     ` Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 22/23] RDMA/core: Report and enforce adversarial mode via GET_CONTEXT Yishai Hadas
2026-11-03 16:39 ` [PATCH rdma-next 23/23] RDMA/mlx5: Advertise adversarial mode via ib_device::adversarial_mode Yishai Hadas
2026-11-07 20:25   ` netdev-bot+sashiko
2026-11-09 10:52     ` Yishai Hadas

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=02149fa6-3ded-4a32-a0fe-986c5c35e8bd@nvidia.com \
    --to=yishaih@nvidia.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=dtatulea@nvidia.com \
    --cc=edumazet@kernel.org \
    --cc=jgg@ziepe.ca \
    --cc=kuba@kernel.org \
    --cc=leon@kernel.org \
    --cc=linux-rdma@vger.kernel.org \
    --cc=maorg@nvidia.com \
    --cc=mbloch@nvidia.com \
    --cc=moshe@nvidia.com \
    --cc=netdev-bot+sashiko@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=saeedm@nvidia.com \
    --cc=tariqt@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