Fix segfault upon SIGWINCH after 'stty -echo'

Resolves: RHEL-91097
This commit is contained in:
Vincent Mihalkovic 2025-05-19 15:30:40 +02:00
parent 4512a15f36
commit 39c92b3117
2 changed files with 271 additions and 1 deletions

View File

@ -0,0 +1,264 @@
From b533bc2feb9a1b58b9cdb3a9f5bc94f4ff060a84 Mon Sep 17 00:00:00 2001
From: Martijn Dekker <martijn@inlv.org>
Date: Fri, 28 Mar 2025 02:59:46 +0000
Subject: [PATCH] Fix segfault upon SIGWINCH after 'stty -echo' (re: 83630f9d)
After disabling terminal echo (stty -echo), ksh either crashes or
writes garbage to the terminal upon resizing the terminal window.
This also affects the use of ksh for 'M-x shell' in emacs.
Analysis: The ed_viread and ed_emacsread functions declare buffers
as local (automatic) variables that are destroyed when the function
exits, but other functions then access then using globally declared
pointers in structs of type Edit_t, Emacs_t and Vi_t.
However, upon 'stty -echo', the terminal cannot be set to raw mode,
so the tty_raw call fails in these two functions. At this point,
they fall back to ed_read and return, never initialising those
pointers. But the SIGWINCH handling routine in edit.c calls redraw
routines that depend on those pointers being valid -- and crash.
src/cmd/ksh93/edit/emacs.c: ed_emacsread():
- Attempt to set raw mode before doing or initialising anything
else: move the tty_raw call and ed_read fallback to the start.
This avoids superfluous init and makes more sense.
- Before returning, reset all global pointers that point to this
function call's local variables to NULL. (To aid with this, use a
'done' goto label and an 'r' variable for the return code.) This
ensures these pointers will be either valid or NULL, making
debugging easier, as well as allowing the code to check for this.
src/cmd/ksh93/edit/vi.c: ed_viread():
- Same changes as for emacs.
src/cmd/ksh93/edit/edit.c: ed_read():
- If e_prompt is NULL, that is now an indication that ed_emacsread
or ed_viread are not being used because the tty_raw call failed;
therefore, skip SIGWINCH handling, except for flushing the notify
buffer (see 667034ff). This fixes the crash.
Thanks to @nickpapadonis for the report.
Resolves: https://github.com/ksh93/ksh/issues/827
---
src/cmd/ksh93/edit/edit.c | 22 ++++++++++++++++++----
src/cmd/ksh93/edit/emacs.c | 27 ++++++++++++++++-----------
src/cmd/ksh93/edit/vi.c | 38 +++++++++++++++-----------------------
3 files changed, 49 insertions(+), 38 deletions(-)
diff --git a/src/cmd/ksh93/edit/edit.c b/src/cmd/ksh93/edit/edit.c
index 1dcef1b..846c0d8 100644
--- a/src/cmd/ksh93/edit/edit.c
+++ b/src/cmd/ksh93/edit/edit.c
@@ -690,6 +690,16 @@ static void ed_nputchar(Edit_t *ep, int n, int c)
}
#endif /* SHOPT_ESH || SHOPT_VSH */
+/*
+ * Show any buffered 'set -b' job notification(s)
+ */
+static void flush_notifybuf(void)
+{
+ char *cp;
+ if(sh.notifybuf && (cp = sfstruse(sh.notifybuf)) && *cp)
+ sfputr(sfstderr, cp, -1);
+}
+
/*
* Do read, restart on interrupt unless SH_SIGSET or SH_SIGTRAP is set
* Use select(2) (via sfpkrd()) to wait for input if possible
@@ -729,7 +739,12 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit)
if(sh.winch && sh_editor_active() && sh_isstate(SH_INTERACTIVE))
{
int n, newsize;
- char *cp;
+ if(!ep->e_prompt)
+ {
+ /* ed_emacsread or ed_viread was unable to put the tty in raw mode */
+ flush_notifybuf();
+ goto skipwinch;
+ }
sh_winsize(NULL,&newsize);
ed_putchar(ep,'\r');
/*
@@ -751,9 +766,7 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit)
ed_putchar(ep,'\r');
}
ed_flush(ep);
- /* show any buffered 'set -b' job notification(s) */
- if(sh.notifybuf && (cp = sfstruse(sh.notifybuf)) && *cp)
- sfputr(sfstderr, cp, -1);
+ flush_notifybuf();
/* update window size */
ep->e_winsz = newsize-1;
if(ep->e_winsz < MINWINDOW)
@@ -777,6 +790,7 @@ int ed_read(void *context, int fd, char *buff, int size, int reedit)
emacs_redraw(ep->e_emacs);
#endif /* SHOPT_ESH && SHOPT_VSH */
}
+ skipwinch:
#endif /* SHOPT_ESH || SHOPT_VSH */
sh.winch = 0;
/* an interrupt that should be ignored */
diff --git a/src/cmd/ksh93/edit/emacs.c b/src/cmd/ksh93/edit/emacs.c
index 1dd6023..128e592 100644
--- a/src/cmd/ksh93/edit/emacs.c
+++ b/src/cmd/ksh93/edit/emacs.c
@@ -185,6 +185,7 @@ int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
Edit_t *ed = (Edit_t*)context;
int c;
int i;
+ int r = -1; /* return code */
genchar *out;
int count;
Emacs_t *ep = ed->e_emacs;
@@ -194,6 +195,10 @@ int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
genchar *kptr;
char prompt[PRSIZE];
genchar Screen[MAXLINE];
+ /* Set raw mode */
+ if(tty_raw(ERRIO,0) < 0)
+ return reedit ? reedit : ed_read(context, fd, buff, scend, 0);
+ /* Initialize some things */
memset(Screen,0,sizeof(Screen));
if(!ep)
{
@@ -206,10 +211,6 @@ int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
ep->screen = Screen;
ep->lastdraw = FINAL;
ep->ehist = 0;
- if(tty_raw(ERRIO,0) < 0)
- {
- return reedit ? reedit : ed_read(context,fd,buff,scend,0);
- }
raw = 1;
/* This mess in case the read system call fails */
ed_setup(ep->ed,fd,reedit);
@@ -240,6 +241,7 @@ int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
}
ep->in_mult = hloff; /* save pos in last command */
#endif /* ESH_NFIRST */
+ /* Handle user interrupt, user quit, or EOF */
i = sigsetjmp(env,0);
if (i !=0)
{
@@ -251,10 +253,8 @@ int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
}
tty_cooked(ERRIO);
if (i == UEOF)
- {
- return 0; /* EOF */
- }
- return -1; /* some other error */
+ r = 0; /* EOF */
+ goto done;
}
out[reedit] = 0;
if(scend+plen > (MAXLINE-2))
@@ -349,7 +349,8 @@ int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
case EOFCHAR:
ed_flush(ep->ed);
tty_cooked(ERRIO);
- return 0;
+ r = 0;
+ goto done;
#ifdef u370
case cntl('S') :
case cntl('Q') :
@@ -754,8 +755,12 @@ process:
#endif /* SHOPT_MULTIBYTE */
i = (int)strlen(buff);
if (i)
- return i;
- return -1;
+ r = i;
+done:
+ /* avoid leaving invalid pointers to destroyed automatic variables */
+ Prompt = NULL;
+ drawbuff = ep->screen = ep->cursor = NULL;
+ return r;
}
static void show_info(Emacs_t *ep,const char *str)
diff --git a/src/cmd/ksh93/edit/vi.c b/src/cmd/ksh93/edit/vi.c
index 0e878df..fac40ee 100644
--- a/src/cmd/ksh93/edit/vi.c
+++ b/src/cmd/ksh93/edit/vi.c
@@ -208,6 +208,7 @@ int ed_viread(void *context, int fd, char *shbuf, int nchar, int reedit)
{
Edit_t *ed = (Edit_t*)context;
int i; /* general variable */
+ int r = -1; /* return value */
int term_char=0; /* read() termination character */
Vi_t *vp = ed->e_vi;
char prompt[PRSIZE+2]; /* prompt */
@@ -218,6 +219,10 @@ int ed_viread(void *context, int fd, char *shbuf, int nchar, int reedit)
int Globals[9]; /* local global variables */
int esc_or_hang=0; /* <ESC> or hangup */
char cntl_char=0; /* TRUE if control character present */
+
+ if( tty_raw(ERRIO,0) < 0 )
+ return reedit ? reedit : ed_read(context, fd, shbuf, nchar, 0);
+
if(!vp)
{
ed->e_vi = vp = sh_newof(0,Vi_t,1,0);
@@ -232,16 +237,9 @@ int ed_viread(void *context, int fd, char *shbuf, int nchar, int reedit)
ed_setup(vp->ed,fd, reedit);
shbuf[reedit] = 0;
- {
- /*** Set raw mode ***/
-
- if(tty_raw(ERRIO,0) < 0 )
- return reedit ? reedit : ed_read(context, fd, shbuf, nchar,0);
- i = last_virt-1;
- }
-
/*** Initialize some things ***/
+ i = last_virt-1;
virtual = (genchar*)shbuf;
#if SHOPT_MULTIBYTE
virtual = (genchar*)roundof((char*)virtual-(char*)0,sizeof(genchar));
@@ -307,18 +305,9 @@ int ed_viread(void *context, int fd, char *shbuf, int nchar, int reedit)
}
virtual[0] = '\0';
tty_cooked(ERRIO);
-
- switch(i)
- {
- case UEOF:
- /*** EOF ***/
- return 0;
-
- case UINTR:
- /** interrupt **/
- return -1;
- }
- return -1;
+ if (i == UEOF)
+ r = 0; /* EOF */
+ goto done;
}
/*** Get a line from the terminal ***/
@@ -363,10 +352,13 @@ int ed_viread(void *context, int fd, char *shbuf, int nchar, int reedit)
if(vp->ed->nhlist)
ed_histlist(vp->ed,0);
#endif /* SHOPT_EDPREDICT */
- return last_virt;
+ r = last_virt;
}
- else
- return -1;
+done:
+ /* avoid leaving invalid pointers to destroyed automatic variables */
+ Prompt = NULL;
+ virtual = physical = window = vp->U_space = vp->u_space = NULL;
+ return r;
}
--
2.49.0

View File

@ -4,7 +4,7 @@ URL: http://www.kornshell.com/
License: EPL-1.0 License: EPL-1.0
Epoch: 3 Epoch: 3
Version: 1.0.6 Version: 1.0.6
Release: 10%{?dist} Release: 11%{?dist}
Source0: https://github.com/ksh93/%{name}/archive/v%{version}/%{name}-%{version}.tar.gz Source0: https://github.com/ksh93/%{name}/archive/v%{version}/%{name}-%{version}.tar.gz
Source1: kshcomp.conf Source1: kshcomp.conf
Source2: kshrc.rhs Source2: kshrc.rhs
@ -48,6 +48,9 @@ Patch10: ksh-1.0.11-stty-noecho.patch
# upstream commit: https://github.com/ksh93/ksh/commit/96d73c08a2786806f3def1fda66641b81e0af988 # upstream commit: https://github.com/ksh93/ksh/commit/96d73c08a2786806f3def1fda66641b81e0af988
Patch11: ksh-1.0.11-ssh-multibyte-long-paste.patch Patch11: ksh-1.0.11-ssh-multibyte-long-paste.patch
# upstream commit: https://github.com/ksh93/ksh/commit/9f03b3bdb577ce74be3122d533c02830e3038e54
Patch12: ksh-1.0.11-segfault-sigwinch.patch
Conflicts: pdksh Conflicts: pdksh
Requires: coreutils, diffutils Requires: coreutils, diffutils
BuildRequires: gcc BuildRequires: gcc
@ -169,6 +172,9 @@ fi
%config(noreplace) %{_sysconfdir}/binfmt.d/kshcomp.conf %config(noreplace) %{_sysconfdir}/binfmt.d/kshcomp.conf
%changelog %changelog
* Mon May 19 2025 Vincent Mihalkovic <vmihalko@redhat.com> - 3:1.0.6-11
- Fix segfault upon SIGWINCH after 'stty -echo' Resolves: RHEL-91097
* Wed Apr 16 2025 Vincent Mihalkovic <vmihalko@redhat.com> - 3:1.0.6-10 * Wed Apr 16 2025 Vincent Mihalkovic <vmihalko@redhat.com> - 3:1.0.6-10
- Fix long multibyte characters paste issue via ssh - Fix long multibyte characters paste issue via ssh
Resolves: RHEL-87561 Resolves: RHEL-87561