1 /*- 2 * Copyright (c) 2023 Klara, Inc. 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #include <errno.h> 8 #include <stdio.h> 9 10 #include <atf-c.h> 11 12 #define BUFSIZE 16 13 14 static const char seq[] = 15 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 16 "abcdefghijklmnopqrstuvwxyz" 17 "0123456789+/"; 18 19 struct stream { 20 char buf[BUFSIZE]; 21 unsigned int len; 22 unsigned int pos; 23 }; 24 25 static int writefn(void *cookie, const char *buf, int len) 26 { 27 struct stream *s = cookie; 28 int written = 0; 29 30 if (len <= 0) 31 return 0; 32 while (len > 0 && s->pos < s->len) { 33 s->buf[s->pos++] = *buf++; 34 written++; 35 len--; 36 } 37 if (written > 0) 38 return written; 39 errno = EAGAIN; 40 return -1; 41 } 42 43 ATF_TC_WITHOUT_HEAD(flushlbuf_partial); 44 ATF_TC_BODY(flushlbuf_partial, tc) 45 { 46 static struct stream s; 47 static char buf[BUFSIZE + 1]; 48 FILE *f; 49 unsigned int i = 0; 50 int ret = 0; 51 52 /* 53 * Create the stream and its buffer, print just enough characters 54 * to the stream to fill the buffer without triggering a flush, 55 * then check the state. 56 */ 57 s.len = BUFSIZE / 2; // write will fail after this amount 58 ATF_REQUIRE((f = fwopen(&s, writefn)) != NULL); 59 ATF_REQUIRE(setvbuf(f, buf, _IOLBF, BUFSIZE) == 0); 60 while (i < BUFSIZE) 61 if ((ret = fprintf(f, "%c", seq[i++])) < 0) 62 break; 63 ATF_CHECK_EQ(BUFSIZE, i); 64 ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]); 65 ATF_CHECK_EQ(1, ret); 66 ATF_CHECK_EQ(0, s.pos); 67 68 /* 69 * At this point, the buffer is full but writefn() has not yet 70 * been called. The next fprintf() call will trigger a preemptive 71 * fflush(), and writefn() will consume s.len characters before 72 * returning EAGAIN, causing fprintf() to fail without having 73 * written anything (which is why we don't increment i here). 74 */ 75 ret = fprintf(f, "%c", seq[i]); 76 ATF_CHECK_ERRNO(EAGAIN, ret < 0); 77 ATF_CHECK_EQ(s.len, s.pos); 78 79 /* 80 * We have consumed s.len characters from the buffer, so continue 81 * printing until it is full again and check that no overflow has 82 * occurred yet. 83 */ 84 while (i < BUFSIZE + s.len) 85 fprintf(f, "%c", seq[i++]); 86 ATF_CHECK_EQ(BUFSIZE + s.len, i); 87 ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]); 88 ATF_CHECK_EQ(0, buf[BUFSIZE]); 89 90 /* 91 * The straw that breaks the camel's back: libc fails to recognize 92 * that the buffer is full and continues to write beyond its end. 93 */ 94 fprintf(f, "%c", seq[i++]); 95 ATF_CHECK_EQ(0, buf[BUFSIZE]); 96 } 97 98 ATF_TC_WITHOUT_HEAD(flushlbuf_full); 99 ATF_TC_BODY(flushlbuf_full, tc) 100 { 101 static struct stream s; 102 static char buf[BUFSIZE]; 103 FILE *f; 104 unsigned int i = 0; 105 int ret = 0; 106 107 /* 108 * Create the stream and its buffer, print just enough characters 109 * to the stream to fill the buffer without triggering a flush, 110 * then check the state. 111 */ 112 s.len = 0; // any attempt to write will fail 113 ATF_REQUIRE((f = fwopen(&s, writefn)) != NULL); 114 ATF_REQUIRE(setvbuf(f, buf, _IOLBF, BUFSIZE) == 0); 115 while (i < BUFSIZE) 116 if ((ret = fprintf(f, "%c", seq[i++])) < 0) 117 break; 118 ATF_CHECK_EQ(BUFSIZE, i); 119 ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]); 120 ATF_CHECK_EQ(1, ret); 121 ATF_CHECK_EQ(0, s.pos); 122 123 /* 124 * At this point, the buffer is full but writefn() has not yet 125 * been called. The next fprintf() call will trigger a preemptive 126 * fflush(), and writefn() will immediately return EAGAIN, causing 127 * fprintf() to fail without having written anything (which is why 128 * we don't increment i here). 129 */ 130 ret = fprintf(f, "%c", seq[i]); 131 ATF_CHECK_ERRNO(EAGAIN, ret < 0); 132 ATF_CHECK_EQ(s.len, s.pos); 133 134 /* 135 * Now make our stream writeable. 136 */ 137 s.len = sizeof(s.buf); 138 139 /* 140 * Flush the stream again. The data we failed to write previously 141 * should still be in the buffer and will now be written to the 142 * stream. 143 */ 144 ATF_CHECK_EQ(0, fflush(f)); 145 ATF_CHECK_EQ(seq[0], s.buf[0]); 146 } 147 148 ATF_TP_ADD_TCS(tp) 149 { 150 151 ATF_TP_ADD_TC(tp, flushlbuf_partial); 152 ATF_TP_ADD_TC(tp, flushlbuf_full); 153 154 return (atf_no_error()); 155 } 156