net: ethtool: Adjust exactly ETH_GSTRING_LEN-long stats to use memcpy
authorKees Cook <kees@kernel.org>
Wed, 16 Apr 2025 01:02:15 +0000 (18:02 -0700)
committerJakub Kicinski <kuba@kernel.org>
Fri, 18 Apr 2025 01:53:52 +0000 (18:53 -0700)
Many drivers populate the stats buffer using C-String based APIs (e.g.
ethtool_sprintf() and ethtool_puts()), usually when building up the
list of stats individually (i.e. with a for() loop). This, however,
requires that the source strings be populated in such a way as to have
a terminating NUL byte in the source.

Other drivers populate the stats buffer directly using one big memcpy()
of an entire array of strings. No NUL termination is needed here, as the
bytes are being directly passed through. Yet others will build up the
stats buffer individually, but also use memcpy(). This, too, does not
need NUL termination of the source strings.

However, there are cases where the strings that populate the
source stats strings are exactly ETH_GSTRING_LEN long, and GCC
15's -Wunterminated-string-initialization option complains that the
trailing NUL byte has been truncated. This situation is fine only if the
driver is using the memcpy() approach. If the C-String APIs are used,
the destination string name will have its final byte truncated by the
required trailing NUL byte applied by the C-string API.

For drivers that are already using memcpy() but have initializers that
truncate the NUL terminator, mark their source strings as __nonstring to
silence the GCC warnings.

For drivers that have initializers that truncate the NUL terminator and
are using the C-String APIs, switch to memcpy() to avoid destination
string truncation and mark their source strings as __nonstring to silence
the GCC warnings. (Also introduce ethtool_cpy() as a helper to make this
an easy replacement).

Specifically the following warnings were investigated and addressed:

../drivers/net/ethernet/chelsio/cxgb/cxgb2.c:364:9: warning: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (33 chars into 32 available) [-Wunterminated-string-initialization]
  364 |         "TxFramesAbortedDueToXSCollisions",
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../drivers/net/ethernet/freescale/enetc/enetc_ethtool.c:165:33: warning: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (33 chars into 32 available) [-Wunterminated-string-initialization]
  165 |         { ENETC_PM_R1523X(0),   "MAC rx 1523 to max-octet packets" },
      |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../drivers/net/ethernet/freescale/enetc/enetc_ethtool.c:190:33: warning: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (33 chars into 32 available) [-Wunterminated-string-initialization]
  190 |         { ENETC_PM_T1523X(0),   "MAC tx 1523 to max-octet packets" },
      |                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../drivers/net/ethernet/google/gve/gve_ethtool.c:76:9: warning: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (33 chars into 32 available) [-Wunterminated-string-initialization]
   76 |         "adminq_dcfg_device_resources_cnt", "adminq_set_driver_parameter_cnt",
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c:117:53: warning: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (33 chars into 32 available) [-Wunterminated-string-initialization]
  117 |         STMMAC_STAT(ptp_rx_msg_type_pdelay_follow_up),
      |                                                     ^
../drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c:46:12: note: in definition of macro 'STMMAC_STAT'
   46 |         { #m, sizeof_field(struct stmmac_extra_stats, m),       \
      |            ^
../drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c:328:24: warning: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (33 chars into 32 available) [-Wunterminated-string-initialization]
  328 |                 .str = "a_mac_control_frames_transmitted",
      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c:340:24: warning: initializer-string for array of 'char' truncates NUL terminator but destination lacks 'nonstring' attribute (33 chars into 32 available) [-Wunterminated-string-initialization]
  340 |                 .str = "a_pause_mac_ctrl_frames_received",
      |                        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Signed-off-by: Kees Cook <kees@kernel.org>
Reviewed-by: Petr Machata <petrm@nvidia.com> # for mlxsw
Reviewed-by: Harshitha Ramamurthy <hramamurthy@google.com>
Link: https://patch.msgid.link/20250416010210.work.904-kees@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/chelsio/cxgb/cxgb2.c
drivers/net/ethernet/freescale/enetc/enetc_ethtool.c
drivers/net/ethernet/google/gve/gve_ethtool.c
drivers/net/ethernet/mellanox/mlxsw/spectrum_ethtool.c
drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c
include/linux/ethtool.h

index 3b7068832f95efd0fb5baecb895e13bbf9970c06..4a0e2d2eb60a498cfc74aad5e0b2434879ce0c68 100644 (file)
@@ -351,7 +351,7 @@ static void set_msglevel(struct net_device *dev, u32 val)
        adapter->msg_enable = val;
 }
 
-static const char stats_strings[][ETH_GSTRING_LEN] = {
+static const char stats_strings[][ETH_GSTRING_LEN] __nonstring_array = {
        "TxOctetsOK",
        "TxOctetsBad",
        "TxUnicastFramesOK",
index ece3ae28ba827fe64c438217f4f6e3bfa1aa99c7..f47c8b6cc511808cd399364c0825ee0f781f0904 100644 (file)
@@ -141,7 +141,7 @@ static const struct {
 
 static const struct {
        int reg;
-       char name[ETH_GSTRING_LEN];
+       char name[ETH_GSTRING_LEN] __nonstring;
 } enetc_port_counters[] = {
        { ENETC_PM_REOCT(0),    "MAC rx ethernet octets" },
        { ENETC_PM_RALN(0),     "MAC rx alignment errors" },
@@ -264,7 +264,7 @@ static void enetc_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
                        break;
 
                for (i = 0; i < ARRAY_SIZE(enetc_port_counters); i++)
-                       ethtool_puts(&data, enetc_port_counters[i].name);
+                       ethtool_cpy(&data, enetc_port_counters[i].name);
 
                break;
        }
index eae1a7595a695aecdb3736a562b931b82a9475c7..3c1da0cf3f61ece3016844b9c951331ed9e299cf 100644 (file)
@@ -67,7 +67,7 @@ static const char gve_gstrings_tx_stats[][ETH_GSTRING_LEN] = {
        "tx_xsk_sent[%u]", "tx_xdp_xmit[%u]", "tx_xdp_xmit_errors[%u]"
 };
 
-static const char gve_gstrings_adminq_stats[][ETH_GSTRING_LEN] = {
+static const char gve_gstrings_adminq_stats[][ETH_GSTRING_LEN] __nonstring_array = {
        "adminq_prod_cnt", "adminq_cmd_fail", "adminq_timeouts",
        "adminq_describe_device_cnt", "adminq_cfg_device_resources_cnt",
        "adminq_register_page_list_cnt", "adminq_unregister_page_list_cnt",
@@ -113,7 +113,7 @@ static void gve_get_strings(struct net_device *netdev, u32 stringset, u8 *data)
                                                i);
 
                for (i = 0; i < ARRAY_SIZE(gve_gstrings_adminq_stats); i++)
-                       ethtool_puts(&s, gve_gstrings_adminq_stats[i]);
+                       ethtool_cpy(&s, gve_gstrings_adminq_stats[i]);
 
                break;
 
index 3f64cdbabfa3c1d9149b0215962516f326033ee8..0a8fb9c842d3bb95cf55c23af83a85f08c579579 100644 (file)
@@ -262,7 +262,7 @@ err_port_pause_configure:
 }
 
 struct mlxsw_sp_port_hw_stats {
-       char str[ETH_GSTRING_LEN];
+       char str[ETH_GSTRING_LEN] __nonstring;
        u64 (*getter)(const char *payload);
        bool cells_bytes;
 };
index 918a32f8fda80254750888992857e3f2cab24436..844f7d516a40b3c49c9a305adc6a73f577aea8d6 100644 (file)
@@ -37,7 +37,7 @@
 #define ETHTOOL_DMA_OFFSET     55
 
 struct stmmac_stats {
-       char stat_string[ETH_GSTRING_LEN];
+       char stat_string[ETH_GSTRING_LEN] __nonstring;
        int sizeof_stat;
        int stat_offset;
 };
index 013d258586420556529a581b3a2c7751f24c37e5..7edb5f5e7134597d479977b7ef3719ce6e1e9de2 100644 (file)
@@ -1330,6 +1330,17 @@ extern __printf(2, 3) void ethtool_sprintf(u8 **data, const char *fmt, ...);
  */
 extern void ethtool_puts(u8 **data, const char *str);
 
+/**
+ * ethtool_cpy - Write possibly-not-NUL-terminated string to ethtool string data
+ * @data: Pointer to a pointer to the start of string to write into
+ * @str: NUL-byte padded char array of size ETH_GSTRING_LEN to copy from
+ */
+#define ethtool_cpy(data, str) do {                            \
+       BUILD_BUG_ON(sizeof(str) != ETH_GSTRING_LEN);           \
+       memcpy(*(data), str, ETH_GSTRING_LEN);                  \
+       *(data) += ETH_GSTRING_LEN;                             \
+} while (0)
+
 /* Link mode to forced speed capabilities maps */
 struct ethtool_forced_speed_map {
        u32             speed;