Hi, what should printf(3) return on %e/%f/%g/%a malloc(3) failure?
Neither POSIX nor our manual page seem fully conclusive. POSIX says: The fprintf() and printf() functions may fail if: [ENOMEM] Insufficient storage space is available. RETURN VALUE Upon successful completion, the fprintf() and printf() functions shall return the number of bytes transmitted. If an output error was encountered, these functions shall return a negative value and set errno to indicate the error. It is not obvious to me whether malloc(3) failure is an "output error". If not, then the return value might be unspecified for that case. Our manual page agrees with almost the same wording, so it doesn't help either. The current behaviour of our implementation is to return the number of characters printed *and* set errno = ENOMEM. In various cases, that yields really weird results. For example, printf("test %.5000000f", 1.0); sets ENOMEM and returns 5 but does not actually print anything because the PRINT() macro only adds "test " to the internal iov[] data structure and the FLUSH() macro does not get called before the %f bails out of the function. Even weirder, ret = asprintf(&cp, "%s%.5000000f", argv[1], 1.0); is equivalent, in our implementation, to ret = strlen(argv[1]); cp = strdup(argv[1]); errno = ENOMEM; so a buffer does get allocated and returned, but its content is incomplete. To use our implementation correctly, the following idiom would be required: char *s; double x; size_t minsz; int ret; minsz = strlen(s) + 2; ret = asprintf(&cp, "%s%f", s, x); if (ret < 0 || ret < (int)minsz) err(1, NULL); Nobody does that. Note in particular that the "ret < 0" is required because minsz may be too large to be represented as an integer, and it is sufficient to guard the (int) cast because in that case, printf(3) returns -1/EOVERFLOW. Also note that the calculation of minsz can become arbitrarily complicated for more complicated format strings, to the point of being almost impossible. For example, for "%.1f%.1f", a return value of 6 may mean that both arguments were 1.0, or it may mean that the first one was 1.2345 and then memory was exhausted. Alternatively, you could do the "save errno, set errno = 0, call printf, inspect errno, restore errno" dance, but nobody does that either, and it would be insane. As related data points, for EOVERFLOW, we do always return -1, and for EILSEQ, we changed the code some time ago to return -1 - even though in both of these cases, it is not completely obvious whether those should be considered "output errors" in the POSIX sense. For ENOMEM, both glibc and Solaris 11 return -1 according to my testing, and NetBSD does the same according to code inspection. In FreeBSD, my impression is that dtoa() uses malloc(3), too, but i failed to find any error handling code, so i guess they chose to simply segfault - not sure, though. In summary, i think we ought to return -1. It's the only option that allows a sane usage pattern (and in particular the one that people *are* actually using, if they check for errors at all), POSIX at least doesn't forbid it, and most others seem to do it, too. What do you think? Ingo Index: stdio/vfprintf.c =================================================================== RCS file: /cvs/src/lib/libc/stdio/vfprintf.c,v retrieving revision 1.77 diff -u -p -r1.77 vfprintf.c --- stdio/vfprintf.c 29 Aug 2016 12:20:57 -0000 1.77 +++ stdio/vfprintf.c 26 Jul 2017 07:29:33 -0000 @@ -701,6 +701,7 @@ reswitch: switch (ch) { &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; + ret = -1; goto error; } } else { @@ -710,6 +711,7 @@ reswitch: switch (ch) { &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; + ret = -1; goto error; } } @@ -747,6 +749,7 @@ fp_begin: &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; + ret = -1; goto error; } } else { @@ -756,6 +759,7 @@ fp_begin: &expt, &signflag, &dtoaend); if (dtoaresult == NULL) { errno = ENOMEM; + ret = -1; goto error; } if (expt == 9999)