Hi Ben!

On Sun, Jul 20, 2025 at 12:48:07PM -0400, Ben Kallus wrote:
> The C standard specifies that it's undefined behavior to dereference
> NULL (even if you use & right after). The hand-rolled offsetof idiom
> &(((s*)NULL)->f) is thus technically undefined. 

I'm having a strong doubt about the second part of that sentence, do
you have a link ? For me this is not a dereference, there's no memory
access nor no following of a pointer. It's how everyone defines offsetof()
in a portable way (i.e. without __builtin_offsetof()), even gcc uses it,
e.g:

  
https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=include/objalloc.h;h=231485a8;hb=HEAD#l61

There are even still ~200 of them in the linux kernel that I can find
with a quick grep. That's why I'm asking for the text reference in the
spec that considers &((ptr)->member) as a dereference, as I wouldn't
be surprised if it was an excessive check in the sanitizer. Note that
I'm not against modifying the code but touching such low levels also
have impacts on included libs and/or older compilers support, so each
time we need to be extremely careful, and more importantly it's so
easy to reintroduce such idioms that we need the exact reference to
the claimed problem if we want to be certain to avoid them in the
future.

I've checked https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf
Annex J.2 (UB) for anything like this and can't find anything mentioning
the address calculation of a member based on nullptr. All of them mention
accesses, like this one:
  (148) Arbitrarily copying or changing the bytes of or copying from a non-null
        pointer into a nullptr_t object and then reading that object

> This clutters the
> output of UBSan and is simple to fix: just use the real offsetof when
> it's available.
> 
> This patch also changes two instances of pointer arithmetic on void *
> to use char * instead, again to avoid UB.

With this one I agree.
 
> After this patch, HAProxy can run without crashing after building w/
> clang-19 -fsanitize=undefined -fno-sanitize=function,alignment

One strange thing here is that your patch makes use of:

     typeof(*(t)NULL)

which according to your own definition above should be seen as a NULL
deref by the sanitizer. Are you sure that the sanitizer didn't just
complain for the void* calculation ?

Thanks,
Willy


Reply via email to