Newlib's strtol() is not standard-compliant

Lavrentiev, Anton (NIH/NLM/NCBI) [C] lavr@ncbi.nlm.nih.gov
Wed Sep 9 14:54:03 GMT 2026


Hello,

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:

-- REPOST BEGINS --
Hi all,

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

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.
-- REPOST ENDS --

Anton Lavrentiev
Contractor NIH/NLM/NCBI



More information about the Newlib mailing list