The branch stable/13 has been updated by emaste:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=66d84f1362f63c23f6f451490e153e0703a7cda6

commit 66d84f1362f63c23f6f451490e153e0703a7cda6
Author:     Dag-Erling Smørgrav <[email protected]>
AuthorDate: 2023-08-03 15:13:45 +0000
Commit:     Ed Maste <[email protected]>
CommitDate: 2023-11-07 13:39:55 +0000

    fflush: Add test for buffer handling in __sflush
    
    Sponsored by:   Klara, Inc.
    
    (cherry picked from commit b8dbfb0a6c181a9aeab0b793deb0813d06052df9)
    (cherry picked from commit ba490dfc95e0941216420a2003757b3c4b5b2ec2)
    
    Approved by:    so
---
 lib/libc/tests/stdio/Makefile         |   1 +
 lib/libc/tests/stdio/flushlbuf_test.c | 155 ++++++++++++++++++++++++++++++++++
 2 files changed, 156 insertions(+)

diff --git a/lib/libc/tests/stdio/Makefile b/lib/libc/tests/stdio/Makefile
index 12ae44855da5..452bcc535d21 100644
--- a/lib/libc/tests/stdio/Makefile
+++ b/lib/libc/tests/stdio/Makefile
@@ -3,6 +3,7 @@
 
 ATF_TESTS_C+=          eintr_test
 ATF_TESTS_C+=          fdopen_test
+ATF_TESTS_C+=          flushlbuf_test
 ATF_TESTS_C+=          fmemopen2_test
 ATF_TESTS_C+=          fopen2_test
 ATF_TESTS_C+=          freopen_test
diff --git a/lib/libc/tests/stdio/flushlbuf_test.c 
b/lib/libc/tests/stdio/flushlbuf_test.c
new file mode 100644
index 000000000000..11d9ea4ecc6c
--- /dev/null
+++ b/lib/libc/tests/stdio/flushlbuf_test.c
@@ -0,0 +1,155 @@
+/*-
+ * Copyright (c) 2023 Klara, Inc.
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <atf-c.h>
+
+#define BUFSIZE 16
+
+static const char seq[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyz"
+    "0123456789+/";
+
+struct stream {
+       char buf[BUFSIZE];
+       unsigned int len;
+       unsigned int pos;
+};
+
+static int writefn(void *cookie, const char *buf, int len)
+{
+       struct stream *s = cookie;
+       int written = 0;
+
+       if (len <= 0)
+               return 0;
+       while (len > 0 && s->pos < s->len) {
+               s->buf[s->pos++] = *buf++;
+               written++;
+               len--;
+       }
+       if (written > 0)
+               return written;
+       errno = EAGAIN;
+       return -1;
+}
+
+ATF_TC_WITHOUT_HEAD(flushlbuf_partial);
+ATF_TC_BODY(flushlbuf_partial, tc)
+{
+       static struct stream s;
+       static char buf[BUFSIZE + 1];
+       FILE *f;
+       unsigned int i = 0;
+       int ret = 0;
+
+       /*
+        * Create the stream and its buffer, print just enough characters
+        * to the stream to fill the buffer without triggering a flush,
+        * then check the state.
+        */
+       s.len = BUFSIZE / 2; // write will fail after this amount
+       ATF_REQUIRE((f = fwopen(&s, writefn)) != NULL);
+       ATF_REQUIRE(setvbuf(f, buf, _IOLBF, BUFSIZE) == 0);
+       while (i < BUFSIZE)
+               if ((ret = fprintf(f, "%c", seq[i++])) < 0)
+                       break;
+       ATF_CHECK_EQ(BUFSIZE, i);
+       ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]);
+       ATF_CHECK_EQ(1, ret);
+       ATF_CHECK_EQ(0, s.pos);
+
+       /*
+        * At this point, the buffer is full but writefn() has not yet
+        * been called.  The next fprintf() call will trigger a preemptive
+        * fflush(), and writefn() will consume s.len characters before
+        * returning EAGAIN, causing fprintf() to fail without having
+        * written anything (which is why we don't increment i here).
+        */
+       ret = fprintf(f, "%c", seq[i]);
+       ATF_CHECK_ERRNO(EAGAIN, ret < 0);
+       ATF_CHECK_EQ(s.len, s.pos);
+
+       /*
+        * We have consumed s.len characters from the buffer, so continue
+        * printing until it is full again and check that no overflow has
+        * occurred yet.
+        */
+       while (i < BUFSIZE + s.len)
+               fprintf(f, "%c", seq[i++]);
+       ATF_CHECK_EQ(BUFSIZE + s.len, i);
+       ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]);
+       ATF_CHECK_EQ(0, buf[BUFSIZE]);
+
+       /*
+        * The straw that breaks the camel's back: libc fails to recognize
+        * that the buffer is full and continues to write beyond its end.
+        */
+       fprintf(f, "%c", seq[i++]);
+       ATF_CHECK_EQ(0, buf[BUFSIZE]);
+}
+
+ATF_TC_WITHOUT_HEAD(flushlbuf_full);
+ATF_TC_BODY(flushlbuf_full, tc)
+{
+       static struct stream s;
+       static char buf[BUFSIZE];
+       FILE *f;
+       unsigned int i = 0;
+       int ret = 0;
+
+       /*
+        * Create the stream and its buffer, print just enough characters
+        * to the stream to fill the buffer without triggering a flush,
+        * then check the state.
+        */
+       s.len = 0; // any attempt to write will fail
+       ATF_REQUIRE((f = fwopen(&s, writefn)) != NULL);
+       ATF_REQUIRE(setvbuf(f, buf, _IOLBF, BUFSIZE) == 0);
+       while (i < BUFSIZE)
+               if ((ret = fprintf(f, "%c", seq[i++])) < 0)
+                       break;
+       ATF_CHECK_EQ(BUFSIZE, i);
+       ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]);
+       ATF_CHECK_EQ(1, ret);
+       ATF_CHECK_EQ(0, s.pos);
+
+       /*
+        * At this point, the buffer is full but writefn() has not yet
+        * been called.  The next fprintf() call will trigger a preemptive
+        * fflush(), and writefn() will immediately return EAGAIN, causing
+        * fprintf() to fail without having written anything (which is why
+        * we don't increment i here).
+        */
+       ret = fprintf(f, "%c", seq[i]);
+       ATF_CHECK_ERRNO(EAGAIN, ret < 0);
+       ATF_CHECK_EQ(s.len, s.pos);
+
+       /*
+        * Now make our stream writeable.
+        */
+       s.len = sizeof(s.buf);
+
+       /*
+        * Flush the stream again.  The data we failed to write previously
+        * should still be in the buffer and will now be written to the
+        * stream.
+        */
+       ATF_CHECK_EQ(0, fflush(f));
+       ATF_CHECK_EQ(seq[0], s.buf[0]);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+       ATF_TP_ADD_TC(tp, flushlbuf_partial);
+       ATF_TP_ADD_TC(tp, flushlbuf_full);
+
+       return (atf_no_error());
+}

Reply via email to