Re: Pointer subtraction with a null pointer

From: Andres Freund <andres(at)anarazel(dot)de>
To: Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>
Cc: pgsql-hackers(at)lists(dot)postgresql(dot)org, Robert Haas <robertmhaas(at)gmail(dot)com>, Thomas Munro <thomas(dot)munro(at)gmail(dot)com>
Subject: Re: Pointer subtraction with a null pointer
Date: 2022-03-26 17:49:53
Message-ID: 20220326174953.gdeasrl5wzal42ic@alap3.anarazel.de
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi,

On 2022-03-26 13:23:34 -0400, Tom Lane wrote:
> I wrote:
> > Andres Freund <andres(at)anarazel(dot)de> writes:
> >> On 2022-03-26 12:13:12 -0400, Tom Lane wrote:
> >>> This code is old, but mylodon wasn't doing that a week ago, so
> >>> Andres must've updated the compiler and/or changed its options.
>
> >> Yep, updated it to clang 13. It's a warning present in 13, but not in 12.
>
> > OK, that answers that.
>
> ... Actually, after looking closer, I misread what our code is doing.
> These call sites are trying to set the relptr value to "null" (zero),
> and AFAICS it should be allowed:
>
> freepage.c:188:2: warning: performing pointer subtraction with a null pointer has undefined behavior [-Wnull-pointer-subtraction]
> relptr_store(base, fpm->btree_root, (FreePageBtree *) NULL);
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> ../../../../src/include/utils/relptr.h:63:59: note: expanded from macro 'relptr_store'
> (rp).relptr_off = ((val) == NULL ? 0 : ((char *) (val)) - (base)))
> ~~~~~~~~~~~~~~~~ ^
>
> clang is complaining about the subtraction despite it being inside
> a conditional arm that cannot be reached when val is null.

Huh, yea. I somehow read the conditional branch as guarding against a an
uninitialized base pointer or such.

> It's hard to see how that isn't a flat-out compiler bug.

It only happens if the NULL is directly passed as an argument to the macro,
not if there's an intermediary variable. Argh.

#include <stddef.h>

#define relptr_store(base, rp, val) \
((rp).relptr_off = ((val) == NULL ? 0 : ((char *) (val)) - (base)))

typedef union { struct foo *relptr_type; size_t relptr_off; } relptr;

void
problem_not_present(relptr *rp, char *base)
{
struct foo *val = NULL;

relptr_store(base, *rp, val);
}

void
problem_present(relptr *rp, char *base)
{
relptr_store(base, *rp, NULL);
}

Looks like that warning is uttered whenever there's a subtraction from a
pointer with NULL, even if the code isn't reachable. Which I guess makes
*some* sense, outside of macros it's not something that'd ever be reasonable.

Wonder if we should try to get rid of the problem by also fixing the double
evaluation of val? I think something like

static inline void
relptr_store_internal(size_t *off, char *base, char *val)
{
if (val == NULL)
*off = 0;
else
*off = val - base;
}

#ifdef HAVE__BUILTIN_TYPES_COMPATIBLE_P
#define relptr_store(base, rp, val) \
(AssertVariableIsOfTypeMacro(base, char *), \
AssertVariableIsOfTypeMacro(val, __typeof__((rp).relptr_type)), \
relptr_store_internal(&(rp).relptr_off, base, (char *) val))
#else
...

should do the trick?

Might also be worth adding an assertion that base < val.

> However, granting that it isn't going to get fixed right away,
> we could replace these call sites with "relptr_store_null()",
> and maybe get rid of the conditional in relptr_store().

Also would be good with that.

Greetings,

Andres Freund

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Tom Lane 2022-03-26 17:51:07 Re: Pointer subtraction with a null pointer
Previous Message Dmitry Dolgov 2022-03-26 17:40:35 Re: pg_stat_statements and "IN" conditions