Hi,

now we have conflicting and incomplete opinions.  What should

  "prefix %.5000000f postfix", 1.0

and

  "%s %.5000000f %s", "prefix", 1.0, "postfix"

do if the %f fails with ENOMEM?


Currently,

 1. [f]printf(..., "prefix %.5000000f postfix", 1.0)

    prints nothing, returns 7, sets ENOMEM.

    deraadt@ says it should preserve errno.
    I assume he means it should print "prefix " and return 7?

 2. [f]printf(..., "%s %.5000000f %s", "prefix", 1.0, "postfix")

    prints "prefix", returns 7 (sic!), sets ENOMEM.

    deraadt@ says it should preserve errno.
    I assume he means it should print "prefix " (one more blank)
    and return 7?

 3. snprintf(..., "prefix %.5000000f postfix", 1.0)

    prints nothing, returns 7, sets ENOMEM.

    deraadt@ says it should preserve errno.
    I assume he means it should print "prefix " and return 7?

 4. snprintf(..., "%s %.5000000f %s", "prefix", 1.0, "postfix")

    prints "prefix", returns 7 (sic!), sets ENOMEM.

    deraadt@ says it should preserve errno.
    I assume he means it should print "prefix " (one more blank)
    and return 7?

 5. asprintf(..., "prefix %.5000000f postfix", 1.0)

    allocates "" (sic!), returns 7, sets ENOMEM.

    deraadt@ says it should preserve errno.
    I assume he means it should allocate "prefix " and return 7?

    millert@ says it should return -1 and set ENOMEM.
    I assume he means it should not allocate anything.

 6. asprintf(..., "%s %.5000000f %s", "prefix", 1.0, "postfix")

    allocates "prefix", returns 7 (sic!), sets ENOMEM.

    deraadt@ says it should preserve errno.
    I assume he means it should print "prefix " (one more blank)
    and return 7?

    millert@ says it should return -1 and set ENOMEM.
    I assume he means it should not allocate anything.

 7. printf("%.5000000f postfix", 1.0);

    prints nothing, returns 0, sets ENOMEM.

    So this reports partial success of printing zero bytes.
    That doesn't make sense to me either.

I certainly agree with deraadt@ that we should never clobber errno,
even though kettenis@ may be right that POSIX does not forbid it:
being more careful than POSIX makes sense in this case.

But i disagree with deraadt@ and agree with millert@ that we should
return failure (-1) on *any* ENOMEM.  Even if something was already
printed.  Even in the case of snprintf(3).  Even though POSIX does
not allow snprintf(3) to fail with ENOMEM, i have no idea how to
implement that (with correct, untruncated results, and in particular
the correct return value of the length that would actually be
required if memory were unlimited).  I think that sprintf(3) should
better fail than produce wrong results (in particular a deceivingly
small return value), and when it fails, i see no guarantee that the
buffer content must remain untouched.

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.

Once we reach consensus, i'll implement that.

A test program is appended.

Yours,
  Ingo


OpenBSD results:
printf literal:   >>><<< ret = 7 errno = 12
printf %s:        >>>prefix<<< ret = 7 errno = 12
snprintf literal: >>><<< ret = 7 errno = 12
snprintf %s:      >>>prefix<<< ret = 7 errno = 12
asprintf literal: >>><<< ret = 7 errno = 12
asprintf %s:      >>>prefix<<< ret = 7 errno = 12
printf %f first:  >>><<< ret = 0 errno = 12

glibc results:
printf literal:   >>>prefix <<< ret = -1 errno = 12
printf %s:        >>>prefix <<< ret = -1 errno = 12
snprintf literal: >>>prefix <<< ret = -1 errno = 12
snprintf %s:      >>>prefix <<< ret = -1 errno = 12
asprintf literal: >>>(null)<<< ret = -1 errno = 12
asprintf %s:      >>>(null)<<< ret = -1 errno = 12
printf %f first:  >>><<< ret = -1 errno = 12

Solaris 11:
printf and fprintf never seem to fail from ENOMEM and happily
print five million zeros with %.5000000f even with all rlimits
clamped down.  asprintf simply segfaults on %f ENOMEM.

With my patch:
printf literal:   >>><<< ret = -1 errno = 12
printf %s:        >>><<< ret = -1 errno = 12
snprintf literal: >>><<< ret = -1 errno = 12
snprintf %s:      >>>prefix<<< ret = -1 errno = 12
asprintf literal: >>>(null)<<< ret = -1 errno = 12
asprintf %s:      >>>(null)<<< ret = -1 errno = 12
printf %f first:  >>><<< ret = -1 errno = 12

The reason why the "printf %s" output changes is that there is yet
another layer of buffering in our code even for _IONBF.  __vfprintf()
sets up a temporary buffer with __sbprintf(), which gets printed
for ret >= 0 but does not get printed for ret = -1, see vfprintf.c
line 141.


#include <sys/resource.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

int
main(int argc, char *argv[])
{
        char buf[128];
        struct rlimit limit;
        char *cp;
        int ret;

        setvbuf(stdout, NULL, _IONBF, 0);

        if (getrlimit(RLIMIT_DATA, &limit) < 0)
                err(1, "getrlimit");
        if (limit.rlim_max == RLIM_INFINITY || limit.rlim_max > 5000000)
                limit.rlim_max = 5000000;
        limit.rlim_cur = limit.rlim_max;
        if (setrlimit(RLIMIT_DATA, &limit) < 0)
                err(1, "setrlimit");

        printf("printf literal:   >>>");
        errno = 0;
        ret = printf("prefix %.5000000f postfix", 1.0);
        printf("<<< ret = %d errno = %d\n", ret, errno);

        printf("printf %%s:        >>>");
        errno = 0;
        ret = printf("%s %.5000000f %s", "prefix", 1.0, "postfix");
        printf("<<< ret = %d errno = %d\n", ret, errno);

        errno = 0;
        ret = snprintf(buf, sizeof(buf), "prefix %.5000000f postfix", 1.0);
        printf("snprintf literal: >>>%s<<< ret = %d errno = %d\n",
            buf, ret, errno);

        errno = 0;
        ret = snprintf(buf, sizeof(buf), "%s %.5000000f %s",
            "prefix", 1.0, "postfix");
        printf("snprintf %%s:      >>>%s<<< ret = %d errno = %d\n",
            buf, ret, errno);

        cp = NULL;
        errno = 0;
        ret = asprintf(&cp, "prefix %.5000000f postfix", 1.0);
        printf("asprintf literal: >>>%s<<< ret = %d errno = %d\n",
            cp, ret, errno);
        free(cp);

        cp = NULL;
        errno = 0;
        ret = asprintf(&cp, "%s %.5000000f %s", "prefix", 1.0, "postfix");
        printf("asprintf %%s:      >>>%s<<< ret = %d errno = %d\n",
            cp, ret, errno);
        free(cp);

        printf("printf %%f first:  >>>");
        errno = 0;
        ret = printf("%.5000000f postfix", 1.0);
        printf("<<< ret = %d errno = %d\n", ret, errno);

        return 0;
}

Reply via email to