From nobody Tue Nov 07 14:30:24 2023 X-Original-To: dev-commits-src-all@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 4SPrGm6pD8z50G1Y; Tue, 7 Nov 2023 14:30:24 +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 4SPrGm6HYtz4b7D; Tue, 7 Nov 2023 14:30:24 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1699367424; 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=5xPsRqAFEs77npUxoLx3/3wuVDu9twVyuq3Psu+XSJc=; b=f1kR89DmbU/0wxX+cD1Fi8XRuG42ndWibxMw+lJfLMq2/QI3is50xK6NiI7yPVEz2VIcPV bhCQBl1REP5+LRA64lJbfGH5WIaXcVJ3Ml7EmO585AfgbHcUMwWFYqFzQIpCR9fZDzDa/3 VPAGVLOFrB36gDNB7Lj41LZnyKa7c2LXdwTyvDi0scyvMLj8q61/kMT4BrOO8gpjPM8QKP //JBvaFg2cY89PpJFn7wcdGOdL4EyIE1ISKBP4rsksTx0s63lk/fRTPIFX+hGQf4afX1hF PK/K1rgZJASSlqbgEm0RrlyJ1m3XFvnsPB005xq/Hb+a14YSQpDkl34lzQ10hw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1699367424; 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=5xPsRqAFEs77npUxoLx3/3wuVDu9twVyuq3Psu+XSJc=; b=RrJ4Np05dj0BsTDcwK9/BN6Uv+zQ/sH3silLe/tqf6ldIfFmVii2D+e7qq7qUnUYXwnStL 3mLkkZhN2LWt68C3L+XmhbGq3fwM69bU/wW7r946AomEEcYHGmL2xJrkop5HUwRP+qo2aF uy8scTyvJShy3ecO6yik8Qm6vxRryf3oYhG/rjAcByv7Vv9eM6zPxhx4cLw64tjaQRM+KG j7z160waOi247qVYvz5WxsuhE3Q9wYbt5LWNGyLLATh9x4t0GuQ2SsLNl6gEs5tGcCSTU5 DH7hcpoXSWTOLmll+hDl2F+8HnQ3predCpyR6tb/aQX08kNIc0KemFMirV9KwQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1699367424; a=rsa-sha256; cv=none; b=hVcgkav9PPAj0s+dYXdRtv72G7IG/VI4cPzo8LvjrbTcAiNbGMQIvHvyCFo+aOlqRHYuR+ bY1ouy5NFrllOYUfti5A8kC1MuFkTJNA8bStU8XY6tcA7U4534SlAP0JCDCUPksCd9UU2f F+Qp6BGBwm5OhTZXwhlAP532Ehe/jcpiytATJbVH5Z7kclIgZglEJ3g7fxGhZAYBgu44+S r+M1x4lDNSJArd60h61rG+Ue8zBQ6B/VG5MnLzq1bqyZrRlKuD2moDGDDF+L022SnFRb0S GtkyTSJ6+khSwnjv4M8hGl0fJkxiwgMDA65zqbU5H9OsZE32awGT8nY0QnF6kA== 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 4SPrGm5MJbztSP; Tue, 7 Nov 2023 14:30:24 +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 3A7EUODd044542; Tue, 7 Nov 2023 14:30:24 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 3A7EUOvi044539; Tue, 7 Nov 2023 14:30:24 GMT (envelope-from git) Date: Tue, 7 Nov 2023 14:30:24 GMT Message-Id: <202311071430.3A7EUOvi044539@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: 6a0891c6ebb7 - stable/12 - fflush: Add test for buffer handling in __sflush List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@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/12 X-Git-Reftype: branch X-Git-Commit: 6a0891c6ebb78bd59a3fc440675e31b6982de896 Auto-Submitted: auto-generated The branch stable/12 has been updated by emaste: URL: https://cgit.FreeBSD.org/src/commit/?id=6a0891c6ebb78bd59a3fc440675e31b6982de896 commit 6a0891c6ebb78bd59a3fc440675e31b6982de896 Author: Dag-Erling Smørgrav AuthorDate: 2023-08-03 15:13:45 +0000 Commit: Ed Maste CommitDate: 2023-11-07 14:30:07 +0000 fflush: Add test for buffer handling in __sflush Sponsored by: Klara, Inc. (cherry picked from commit b8dbfb0a6c181a9aeab0b793deb0813d06052df9) (cherry picked from commit ba490dfc95e0941216420a2003757b3c4b5b2ec2) (cherry picked from commit 66d84f1362f63c23f6f451490e153e0703a7cda6) 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 248a05bc45e7..5280fff8bfb4 100644 --- a/lib/libc/tests/stdio/Makefile +++ b/lib/libc/tests/stdio/Makefile @@ -3,6 +3,7 @@ .include 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()); +}