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