NetBSD Problem Report #60289

From Manuel.Bouyer@lip6.fr  Sat May 23 18:41:20 2026
Return-Path: <Manuel.Bouyer@lip6.fr>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits)
	 client-signature RSA-PSS (2048 bits))
	(Client CN "mail.netbsd.org", Issuer "R13" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id 901621A923C
	for <gnats-bugs@gnats.NetBSD.org>; Sat, 23 May 2026 18:41:20 +0000 (UTC)
Message-Id: <20260523183457.4F6F910E85@armandeche.soc.lip6.fr>
Date: Sat, 23 May 2026 20:34:57 +0200 (CEST)
From: Manuel.Bouyer@lip6.fr
Reply-To: Manuel.Bouyer@lip6.fr
To: gnats-bugs@NetBSD.org
Subject: NetBSD 11 gcc mis-optimises some NULL checks
X-Send-Pr-Version: 3.95
X-From4GNATS: "Manuel.Bouyer@lip6.fr via gnats" <gnats-admin@NetBSD.org>

>Number:         60289
>Category:       toolchain
>Synopsis:       NetBSD 11 gcc mis-optimises some NULL checks
>Confidential:   no
>Severity:       serious
>Priority:       high
>Responsible:    toolchain-manager
>State:          analyzed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Sat May 23 18:45:00 +0000 2026
>Closed-Date:    
>Last-Modified:  Wed Jun 03 18:25:03 +0000 2026
>Originator:     Manuel Bouyer
>Release:        NetBSD 11.0_RC4
>Organization:
>Environment:
System: NetBSD armandeche.soc.lip6.fr 10.1_STABLE NetBSD 10.1_STABLE (GENERIC_CAN) #36: Mon Oct 13 12:20:13 CEST 2025 bouyer@armandeche.soc.lip6.fr:/local/armandeche1/tmp/build/amd64/obj/local/armandeche2/netbsd-10/src/sys/arch/amd64/compile/GENERIC_CAN amd64
Architecture: x86_64
Machine: amd64
>Description:
	I tried updating a server from 10.1_STABLE to 11.0_RC4 and got a
	panic within minutes, related to a NULL pointer dereference.
	The issue is in ipf_fastroute(), but the code looks good and
	dissasembly shows that gcc dropped a NULL pointer check.

	I've been able to reproduce it with a simple test funtion (see
	How-To-Repeat). It seems that construct like
	if (fdp != &fr->fr_dif)
	causes gcc to assume (wrongly) that fr cannot be NULL. 
	This happens at -O2 or highter, -O1 produces the expected code.

>How-To-Repeat:
cat > test-misopt.h << EOF
typedef struct foo {
	void *fp;
} foo_t;

typedef struct bar {
	void *bp;
	foo_t foo;
	int flags;
} bar_t;

void testf(foo_t *, bar_t *);
EOF
cat > test-misopt.c << EOF
#include <stdio.h>
#include "test-misopt.h"


void
testf(foo_t *foop, bar_t *barp)
{
	if (&barp->foo != foop) {
		printf("A ");
		if (!barp || !(barp->flags & 0x01)) {
			printf("B ");
		}
		printf("C ");
	}
	printf("D\n");
}
EOF
/home/build/amd64/obj/netbsd-11/tooldir/bin/x86_64--netbsd-gcc -g -c -O2 test-misopt.c
gdb  -ex "disas /s testf" test-misopt.o
and notice:
10                      if (!barp || !(barp->flags & 0x01)) {
   0x0000000000000021 <+33>:    testb  $0x1,0x10(%rbx)
   0x0000000000000025 <+37>:    je     0x4d <testf+77>

%rsi or %rbx is never tested for 0.

>Fix:
	#pragma GCC optimize ("O1")
	before the affected funtion works around it
	But this behavior is worrisome

>Release-Note:

>Audit-Trail:
From: Christos Zoulas <christos@zoulas.com>
To: gnats-bugs@netbsd.org
Cc: toolchain-manager@netbsd.org,
 gnats-admin@netbsd.org,
 netbsd-bugs@netbsd.org
Subject: Re: toolchain/60289: NetBSD 11 gcc mis-optimises some NULL checks
Date: Sat, 23 May 2026 16:28:06 -0400

 --Apple-Mail=_C0AD64E4-92BB-4398-B39F-BD45041F0A5F
 Content-Transfer-Encoding: 7bit
 Content-Type: text/plain;
 	charset=us-ascii

 &barp->foo is UB if barp is NULL, so the compiler skips the next test.
 This is what the language lawyers have decided.

 christos


 --Apple-Mail=_C0AD64E4-92BB-4398-B39F-BD45041F0A5F
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
 	filename=signature.asc
 Content-Type: application/pgp-signature;
 	name=signature.asc
 Content-Description: Message signed with OpenPGP

 -----BEGIN PGP SIGNATURE-----
 Comment: GPGTools - http://gpgtools.org

 iF0EARECAB0WIQS+BJlbqPkO0MDBdsRxESqxbLM7OgUCahIN1wAKCRBxESqxbLM7
 OpbzAJ4qmEjMbRrRgT0u0M3GUTAWthHaNwCgxVqqZQFBPCzyeG3v/j1NQYn5Q0s=
 =zWhX
 -----END PGP SIGNATURE-----

 --Apple-Mail=_C0AD64E4-92BB-4398-B39F-BD45041F0A5F--

State-Changed-From-To: open->analyzed
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Sat, 23 May 2026 21:59:11 +0000
State-Changed-Why:
This actually may be a gcc bug, because we compile the kernel with
-fno-delete-null-pointer-checks, which is supposed to prevent gcc from
eliminating these checks:

https://nxr.netbsd.org/xref/src/sys/conf/Makefile.kern.inc?r=1.306#108
https://gcc.gnu.org/onlinedocs/gcc-14.3.0/gcc/Optimize-Options.html#index-fdelete-null-pointer-checks

It's not NetBSD-specific -- here's a godbolt.org example with 14.3.0,
presumably not on NetBSD:

https://godbolt.org/z/aT6xqdMer

.LC0:
        .string "A "
...
        mov     edi, OFFSET FLAT:.LC0
        call    printf
        test    BYTE PTR [rbx+16], 1
        je      .L9

So I think someone should file a bug with gcc upstream.

In the mean time, we should also fix this idiom and avoid evaluating
&barp->foo when barp might be null -- we did nix some cases of it when
clang found them, but they were slightly different, like this:

https://mail-index.netbsd.org/source-changes/2014/11/06/msg060206.html

> Fix little C issues in i915drmkms hindering the Clang build.
> 
> - Test `x == NULL', not `&container_of(x, t, base)->base == NULL'.
> ...

-       obj = to_intel_bo(drm_gem_object_lookup(dev, file, args->handle));
-       if (&obj->base == NULL)
+       gobj = drm_gem_object_lookup(dev, file, args->handle);
+       if (gobj == NULL)
                return -ENOENT;
+       obj = to_intel_bo(gobj);

The null pointer check in _this_ case vanished some time between gcc10
(as shipped in netbsd-10) and gcc11:

https://godbolt.org/z/fT6P66KT4 (gcc 10.5)

        mov     edi, OFFSET FLAT:.LC0
        call    printf
        test    rbx, rbx
        je      .L3
        test    BYTE PTR [rbx+16], 1
        jne     .L4

https://godbolt.org/z/Ee8hW3z37 (gcc 11.5)

        mov     edi, OFFSET FLAT:.LC0
        call    printf
        test    BYTE PTR [rbx+16], 1
        je      .L9


From: Manuel Bouyer <manuel.bouyer@lip6.fr>
To: gnats-bugs@netbsd.org
Cc: toolchain-manager@netbsd.org, netbsd-bugs@netbsd.org,
        gnats-admin@netbsd.org, riastradh@NetBSD.org
Subject: Re: toolchain/60289 (NetBSD 11 gcc mis-optimises some NULL checks)
Date: Sun, 24 May 2026 10:49:30 +0200

 On Sat, May 23, 2026 at 09:59:12PM +0000, riastradh@NetBSD.org wrote:
 > Synopsis: NetBSD 11 gcc mis-optimises some NULL checks
 > 
 > State-Changed-From-To: open->analyzed
 > State-Changed-By: riastradh@NetBSD.org
 > State-Changed-When: Sat, 23 May 2026 21:59:11 +0000
 > State-Changed-Why:
 > This actually may be a gcc bug, because we compile the kernel with
 > -fno-delete-null-pointer-checks, which is supposed to prevent gcc from
 > eliminating these checks:
 > 
 > https://nxr.netbsd.org/xref/src/sys/conf/Makefile.kern.inc?r=1.306#108
 > https://gcc.gnu.org/onlinedocs/gcc-14.3.0/gcc/Optimize-Options.html#index-fdelete-null-pointer-checks
 > 
 > It's not NetBSD-specific -- here's a godbolt.org example with 14.3.0,
 > presumably not on NetBSD:
 > 
 > https://godbolt.org/z/aT6xqdMer
 > 
 > .LC0:
 >         .string "A "
 > ...
 >         mov     edi, OFFSET FLAT:.LC0
 >         call    printf
 >         test    BYTE PTR [rbx+16], 1
 >         je      .L9
 > 
 > So I think someone should file a bug with gcc upstream.

 I found at last one gcc bug report on this topic:
 https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100404

 Adding
 	asm("" : "+r"(barp)); 
 just before the !barp test works around the isssue.

 but explicitely checking for NULL before &barp->foo is a more correct
 fix, even if it leads to an extra test which is not needed (could an
 overly agressive optimisation leading to slower code be considered as a bug ?)

 But,
  -fno-delete-null-pointer-checks doesn't work in this case; this is a real
    problem
  It seems that there's no way to get a warning when this optimisation is
    triggered, which I think is also a problem for developers.

 A quick search showed other projects (bind, openjdk) running into this

 -- 
 Manuel Bouyer, LIP6, Sorbonne Université.           Manuel.Bouyer@lip6.fr
      NetBSD: 26 ans d'experience feront toujours la difference
 --

From: RVP <rvp@SDF.ORG>
To: Manuel Bouyer <manuel.bouyer@lip6.fr>
Cc: gnats-bugs@netbsd.org
Subject: Re: toolchain/60289 (NetBSD 11 gcc mis-optimises some NULL checks)
Date: Sun, 24 May 2026 09:10:03 +0000 (UTC)

 On Sun, 24 May 2026, Manuel Bouyer wrote:

 > A quick search showed other projects (bind, openjdk) running into this
 >

 This is an old issue, and the gcc devs' response to all the howling on the
 Linux side was "Will not fix".

 https://lwn.net/Articles/342330/
 https://lwn.net/Articles/342420/

 -RVP

From: Manuel Bouyer <manuel.bouyer@lip6.fr>
To: RVP <rvp@SDF.ORG>
Cc: gnats-bugs@netbsd.org
Subject: Re: toolchain/60289 (NetBSD 11 gcc mis-optimises some NULL checks)
Date: Sun, 24 May 2026 13:42:46 +0200

 On Sun, May 24, 2026 at 09:10:03AM +0000, RVP wrote:
 > On Sun, 24 May 2026, Manuel Bouyer wrote:
 > 
 > > A quick search showed other projects (bind, openjdk) running into this
 > > 
 > 
 > This is an old issue, and the gcc devs' response to all the howling on the
 > Linux side was "Will not fix".
 > 
 > https://lwn.net/Articles/342330/
 > https://lwn.net/Articles/342420/

 OK, but both articles mention -fno-delete-null-pointer-checks to deal with it.
 But in the specific case this PR is about, -fno-delete-null-pointer-checks
 doens't prevent the optimisation from removing the NULL pointer
 check. I think this is a bug in gcc, and none of the article above
 mention this issue (and I couldn't find a gcc bug report about it either).

 -- 
 Manuel Bouyer, LIP6, Sorbonne Université.           Manuel.Bouyer@lip6.fr
      NetBSD: 26 ans d'experience feront toujours la difference
 --

From: Taylor R Campbell <riastradh@NetBSD.org>
To: RVP <rvp@SDF.ORG>
Cc: gnats-bugs@netbsd.org, toolchain-manager@netbsd.org,
	gnats-admin@netbsd.org,
	netbsd-bugs@netbsd.org, Manuel.Bouyer@lip6.fr
Subject: Re: toolchain/60289 (NetBSD 11 gcc mis-optimises some NULL checks)
Date: Sun, 24 May 2026 14:43:31 +0000

 > Date: Sun, 24 May 2026 09:10:03 +0000 (UTC)
 > From: RVP <rvp@SDF.ORG>
 >=20
 > On Sun, 24 May 2026, Manuel Bouyer wrote:
 >=20
 > > A quick search showed other projects (bind, openjdk) running into this
 >=20
 > This is an old issue, and the gcc devs' response to all the howling on the
 > Linux side was "Will not fix".
 >=20
 > https://lwn.net/Articles/342330/
 > https://lwn.net/Articles/342420/

 The case in https://lwn.net/Articles/342330/ is different for two
 reasons:

 1. The Linux code quoted there actually dereferences the null pointer
    _before_ the check, rather than simply computing an address
    indirection.

 	struct sock *sk =3D tun->sk;
 	unsigned int mask =3D 0;

 	if (!tun)
 	    return POLLERR;

    Actually dereferencing a null pointer is unambiguously UB without
    any involvement of language lawyers.  C99, Sec. 6.5.3.2 `Address
    and indirection operators', clause 4:

 	If an invalid value has been assigned to the pointer, the
 	behavior of the unary * operator is undefined.^87)

 	87) ... Among the invalid values for dereferencing a pointer by
 	    the unary * operator are a null pointer, ...

    In contrast, the code bouyer@ was discussing involves _address of_
    a member operator.  It would be as if the code were instead:

 	struct sock **sk =3D &tun->sk;
 	unsigned int mask =3D 0;

 	if (!tun)
 	    return POLLERR;

    Whether this is UB takes more language-lawyering to argue (and,
    e.g., if it is UB, that breaks the traditional definition of
    offsetof(T, F) as (size_t)((char *)&((T *)0)->F - (char *)(T *)0)).

 2. We are using -fno-delete-null-pointer-checks here.  GCC is supposed
    to assume _even if_ the pointer is dereferenced, downstream logic
    _still_ cannot assume the pointer is nonnull:

 	-fdelete-null-pointer-checks

 		Assume that programs cannot safely dereference null
 		pointers, and that no code or data element resides at
 		address zero.  This option enables simple constant
 		folding optimizations at all optimization levels.  In
 		addition, other optimization passes in GCC use this
 		flag to control global dataflow analyses that
 		eliminate useless checks for null pointers; these
 		assume that a memory access to address zero always
 		results in a trap, so that if a pointer is checked
 		after it has already been dereferenced, it cannot be
 		null.

 		Note however that in some environments this assumption
 		is not true.  Use -fno-delete-null-pointer-checks to
 		disable this optimization for programs that depend on
 		that behavior.

 	https://gcc.gnu.org/onlinedocs/gcc-14.3.0/gcc/Optimize-Options.html#index-=
 fdelete-null-pointer-checks

    If dereferencing the pointer as barp->foo weren't enough to justify
    pruning downstream null checks, surely taking the address
    &barp->foo shouldn't justify pruning downstream null checks.

From: "Manuel Bouyer" <bouyer@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60289 CVS commit: src/sys/external/bsd/ipf/netinet
Date: Sun, 24 May 2026 17:43:18 +0000

 Module Name:	src
 Committed By:	bouyer
 Date:		Sun May 24 17:43:18 UTC 2026

 Modified Files:
 	src/sys/external/bsd/ipf/netinet: ip_fil_netbsd.c

 Log Message:
 Explicitely check a pointer against NULL before computing a member address,
 to avoid undefined behavior. Avoids the same NULL pointer check being
 "optimised" later, leading to NULL pointer dereference.
 It is expected that -fno-delete-null-pointer-checks would avoid this but
 it does not.
 For details see
 https://mail-index.netbsd.org/tech-toolchain/2026/05/23/msg004818.html
 and PR toolchain/60289
 Needs pullup to netbsd-11.


 To generate a diff of this commit:
 cvs rdiff -u -r1.39 -r1.40 src/sys/external/bsd/ipf/netinet/ip_fil_netbsd.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60289 CVS commit: [netbsd-11] src/sys/external/bsd/ipf/netinet
Date: Wed, 3 Jun 2026 18:24:52 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Wed Jun  3 18:24:52 UTC 2026

 Modified Files:
 	src/sys/external/bsd/ipf/netinet [netbsd-11]: ip_fil_netbsd.c

 Log Message:
 Pull up following revision(s) (requested by bouyer in ticket #295):

 	sys/external/bsd/ipf/netinet/ip_fil_netbsd.c: revision 1.40

 Explicitely check a pointer against NULL before computing a member address,
 to avoid undefined behavior. Avoids the same NULL pointer check being
 "optimised" later, leading to NULL pointer dereference.

 It is expected that -fno-delete-null-pointer-checks would avoid this but
 it does not.

 For details see
 https://mail-index.netbsd.org/tech-toolchain/2026/05/23/msg004818.html
 and PR toolchain/60289


 To generate a diff of this commit:
 cvs rdiff -u -r1.39 -r1.39.4.1 \
     src/sys/external/bsd/ipf/netinet/ip_fil_netbsd.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.