NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: toolchain/60289 (NetBSD 11 gcc mis-optimises some NULL checks)
The following reply was made to PR toolchain/60289; it has been noted by GNATS.
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
To: RVP <rvp%SDF.ORG@localhost>
Cc: gnats-bugs%netbsd.org@localhost, toolchain-manager%netbsd.org@localhost,
gnats-admin%netbsd.org@localhost,
netbsd-bugs%netbsd.org@localhost, Manuel.Bouyer%lip6.fr@localhost
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@localhost>
>=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.
Home |
Main Index |
Thread Index |
Old Index