Bug 21360

Summary: snprintf %n does not conform to ISO C when characters are not printed
Product: glibc Reporter: Vincent Lefèvre <vincent-srcware>
Component: stdioAssignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED INVALID    
Severity: normal CC: fweimer
Priority: P2 Flags: fweimer: security-
Version: 2.24   
Target Milestone: ---   
Host: Target:
Build: Last reconfirmed:

Description Vincent Lefèvre 2017-04-07 14:17:52 UTC
Consider the following example:

#include <stdio.h>

int main(void)
{
  int r, n = -1;

  r = snprintf (NULL, 0, "foo%n", &n);
  printf ("%d %d\n", r, n);
  return 0;
}

With glibc 2.24, I get 3 3 instead of the expected 3 0. Indeed, the glibc manual (whose description seems equivalent to ISO C11) says for %n:

12.12.6 Other Output Conversions
--------------------------------
[...]
   The '%n' conversion is unlike any of the other output conversions.
It uses an argument which must be a pointer to an 'int', but instead of
printing anything it stores the number of characters printed so far by
this call at that location.

but due to the size 0, nothing has been printed. Thus one should have n = 0.

Note that in its snprintf description:

     The return value is the number of characters which would be
     generated for the given input, excluding the trailing null.

"would be", thus the expected r = 3. There is no such "would be" for %n. In the C11 draft I have, this is said in a similar way:

     The snprintf function returns the number of characters that would
     have been written had n been sufficiently large, [...]

The "had n been sufficiently large" is quite explicit. But again, there is no such thing for %n.
Comment 1 Andreas Schwab 2017-04-07 17:22:18 UTC
The description only talks about the produced characters being discarded instead of written to memory.  The side effects of producing the output is still performed.  In case of lack of space the output stream is just the equivalent of /dev/null.
Comment 2 Florian Weimer 2017-04-12 11:05:12 UTC
Agreed, this looks like an imprecision or a defect in the C standard.