From nobody Tue Nov 7 13:37:32 2023 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4SPq5m3gLdz506bq; Tue, 7 Nov 2023 13:37:32 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4SPq5m348qz4S30; Tue, 7 Nov 2023 13:37:32 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1699364252; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=3VRUfO/ldXvCRzRtpRjLQ3aAVdQPis12XHVxY3RFrHc=; b=PE/M9keJAtXCqMcZUl/hRR5w9ROFnDqlQGH62f+9VlLlV90gj4060vBXQuP7DfbqgLO1vA xeuo+noMH/xxaCNZVY/jp02BcUoW/VlhEj3+pDGkRj9FdaLLSSlX2eyhipYQaubfGkhGF6 ljjyl9JiMJQJB6O4e4Olzr9ceDswrobME3LquQOjO5pg6hfLCStmv7oQhotn4CdwniZDJo qdcwWhk+ncMXf9i1wecSVXfUcqJpep+b1gGQgJUcqWccOAF0mseUgIL/tJA9RTydCXXYGt md+wncpzSm2waA+7wmz0WamaI6OmmlGZplr0YSorWJhQXHxqmWZOg/eBqHfG6w== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1699364252; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=3VRUfO/ldXvCRzRtpRjLQ3aAVdQPis12XHVxY3RFrHc=; b=RpHpMaCI3kTj2mUUTjAnOjcwqMhj/bNgx2EkNg+IFCOjj4iwyEBOMGyZwlWRMlnU70mH+X iFIsTXTJwyFhXuK656HIdAntk40ULsTi8PKfmqEXLlJGY5TmSBAXU6M32uDetYeJPjMbXB 5u4bYwJ2fmMYDMZInaOCpknnE8j85TvpXZVCUOqG1iRTgVGV1SI2yAj12MWJw1TG+SiKWL 5BriNEedXJVwZFbRWYoXRnZzReJyV2/c3gNjABsjYctRBYuTCCUNYjAQKX/2nTZ2taIDxI wtSUYaqRvjNRvPrH8+WxNbQEUE5/ckfR7M2oBrYJBiXrmb+qH7axRqBNJDTBtw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1699364252; a=rsa-sha256; cv=none; b=XwOlI80B7J548Oa+IFdqZOtGVMUAzKvF+/IzcBTBtGXkwtq9QJq36PDyK6LKENm1hAaQJ2 9lqwQjsczH2YfgYC3XPK0xkYse3ITzTNdTQUANvhpm59AGmYBExs4O0uHPxJiwQQcCc1oc MurY9C0/IAzoPgpT16EWivfdLAvcjxlTkTZOnjG1yMZ1fsHWq8DOtZ+6co53oUVDNf6Ueq 84+sXumoPmriLJ6ZIpZWMVIMxdeZNGqrBZMdzFBZrpoSHUrYXntWijNHUko2E1sj58MrTU xwzfr0o037oYoaxwThLjqX3xYLAIGRjnjh2qDsGSWD9P0TuPdYMorUB3xXfFYw== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 4SPq5m28PLzsVZ; Tue, 7 Nov 2023 13:37:32 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.17.1/8.17.1) with ESMTP id 3A7DbWsN051858; Tue, 7 Nov 2023 13:37:32 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 3A7DbWMC051855; Tue, 7 Nov 2023 13:37:32 GMT (envelope-from git) Date: Tue, 7 Nov 2023 13:37:32 GMT Message-Id: <202311071337.3A7DbWMC051855@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Ed Maste Subject: git: b8dbfb0a6c18 - main - fflush: Add test for buffer handling in __sflush List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-main@freebsd.org X-BeenThere: dev-commits-src-main@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: emaste X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: b8dbfb0a6c181a9aeab0b793deb0813d06052df9 Auto-Submitted: auto-generated The branch main has been updated by emaste: URL: https://cgit.FreeBSD.org/src/commit/?id=b8dbfb0a6c181a9aeab0b793deb0813d06052df9 commit b8dbfb0a6c181a9aeab0b793deb0813d06052df9 Author: Dag-Erling Smørgrav AuthorDate: 2023-08-03 15:13:45 +0000 Commit: Ed Maste CommitDate: 2023-11-07 13:21:12 +0000 fflush: Add test for buffer handling in __sflush Sponsored by: Klara, Inc. --- 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 7716a60c0e6b..f0822601e34b 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 +#include + +#include + +#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()); +}