From nobody Mon Aug 3 21:38:48 2026 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 4hDVRf2nnkz6n4Tn for ; Mon, 03 Aug 2026 21:38:54 +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 "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hDVRf1pFHz3DNM for ; Mon, 03 Aug 2026 21:38:54 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1785793134; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=1R0BTeH4yPeVc34BrJSdhMJKdOORLvwK2MpKAdWoaI8=; b=iZ2pXc3yXvaPVJ3qSskTdBt8C1qKs7Q2e7e1EEb+0QvbryINEmGvl+DOnMUU77yjyYuTFD DglBcO9TfNeQ+dYiMh7l7JKD/3hetIaNa8g7bxVlnB4/zmPIds70prDZPhGmt2JQXbqr/p eCZLSIMU+cKooVj6FfIoZ9xE3MBNkq0sE2lvnAtfN/WSRBwjzHqgfOCqt0r5Hgyj8vT+US sTVUQgR5ZNW2TsS+CuBkl4EDtTKPKC9yY7Hu+yrph9GbwMpPjpp05jSTcxeHCTUJKQxsQM r5SwN2jjcqnOoIRhcNgUJ9gLzoMoBzaIhP374w8SixfjHNQ977mjKnYpgryKNg== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1785793134; a=rsa-sha256; cv=none; b=kLMY+fuKGPSrb8xSZMO9qQ+jfrROOo80tQbGoA4zcN6kQH0Xs5w+39CBLDFGsPbsvfX9Lb qG0nEJ5YihAXR2rmIpwznUt3s2W9okxOgjR+qITcBia6fqbHS5nJEx0snj2cLsMaXphJqL 5riZm489dUtK0wl8Bv9g6cTky1FsyqMMQklNwPIM6osUlC9fEetiKzn+Y2JIklqP8fBOSm ZGjzLOtKShflfcruzAA6K82WKc9lEcUiBCEnVHWVojoMHvYOAgrxlo8ZKyP0275+Fh1jCi XK9e9TZ48TQUR1WXOQqXQz2I75IySEW22n+wsju0tVz+OPxO+icNeTqaQPzqaA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1785793134; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=1R0BTeH4yPeVc34BrJSdhMJKdOORLvwK2MpKAdWoaI8=; b=jdFYsSX0FdJzzrSG0J1NuPCvmGjj0rLSii4zAZIF33Vzwo1Bm/5h8e5oxRmKx3bRkhL6AE U9PJZUiIBY5v5WuhxFUhzX+1fKT9A4ciLOrPuvijNPNO9vKJp9tUWdyB6derem/l2NE2RI uIsPieV8SfZoqfYysmrNNHc7Qm8GpR5of5g/u2jjH0Mh9KgfQ4d75Ox8XrN1aExVO5w3Re oKN9DmCIGE0RhBmaoPMQixGfp3IhWnARjtmaTXgsgUOX1KEl/M72iLjh2HovAOzcPeMMS4 aRxQY7Iwnl2faGhmj+Kw/KyujGElUDsHdKS5Bzm05p5/G6df9PS3/FtAN5Pfbg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hDVRf0fyPz3cp for ; Mon, 03 Aug 2026 21:38:54 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3b614 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Mon, 03 Aug 2026 21:38:48 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org Cc: Kristofer Peterson From: Jilles Tjoelker Subject: git: e96ecc712e85 - stable/15 - bin/sh: Fix history long line truncation/corruption 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: X-BeenThere: dev-commits-src-branches@freebsd.org Sender: owner-dev-commits-src-branches@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: jilles X-Git-Repository: src X-Git-Refname: refs/heads/stable/15 X-Git-Reftype: branch X-Git-Commit: e96ecc712e85af65a728f70a84ca46df7c8a5d67 Auto-Submitted: auto-generated Date: Mon, 03 Aug 2026 21:38:48 +0000 Message-Id: <6a710a68.3b614.75937726@gitrepo.freebsd.org> The branch stable/15 has been updated by jilles: URL: https://cgit.FreeBSD.org/src/commit/?id=e96ecc712e85af65a728f70a84ca46df7c8a5d67 commit e96ecc712e85af65a728f70a84ca46df7c8a5d67 Author: Kristofer Peterson AuthorDate: 2026-02-16 15:53:47 +0000 Commit: Jilles Tjoelker CommitDate: 2026-08-03 21:37:37 +0000 bin/sh: Fix history long line truncation/corruption When reading from standard input with editline history enabled, increase buffer size to accomodate long lines so that history is recorded correctly. Cleanup el_gets() handling avoiding potentially dangerous retention of pointers to editline buffers across calls. Ensure struct parsefile objects are properly zero initialised when created. Remove push argument from setinputstring() and simplify logic as it was always called with a value of one and as was written was potentially dangerous if ever called with a value of zero. This commit does not fix long lines when history is enabled but editing is not (e.g. if there is no terminal). MFC after: 3 weeks Pull Request: https://github.com/freebsd/freebsd-src/pull/2028 Signed-off-by: Kristofer Peterson (cherry picked from commit 95e4fce8f0c4fc6bf828288b1d63faf0f1300198) --- bin/sh/eval.c | 2 +- bin/sh/input.c | 146 ++++++++++++++++++++++++++++---------------------------- bin/sh/input.h | 2 +- bin/sh/parser.c | 4 +- 4 files changed, 76 insertions(+), 78 deletions(-) diff --git a/bin/sh/eval.c b/bin/sh/eval.c index fdc7a2560694..466193e20bb2 100644 --- a/bin/sh/eval.c +++ b/bin/sh/eval.c @@ -151,7 +151,7 @@ evalstring(const char *s, int flags) flags &= ~EV_EXIT; any = 0; setstackmark(&smark); - setinputstring(s, 1); + setinputstring(s); while ((n = parsecmd(0)) != NEOF) { if (n != NULL && !nflag) { if (flags_exit && preadateof()) diff --git a/bin/sh/input.c b/bin/sh/input.c index 26f233ebf7b6..64bbae04149b 100644 --- a/bin/sh/input.c +++ b/bin/sh/input.c @@ -79,6 +79,7 @@ struct parsefile { int lleft; /* number of lines left in this buffer */ const char *nextc; /* next char in buffer */ char *buf; /* input buffer */ + size_t bufsize; /* input buffer size */ struct strpush *strpush; /* for pushing strings at this level */ struct strpush basestrpush; /* so pushing one is fast */ }; @@ -91,7 +92,8 @@ const char *parsenextc; /* copy of parsefile->nextc */ static char basebuf[BUFSIZ + 1];/* buffer for top level input file */ static struct parsefile basepf = { /* top level input file */ .nextc = basebuf, - .buf = basebuf + .buf = basebuf, + .bufsize = sizeof(basebuf), }; static struct parsefile *parsefile = &basepf; /* current input file */ int whichprompt; /* 1 == PS1, 2 == PS2 */ @@ -125,52 +127,61 @@ static int preadfd(void) { int nr; - parsenextc = parsefile->buf; retry: #ifndef NO_HISTORY if (parsefile->fd == 0 && el) { - static const char *rl_cp; - static int el_len; - - if (rl_cp == NULL) { - el_resize(el); - rl_cp = el_gets(el, &el_len); - } - if (rl_cp == NULL) - nr = el_len == 0 ? 0 : -1; - else { - nr = el_len; - if (nr > BUFSIZ) - nr = BUFSIZ; - memcpy(parsefile->buf, rl_cp, nr); - if (nr != el_len) { - el_len -= nr; - rl_cp += nr; - } else - rl_cp = NULL; + const char *line; + + el_resize(el); + line = el_gets(el, &nr); + if (nr > 0 && parsefile->bufsize < (size_t)nr + 1) { + size_t bufsize; + + INTOFF; + if (parsefile->buf != basebuf) { + ckfree(parsefile->buf); + parsefile->buf = NULL; + parsefile->bufsize = 0; + } + bufsize = (size_t)nr + BUFSIZ + 1; + bufsize -= bufsize % BUFSIZ; + parsefile->buf = ckmalloc(bufsize); + parsefile->bufsize = bufsize; + INTON; } + if (nr > 0 && line != NULL) + memcpy(parsefile->buf, line, nr); + else + nr = nr ? -1 : 0; } else #endif - nr = read(parsefile->fd, parsefile->buf, BUFSIZ); - - if (nr <= 0) { - if (nr < 0) { - if (errno == EINTR) - goto retry; - if (parsefile->fd == 0 && errno == EWOULDBLOCK) { - int flags = fcntl(0, F_GETFL, 0); - if (flags >= 0 && flags & O_NONBLOCK) { - flags &=~ O_NONBLOCK; - if (fcntl(0, F_SETFL, flags) >= 0) { - out2fmt_flush("sh: turning off NDELAY mode\n"); - goto retry; - } - } - } + nr = read(parsefile->fd, parsefile->buf, parsefile->bufsize - 1); + + if (nr < 0) + switch (errno) { + int flags; + + case EINTR: + goto retry; + case EWOULDBLOCK: + if (parsefile->fd != 0) + break; + if ((flags = fcntl(0, F_GETFL, 0)) < 0) + break; + if (!(flags & O_NONBLOCK)) + break; + if (fcntl(0, F_SETFL, flags & ~O_NONBLOCK) < 0) + break; + out2fmt_flush("sh: turning off NDELAY mode\n"); + goto retry; } - nr = -1; - } + else if (nr > 0) + parsefile->buf[nr] = '\0'; + else + nr = -1; + + parsenextc = parsefile->buf; return nr; } @@ -187,7 +198,8 @@ retry: int preadbuffer(void) { - char *p, *q, *r, *end; + const char *end; + char *q, *r; char savec; while (parsefile->strpush) { @@ -206,31 +218,22 @@ preadbuffer(void) return PEOF; again: - if (parselleft <= 0) { - if ((parselleft = preadfd()) == -1) { - parselleft = parsenleft = EOF_NLEFT; - return PEOF; - } + if (parselleft <= 0 && (parselleft = preadfd()) == -1) { + parselleft = parsenleft = EOF_NLEFT; + return (PEOF); } - - p = parsefile->buf + (parsenextc - parsefile->buf); - end = p + parselleft; - *end = '\0'; - q = strchrnul(p, '\n'); - if (q != end && *q == '\0') { + end = parsenextc + parselleft; + q = strchrnul(parsenextc, '\n'); + if (*q == '\0' && q != end) { /* delete nul characters */ - for (r = q; q != end; q++) { + for (r = q++; q != end; q++) if (*q != '\0') *r++ = *q; - } - parselleft -= end - r; - if (parselleft == 0) - goto again; - end = p + parselleft; - *end = '\0'; - q = strchrnul(p, '\n'); + *r = '\0'; + parselleft = r - parsenextc; + goto again; } - if (q == end) { + if (*q == '\0') { parsenleft = parselleft; parselleft = 0; } else /* *q == '\n' */ { @@ -305,7 +308,7 @@ pushstring(const char *s, int len, struct alias *ap) INTOFF; /*out2fmt_flush("*** calling pushstring: %s, %d\n", s, len);*/ if (parsefile->strpush) { - sp = ckmalloc(sizeof (struct strpush)); + sp = ckmalloc(sizeof(struct strpush)); sp->prev = parsefile->strpush; parsefile->strpush = sp; } else @@ -389,15 +392,15 @@ setinputfile(const char *fname, int push, int verify) void setinputfd(int fd, int push) { - if (push) { + if (push) pushfile(); - parsefile->buf = ckmalloc(BUFSIZ + 1); - } if (parsefile->fd > 0) close(parsefile->fd); parsefile->fd = fd; - if (parsefile->buf == NULL) + if (parsefile->buf == NULL) { parsefile->buf = ckmalloc(BUFSIZ + 1); + parsefile->bufsize = BUFSIZ + 1; + } parselleft = parsenleft = 0; plinno = 1; } @@ -408,14 +411,12 @@ setinputfd(int fd, int push) */ void -setinputstring(const char *string, int push) +setinputstring(const char *string) { INTOFF; - if (push) - pushfile(); + pushfile(); parsenextc = string; parselleft = parsenleft = strlen(string); - parsefile->buf = NULL; plinno = 1; INTON; } @@ -432,15 +433,12 @@ pushfile(void) { struct parsefile *pf; + pf = (struct parsefile *)ckmalloc(sizeof(struct parsefile)); + *pf = (struct parsefile){ .prev = parsefile, .fd = -1 }; parsefile->nleft = parsenleft; parsefile->lleft = parselleft; parsefile->nextc = parsenextc; parsefile->linno = plinno; - pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); - pf->prev = parsefile; - pf->fd = -1; - pf->strpush = NULL; - pf->basestrpush.prev = NULL; parsefile = pf; } diff --git a/bin/sh/input.h b/bin/sh/input.h index 8cf4df1e4044..f98b592c4c6b 100644 --- a/bin/sh/input.h +++ b/bin/sh/input.h @@ -52,7 +52,7 @@ void pungetc(void); void pushstring(const char *, int, struct alias *); void setinputfile(const char *, int, int); void setinputfd(int, int); -void setinputstring(const char *, int); +void setinputstring(const char *); void popfile(void); struct parsefile *getcurrentfile(void); void popfilesupto(struct parsefile *); diff --git a/bin/sh/parser.c b/bin/sh/parser.c index 0c1b7a91c257..68d4238c00ad 100644 --- a/bin/sh/parser.c +++ b/bin/sh/parser.c @@ -1165,7 +1165,7 @@ parsebackq(char *out, struct nodelist **pbqlist, INTOFF; ostr = ckmalloc(olen); memcpy(ostr, stackblock(), olen); - setinputstring(ostr, 1); + setinputstring(ostr); INTON; } nlpp = pbqlist; @@ -2244,7 +2244,7 @@ expandstr(const char *ps) if (!setjmp(jmploc.loc)) { handler = &jmploc; parser_temp = NULL; - setinputstring(ps, 1); + setinputstring(ps); doprompt = 0; readtoken1(pgetc(), DQSYNTAX, NOEOFMARK, 0); if (backquotelist != NULL)