From nobody Tue Jan 30 17:11:28 2024 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 4TPWss2RDVz59CDk; Tue, 30 Jan 2024 17:11:29 +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 4TPWss18Ssz4FQJ; Tue, 30 Jan 2024 17:11:29 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1706634689; 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=IzmabWnyjDvuFZH+RfBgxOhVXtuFmF15YtSzrToRoJk=; b=MdIC/RmCEh4aiz70njG5aHR/oAS8mAK7Ijjibd9m3F7MnoHGpwV9d9o/qEsOy1HQ3ZriSe aNgS62UAY8zGpNWS74EVD2UFVkunI5jwUS1noUhB/sWWNzQeHG9wPFpwuNzKbHCmH1a4xK pyXzTm0meg4F6+sLZ1D8LWzXQperi0L37jOClgu0133LbQVzpWMKlAlvlP4XUieWwxK2c3 Rg3UisGnJr76m+pG0Dmko6sjzHFslMrx6pf6791m2DLpuqEPhWzvv9KZ3WCHePOCW/u2PG g0ZV7rYmyW88y7oYnXpSD2QV09v4PAY6Bjn2Wy/THT6gykJAApJhjgItDHH/Aw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1706634689; 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=IzmabWnyjDvuFZH+RfBgxOhVXtuFmF15YtSzrToRoJk=; b=qDf38yw9f3epogmRghO0z6/V2lSNAwG8uVi3IC0IM/7uCS560zhMy+MpsGskwic0eOqOFD VtOH95tZd9tNRzgNkEKpjIyJSlr1kkOY3kpSyWhUG6kijRD+xq6PNqp8W8bmjc8dTr9/VE 0+6iqynPg/apANVkJz/U9x2C87xQ85sb2d9f985WOkYObDx3lxbSL2YCP2wGkwHtCK2XiR MHr61v6I3Br6OOdL1cArUkfyXGg6u2z2kqKzXMVEbRpAKT2C2iajkoHTlreEPc04ELHpLR hSyIniFJ6IKDkTX1g9h37m+z4sDk8PPmjPuDzW/cGMm+y49s6dasH/irA4atGw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1706634689; a=rsa-sha256; cv=none; b=Wa05xajQ7hdNzGfJarCsB9xaREmerhRu6H9WFYNCNtOu+YNWSnG5v9sKeXK7AZUbC9fH57 OR2NYmwSG0F7AF09NUn1JaOMZ37ZzCcmUFG+Sf3otCoaS8Qgmseaw69KCZ3YXNnL72iyq5 G73s7QUON0iJmS0tmspBKJTEyT5thzTSPSlfGF4LJKFzcfvdTsSUHoNmz2TYvIAc52EFLQ QqJQvKfbL2pTJjL36EdkrmeJNq4Esay+oNr3K7bQWl7Zy6AKvFEKU5kS2OwDX3BoihxNXt CNHTJHBc8JpwrPlzGJo1K9rChE1auMLyj+poldfmvvcYawNH+88TA+CNL+nnYQ== 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 4TPWss0DqqzkXW; Tue, 30 Jan 2024 17:11:29 +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 40UHBSBi093838; Tue, 30 Jan 2024 17:11:28 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.17.1/8.17.1/Submit) id 40UHBSHp093835; Tue, 30 Jan 2024 17:11:28 GMT (envelope-from git) Date: Tue, 30 Jan 2024 17:11:28 GMT Message-Id: <202401301711.40UHBSHp093835@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Kyle Evans Subject: git: 8fb7d0ddd3e3 - stable/14 - kern: tty: fix EOF handling for canonical reads 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: kevans X-Git-Repository: src X-Git-Refname: refs/heads/stable/14 X-Git-Reftype: branch X-Git-Commit: 8fb7d0ddd3e3b4af91f10536b6f307f8f8792190 Auto-Submitted: auto-generated The branch stable/14 has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=8fb7d0ddd3e3b4af91f10536b6f307f8f8792190 commit 8fb7d0ddd3e3b4af91f10536b6f307f8f8792190 Author: Kyle Evans AuthorDate: 2024-01-16 02:55:58 +0000 Commit: Kyle Evans CommitDate: 2024-01-30 17:11:24 +0000 kern: tty: fix EOF handling for canonical reads If the read(2) buffer is one byte short of an EOF, then we'll end up reading the line into the buffer, then re-entering and seeing an EOF at the beginning of the inq, assuming it's a zero-length line. Fix this corner-case by searching one more byte than we have available for an EOF. If we found it, then we'll trim it here; otherwise, we'll limit our read to just the space we have in the out buffer and the next read(2) will (potentially) read the remainder of the line. Fix FIONREAD while we're here to match what an application can expect read(2) to return -- scan for the first break character in the part of the input that's been canonicalized, we'll never return more than that. PR: 276220 Reviewed by: cy, imp (both previous version), kib (cherry picked from commit d51dac5f1370bdca1ea20c6b48cdea463f6f5dda) --- sys/kern/tty.c | 2 +- sys/kern/tty_ttydisc.c | 72 ++++++++++++++++++++++++++++++++++++++++++++------ sys/sys/ttydisc.h | 1 + 3 files changed, 66 insertions(+), 9 deletions(-) diff --git a/sys/kern/tty.c b/sys/kern/tty.c index e051c66ab0c9..959a5644e9e2 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -1671,7 +1671,7 @@ tty_generic_ioctl(struct tty *tp, u_long cmd, void *data, int fflag, /* This device supports non-blocking operation. */ return (0); case FIONREAD: - *(int *)data = ttyinq_bytescanonicalized(&tp->t_inq); + *(int *)data = ttydisc_bytesavail(tp); return (0); case FIONWRITE: case TIOCOUTQ: diff --git a/sys/kern/tty_ttydisc.c b/sys/kern/tty_ttydisc.c index cb5bf672d040..04d99c336438 100644 --- a/sys/kern/tty_ttydisc.c +++ b/sys/kern/tty_ttydisc.c @@ -113,15 +113,20 @@ ttydisc_close(struct tty *tp) ttyhook_close(tp); } -static int -ttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) +/* + * Populate our break array; it should likely be at least 4 bytes in size to + * allow for \n, VEOF, and VEOL. + */ +static void +ttydisc_read_break(struct tty *tp, char *breakc, size_t breaksz) { - char breakc[4] = { CNL }; /* enough to hold \n, VEOF and VEOL. */ - int error; - size_t clen, flen = 0, n = 1; - unsigned char lastc = _POSIX_VDISABLE; + size_t n = 0; + MPASS(breaksz != 0); + + breakc[n++] = CNL; #define BREAK_ADD(c) do { \ + MPASS(n < breaksz - 1); /* NUL terminated */ \ if (tp->t_termios.c_cc[c] != _POSIX_VDISABLE) \ breakc[n++] = tp->t_termios.c_cc[c]; \ } while (0) @@ -129,7 +134,48 @@ ttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) BREAK_ADD(VEOF); BREAK_ADD(VEOL); #undef BREAK_ADD + breakc[n] = '\0'; +} + +size_t +ttydisc_bytesavail(struct tty *tp) +{ + size_t clen; + char breakc[4]; + unsigned char lastc = _POSIX_VDISABLE; + + clen = ttyinq_bytescanonicalized(&tp->t_inq); + if (!CMP_FLAG(l, ICANON) || clen == 0) + return (clen); + + ttydisc_read_break(tp, &breakc[0], sizeof(breakc)); + clen = ttyinq_findchar(&tp->t_inq, breakc, clen, &lastc); + + /* + * We might have a partial line canonicalized in the input queue if we, + * for instance, switched to ICANON after taking some input in raw mode. + * In this case, read(2) will block because we only have a partial line. + */ + if (lastc == _POSIX_VDISABLE) + return (0); + + /* If VEOF was our terminal, it must be discarded (not counted). */ + if (CMP_CC(VEOF, lastc)) + clen--; + + return (clen); +} + +static int +ttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) +{ + char breakc[4]; /* enough to hold \n, VEOF and VEOL. */ + int error; + size_t clen, flen = 0; + unsigned char lastc = _POSIX_VDISABLE; + + ttydisc_read_break(tp, &breakc[0], sizeof(breakc)); do { error = tty_wait_background(tp, curthread, SIGTTIN); @@ -154,7 +200,7 @@ ttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) * cause the TTY layer to return data in chunks using * the blocksize (except the first and last blocks). */ - clen = ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid, + clen = ttyinq_findchar(&tp->t_inq, breakc, uio->uio_resid + 1, &lastc); /* No more data. */ @@ -170,10 +216,20 @@ ttydisc_read_canonical(struct tty *tp, struct uio *uio, int ioflag) continue; } - /* Don't send the EOF char back to userspace. */ + /* + * Don't send the EOF char back to userspace. Our above call to + * ttyinq_findchar overreads by 1 character in case we would + * otherwise be leaving an EOF for the next read(). We'll trim + * clen back down to uio_resid whether we find our EOF or not. + */ if (CMP_CC(VEOF, lastc)) flen = 1; + /* + * Trim clen back down to the buffer size, since we had + * intentionally over-read. + */ + clen = MIN(uio->uio_resid + flen, clen); MPASS(flen <= clen); /* Read and throw away the EOF character. */ diff --git a/sys/sys/ttydisc.h b/sys/sys/ttydisc.h index 0458fae6e34b..81d436139555 100644 --- a/sys/sys/ttydisc.h +++ b/sys/sys/ttydisc.h @@ -44,6 +44,7 @@ struct uio; /* Top half routines. */ void ttydisc_open(struct tty *tp); void ttydisc_close(struct tty *tp); +size_t ttydisc_bytesavail(struct tty *tp); int ttydisc_read(struct tty *tp, struct uio *uio, int ioflag); int ttydisc_write(struct tty *tp, struct uio *uio, int ioflag); void ttydisc_optimize(struct tty *tp);