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