Commit d9561e71 authored by Shailend Chand's avatar Shailend Chand Committed by Zhang Changzhong
Browse files

gve: Account for stopped queues when reading NIC stats

mainline inclusion
from mainline-v6.10-rc1
commit af9bcf910b1f86244f39e15e701b2dc564b469a6
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IAGRY7
CVE: CVE-2024-42162

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=af9bcf910b1f86244f39e15e701b2dc564b469a6



--------------------------------

We now account for the fact that the NIC might send us stats for a
subset of queues. Without this change, gve_get_ethtool_stats might make
an invalid access on the priv->stats_report->stats array.

Tested-by: default avatarMina Almasry <almasrymina@google.com>
Reviewed-by: default avatarPraveen Kaligineedi <pkaligineedi@google.com>
Reviewed-by: default avatarHarshitha Ramamurthy <hramamurthy@google.com>
Signed-off-by: default avatarShailend Chand <shailend@google.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Conflicts:
	drivers/net/ethernet/google/gve/gve_ethtool.c
[several refactor and feature patches are not merged]
Signed-off-by: default avatarZhang Changzhong <zhangchangzhong@huawei.com>
parent 4f1da763
Loading
Loading
Loading
Loading
+52 −6
Original line number Diff line number Diff line
@@ -32,6 +32,22 @@ static u32 gve_get_msglevel(struct net_device *netdev)
	return priv->msg_enable;
}

static bool gve_tx_was_added_to_block(struct gve_priv *priv, int queue_idx)
{
	struct gve_notify_block *block =
			&priv->ntfy_blocks[gve_tx_idx_to_ntfy(priv, queue_idx)];

	return block->tx != NULL;
}

static bool gve_rx_was_added_to_block(struct gve_priv *priv, int queue_idx)
{
	struct gve_notify_block *block =
			&priv->ntfy_blocks[gve_rx_idx_to_ntfy(priv, queue_idx)];

	return block->rx != NULL;
}

static const char gve_gstrings_main_stats[][ETH_GSTRING_LEN] = {
	"rx_packets", "tx_packets", "rx_bytes", "tx_bytes",
	"rx_dropped", "tx_dropped", "tx_timeouts",
@@ -144,6 +160,8 @@ gve_get_ethtool_stats(struct net_device *netdev,
	struct stats *report_stats;
	int *rx_qid_to_stats_idx;
	int *tx_qid_to_stats_idx;
	int num_stopped_rxqs = 0;
	int num_stopped_txqs = 0;
	struct gve_priv *priv;
	bool skip_nic_stats;
	unsigned int start;
@@ -158,12 +176,23 @@ gve_get_ethtool_stats(struct net_device *netdev,
					    sizeof(int), GFP_KERNEL);
	if (!rx_qid_to_stats_idx)
		return;
	for (ring = 0; ring < priv->rx_cfg.num_queues; ring++) {
		rx_qid_to_stats_idx[ring] = -1;
		if (!gve_rx_was_added_to_block(priv, ring))
			num_stopped_rxqs++;
	}
	tx_qid_to_stats_idx = kmalloc_array(priv->tx_cfg.num_queues,
					    sizeof(int), GFP_KERNEL);
	if (!tx_qid_to_stats_idx) {
		kfree(rx_qid_to_stats_idx);
		return;
	}
	for (ring = 0; ring < priv->tx_cfg.num_queues; ring++) {
		tx_qid_to_stats_idx[ring] = -1;
		if (!gve_tx_was_added_to_block(priv, ring))
			num_stopped_txqs++;
	}

	for (rx_pkts = 0, rx_bytes = 0, rx_skb_alloc_fail = 0,
	     rx_buf_alloc_fail = 0, rx_desc_err_dropped_pkt = 0, ring = 0;
	     ring < priv->rx_cfg.num_queues; ring++) {
@@ -229,7 +258,13 @@ gve_get_ethtool_stats(struct net_device *netdev,
	/* For rx cross-reporting stats, start from nic rx stats in report */
	base_stats_idx = GVE_TX_STATS_REPORT_NUM * priv->tx_cfg.num_queues +
		GVE_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues;
	max_stats_idx = NIC_RX_STATS_REPORT_NUM * priv->rx_cfg.num_queues +
	/* The boundary between driver stats and NIC stats shifts if there are
	 * stopped queues.
	 */
	base_stats_idx += NIC_RX_STATS_REPORT_NUM * num_stopped_rxqs +
		NIC_TX_STATS_REPORT_NUM * num_stopped_txqs;
	max_stats_idx = NIC_RX_STATS_REPORT_NUM *
		(priv->rx_cfg.num_queues - num_stopped_rxqs) +
		base_stats_idx;
	/* Preprocess the stats report for rx, map queue id to start index */
	skip_nic_stats = false;
@@ -243,6 +278,10 @@ gve_get_ethtool_stats(struct net_device *netdev,
			skip_nic_stats = true;
			break;
		}
		if (queue_id < 0 || queue_id >= priv->rx_cfg.num_queues) {
			net_err_ratelimited("Invalid rxq id in NIC stats\n");
			continue;
		}
		rx_qid_to_stats_idx[queue_id] = stats_idx;
	}
	/* walk RX rings */
@@ -270,14 +309,15 @@ gve_get_ethtool_stats(struct net_device *netdev,
			data[i++] = rx->rx_copybreak_pkt;
			data[i++] = rx->rx_copied_pkt;
			/* stats from NIC */
			if (skip_nic_stats) {
			stats_idx = rx_qid_to_stats_idx[ring];
			if (skip_nic_stats || stats_idx < 0) {
				/* skip NIC rx stats */
				i += NIC_RX_STATS_REPORT_NUM;
				continue;
			}
			for (j = 0; j < NIC_RX_STATS_REPORT_NUM; j++) {
				u64 value =
				be64_to_cpu(report_stats[rx_qid_to_stats_idx[ring] + j].value);
				be64_to_cpu(report_stats[stats_idx + j].value);

				data[i++] = value;
			}
@@ -288,7 +328,8 @@ gve_get_ethtool_stats(struct net_device *netdev,

	/* For tx cross-reporting stats, start from nic tx stats in report */
	base_stats_idx = max_stats_idx;
	max_stats_idx = NIC_TX_STATS_REPORT_NUM * priv->tx_cfg.num_queues +
	max_stats_idx = NIC_TX_STATS_REPORT_NUM *
		(priv->tx_cfg.num_queues - num_stopped_txqs) +
		max_stats_idx;
	/* Preprocess the stats report for tx, map queue id to start index */
	skip_nic_stats = false;
@@ -302,6 +343,10 @@ gve_get_ethtool_stats(struct net_device *netdev,
			skip_nic_stats = true;
			break;
		}
		if (queue_id < 0 || queue_id >= priv->tx_cfg.num_queues) {
			net_err_ratelimited("Invalid txq id in NIC stats\n");
			continue;
		}
		tx_qid_to_stats_idx[queue_id] = stats_idx;
	}
	/* walk TX rings */
@@ -323,14 +368,15 @@ gve_get_ethtool_stats(struct net_device *netdev,
			data[i++] = be32_to_cpu(gve_tx_load_event_counter(priv,
									  tx));
			/* stats from NIC */
			if (skip_nic_stats) {
			stats_idx = tx_qid_to_stats_idx[ring];
			if (skip_nic_stats || stats_idx < 0) {
				/* skip NIC tx stats */
				i += NIC_TX_STATS_REPORT_NUM;
				continue;
			}
			for (j = 0; j < NIC_TX_STATS_REPORT_NUM; j++) {
				u64 value =
				be64_to_cpu(report_stats[tx_qid_to_stats_idx[ring] + j].value);
				be64_to_cpu(report_stats[stats_idx + j].value);
				data[i++] = value;
			}
		}