public inbox for libc-alpha@sourceware.org
 help / color / mirror / Atom feed
* Handling C2Y zero-length operations on null pointers
@ 2024-10-03 22:16 Joseph Myers
  2024-10-03 22:42 ` Florian Weimer
  0 siblings, 1 reply; 14+ messages in thread
From: Joseph Myers @ 2024-10-03 22:16 UTC (permalink / raw)
  To: gcc, libc-alpha

WG14 accepted https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3322.pdf 
at this week's meeting in Minneapolis, allowing various zero-length 
language and library operations on null pointers in C2Y (in support of the 
idiom where an empty array may be represented by a null pointer with zero 
length rather than allocated memory).

As far as I know the language pieces should already work in GCC (NULL + 0, 
NULL - NULL, NULL <= NULL).  So the main implementation question relates 
to the library pieces, which have both compiler and library implications.  
nonnull attributes on the affected library functions are no longer correct 
(either in library headers or on built-in functions), because GCC uses 
such attributes for optimization and valid code may pass null pointers to 
these functions when the length passed is zero.

So we need to remove the nonnull attributes on those functions (in both 
GCC and glibc) - and I think that's naturally a 
language-version-independent change rather than keeping the attributes for 
pre-C2Y standards.  (N3322 was voted onto the list of papers to consider 
for previous revisions of the standard.  I think WG14 is putting too much 
on that list - I'd like it to be only for things that are unambiguously 
defects.  But while I don't think this is a defect and don't think it 
belongs on the list, it does nevertheless seem reasonable to support this 
usage with older language versions in GCC and glibc.)

The real question is how to achieve optimal warnings in the absence of the 
attribute.  Should we have a variant of the nonnull attribute that warns 
for NULL arguments but without optimizing based on them?  Or one that 
warns and potentially optimizes but only optimizes when the size is known 
to be nonzero?  Or do we already have any suitable attribute?  (I think 
warning when the size might be zero is fine, given that actually 
explicitly passing a null pointer with a zero size is not the intended use 
of this feature - the feature is only of use when the array might or might 
not have zero size and you want to avoid the code needing to check for 
that case.)

-- 
Joseph S. Myers
josmyers@redhat.com


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-10-03 22:16 Handling C2Y zero-length operations on null pointers Joseph Myers
@ 2024-10-03 22:42 ` Florian Weimer
  2024-10-03 22:50   ` Paul Eggert
  2024-10-07 14:13   ` Jakub Jelinek
  0 siblings, 2 replies; 14+ messages in thread
From: Florian Weimer @ 2024-10-03 22:42 UTC (permalink / raw)
  To: Joseph Myers; +Cc: gcc, libc-alpha

* Joseph Myers:

> The real question is how to achieve optimal warnings in the absence of the 
> attribute.  Should we have a variant of the nonnull attribute that warns 
> for NULL arguments but without optimizing based on them?

I think attribute access already covers part of it:

#include <stddef.h>
void read_array (void *, size_t) __attribute__ ((access (read_only, 1, 2)));
void
f (void)
{
  read_array (NULL, 0); // No warning.
  read_array (NULL, 1); // Warning.
}

It does not work for functions like strndup that support both string
arguments (of any length) and array arguments of a specified size.
The read_only variant requires an initialized array of the specified
length.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-10-03 22:42 ` Florian Weimer
@ 2024-10-03 22:50   ` Paul Eggert
  2024-10-07 14:13   ` Jakub Jelinek
  1 sibling, 0 replies; 14+ messages in thread
From: Paul Eggert @ 2024-10-03 22:50 UTC (permalink / raw)
  To: Florian Weimer, Joseph Myers; +Cc: gcc, libc-alpha

On 2024-10-03 15:42, Florian Weimer wrote:
> I think attribute access already covers part of it:

The GCC documentation for attribute access[1] is unclear as to whether 
the pointer is allowed to be null when the size is zero. Perhaps we 
could ask the GCC maintainers to document that it's allowed and to 
implement it that way. Failing that, I suppose we'd need a new attribute 
with the desired semantics.

[1]: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-10-03 22:42 ` Florian Weimer
  2024-10-03 22:50   ` Paul Eggert
@ 2024-10-07 14:13   ` Jakub Jelinek
  2024-10-07 14:21     ` Florian Weimer
  2024-10-07 15:14     ` Qing Zhao
  1 sibling, 2 replies; 14+ messages in thread
From: Jakub Jelinek @ 2024-10-07 14:13 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Joseph Myers, gcc, libc-alpha

On Fri, Oct 04, 2024 at 12:42:24AM +0200, Florian Weimer wrote:
> * Joseph Myers:
> 
> > The real question is how to achieve optimal warnings in the absence of the 
> > attribute.  Should we have a variant of the nonnull attribute that warns 
> > for NULL arguments but without optimizing based on them?
> 
> I think attribute access already covers part of it:
> 
> #include <stddef.h>
> void read_array (void *, size_t) __attribute__ ((access (read_only, 1, 2)));
> void
> f (void)
> {
>   read_array (NULL, 0); // No warning.
>   read_array (NULL, 1); // Warning.
> }
> 
> It does not work for functions like strndup that support both string
> arguments (of any length) and array arguments of a specified size.
> The read_only variant requires an initialized array of the specified
> length.

access attribute can't deal with various other things.

Consider the qsort case.  My understanding was that the paper is making
typedef int (*cmpfn) (const void *, const void *);
qsort (NULL, 0, 1, (cmpfn) NULL);
valid (but is
qsort (NULL, 1, 0, (cmpfn) NULL);
still invalid?).
How do you express that with access attribute, which can only have 1 size
argument?  The accessed memory for the read/write pointee of the first
argument has nmemb * size parameter bytes size.
And using access attribute for function pointers doesn't work, there is
no data to be read/written there, just code.

Guess some of the nonnull cases could be replaced by access attribute
if we clarify the documentation that if SIZE_INDEX is specified and that
argument is non-zero then the pointer has to be non-NULL, and teach
sanitizers etc. to sanitize those.

For the rest, perhaps we need some nonnull_if_nonzero argument
which requires that the parameter identified by the first attribute
argument must be pointer which is non-NULL if the parameter identified
by the second attribute argument is non-zero.
And get clarified the qsort/bsearch cases whether it is about just
nmemb == 0 or nmemb * size == 0.

	Jakub


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-10-07 14:13   ` Jakub Jelinek
@ 2024-10-07 14:21     ` Florian Weimer
  2024-12-01 17:28       ` Alejandro Colomar
  2024-10-07 15:14     ` Qing Zhao
  1 sibling, 1 reply; 14+ messages in thread
From: Florian Weimer @ 2024-10-07 14:21 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Joseph Myers, gcc, libc-alpha

* Jakub Jelinek:

> How do you express that with access attribute, which can only have 1
> size argument?

Don't we sometimes use inline functions to handle such special cases?

> For the rest, perhaps we need some nonnull_if_nonzero argument
> which requires that the parameter identified by the first attribute
> argument must be pointer which is non-NULL if the parameter identified
> by the second attribute argument is non-zero.
> And get clarified the qsort/bsearch cases whether it is about just
> nmemb == 0 or nmemb * size == 0.

C does not support zero-sized objects, so that's something for us to
figure out on our own.  We can treat size == 0 as invalid because the
functions can't work, as they use pointers for the comparison function
and not array indices.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-10-07 14:13   ` Jakub Jelinek
  2024-10-07 14:21     ` Florian Weimer
@ 2024-10-07 15:14     ` Qing Zhao
  2024-10-07 15:22       ` Jakub Jelinek
  2024-11-12  6:51       ` Martin Uecker
  1 sibling, 2 replies; 14+ messages in thread
From: Qing Zhao @ 2024-10-07 15:14 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Florian Weimer, Joseph Myers, gcc, libc-alpha



> On Oct 7, 2024, at 10:13, Jakub Jelinek via Gcc <gcc@gcc.gnu.org> wrote:
> 
> On Fri, Oct 04, 2024 at 12:42:24AM +0200, Florian Weimer wrote:
>> * Joseph Myers:
>> 
>>> The real question is how to achieve optimal warnings in the absence of the 
>>> attribute.  Should we have a variant of the nonnull attribute that warns 
>>> for NULL arguments but without optimizing based on them?
>> 
>> I think attribute access already covers part of it:
>> 
>> #include <stddef.h>
>> void read_array (void *, size_t) __attribute__ ((access (read_only, 1, 2)));
>> void
>> f (void)
>> {
>>  read_array (NULL, 0); // No warning.
>>  read_array (NULL, 1); // Warning.
>> }
>> 
>> It does not work for functions like strndup that support both string
>> arguments (of any length) and array arguments of a specified size.
>> The read_only variant requires an initialized array of the specified
>> length.
> 
> access attribute can't deal with various other things.
> 
> Consider the qsort case.  My understanding was that the paper is making
> typedef int (*cmpfn) (const void *, const void *);
> qsort (NULL, 0, 1, (cmpfn) NULL);
> valid (but is
> qsort (NULL, 1, 0, (cmpfn) NULL);
> still invalid?).
> How do you express that with access attribute, which can only have 1 size
> argument?  The accessed memory for the read/write pointee of the first
> argument has nmemb * size parameter bytes size.

For the other attribute “alloc_size”, we have two forms, 
A. alloc_size (position)
and
B. alloc_size (position-1, position-2)

The 2nd form is used to represent nmemb * size. 

Is it possible that we extend the attribute “access” similarly? 

Then we can use the attribute “access” consistently for this purpose?

Qing

> And using access attribute for function pointers doesn't work, there is
> no data to be read/written there, just code.
> 
> Guess some of the nonnull cases could be replaced by access attribute
> if we clarify the documentation that if SIZE_INDEX is specified and that
> argument is non-zero then the pointer has to be non-NULL, and teach
> sanitizers etc. to sanitize those.
> 
> For the rest, perhaps we need some nonnull_if_nonzero argument
> which requires that the parameter identified by the first attribute
> argument must be pointer which is non-NULL if the parameter identified
> by the second attribute argument is non-zero.
> And get clarified the qsort/bsearch cases whether it is about just
> nmemb == 0 or nmemb * size == 0.
> 
> Jakub
> 


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-10-07 15:14     ` Qing Zhao
@ 2024-10-07 15:22       ` Jakub Jelinek
  2024-10-07 16:33         ` Qing Zhao
  2024-11-12  6:51       ` Martin Uecker
  1 sibling, 1 reply; 14+ messages in thread
From: Jakub Jelinek @ 2024-10-07 15:22 UTC (permalink / raw)
  To: Qing Zhao; +Cc: Florian Weimer, Joseph Myers, gcc, libc-alpha

On Mon, Oct 07, 2024 at 03:14:22PM +0000, Qing Zhao wrote:
> > Consider the qsort case.  My understanding was that the paper is making
> > typedef int (*cmpfn) (const void *, const void *);
> > qsort (NULL, 0, 1, (cmpfn) NULL);
> > valid (but is
> > qsort (NULL, 1, 0, (cmpfn) NULL);
> > still invalid?).
> > How do you express that with access attribute, which can only have 1 size
> > argument?  The accessed memory for the read/write pointee of the first
> > argument has nmemb * size parameter bytes size.
> 
> For the other attribute “alloc_size”, we have two forms, 
> A. alloc_size (position)
> and
> B. alloc_size (position-1, position-2)
> 
> The 2nd form is used to represent nmemb * size. 
> 
> Is it possible that we extend the attribute “access” similarly? 
> 
> Then we can use the attribute “access” consistently for this purpose?

We could do that and express the array pointer of qsort/bsearch that way.
But there is also the function pointer case, there we don't access any bytes
(and what exactly a function pointer means depends on architecture, can be
code pointer, or can be pointer to function descriptor etc.), so we really
need to express this pointer must be non-NULL if some other argument (or
their product?) is non-0.
If one passes constant(s) to those arguments, then such checking can be
done through a warning like we warn for passing NULL to nonnull attributed
parameters right now (or not if 0), if it is non-constant, then it can be
diagnosed in sanitizers.

	Jakub


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-10-07 15:22       ` Jakub Jelinek
@ 2024-10-07 16:33         ` Qing Zhao
  0 siblings, 0 replies; 14+ messages in thread
From: Qing Zhao @ 2024-10-07 16:33 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Florian Weimer, Joseph Myers, gcc, libc-alpha



> On Oct 7, 2024, at 11:22, Jakub Jelinek <jakub@redhat.com> wrote:
> 
> On Mon, Oct 07, 2024 at 03:14:22PM +0000, Qing Zhao wrote:
>>> Consider the qsort case.  My understanding was that the paper is making
>>> typedef int (*cmpfn) (const void *, const void *);
>>> qsort (NULL, 0, 1, (cmpfn) NULL);
>>> valid (but is
>>> qsort (NULL, 1, 0, (cmpfn) NULL);
>>> still invalid?).
>>> How do you express that with access attribute, which can only have 1 size
>>> argument?  The accessed memory for the read/write pointee of the first
>>> argument has nmemb * size parameter bytes size.
>> 
>> For the other attribute “alloc_size”, we have two forms, 
>> A. alloc_size (position)
>> and
>> B. alloc_size (position-1, position-2)
>> 
>> The 2nd form is used to represent nmemb * size. 
>> 
>> Is it possible that we extend the attribute “access” similarly? 
>> 
>> Then we can use the attribute “access” consistently for this purpose?
> 
> We could do that and express the array pointer of qsort/bsearch that way.
> But there is also the function pointer case, there we don't access any bytes
> (and what exactly a function pointer means depends on architecture, can be
> code pointer, or can be pointer to function descriptor etc.), so we really
> need to express this pointer must be non-NULL if some other argument (or
> their product?) is non-0.

I have a question here (maybe a stupid question-:): do we really need to express such situation for function pointers? 
I cannot construct a use case for this…

Qing

> If one passes constant(s) to those arguments, then such checking can be
> done through a warning like we warn for passing NULL to nonnull attributed
> parameters right now (or not if 0), if it is non-constant, then it can be
> diagnosed in sanitizers.
> 
> Jakub
> 


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-10-07 15:14     ` Qing Zhao
  2024-10-07 15:22       ` Jakub Jelinek
@ 2024-11-12  6:51       ` Martin Uecker
  2024-11-12  6:55         ` Martin Uecker
  2024-11-13 19:47         ` Qing Zhao
  1 sibling, 2 replies; 14+ messages in thread
From: Martin Uecker @ 2024-11-12  6:51 UTC (permalink / raw)
  To: Qing Zhao, Jakub Jelinek; +Cc: Florian Weimer, Joseph Myers, gcc, libc-alpha

Am Montag, dem 07.10.2024 um 15:14 +0000 schrieb Qing Zhao:
> 
> > On Oct 7, 2024, at 10:13, Jakub Jelinek via Gcc <gcc@gcc.gnu.org> wrote:
> > 
> > On Fri, Oct 04, 2024 at 12:42:24AM +0200, Florian Weimer wrote:
> > > * Joseph Myers:
> > > 
> > > > The real question is how to achieve optimal warnings in the absence of the 
> > > > attribute.  Should we have a variant of the nonnull attribute that warns 
> > > > for NULL arguments but without optimizing based on them?
> > > 
> > > I think attribute access already covers part of it:
> > > 
> > > #include <stddef.h>
> > > void read_array (void *, size_t) __attribute__ ((access (read_only, 1, 2)));
> > > void
> > > f (void)
> > > {
> > >  read_array (NULL, 0); // No warning.
> > >  read_array (NULL, 1); // Warning.
> > > }
> > > 
> > > It does not work for functions like strndup that support both string
> > > arguments (of any length) and array arguments of a specified size.
> > > The read_only variant requires an initialized array of the specified
> > > length.
> > 
> > access attribute can't deal with various other things.
> > 
> > Consider the qsort case.  My understanding was that the paper is making
> > typedef int (*cmpfn) (const void *, const void *);
> > qsort (NULL, 0, 1, (cmpfn) NULL);
> > valid (but is
> > qsort (NULL, 1, 0, (cmpfn) NULL);
> > still invalid?).
> > How do you express that with access attribute, which can only have 1 size
> > argument?  The accessed memory for the read/write pointee of the first
> > argument has nmemb * size parameter bytes size.
> 
> For the other attribute “alloc_size”, we have two forms, 
> A. alloc_size (position)
> and
> B. alloc_size (position-1, position-2)
> 
> The 2nd form is used to represent nmemb * size. 
> 
> Is it possible that we extend the attribute “access” similarly? 
> 
> Then we can use the attribute “access” consistently for this purpose?

We also miss sanitizer support.

How about letting "access" only be about access range
and instead have separate attribute that can be used to
express more complicated preconditions?

void* foo(void *p, size_t mmemb, size_t size)
	[[precondition((p == NULL) == (mmemb * size == 0)]];

(not saying this is the right condition for any function
in the standard library)

Martin

> 
> Qing
> 
> > And using access attribute for function pointers doesn't work, there is
> > no data to be read/written there, just code.
> > 
> > Guess some of the nonnull cases could be replaced by access attribute
> > if we clarify the documentation that if SIZE_INDEX is specified and that
> > argument is non-zero then the pointer has to be non-NULL, and teach
> > sanitizers etc. to sanitize those.
> > 
> > For the rest, perhaps we need some nonnull_if_nonzero argument
> > which requires that the parameter identified by the first attribute
> > argument must be pointer which is non-NULL if the parameter identified
> > by the second attribute argument is non-zero.
> > And get clarified the qsort/bsearch cases whether it is about just
> > nmemb == 0 or nmemb * size == 0.
> > 
> > Jakub
> > 
> 


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-11-12  6:51       ` Martin Uecker
@ 2024-11-12  6:55         ` Martin Uecker
  2024-11-13 19:47         ` Qing Zhao
  1 sibling, 0 replies; 14+ messages in thread
From: Martin Uecker @ 2024-11-12  6:55 UTC (permalink / raw)
  To: Qing Zhao, Jakub Jelinek; +Cc: Florian Weimer, Joseph Myers, gcc, libc-alpha

Am Dienstag, dem 12.11.2024 um 07:51 +0100 schrieb Martin Uecker:
> Am Montag, dem 07.10.2024 um 15:14 +0000 schrieb Qing Zhao:
> > 
> > > On Oct 7, 2024, at 10:13, Jakub Jelinek via Gcc <gcc@gcc.gnu.org> wrote:
> > > 
> > > On Fri, Oct 04, 2024 at 12:42:24AM +0200, Florian Weimer wrote:
> > > > * Joseph Myers:
> > > > 
> > > > > The real question is how to achieve optimal warnings in the absence of the 
> > > > > attribute.  Should we have a variant of the nonnull attribute that warns 
> > > > > for NULL arguments but without optimizing based on them?
> > > > 
> > > > I think attribute access already covers part of it:
> > > > 
> > > > #include <stddef.h>
> > > > void read_array (void *, size_t) __attribute__ ((access (read_only, 1, 2)));
> > > > void
> > > > f (void)
> > > > {
> > > >  read_array (NULL, 0); // No warning.
> > > >  read_array (NULL, 1); // Warning.
> > > > }
> > > > 
> > > > It does not work for functions like strndup that support both string
> > > > arguments (of any length) and array arguments of a specified size.
> > > > The read_only variant requires an initialized array of the specified
> > > > length.
> > > 
> > > access attribute can't deal with various other things.
> > > 
> > > Consider the qsort case.  My understanding was that the paper is making
> > > typedef int (*cmpfn) (const void *, const void *);
> > > qsort (NULL, 0, 1, (cmpfn) NULL);
> > > valid (but is
> > > qsort (NULL, 1, 0, (cmpfn) NULL);
> > > still invalid?).
> > > How do you express that with access attribute, which can only have 1 size
> > > argument?  The accessed memory for the read/write pointee of the first
> > > argument has nmemb * size parameter bytes size.
> > 
> > For the other attribute “alloc_size”, we have two forms, 
> > A. alloc_size (position)
> > and
> > B. alloc_size (position-1, position-2)
> > 
> > The 2nd form is used to represent nmemb * size. 
> > 
> > Is it possible that we extend the attribute “access” similarly? 
> > 
> > Then we can use the attribute “access” consistently for this purpose?
> 
> We also miss sanitizer support.
> 
> How about letting "access" only be about access range
> and instead have separate attribute that can be used to
> express more complicated preconditions?
> 
> void* foo(void *p, size_t mmemb, size_t size)
> 	[[precondition((p == NULL) == (mmemb * size == 0)]];
> 
> (not saying this is the right condition for any function
> in the standard library)

And the condition should avoid wraparound.

Martin

> 
> Martin
> 
> > 
> > Qing
> > 
> > > And using access attribute for function pointers doesn't work, there is
> > > no data to be read/written there, just code.
> > > 
> > > Guess some of the nonnull cases could be replaced by access attribute
> > > if we clarify the documentation that if SIZE_INDEX is specified and that
> > > argument is non-zero then the pointer has to be non-NULL, and teach
> > > sanitizers etc. to sanitize those.
> > > 
> > > For the rest, perhaps we need some nonnull_if_nonzero argument
> > > which requires that the parameter identified by the first attribute
> > > argument must be pointer which is non-NULL if the parameter identified
> > > by the second attribute argument is non-zero.
> > > And get clarified the qsort/bsearch cases whether it is about just
> > > nmemb == 0 or nmemb * size == 0.
> > > 
> > > Jakub
> > > 
> > 
> 


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-11-12  6:51       ` Martin Uecker
  2024-11-12  6:55         ` Martin Uecker
@ 2024-11-13 19:47         ` Qing Zhao
  2024-12-01 17:24           ` Alejandro Colomar
  1 sibling, 1 reply; 14+ messages in thread
From: Qing Zhao @ 2024-11-13 19:47 UTC (permalink / raw)
  To: Martin Uecker
  Cc: Jakub Jelinek, Florian Weimer, Joseph Myers, gcc, libc-alpha



> On Nov 12, 2024, at 01:51, Martin Uecker <uecker@tugraz.at> wrote:
> 
> Am Montag, dem 07.10.2024 um 15:14 +0000 schrieb Qing Zhao:
>> 
>>> On Oct 7, 2024, at 10:13, Jakub Jelinek via Gcc <gcc@gcc.gnu.org> wrote:
>>> 
>>> On Fri, Oct 04, 2024 at 12:42:24AM +0200, Florian Weimer wrote:
>>>> * Joseph Myers:
>>>> 
>>>>> The real question is how to achieve optimal warnings in the absence of the 
>>>>> attribute.  Should we have a variant of the nonnull attribute that warns 
>>>>> for NULL arguments but without optimizing based on them?
>>>> 
>>>> I think attribute access already covers part of it:
>>>> 
>>>> #include <stddef.h>
>>>> void read_array (void *, size_t) __attribute__ ((access (read_only, 1, 2)));
>>>> void
>>>> f (void)
>>>> {
>>>> read_array (NULL, 0); // No warning.
>>>> read_array (NULL, 1); // Warning.
>>>> }
>>>> 
>>>> It does not work for functions like strndup that support both string
>>>> arguments (of any length) and array arguments of a specified size.
>>>> The read_only variant requires an initialized array of the specified
>>>> length.
>>> 
>>> access attribute can't deal with various other things.
>>> 
>>> Consider the qsort case.  My understanding was that the paper is making
>>> typedef int (*cmpfn) (const void *, const void *);
>>> qsort (NULL, 0, 1, (cmpfn) NULL);
>>> valid (but is
>>> qsort (NULL, 1, 0, (cmpfn) NULL);
>>> still invalid?).
>>> How do you express that with access attribute, which can only have 1 size
>>> argument?  The accessed memory for the read/write pointee of the first
>>> argument has nmemb * size parameter bytes size.
>> 
>> For the other attribute “alloc_size”, we have two forms, 
>> A. alloc_size (position)
>> and
>> B. alloc_size (position-1, position-2)
>> 
>> The 2nd form is used to represent nmemb * size. 
>> 
>> Is it possible that we extend the attribute “access” similarly? 
>> 
>> Then we can use the attribute “access” consistently for this purpose?
> 
> We also miss sanitizer support.
> 
> How about letting "access" only be about access range
> and instead have separate attribute that can be used to
> express more complicated preconditions?

Sounds reasonable to me. 
Yes, it’s not a good idea to mix them together with one attribute. 

Qing
> 
> void* foo(void *p, size_t mmemb, size_t size)
> [[precondition((p == NULL) == (mmemb * size == 0)]];
> 
> (not saying this is the right condition for any function
> in the standard library)
> 
> Martin
> 
>> 
>> Qing
>> 
>>> And using access attribute for function pointers doesn't work, there is
>>> no data to be read/written there, just code.
>>> 
>>> Guess some of the nonnull cases could be replaced by access attribute
>>> if we clarify the documentation that if SIZE_INDEX is specified and that
>>> argument is non-zero then the pointer has to be non-NULL, and teach
>>> sanitizers etc. to sanitize those.
>>> 
>>> For the rest, perhaps we need some nonnull_if_nonzero argument
>>> which requires that the parameter identified by the first attribute
>>> argument must be pointer which is non-NULL if the parameter identified
>>> by the second attribute argument is non-zero.
>>> And get clarified the qsort/bsearch cases whether it is about just
>>> nmemb == 0 or nmemb * size == 0.
>>> 
>>> Jakub



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-11-13 19:47         ` Qing Zhao
@ 2024-12-01 17:24           ` Alejandro Colomar
  2024-12-12 12:46             ` Chris Bazley
  0 siblings, 1 reply; 14+ messages in thread
From: Alejandro Colomar @ 2024-12-01 17:24 UTC (permalink / raw)
  To: qing.zhao; +Cc: fw, gcc, jakub, josmyers, libc-alpha, uecker, Chris.Bazley

[-- Attachment #1: Type: text/plain, Size: 1600 bytes --]

Hi,

I had a discussion about this with another WG14 member when this was
voted in.  We both voted against, because this is nefarious for static
analysis.

However, I think this can be though to resemble how 'const' works in the
standard:

	const char cbuf[10];

	memcpy((char *)cbuf, "", 0);

The code above is legal even if it is passing a const pointer where a
non-const one is expected.  This is because memcpy(3) will not write to
it.

Nevertheless, if one does

	memcpy(NULL, "", 0);

without a cast, the compiler will still diagnose.  This is important,
because if we would remove the diagnostics, it would be a footgun.

Similarly, we should allow null pointers (just like const pointers), in
the sense that there's no Undefined Behavior.  BUT there should be a
diagnostic.  Passing NULL is bad, and if one project wants to pass it,
it should do so with whatever compiler shenanigans to disable the
diagnostic (a cast, or a pragma, or whatever, not my problem).  In my
code, I want to see a diagnostic if I pass NULL to it, because in my
dialect (and in every C language before C2y), a null pointer is an
invalid pointer, and that distinction makes the code more robust.

I would either transform [[gnu::nonnull]] to be only about diagnostics
and not optimizations, or maybe add a _Optional qualifier that would
be used for this.

TL;DR:  Removing UB is nice, but removing diagnostics is NOT nice.

Have a lovely day!
Alex

P.S.:  I think it was a big mistake to vote this into C2y, and I voted
against.

-- 
<https://www.alejandro-colomar.es/>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-10-07 14:21     ` Florian Weimer
@ 2024-12-01 17:28       ` Alejandro Colomar
  0 siblings, 0 replies; 14+ messages in thread
From: Alejandro Colomar @ 2024-12-01 17:28 UTC (permalink / raw)
  To: fw; +Cc: gcc, jakub, josmyers, libc-alpha

[-- Attachment #1: Type: text/plain, Size: 705 bytes --]

> > And get clarified the qsort/bsearch cases whether it is about just
> > nmemb == 0 or nmemb * size == 0.
> 
> C does not support zero-sized objects, so that's something for us to
> figure out on our own.  We can treat size == 0 as invalid because the
> functions can't work, as they use pointers for the comparison function
> and not array indices.

Hi Florian,

This is not really true.  ISO C claims to not support 0-sized objects,
but then allows malloc(0) to return non-null, and

	memcpy(malloc(0), malloc(0), 0);

has always been fully-complying in platforms like glibc, where malloc(0)
returns non-NULL.

Have a lovely day!
Alex


-- 
<https://www.alejandro-colomar.es/>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: Handling C2Y zero-length operations on null pointers
  2024-12-01 17:24           ` Alejandro Colomar
@ 2024-12-12 12:46             ` Chris Bazley
  0 siblings, 0 replies; 14+ messages in thread
From: Chris Bazley @ 2024-12-12 12:46 UTC (permalink / raw)
  To: Alejandro Colomar, qing.zhao; +Cc: fw, gcc, jakub, josmyers, libc-alpha, uecker

Thank you, Alex.

I agree this was a mistake.

From experience, there is only one robust interface design when it comes to null pointer arguments: either a function handles them, or it doesn't. Whether or not it handles them should have nothing to do with the value of other parameters. This is also how Python's type system works.

I have no objection to the long-established (if it ain't broken, don't fix it) memcpy interface being upgraded to handle null pointers. The cost in CPU cycles and code size is probably negligible.

What I *do* object to is a halfway house solution where memcpy only handles null pointers contingent on the value of some other parameter. This is hard to document, hard to teach static analysis tools, and hard to verify when reading calling code.

I do not think it serves anyone.

Best regards,
Chris
________________________________________
From: Alejandro Colomar
Sent: Sunday, December 01, 2024 17:24
To: qing.zhao@oracle.com
Cc: fw@deneb.enyo.de; gcc@gcc.gnu.org; jakub@redhat.com; josmyers@redhat.com; libc-alpha@sourceware.org; uecker@tugraz.at; Chris Bazley
Subject: Re: Handling C2Y zero-length operations on null pointers


Hi,

I had a discussion about this with another WG14 member when this was
voted in.  We both voted against, because this is nefarious for static
analysis.

However, I think this can be though to resemble how 'const' works in the
standard:

        const char cbuf[10];

        memcpy((char *)cbuf, "", 0);

The code above is legal even if it is passing a const pointer where a
non-const one is expected.  This is because memcpy(3) will not write to
it.

Nevertheless, if one does

        memcpy(NULL, "", 0);

without a cast, the compiler will still diagnose.  This is important,
because if we would remove the diagnostics, it would be a footgun.

Similarly, we should allow null pointers (just like const pointers), in
the sense that there's no Undefined Behavior.  BUT there should be a
diagnostic.  Passing NULL is bad, and if one project wants to pass it,
it should do so with whatever compiler shenanigans to disable the
diagnostic (a cast, or a pragma, or whatever, not my problem).  In my
code, I want to see a diagnostic if I pass NULL to it, because in my
dialect (and in every C language before C2y), a null pointer is an
invalid pointer, and that distinction makes the code more robust.

I would either transform [[gnu::nonnull]] to be only about diagnostics
and not optimizations, or maybe add a _Optional qualifier that would
be used for this.

TL;DR:  Removing UB is nice, but removing diagnostics is NOT nice.

Have a lovely day!
Alex

P.S.:  I think it was a big mistake to vote this into C2y, and I voted
against.

-- 
<https://www.alejandro-colomar.es/>



^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2024-12-12 12:46 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-10-03 22:16 Handling C2Y zero-length operations on null pointers Joseph Myers
2024-10-03 22:42 ` Florian Weimer
2024-10-03 22:50   ` Paul Eggert
2024-10-07 14:13   ` Jakub Jelinek
2024-10-07 14:21     ` Florian Weimer
2024-12-01 17:28       ` Alejandro Colomar
2024-10-07 15:14     ` Qing Zhao
2024-10-07 15:22       ` Jakub Jelinek
2024-10-07 16:33         ` Qing Zhao
2024-11-12  6:51       ` Martin Uecker
2024-11-12  6:55         ` Martin Uecker
2024-11-13 19:47         ` Qing Zhao
2024-12-01 17:24           ` Alejandro Colomar
2024-12-12 12:46             ` Chris Bazley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).