Newlib's strtol() is not standard-compliant

Brian Inglis Brian.Inglis@SystematicSW.ab.ca
Wed Sep 9 20:06:36 GMT 2026


On 2026-09-09 08:54, Lavrentiev, Anton (NIH/NLM/NCBI) [C] via Newlib wrote:
> I am reposting this from Cygwin ML (as there it seems to not have drawn any attention at all):
>	https://cygwin.com/pipermail/cygwin/2026-September/260018.html
> 
> I'm using a fairly modern Cygwin (which in turn is using newlib as its C RTL):
> $ uname -a
> CYGWIN_NT-10.0-22631 NCBIPC9109 3.6.10-1.x86_64 2026-07-13 20:20 UTC x86_64 Cygwin
> 
> and here's the problem:

> IDK if this was brought up previously, but I just found out that Cygwin's version of strtol() does not adhere to the standard behavior in this corner case.
> 
> Consider the test case:
> 
> $ cat strtol.c
> #include <errno.h>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
> 
> int main(int argc, const char* argv[])
> {
>      char* end;
>      long val = strtol(argv[1], &end, 0);
>      printf("val = %ld, char(s) read = %tu, errno = %s\n",
>             val, end - argv[1], strerror(errno));
>      return 0;
> }
> 
> $ gcc -Wall strtol.c
> 
> As observed, Cygwin cannot convert any string that begins with "0x" but which is not immediately followed by a proper hex digit, while in fact it should have been able to convert the leading "0" and reject the remaining characters (which start with "x"):
> 
> $ ./a.exe 0x
> val = 0, char(s) read = 0, errno = No error
> 
> $ ./a.exe 0y
> val = 0, char(s) read = 1, errno = No error
> 
> $ ./a.exe 0xz
> val = 0, char(s) read = 0, errno = No error
> 
> $ ./a.exe 0xa
> val = 10, char(s) read = 3, errno = No error
> 
> $ ./a.exe 0xaz
> val = 10, char(s) read = 3, errno = No error
> 
> The buggy behavior is the same if the 3rd argument "base" of strtol() is set 16 (instead of 0, as shown in code above).
> Note that it correctly converts "0y" to 0, and rejects the following "y" (and it correctly converts "0xaz" by stopping at "z").
> 
> The same code on Linux (libc) yields correct results in all cases:
> 
> $ ./a.out 0x
> val = 0, char(s) read = 1, errno = Success
> 
> $ ./a.out 0y
> val = 0, char(s) read = 1, errno = Success
> 
> $ ./a.out 0xz
> val = 0, char(s) read = 1, errno = Success
> 
> $ ./a.out 0xa
> val = 10, char(s) read = 3, errno = Success
> 
> $ ./a.out 0xaz
> val = 10, char(s) read = 3, errno = Success
> 
> Here's a reference to the standard:
> https://pubs.opengroup.org/onlinepubs/9699919799/functions/strtol.html

The latest is:

	https://pubs.opengroup.org/onlinepubs/9799919799/functions/strtol.html

deferring to ISO C e.g. 203Y Working (or Committee, or Final) Draft:

	https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3685.pdf
	https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3886.pdf or later

	https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3685.pdf#page=401
	https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3886.pdf#page=403
	¶7.25.2.8 The strtol, strtoll, strtoul, and strtoull functions

	https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3685.pdf#page=75
	https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3886.pdf#page=75
	¶6.4.5.2 integer literal including 0[Bb][01]... if base is 0, or 2, 8,
	10, 16 respectively

	or later documents changing those paragraphs

where with base 0, integers may begin with a sign [-+], decimals begin with a 
non-zero digit, 0[Bb] must be followed by a binary digit or base > 11, as value 
is 11*BASE^N, 0[Xx] must be followed by a hex digit or base > 33, as the value 
is 33*BASE^N depending on position, and 0 is (strictly) octal and may be 
followed by octal digits.

Neither grouping or digit delimiter characters are allowed, nor type suffixes.

Any result of zero or where *endptr is not a NUL or some other expected 
delimiter character may be an issue, and some diagnostic may be desirable from 
the invoker.

> Specifically, this applies:
> 
> "The subject sequence is defined as the longest initial subsequence of the input string, starting with the first non-white-space character that is of the expected form."
> 
> In the case of "0x" not followed by any hex digit, the "subject sequence" is exactly first "0" character, which is perfectly convertible (to 0, obviously) in any base (including 0 and 16).  Libc behavior on Linux exactly demonstrates this.
-- 
Take care. Thanks, Brian Inglis              Calgary, Alberta, Canada

La perfection est atteinte                   Perfection is achieved
non pas lorsqu'il n'y a plus rien à ajouter  not when there is no more to add
mais lorsqu'il n'y a plus rien à retrancher  but when there is no more to cut
                                 -- Antoine de Saint-Exupéry


More information about the Cygwin mailing list