From nobody Tue Nov 07 13:39:01 2023 X-Original-To: dev-commits-src-branches@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 4SPq7T4gmxz506g2; Tue, 7 Nov 2023 13:39:01 +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 4SPq7T3hTLz4SYn; Tue, 7 Nov 2023 13:39:01 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1699364341; 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=B4yuHi9zA7JCnYczfMA6R5NFg4FjjaDNY3mrzHk4i8M=; b=W06Xz/Gk+B7YmkzhOY4f/GTF0bV1ZsOymF88v0bUdZ2K7Ine2RcKAmnkjsj45it5suoouW 33NXSRIAqpss2RADu6FJ0XbWVFYdQDuV3Xizyy7mYIZpbKcULY7Gu9q97SFCnymC6oIpRg qwGmaTAb+4BNyJQla4VG/WZRd85zhXQ8kH7Rfo7oZdDZWIoyQY6BkLyvoHgqS6m7AZCnqO DFAtS9eUyOFJk2cCFkHUWvCc8nwwx3/dyHMNmr9SRrvif6ly3/PWElhWzpYccKlbigDSmI 6Eu/LEs4tqz8bI8gHz+dSEaLu9RvczelWkNT2QBUMN9FxT4qurYz0RSwmZ6HnQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1699364341; 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=B4yuHi9zA7JCnYczfMA6R5NFg4FjjaDNY3mrzHk4i8M=; b=KrFDlDC1ZivnoJV4Xw1BAGpCRB2WKu2auyIQivr14Mu9unhu2GSVIJvgBZ1elHSUIfwaEc NniRByI0BV42OvsPzbq5tDlec6AwatOwkWbRrdEMf8p0X9884fgf1pdj7AtBxae3bSiQxU MfhOI/cleApFtKJPEMlH9lIDr9MYhqSDEkMnZxK3cn/3SlWkmoDQefE5TcrqyLvURXiPLz 0CI0zDS4mMeG3RW0KKrQtIW8XaokMyOIrBXtg5w4xn1kTfbjp+0WAiCI1rDwYr2ciMQKjv wLkKSQU5KhugUXqxwBqPt0EhsR6EBcTnBAOt1JZzIykNwACZ3OMoXJxtq7I+gg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1699364341; a=rsa-sha256; cv=none; b=D5t7pwtNF92rMR3p9/DbcRm7Xhywtfo+9qLBf7R2yV/SyXWK9cDzeuelGvsxQgQaOrPqAb a56MWbrVO4RzvTJsFZR/QN8V41GGsKsgnlEzznVf3okT+GqOOMhMA8YPYtTbwE0DH/RLnW Hf9FK0Mb94E3J3XkTOuFE7KpUo7u4G0uzdwRCL4PI9IEk2EzSNs+4DXdIdn/Ibm6aVbyCK drj5pkaPoPSTT9feCSrASvM/6edReKnhAscFWktqw5JJFCRTEYHsU0xtHufDCBYZcKDH3Q tc4UY2bIn6/8OC7dNT5Je3kY3ueIQUJOQLueiZM2VXsgEEGCyq5YTriurkMmdQ== 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 4SPq7T2lrqzrbj; Tue, 7 Nov 2023 13:39:01 +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 3A7Dd179052211; Tue, 7 Nov 2023 13:39:01 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 3A7Dd15c052208; Tue, 7 Nov 2023 13:39:01 GMT (envelope-from git) Date: Tue, 7 Nov 2023 13:39:01 GMT Message-Id: <202311071339.3A7Dd15c052208@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Ed Maste Subject: git: ba490dfc95e0 - stable/14 - fflush: Add test for buffer handling in __sflush List-Id: Commits to the stable branches of the FreeBSD src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-branches List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-branches@freebsd.org X-BeenThere: dev-commits-src-branches@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/stable/14 X-Git-Reftype: branch X-Git-Commit: ba490dfc95e0941216420a2003757b3c4b5b2ec2 Auto-Submitted: auto-generated The branch stable/14 has been updated by emaste: URL: https://cgit.FreeBSD.org/src/commit/?id=ba490dfc95e0941216420a2003757b3c4b5b2ec2 commit ba490dfc95e0941216420a2003757b3c4b5b2ec2 Author: Dag-Erling Smørgrav AuthorDate: 2023-08-03 15:13:45 +0000 Commit: Ed Maste CommitDate: 2023-11-07 13:38:35 +0000 fflush: Add test for buffer handling in __sflush Sponsored by: Klara, Inc. (cherry picked from commit b8dbfb0a6c181a9aeab0b793deb0813d06052df9) Approved by: so --- 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()); +}