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