Path: blob/main/lib/libc/tests/stdio/flushlbuf_test.c
39530 views
/*-1* Copyright (c) 2023 Klara, Inc.2*3* SPDX-License-Identifier: BSD-2-Clause4*/56#include <errno.h>7#include <stdio.h>89#include <atf-c.h>1011#define BUFSIZE 161213static const char seq[] =14"ABCDEFGHIJKLMNOPQRSTUVWXYZ"15"abcdefghijklmnopqrstuvwxyz"16"0123456789+/";1718struct stream {19char buf[BUFSIZE];20unsigned int len;21unsigned int pos;22};2324static int25writefn(void *cookie, const char *buf, int len)26{27struct stream *s = cookie;28int written = 0;2930if (len <= 0)31return (0);32while (len > 0 && s->pos < s->len) {33s->buf[s->pos++] = *buf++;34written++;35len--;36}37if (written > 0)38return (written);39errno = EAGAIN;40return (-1);41}4243ATF_TC_WITHOUT_HEAD(flushlbuf_partial);44ATF_TC_BODY(flushlbuf_partial, tc)45{46static struct stream s;47static char buf[BUFSIZE + 1];48FILE *f;49unsigned int i = 0;50int ret = 0;5152/*53* Create the stream and its buffer, print just enough characters54* to the stream to fill the buffer without triggering a flush,55* then check the state.56*/57s.len = BUFSIZE / 2; // write will fail after this amount58ATF_REQUIRE((f = fwopen(&s, writefn)) != NULL);59ATF_REQUIRE(setvbuf(f, buf, _IOLBF, BUFSIZE) == 0);60while (i < BUFSIZE)61if ((ret = fprintf(f, "%c", seq[i++])) < 0)62break;63ATF_CHECK_EQ(BUFSIZE, i);64ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]);65ATF_CHECK_EQ(1, ret);66ATF_CHECK_EQ(0, s.pos);6768/*69* At this point, the buffer is full but writefn() has not yet70* been called. The next fprintf() call will trigger a preemptive71* fflush(), and writefn() will consume s.len characters before72* returning EAGAIN, causing fprintf() to fail without having73* written anything (which is why we don't increment i here).74*/75ret = fprintf(f, "%c", seq[i]);76ATF_CHECK_ERRNO(EAGAIN, ret < 0);77ATF_CHECK_EQ(s.len, s.pos);7879/*80* We have consumed s.len characters from the buffer, so continue81* printing until it is full again and check that no overflow has82* occurred yet.83*/84while (i < BUFSIZE + s.len)85fprintf(f, "%c", seq[i++]);86ATF_CHECK_EQ(BUFSIZE + s.len, i);87ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]);88ATF_CHECK_EQ(0, buf[BUFSIZE]);8990/*91* The straw that breaks the camel's back: libc fails to recognize92* that the buffer is full and continues to write beyond its end.93*/94fprintf(f, "%c", seq[i++]);95ATF_CHECK_EQ(0, buf[BUFSIZE]);96}9798ATF_TC_WITHOUT_HEAD(flushlbuf_full);99ATF_TC_BODY(flushlbuf_full, tc)100{101static struct stream s;102static char buf[BUFSIZE];103FILE *f;104unsigned int i = 0;105int ret = 0;106107/*108* Create the stream and its buffer, print just enough characters109* to the stream to fill the buffer without triggering a flush,110* then check the state.111*/112s.len = 0; // any attempt to write will fail113ATF_REQUIRE((f = fwopen(&s, writefn)) != NULL);114ATF_REQUIRE(setvbuf(f, buf, _IOLBF, BUFSIZE) == 0);115while (i < BUFSIZE)116if ((ret = fprintf(f, "%c", seq[i++])) < 0)117break;118ATF_CHECK_EQ(BUFSIZE, i);119ATF_CHECK_EQ(seq[i - 1], buf[BUFSIZE - 1]);120ATF_CHECK_EQ(1, ret);121ATF_CHECK_EQ(0, s.pos);122123/*124* At this point, the buffer is full but writefn() has not yet125* been called. The next fprintf() call will trigger a preemptive126* fflush(), and writefn() will immediately return EAGAIN, causing127* fprintf() to fail without having written anything (which is why128* we don't increment i here).129*/130ret = fprintf(f, "%c", seq[i]);131ATF_CHECK_ERRNO(EAGAIN, ret < 0);132ATF_CHECK_EQ(s.len, s.pos);133134/*135* Now make our stream writeable.136*/137s.len = sizeof(s.buf);138139/*140* Flush the stream again. The data we failed to write previously141* should still be in the buffer and will now be written to the142* stream.143*/144ATF_CHECK_EQ(0, fflush(f));145ATF_CHECK_EQ(seq[0], s.buf[0]);146}147148ATF_TP_ADD_TCS(tp)149{150ATF_TP_ADD_TC(tp, flushlbuf_partial);151ATF_TP_ADD_TC(tp, flushlbuf_full);152153return (atf_no_error());154}155156157