strtoul(-1) discussion

Fabrice Bauzac
Sun Apr 7 13:20:00 GMT 2013


I might have found a defect of glibc's strtoul with regards to POSIX.
I have done that long ago, but hesitated to post it until now.
Please see below what I have found, and a test program in attachment.

Results of the experiment on glibc:

strtol: overflows in a sensible manner, i.e. outside its output range
[-2^31 ; 2^31-1].

strtoul: overflows when outside the range [-(2^32-1) ; (2^32-1)] even
though its output range is [0 ; (2^32-1)].

When N is inside strtoul's no-overflow range, we have:

  strtoul (-N) = - strtoul (N)

which seems correct according to some ISO C specs [8].  However, the
result is truncated to the 32 rightmost bits since it is an ulong.

The algorithm behind GNU libc's strtoul seems to be:

  * store the sign independently from the digits.
  * perform a strtoul on the digits without the sign, producing ulong
    value N.
  * if the sign is negative, perform N := -N.
  * return N.

The problem is that [1] says:

  "If the correct value is outside the range of representable values,
  {ULONG_MAX} or {ULLONG_MAX} shall be returned and errno set to

If my interpretation is correct, this means that strtoul should not
fail when given "-0" but should raise ERANGE for all strictly negative
values ("-1" and below), which it currently does not in the GNU

[As a side note, the specs in [1] mean that the spirit for strtol and
strtoul are different concerning the following matter:

  When the string represents an integer below strtol's range, strtol
  returns the lowest representable integer, i.e. LONG_MIN.

  When the string represents an integer below strtoul's range, strtoul
  returns ULONG_MAX, instead of the lowest representable integer
  (which is of course 0 for ulong).

  For that matter, we might want to add a function to the GNU libc
  similar to ISO C strtoul but which has a more sensible behaviour
  (return 0 when provided negative numbers).]

The Solaris 9 man page [2] says the same as [1]: strtoul should raise
ERANGE and return ULONG_MAX "If the correct value is outside the range
of representable values".

The HP-UX man page [3] says that strtoul should raise ERANGE and
return ULONG_MAX "If the correct value would cause overflow".

The AIX man page [4] says that strtoul should return ULONG_MAX "If the
correct value is outside the range of representable values", but seems
a bit unclear to me concerning the return value in this case.

The FreeBSD [5], NetBSD [6] and OpenBSD [7] man pages describe a
behaviour similar to that of GNU libc: strtoul raises ERANGE (and
return ULONG_MAX) only if "the original (non-negated) value would

The IRIX man page [9] is not quite clear about this; it says that the
ERANGE error is raised "If the value represented by STR would cause

The eCos test suite [10] requires that when given string "-479",
strtoul should return (unsigned long) -479.

In short, there are two main specifications of the behaviour of
strtoul: the GNU/*BSD side and the standard/Solaris/HPUX side.  Except
that the GNU specifications seem less clear than the *BSD ones.

I have made some experiments on Solaris, and the Solaris
implementation does not follow its specification.  The behaviour of
Solaris's strtoul seems to be the same as GNU libc's strtoul!

Can somebody check the AIX behaviour?

If you decide to change the GNU libc so that it conforms more to ISO C
([1] and [8]), then I believe something along the lines of the
following code should be added to strtol_l.c (untested though):

  /* Check cases where the value falls outside the range of unsigned
     LONG int even though i is within the range of unsigned LONG
     int  */
  if (i > 0 && negative)
    overflow = 1;

Of course there is the possibility that it break some programs,
even if it is a quite special case.

The set of programs where this change would make any difference is
probably only a subset of the programs that have never been ported
from GNU to Solaris/AIX/HPUX/etc.

In any case, the texinfo manual for strtoul is currently at least
ambiguous to me.  It should also mention the fact that strtoul is not
portable unless the input string is known to represent a nonnegative

[8] (section page ~310)


Best regards
-------------- next part --------------
A non-text attachment was scrubbed...
Name: strtoul.c
Type: text/x-csrc
Size: 2079 bytes
Desc: not available
URL: <>

More information about the Libc-help mailing list