[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Bug-gnulib] proposed vasnprintf patches for address arithmetic and

From: Paul Eggert
Subject: Re: [Bug-gnulib] proposed vasnprintf patches for address arithmetic and stack overflow
Date: 18 Nov 2003 11:28:33 -0800
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.3

Bruno Haible <address@hidden> writes:

> Paul Eggert wrote:
> > OK, but the code should use ptrdiff_t instead of ssize_t for this purpose.
> > This should avoid the need for explicit checks against SSIZE_MAX.
> What I needed here was a signed type, of the same size as 'size_t'.

I don't see why.  If you could assume a signed type that is wider than
size_t, and can represent all signed_t values, then the code would be
simpler and more reliable; there would be no need to check against
arithmetic overflow.

> You cannot guarantee that ssize_t and ptrdiff_t have the same size.

No.  However, as a practical matter I can say that ptrdiff_t is at
least as wide as ssize_t.  There have been C implementations where
ptrdiff_t was wider than size_t.  I know of no implementations where
ssize_t was wider than size_t.  So, as a practical matter (i.e.,
ignoring standards issues), it's safer to use ptrdiff_t to represent
differences between pointers into the same array.

There's also a hint of this in the C standard.  ptrdiff_t is required
to be at least 17 bits wide, but size_t is required to be only 16 bits
wide.  This is an indication that the intent is for ptrdiff_t to be
wider than size_t, at least for implementations that can allocate
objects larger than SIZE_MAX/2 bytes.

The main reason ssize_t exists is because POSIX didn't want to require
implementations to use ptrdiff_t even for values that logically should
have been ptrdiff_t, because historical practice used 'int' for those
values.  POSIX wanted to allow systems where, for example, 'read'
returned at-most-32-bit values even though size_t was 64 bits.

> When ISO C says "ptrdiff_t is the signed integer type of the result
> of subtracting two pointers" it's a statement about the compiler, not
> about the environment.

No, it's a statement about the entire implementation: this includes the
compiler and the C library.

> Or maybe the Linux with 4 GB patch is not ISO C compliant?

Hmm, it conforms.  (Ouch!  I just checked C99 and it seems to differ
from my vague recollection of C89 in this respect.)  C99 provides an
escape hatch.  When you are subtracting two pointers into the same
object, the result is of type ptrdiff_t; but if the resulting value
does not fit in ptrdiff_t, the behavior is undefined.

Perhaps our malloc wrapper should reject all attempts to create
objects with sizes greater than or equal to PTRDIFF_MAX; that would
remove a lot of potential complexity from our code.

> Btw, do you know a platform where size_t and ptrdiff_t have different
> size? And which one is larger on this platform?

I have heard of environments where ptrdiff_t was wider than size_t.  I
don't recall their names offhand.  I believe they used 16-bit size_t
and 32-bit ptrdiff_t, so they don't really apply to the GNU project.
I wouldn't be surprised if there were similar hosts in the 32-bit
world, but I don't know of any.

But the question here is not ptrdiff_t versus size_t: it's ptrdiff_t
versus ssize_t.  And the old tradition was that ssize_t was int (for
backwards compatibility to old system calls that used 32-bit 'int'),
whereas ptrdiff_t was long int.  A quick Google search revealed the
following examples of this tradition in possibly-GNU-related code:


Another argument for preferring ptrdiff_t to ssize_t is that ptrdiff_t
has been around longer, and is required by C89; ssize_t is of more
recent vintage, and is merely a POSIX standard.  All other things
being equal, it's better to use the construct that has been
standardized longer in the lower-level standard.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]