Package: libc6 Version: 2.36-9+deb12u1 Severity: normal X-Debbugs-Cc: tim.ba...@hitachivantara.com
Dear Maintainer, Consider this test case: #include <stdio.h> int main(void) { char buf[3]; int n; snprintf(buf, sizeof buf, "%s%n", "foobar", &n); printf("%d\n", n); } I believe it ought, as specified, to output "2"; glibc prints "6". The difference is in whether %n keeps counting when snprintf has no room for more output. I quote below from a C23 draft[1] but the salient points are essentially unchanged since C99. The wording in POSIX[2] is also equivalent, and no relevant parts are flagged as deliberate deviations from the C standard. [1] <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf> [2] <https://pubs.opengroup.org/onlinepubs/9699919799/functions/fprintf.html> The %n conversion specifier is defined under fprintf (7.23.6.1/8 in the abovementioned draft): The number of characters written to the output stream so far by this call to fprintf is stored into the integer object pointed to by the argument. For snprintf we then have (7.23.6.5/2): The snprintf function is equivalent to fprintf, except that the output is written into an array (specified by argument s) rather than to a stream. If n is zero, nothing is written, and s may be a null pointer. Otherwise, output characters beyond the n-1st are discarded rather than being written to the array, Assuming the text accurately reflects WG14's intent, the only consistent interpretation here, it seems to me, is that: "nothing is written" or "discarded rather than being written" implies not "written to the output [array]". Therefore any bytes discarded ought not to be counted in the value assigned by %n. It follows that that value should never exceed the buffer length, and should be strictly less unless the latter is 0. (Internally, glibc may implement snprintf by output to an in-memory stream, as if using fmemopen, but that is purely an implementation detail. It is not how C/POSIX define snprintf.) The same obviously applies identically to vsnprintf; and similarly to [v]swprintf, although the wording (7.31.2.3/2) is slightly different. A related question also arises when output to a stream suffers an error. In the event of an output error, glibc continues processing the format string, correctly since fprintf "returns when the end of the format string is encountered" (7.23.6.1/2); but what should %n do in that case? It is much less clear whether "written" ought to be interpreted as "successfully written". For example: FILE *f = fopen("/dev/full", "w"); setbuf(f, 0); fprintf(f, "%d%n", 12345, &n); Should %n assign 5, or 0? There is no such difficulty of interpretation with snprintf etc., as the text is clear that the excess characters are not written at all. -- System Information: Debian Release: 12.1 APT prefers stable APT policy: (500, 'stable') Architecture: amd64 (x86_64) Kernel: Linux 6.1.0-10-amd64 (SMP w/12 CPU threads; PREEMPT) Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8), LANGUAGE=en_GB.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash Init: systemd (via /run/systemd/system) LSM: AppArmor: enabled Versions of packages libc6 depends on: ii libgcc-s1 12.2.0-14 Versions of packages libc6 recommends: ii libidn2-0 2.3.3-1+b1 Versions of packages libc6 suggests: ii debconf [debconf-2.0] 1.5.82 pn glibc-doc <none> ii libc-l10n 2.36-9+deb12u1 pn libnss-nis <none> pn libnss-nisplus <none> ii locales 2.36-9+deb12u1 -- debconf information: glibc/upgrade: true glibc/kernel-too-old: glibc/kernel-not-supported: glibc/restart-failed: glibc/disable-screensaver: libraries/restart-without-asking: false glibc/restart-services: