Hi Ted,

Ted Unangst wrote on Thu, Jul 27, 2017 at 01:08:24AM -0400:
> Ingo Schwarze wrote:

>> So i say in all cases above, return -1, set ENOMEM, and it doesn't
>> matter much whether anything is printed, except that asprintf(3)
>> must of course free(3) any allocated memory before returning and
>> set the pointer to NULL.

> yeah. the number of bytes returned seems like a mistake in the api
> design.  there is almost nothing one can do with this information.

Here you are going overboard.  I just did a quick audit of /bin, /sbin, 
and /usr/bin/mandoc for uses of *printf(3) returns values.

 * The overwhelming majority of uses ignores the return value.
 * Of the rest, the vast majority only checks against -1.
 * Of the rest, most are mere snprintf(3) truncation checks.
 * But i did find a dozen real uses of the return value
   (see below for details).

However, you are right that reporting errors is *much* more
important than positive return values.

 * If a malloc error is not reported, but instead the length of
   what was already printed is returned, programs typically
   SILENTLY print WRONG CONTENT in a nice, well-aligned format.
   (But note that our current implementation usually reports
    a wrong length in these cases - which could be fixed, but
    is currently broken.  So right now, the content is usually
    wrong *and* misaligned.)

 * If malloc error is returned even though something was already
   printed, output may sometimes be misaligned, but the program
   has a chance to notify the user that the requested information
   is not (completely) available.

Fortunately, most of the cases where the return value is used do
not involve floating point format specifiers.  But that is beside
the point and mere luck.  The fact stands that most programs really
need to know about failure, and not having access to the number of
characters printed during failure is usually of minor concern.

Besides, i did find one real bug.  As long as printf(%f) reports
partial success rather than failure, it requires a somewhat ugly
workaround for correct operation.


The longer i'm looking at this, the more i think that the answer
to kettenis@ good question "Do we want partial success or failure
after %f malloc failure?" should be "failure".  Also, it is unlikely
to cause many real-world surprises because it is also what glibc
and NetBSD do.

Yours,
  Ingo


AUDIT RESULTS
=============

The real bug
------------

  1. mandoc PDF output module, mdoc/term_ps.c
     The printf(3) return value is used for keeping track of
     the number of bytes printed to the PDF document, and many
     of these values are later used to write the PDF table of
     contents.
     One of the (frequently called) output statements is:
       ps_printf(p, "%.3f %.3f moveto\n(", ...)
     At this point, a short write results in invalid PDF document
     syntax, so we *really* need a -1 return value, such that we
     can abort writing the document.
     Besides, wrong positive return values (which we have now!)
     result in a corrupt PDF TOC.

Other uses of the return value (without floating point)
-------------------------------------------------------

  2. ls(1): printf(3): for columnation
  3. stty(1): printf(3): for line breaking
  4. dhclient(8): snprintf(3): to keep track of the position in the
        static buffer used for printing DHCP options
  5./6. newfs(8) and growfs(8): line breaking in progress output
  7. isakmpd(8): vsnprintf(3): used to assemble error messages
        in a static buffer
  8. mandoc(1) eqn(7) parser: asprintf(3): keep track of the length
        of an allocated string - not relying on it for iteration,
        only as an upper bound
  9. mandoc(1) date formatter: snprintf(3): advance the pointer
        after printing the number of the day
 10. mandoc(1) internal preconv(1) module: snprintf(3): advance in
        the output buffer after printing an \[uXXXX] escape sequence
 11. mandoc(1) input file reader and roff(7) parser: asprintf(3):
        keep track of the length of allocated strings, mostly as
        an upper bound, but in one case also to iterate over all
        the bytes printed

Reply via email to