mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Kees Cook <kees@kernel.org>
To: Tom Rix <trix@redhat.com>
Cc: Qing Zhao <qing.zhao@oracle.com>,
	mhiramat@kernel.org, akpm@linux-foundation.org,
	ndesaulniers@google.com, masahiroy@kernel.org,
	paulmck@kernel.org, hannes@cmpxchg.org, ojeda@kernel.org,
	thunder.leizhen@huawei.com, christophe.leroy@csgroup.eu,
	vbabka@suse.cz, linux-kernel@vger.kernel.org,
	linux-hardening@vger.kernel.org
Subject: Re: [PATCH] init/Kconfig: extend -Wno-array-bounds to gcc 13
Date: Mon, 17 Jun 2024 14:02:51 -0700	[thread overview]
Message-ID: <202406171348.8D43518308@keescook> (raw)
In-Reply-To: <20230306220947.1982272-1-trix@redhat.com>

*thread necromancy*

On Mon, Mar 06, 2023 at 05:09:47PM -0500, Tom Rix wrote:
> With gcc 13.0.1 on x86, there are several false positives like
> 
> drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c:167:31:
>   error: array subscript 4 is above array bounds of ‘const struct sparx5_psfp_gce[4]’ [-Werror=array-bounds=]
>   167 |                 gce = &sg->gce[i];
>       |                        ~~~~~~~^~~
> In file included from drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c:8:
> drivers/net/ethernet/microchip/sparx5/sparx5_main.h:506:32: note: while referencing ‘gce’
>   506 |         struct sparx5_psfp_gce gce[SPX5_PSFP_GCE_CNT];
>       |                                ^~~
> 
> The code lines for the reported problem
> 	/* For each scheduling entry */
> 	for (i = 0; i < sg->num_entries; i++) {
> 		gce = &sg->gce[i];
> 
> i is bounded by num_entries, which is set in sparx5_tc_flower.c
> 	if (act->gate.num_entries >= SPX5_PSFP_GCE_CNT) {
> 		NL_SET_ERR_MSG_MOD(extack, "Invalid number of gate entries");
> 		return -EINVAL;
> 	}
> ..
> 	sg->num_entries = act->gate.num_entries;
> 
> So disable array-bounds as was done on gcc 11 and 12

So, as it turns out, after much discussion with GCC devs (and Qing's great
patience with me), I finally see that this is not a false positive. GCC
correctly sees a test for "i >= SPX5_PSFP_GCE_CNT", hidden away in the
macros and inlines, and is reporting "Look, you have an execution path
where i is out of bounds."

(It is not a result of the shift sanitizer nor the array bounds sanitizer
-- I was convinced these were responsible, but I was wrong.)

Here are the details:


"gce" is this array of size 4:

struct sparx5_psfp_sg {
	...
        u32 num_entries;            /* PSFPAdminControlListLength */
        struct sparx5_psfp_gce gce[SPX5_PSFP_GCE_CNT];
};

#define SPX5_PSFP_GCE_CNT 4

with the loop:

        for (i = 0; i < sg->num_entries; i++) {
                gce = &sg->gce[i];
		...
                spx5_wr(ANA_AC_SG_GCL_GS_CONFIG_IPS_SET(ips) |
                        ANA_AC_SG_GCL_GS_CONFIG_GATE_STATE_SET(gce->gate_state),
                        sparx5, ANA_AC_SG_GCL_GS_CONFIG(i));

That last macro is:

#define ANA_AC_SG_GCL_GS_CONFIG(r) __REG(TARGET_ANA_AC,\
                                        0, 1, 851584, 0, 1, 128, 0, r, 4, 4)

Note the "r, 4", which is actually "i, 4". These are passed into
spx5_wr() as rinst and rcnt respectively:

static inline void spx5_wr(u32 val, struct sparx5 *sparx5,
                           int id, int tinst, int tcnt,
                           int gbase, int ginst, int gcnt, int gwidth,
                           int raddr, int rinst, int rcnt, int rwidth)
{
        writel(val, spx5_addr(sparx5->regs, id, tinst, tcnt,
                              gbase, ginst, gcnt, gwidth,
                              raddr, rinst, rcnt, rwidth));
}

and spx5_addr() does:

static inline void __iomem *spx5_addr(void __iomem *base[],
                                      int id, int tinst, int tcnt,
                                      int gbase, int ginst,
                                      int gcnt, int gwidth,
                                      int raddr, int rinst,
                                      int rcnt, int rwidth)
{
	...
        WARN_ON((rinst) >= rcnt);


It is specifically checking for "i >= 4", and GCC is correctly reporting
that this is therefore a reachable path.

So, the correct fix here is to do the check up front and make sure an
invalid array indexing cannot happen:

diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c
index 8dee1ab1fa75..23c40b9b74d5 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c
@@ -163,7 +163,7 @@ static int sparx5_psfp_sg_set(struct sparx5 *sparx5, u32 id,
 	spx5_wr(sg->cycletimeext, sparx5, ANA_AC_SG_CONFIG_REG_5);
 
 	/* For each scheduling entry */
-	for (i = 0; i < sg->num_entries; i++) {
+	for (i = 0; i < ARRAY_SIZE(sg->gce) && i < sg->num_entries; i++) {
 		gce = &sg->gce[i];
 		ips = sparx5_psfp_ipv_to_ips(gce->ipv);
 		/* hardware needs TimeInterval to be cumulative */

Or, if you want to retain the WARN:


diff --git a/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c b/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c
index 8dee1ab1fa75..5ffeb299fcb4 100644
--- a/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c
+++ b/drivers/net/ethernet/microchip/sparx5/sparx5_psfp.c
@@ -164,6 +164,8 @@ static int sparx5_psfp_sg_set(struct sparx5 *sparx5, u32 id,
 
 	/* For each scheduling entry */
 	for (i = 0; i < sg->num_entries; i++) {
+		if (WARN_ON_ONCE(i >= ARRAY_SIZE(sg->gce)))
+			break;
 		gce = &sg->gce[i];
 		ips = sparx5_psfp_ipv_to_ips(gce->ipv);
 		/* hardware needs TimeInterval to be cumulative */


-- 
Kees Cook

      parent reply	other threads:[~2024-06-17 21:02 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-06 22:09 Tom Rix
2023-03-06 22:20 ` Nick Desaulniers
2023-03-06 23:02   ` Kees Cook
2023-03-07  1:07     ` Tom Rix
2023-03-07 11:42       ` Miguel Ojeda
2023-03-07 13:28         ` Tom Rix
2024-06-17 21:02 ` Kees Cook [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=202406171348.8D43518308@keescook \
    --to=kees@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=christophe.leroy@csgroup.eu \
    --cc=hannes@cmpxchg.org \
    --cc=linux-hardening@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=masahiroy@kernel.org \
    --cc=mhiramat@kernel.org \
    --cc=ndesaulniers@google.com \
    --cc=ojeda@kernel.org \
    --cc=paulmck@kernel.org \
    --cc=qing.zhao@oracle.com \
    --cc=thunder.leizhen@huawei.com \
    --cc=trix@redhat.com \
    --cc=vbabka@suse.cz \
    /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

all inboxes | Powered by JetHome®