seq misleading diagnostic

Version:

coreutils-6.10 (fixed in 6.11)

How it is diagnosed (reproduced or source analysis)?

Reproduced and source analysis

How to reproduce?

$ seq -f % 1

  -- This is an invalid input!

Symptom:

Early termination.

‘seq -f % 1’ issues and erroneous diagnostic

"seq: memory exhausted"

rather than reporting the invalid string format.

The correct  behavior should be to print the following:

$ coreutils-6.11/src/seq -f % 1

coreutils-6.11/src/seq: no % directive in format string `%'

Try `../../coreutils-6.11/src/seq --help' for more information.

Root cause:

Developer didn’t consider this case.

In version 6.11, simply adds the function:

/* The confusing msg is printed in this func: */

void xalloc_die (void)

{

 error (exit_failure, 0, "%s", _("memory exhausted"));

 abort ();

}

/* Why it xalloc_die is called? It’s from the function below. */

print_numbers (... ...) {

           .. ..      

 // Here, fmt = “%L”, x = 2, this is normal!!!

 int x_strlen = asprintf (&x_str, fmt, x);

 if (x_strlen < 0)

        xalloc_die ();

}

// Eventually, asprintf will end up calling PRINTF_PARSE...

PRINTF_PARSE(...) {

.. ..

 /* Here, c = ‘\0’ because of the invalid input... */

 switch (c) {

        case 'd': case 'i':

                                ...

        default: // This was reached when c == 0

          goto error;

           --- ERROR point!!!

                     }

 error:

   /* It will cause PRINTF_PARSE to return -1, and thus return

      error all the way to print_numbers, causing it to call

      xalloc_die. */

   errno = EINVAL;

   return -1;

}

/* So the root cause is that seq did not check the format for

  the % directive, therefore allowing this invalid input to

  escape from any checks..

  The fix is to add a new function ‘validate_format’ to check

  the input format for the % directive. */

+ /* Validate the format, FMT.  Print a diagnostic and exit

+   if there is not exactly one %-directive.  */

+ int validate_format (char const *fmt) {

+   unsigned int n_directives = 0;

+   char const *p;

+   for (p = fmt; *p; p++) {

+           if (p[0] == '%' && p[1] != '%' && p[1] != '\0')  {

+            ++n_directives;

+            ++p;

+     }

+  }

+  if (n_directives == 0) { /* given the invalid input, it will reject it. */

+          error (0, 0, _("no %% directive in format string %s"), quote (fmt));

+          usage (EXIT_FAILURE);

+   }

+  else if (1 < n_directives)

+        error (EXIT_FAILURE, 0, _("too many %% directives in format string %s"),

+        quote (fmt));

+ }

main () {

   if (format_str) {

      // format_str == ‘%’ here.

+      validate_format (format_str);

      char const *f = long_double_format (format_str, &layout);

          …

      print_numbers(format_str, …);

}

Is there Error Message?

Yes

Can Errlog print an error message?

Yes. There are two error locations where Errlog will insert a message. The first one is the ‘default’ case in PRINTF_PARSE, the 2nd one is in xalloc_die before the ‘abort’ (abnormal exit). So Errlog can provide more evidences than the original logging. In particular, the message printed at the default case will be very helpful to diagnosis as it’s very close to the root cause.