Re: svn commit: r1917082 - /apr/apr/trunk/buffer/apr_buffer.c

2024-04-22 Thread Graham Leggett via dev
On 18 Apr 2024, at 17:07, Yann Ylavic  wrote:

> If so maybe:
> 
> typedef struct
> {
>union {
>char *str;
>void *mem;
>} d;
> #if APR_SIZEOF_VOIDP == 8
> #   define APR_BUFFER_SIZE_WIDTH 63
> #else
> #   define APR_BUFFER_SIZE_WIDTH 31
> #endif
>   apr_size_t size:APR_BUFFER_SIZE_WIDTH,
>   zero_terminated:1;
> } apr_buffer_t;
> 
> One could still use buf->size directly.

This is way simpler, thank you - I have made it so.

http://svn.apache.org/viewvc?view=revision=1917266

Regards,
Graham
--



Re: svn commit: r1917082 - /apr/apr/trunk/buffer/apr_buffer.c

2024-04-18 Thread Yann Ylavic
On Thu, Apr 18, 2024 at 3:58 PM Graham Leggett  wrote:
>
> On 18 Apr 2024, at 13:15, Yann Ylavic  wrote:
>
>> But let me plead again for a much simpler ->size of type apr_size_t,
>> checked against APR_BUFFER_MAX=APR_SIZE_MAX/2 wherever an apr_buffer_t
>> is initialized, using the high bit of ->size for string vs plain
>> buffer, and then getting rid of off_t/ssize_t plus all the fancy
>> signed arithmetics in the apr_buffer code (we don't care about the
>> sizeof(off_t) or anything like that anymore)..
>
> I looked at it, and it was more confusing.

There is also another completely obvious alternative which is:

typedef struct
{
union {
char *str;
void *mem;
} d;
apr_size_t size;
unsigned int flags;
} apr_buffer_t;

which also makes ->size directly usable without a helper, but you want
the type to be to words only?

If so maybe:

typedef struct
{
union {
char *str;
void *mem;
} d;
#if APR_SIZEOF_VOIDP == 8
#   define APR_BUFFER_SIZE_WIDTH 63
#else
#   define APR_BUFFER_SIZE_WIDTH 31
#endif
   apr_size_t size:APR_BUFFER_SIZE_WIDTH,
   zero_terminated:1;
} apr_buffer_t;

One could still use buf->size directly.

Regards;
Yann.


Re: svn commit: r1917082 - /apr/apr/trunk/buffer/apr_buffer.c

2024-04-18 Thread Graham Leggett via dev
On 18 Apr 2024, at 15:57, Yann Ylavic  wrote:

>> could be:
>> APR_DECLARE(apr_status_t) apr_buffer_str_set(apr_buffer_t *buf,
>> char *str, apr_size_t len)
>> {
>>if (len == APR_BUFFER_STRING) {
>>len = str ? strlen(str) : 0;
>>}
>>if (len > APR_BUFFER_MAX) {
>>return APR_EINVAL;
>>}
>> 
>>if (!str) {
>>buf->d.str = NULL;
>>buf->size = 0;
>>}
>>else {
>>buf->d.str = str;
>>buf->size = len;
>>}
>>buf->size |= ~APR_BUFFER_MAX;
> 
> The above conditional is not even needed, just:
>  buf->d.str = str;
>  buf->size = len | ~APR_BUFFER_MAX;
> works too.

Hmmm…

In the current implementation apr_buffer_alloc() is the simple case (absolute 
value of buf->size) and apr_buffer_len() is the complicated case.

Swapping them round as you’ve suggested makes a lot of sense, as we len far 
more often than we alloc.

We still have the case that alloc is one larger than len, so the real limit is 
SIZE_MAX/2-1. We can probably detect this with a simple mask, let me ponder.

All of this is a private implementation detail, your point this needs to be 
emphasized is spot on.

The only thing public about apr_buffer_t is sizeof, nothing else.

Regards,
Graham
—





Re: svn commit: r1917082 - /apr/apr/trunk/buffer/apr_buffer.c

2024-04-18 Thread Yann Ylavic
On Thu, Apr 18, 2024 at 4:41 PM Yann Ylavic  wrote:
>
> could be:
> APR_DECLARE(apr_status_t) apr_buffer_str_set(apr_buffer_t *buf,
>  char *str, apr_size_t len)
> {
> if (len == APR_BUFFER_STRING) {
> len = str ? strlen(str) : 0;
> }
> if (len > APR_BUFFER_MAX) {
> return APR_EINVAL;
> }
>
> if (!str) {
> buf->d.str = NULL;
> buf->size = 0;
> }
> else {
> buf->d.str = str;
> buf->size = len;
> }
> buf->size |= ~APR_BUFFER_MAX;

The above conditional is not even needed, just:
  buf->d.str = str;
  buf->size = len | ~APR_BUFFER_MAX;
works too.

>
> return APR_SUCCESS;
> }


Re: svn commit: r1917082 - /apr/apr/trunk/buffer/apr_buffer.c

2024-04-18 Thread Yann Ylavic
On Thu, Apr 18, 2024 at 3:58 PM Graham Leggett  wrote:
>
> On 18 Apr 2024, at 13:15, Yann Ylavic  wrote:
>
>> Indeed at least APR_BUFFER_MAX and buf->size of should be of the same
>> signedness.
>
>
> APR_BUFFER_MAX represents the size limit visible outside the API. This is 
> always positive.

I meant the same signedness of the type, not the value itself.
If APR_BUFFER_MAX is APR_SIZE_MAX/2 it's a size_t (thus unsigned),
while buf->size is an apr_off_t (thus signed), not the same types.

>
> buf->size is an internal implementation detail. This is invisible.
>
> The only reason we can see inside apr_buffer_t is because we want to allocate 
> them on the stack, and make arrays of them, so code outside needs to know the 
> size.

Fair enough, though there probably should be big warning a the struct
apr_buffer_t definition to not use ->size directly.

>
>> But let me plead again for a much simpler ->size of type apr_size_t,
>> checked against APR_BUFFER_MAX=APR_SIZE_MAX/2 wherever an apr_buffer_t
>> is initialized, using the high bit of ->size for string vs plain
>> buffer, and then getting rid of off_t/ssize_t plus all the fancy
>> signed arithmetics in the apr_buffer code (we don't care about the
>> sizeof(off_t) or anything like that anymore)..
>
>
> I looked at it, and it was more confusing.
>
> Right now, positive is a memory buffer, negative is a string. I don't know 
> why we would need to make it more complicated than that.

I think we can hardly make more complicated than:
APR_DECLARE(apr_size_t) apr_buffer_len(const apr_buffer_t *buf)
{
if (buf->size < 0) {
return (apr_size_t)((-buf->size) - 1);
}
else {
return (apr_size_t)buf->size;
}
}
...

With an apr_size_t ->size it could be:
APR_DECLARE(apr_size_t) apr_buffer_len(const apr_buffer_t *buf)
{
return buf->size & APR_BUFFER_MAX;
}
which I find much more simpler (and efficient) personally.

Another exemple:

APR_DECLARE(apr_status_t) apr_buffer_str_set(apr_buffer_t *buf,
 char *str, apr_ssize_t len)
{

if (len > APR_BUFFER_MAX) {
return APR_EINVAL;
}

if (!str) {
buf->d.str = NULL;
buf->size = 0;
}
else if (len < 0) {
apr_size_t slen = strlen(str);
if (slen <= APR_BUFFER_MAX) {
buf->d.str = str;
buf->size = -(apr_off_t)(slen + 1);
}
else {
buf->d.str = NULL;
buf->size = 0;
}
}
else {
buf->d.str = str;
buf->size = -(len + 1);
}

return APR_SUCCESS;
}

could be:
APR_DECLARE(apr_status_t) apr_buffer_str_set(apr_buffer_t *buf,
 char *str, apr_size_t len)
{
if (len == APR_BUFFER_STRING) {
len = str ? strlen(str) : 0;
}
if (len > APR_BUFFER_MAX) {
return APR_EINVAL;
}

if (!str) {
buf->d.str = NULL;
buf->size = 0;
}
else {
buf->d.str = str;
buf->size = len;
}
buf->size |= ~APR_BUFFER_MAX;

return APR_SUCCESS;
}

Should be the same kind of simplification everywhere I suppose.

>
>> Currently apr_buffer_str_make(mystring, strlen(mystring)) is UB, the
>> API is just broken IMHO.
>
> Can you clarify why? What, specifically, is broken, so I can fix it?

Passing/converting an unsigned value to a signed one where the
unsigned value cannot be represented in the signed one is undefined
behaviour in C.

So with:
  APR_DECLARE(apr_status_t) apr_buffer_str_set(apr_buffer_t *buf,
   char *str, apr_ssize_t len);
one cannot simply do (w/o UB):
  apr_size_t len = strlen(mystr);
  apr_buffer_str_set(buf, mystr, len);
without first checking that len <= APR_BUFFER_MAX, which is not really
user friendly.

Whereas with the proposed:
  APR_DECLARE(apr_status_t) apr_buffer_str_set(apr_buffer_t *buf,
   char *str, apr_size_t len);
the will return EINVAL if len > APR_BUFFER_MAX without the user having
to do the check to avoid UB.


Regards;
Yann.


Re: svn commit: r1917082 - /apr/apr/trunk/buffer/apr_buffer.c

2024-04-18 Thread Graham Leggett via dev
On 18 Apr 2024, at 13:15, Yann Ylavic  wrote:

> Indeed at least APR_BUFFER_MAX and buf->size of should be of the same
> signedness.

APR_BUFFER_MAX represents the size limit visible outside the API. This is 
always positive.

buf->size is an internal implementation detail. This is invisible.

The only reason we can see inside apr_buffer_t is because we want to allocate 
them on the stack, and make arrays of them, so code outside needs to know the 
size.

> But let me plead again for a much simpler ->size of type apr_size_t,
> checked against APR_BUFFER_MAX=APR_SIZE_MAX/2 wherever an apr_buffer_t
> is initialized, using the high bit of ->size for string vs plain
> buffer, and then getting rid of off_t/ssize_t plus all the fancy
> signed arithmetics in the apr_buffer code (we don't care about the
> sizeof(off_t) or anything like that anymore)..

I looked at it, and it was more confusing.

Right now, positive is a memory buffer, negative is a string. I don't know why 
we would need to make it more complicated than that.

> Currently apr_buffer_str_make(mystring, strlen(mystring)) is UB, the
> API is just broken IMHO.

Can you clarify why? What, specifically, is broken, so I can fix it?

Regards,
Graham
--



Re: svn commit: r1917082 - /apr/apr/trunk/buffer/apr_buffer.c

2024-04-18 Thread Graham Leggett via dev
On 18 Apr 2024, at 12:14, Ruediger Pluem  wrote:

>> +#define APR_BUFFER_MAX APR_SIZE_MAX/2
> 
> Why no longer APR_OFF_MAX?

As was pointed out, apr_off_t is always 64 bits would still break on Windows.

Inside the API, we need a very big, signed, value, and apr_off_t is perfect. 
Outside the API, we need apr_size_t, halved.

Regards,
Graham
--



Re: svn commit: r1917082 - /apr/apr/trunk/buffer/apr_buffer.c

2024-04-18 Thread Yann Ylavic
On Thu, Apr 18, 2024 at 1:16 PM Ruediger Pluem  wrote:
>
> On 4/18/24 12:37 AM, minf...@apache.org wrote:
> > Author: minfrin
> > Date: Wed Apr 17 22:37:07 2024
> > New Revision: 1917082
> >
> > URL: http://svn.apache.org/viewvc?rev=1917082=rev
> > Log:
> > apr_buffer: Add explicit casts on all potentially narrowing conversions
> > to apr_size_t. Define the maximum buffer size as APR_SIZE_MAX/2.
> >
> > Modified:
> > apr/apr/trunk/buffer/apr_buffer.c
> >
> > Modified: apr/apr/trunk/buffer/apr_buffer.c
> > URL: 
> > http://svn.apache.org/viewvc/apr/apr/trunk/buffer/apr_buffer.c?rev=1917082=1917081=1917082=diff
> > ==
> > --- apr/apr/trunk/buffer/apr_buffer.c (original)
> > +++ apr/apr/trunk/buffer/apr_buffer.c Wed Apr 17 22:37:07 2024
> > @@ -28,12 +28,13 @@
> >  #include "apr_strings.h"
> >  #include "apr_private.h"
> >
> > +#define APR_BUFFER_MAX APR_SIZE_MAX/2
>
> Why no longer APR_OFF_MAX?

Indeed at least APR_BUFFER_MAX and buf->size of should be of the same
signedness.

But let me plead again for a much simpler ->size of type apr_size_t,
checked against APR_BUFFER_MAX=APR_SIZE_MAX/2 wherever an apr_buffer_t
is initialized, using the high bit of ->size for string vs plain
buffer, and then getting rid of off_t/ssize_t plus all the fancy
signed arithmetics in the apr_buffer code (we don't care about the
sizeof(off_t) or anything like that anymore)..

Currently apr_buffer_str_make(mystring, strlen(mystring)) is UB, the
API is just broken IMHO.


Regards;
Yann.


Re: svn commit: r1917082 - /apr/apr/trunk/buffer/apr_buffer.c

2024-04-18 Thread Ruediger Pluem



On 4/18/24 12:37 AM, minf...@apache.org wrote:
> Author: minfrin
> Date: Wed Apr 17 22:37:07 2024
> New Revision: 1917082
> 
> URL: http://svn.apache.org/viewvc?rev=1917082=rev
> Log:
> apr_buffer: Add explicit casts on all potentially narrowing conversions
> to apr_size_t. Define the maximum buffer size as APR_SIZE_MAX/2.
> 
> Modified:
> apr/apr/trunk/buffer/apr_buffer.c
> 
> Modified: apr/apr/trunk/buffer/apr_buffer.c
> URL: 
> http://svn.apache.org/viewvc/apr/apr/trunk/buffer/apr_buffer.c?rev=1917082=1917081=1917082=diff
> ==
> --- apr/apr/trunk/buffer/apr_buffer.c (original)
> +++ apr/apr/trunk/buffer/apr_buffer.c Wed Apr 17 22:37:07 2024
> @@ -28,12 +28,13 @@
>  #include "apr_strings.h"
>  #include "apr_private.h"
>  
> +#define APR_BUFFER_MAX APR_SIZE_MAX/2

Why no longer APR_OFF_MAX?

Regards

Rüdiger