Bug 15744 - strtod is incorrect on INF/inf case variations in tr_TR.iso88599 locale
Summary: strtod is incorrect on INF/inf case variations in tr_TR.iso88599 locale
Status: NEW
Alias: None
Product: glibc
Classification: Unclassified
Component: math (show other bugs)
Version: 2.17
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-07-16 07:55 UTC by Vincent Lefèvre
Modified: 2015-08-27 22:16 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Vincent Lefèvre 2013-07-16 07:55:34 UTC
strtod doesn't recognize some "INF" and "inf" case variations in the tr_TR.iso88599 locale, e.g. "ınf" and "İNF", due to the dotless "i" and the "I" with dot in Turkish. The C99 standard says (and it seems that this hasn't be modified in C11):

    7.20.1.3 The strtod, strtof, and strtold functions
[...]
 3  The expected form of the subject sequence is an optional plus or minus
    sign, then one of the following:
    — a nonempty sequence of decimal digits optionally containing a
      decimal-point character, then an optional exponent part as defined
      in 6.4.4.2;
    — a 0x or 0X, then a nonempty sequence of hexadecimal digits
      optionally containing a decimal-point character, then an optional
      binary exponent part as defined in 6.4.4.2;
    — INF or INFINITY, ignoring case
    — NAN or NAN(n-char-sequence_opt), ignoring case in the NAN part,
[...]
 5  In other than the "C" locale, additional locale-specific subject
    sequence forms may be accepted.

Note that since strtod is locale sensitive (as confirmed in the comments of bug 7021), the "ignoring case" needs to take the current locale into account.

Testcase:

#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>

#define STRINGIFY(S) #S
#define MAKE_STR(S) STRINGIFY(S)

#define NUMD 1.67111104753282335
#define NUMS MAKE_STR(NUMD)

void stest (const char *buffer, const char *prefix)
{
  char *endptr;
  double x;

  x = strtod (buffer, &endptr);
  printf ("  %sstrtod(\"%s\",endptr) = %f%s\n", prefix,
          buffer, x, *endptr ? " (endptr error)" : "");
}

int main (int argc, char **argv)
{
  int i, j, k;
  double d = NUMD;
  double inf = 1.0 / 0.0;
  char *infs[] = { "INF", "inf" };
  char buffer[64], prefix[6];

  if (setlocale (LC_ALL, "") == NULL)
    {
      fprintf (stderr, "locale-test: can't set locales\n");
      exit (EXIT_FAILURE);
    }

  printf ("* Output:\n  d = " NUMS " (string)\n");
  printf ("  d = %f (in decimal, %%f format)\n", d);
  printf ("  Infinity: %f\n", inf);
  printf ("* Input:\n");
  strcpy (buffer, NUMS);
  stest (buffer, "");
  sprintf (buffer, "%f", d);
  stest (buffer, "");
  for (i = 0; i < 2; i++)
    for (j = 0; j < 4; j++)
      {
        for (k = 0; k < 3; k++)
          {
            buffer[k] = infs[i][k];
            if (j > k)
              buffer[k] = (i ? toupper : tolower)(buffer[k]);
          }
        buffer[3] = '\0';
        sprintf (prefix, "[%d%d] ",
                 !strcasecmp (buffer, "INF"),
                 !strcasecmp (buffer, "inf"));
        stest (buffer, prefix);
      }

  for (i = 1; i < argc; i++)
    {
      double x;
      char *end;

      x = strtod (argv[i], &end);
      printf ("Argument %d: ", i);
      if (*end == '\0')
        printf ("%.17g\n", x);
      else
        printf ("error\n");
    }

  return 0;
}

It can be tested by running it under the tr_TR.iso88599 locale directly (if the terminal uses the associated fonts), or with:

  LC_ALL=tr_TR.iso88599 ./locale-test | iconv -f iso88599

from any UTF8-based locales. I get:

* Output:
  d = 1.67111104753282335 (string)
  d = 1,671111 (in decimal, %f format)
  Infinity: inf
* Input:
  strtod("1.67111104753282335",endptr) = 1,000000 (endptr error)
  strtod("1,671111",endptr) = 1,671111
  [11] strtod("INF",endptr) = inf
  [00] strtod("ıNF",endptr) = 0,000000 (endptr error)
  [00] strtod("ınF",endptr) = 0,000000 (endptr error)
  [00] strtod("ınf",endptr) = 0,000000 (endptr error)
  [11] strtod("inf",endptr) = inf
  [00] strtod("İnf",endptr) = 0,000000 (endptr error)
  [00] strtod("İNf",endptr) = 0,000000 (endptr error)
  [00] strtod("İNF",endptr) = 0,000000 (endptr error)

The endptr error for "1.67111104753282335" is normal: with the following line, it shows that strtod is locale sensitive, as expected. Then the following 8 lines should all succeed, which is not the case here.

Note that the C standard requires to recognize "INF", ignoring case, but says nothing about "inf". However the IEEE 754-2008 standard mentions "inf" (ignoring case) and glibc uses "inf" as the output, so that it should also be recognized, ignoring case.