about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Stephenson <p.stephenson@samsung.com>2021-08-27 09:36:06 +0100
committerdana <dana@dana.is>2022-02-12 07:22:15 -0600
commitb8cffa329b528f8b072d8efd0fed75bcc22f2cdd (patch)
tree72d417160576ae7d22dde69000dc67c708ee28fa
parent9d20469c030ead700959fa3b95d7bc85bb9578a8 (diff)
downloadzsh-b8cffa329b528f8b072d8efd0fed75bcc22f2cdd.tar.gz
zsh-b8cffa329b528f8b072d8efd0fed75bcc22f2cdd.tar.xz
zsh-b8cffa329b528f8b072d8efd0fed75bcc22f2cdd.zip
49290: Replace stdio for buffered shell input.
The previous method allowed memory management to interact with signal
handlers, causing occasional crashes on some system.

Instead, use a simple pre-allocated buffer and raw system calls.

(cherry picked from commit e5cd2dd980302f328d232d933f646c3dc02828bf)
-rw-r--r--ChangeLog4
-rw-r--r--Src/init.c14
-rw-r--r--Src/input.c122
3 files changed, 121 insertions, 19 deletions
diff --git a/ChangeLog b/ChangeLog
index 7136b210a..96484aba2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2022-01-27  dana  <dana@dana.is>
 
+	* Peter Stephenson: 49290: Src/init.c, Src/input.c: Replace
+	stdio for buffered shell input to avoid memory management
+	interacting with signal handlers.
+
 	* Jun-ichi Takimoto: 49377: Src/Zle/zle_keymap.c,
 	Test/X03zlebindkey.ztst: fix segfault by 'bindkey -d' with
 	reordered keymapnamtab
diff --git a/Src/init.c b/Src/init.c
index 04a5856ff..99ccc16cf 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -1222,7 +1222,7 @@ setupshin(char *runscript)
     /*
      * Finish setting up SHIN and its relatives.
      */
-    bshin = SHIN ? fdopen(SHIN, "r") : stdin;
+    shinbufalloc();
     if (isset(SHINSTDIN) && !SHIN && unset(INTERACTIVE)) {
 #ifdef _IONBF
 	setvbuf(stdin, NULL, _IONBF, 0);
@@ -1377,9 +1377,9 @@ init_misc(char *cmd, char *zsh_name)
 	dosetopt(RESTRICTED, 1, 0, opts);
     if (cmd) {
 	if (SHIN >= 10)
-	    fclose(bshin);
+	    close(SHIN);
 	SHIN = movefd(open("/dev/null", O_RDONLY | O_NOCTTY));
-	bshin = fdopen(SHIN, "r");
+	shinbufreset();
 	execstring(cmd, 0, 1, "cmdarg");
 	stopmsg = 1;
 	zexit((exit_pending || shell_exiting) ? exit_val : lastval, ZEXIT_NORMAL);
@@ -1402,7 +1402,6 @@ source(char *s)
     int tempfd = -1, fd, cj;
     zlong oldlineno;
     int oldshst, osubsh, oloops;
-    FILE *obshin;
     char *old_scriptname = scriptname, *us;
     char *old_scriptfilename = scriptfilename;
     unsigned char *ocs;
@@ -1419,7 +1418,6 @@ source(char *s)
 
     /* save the current shell state */
     fd        = SHIN;            /* store the shell input fd                  */
-    obshin    = bshin;          /* store file handle for buffered shell input */
     osubsh    = subsh;           /* store whether we are in a subshell        */
     cj        = thisjob;         /* store our current job number              */
     oldlineno = lineno;          /* store our current lineno                  */
@@ -1432,7 +1430,7 @@ source(char *s)
 
     if (!prog) {
 	SHIN = tempfd;
-	bshin = fdopen(SHIN, "r");
+	shinbufsave();
     }
     subsh  = 0;
     lineno = 1;
@@ -1500,10 +1498,10 @@ source(char *s)
     if (prog)
 	freeeprog(prog);
     else {
-	fclose(bshin);
+	close(SHIN);
 	fdtable[SHIN] = FDT_UNUSED;
 	SHIN = fd;		     /* the shell input fd                   */
-	bshin = obshin;		     /* file handle for buffered shell input */
+	shinbufrestore();
     }
     subsh = osubsh;                  /* whether we are in a subshell         */
     thisjob = cj;                    /* current job number                   */
diff --git a/Src/input.c b/Src/input.c
index e9989ffe4..874776c59 100644
--- a/Src/input.c
+++ b/Src/input.c
@@ -80,11 +80,6 @@
 /**/
 int SHIN;
 
-/* buffered shell input for non-interactive shells */
-
-/**/
-FILE *bshin;
-
 /* != 0 means we are reading input from a string */
  
 /**/
@@ -129,7 +124,116 @@ static struct instacks *instack, *instacktop;
 
 static int instacksz = INSTACK_INITIAL;
 
-/* Read a line from bshin.  Convert tokens and   *
+/* Size of buffer for non-interactive command input */
+
+#define SHINBUFSIZE 8192
+
+/* Input buffer for non-interactive command input */
+static char *shinbuffer;
+
+/* Pointer into shinbuffer */
+static char *shinbufptr;
+
+/* End of contents read into shinbuffer */
+static char *shinbufendptr;
+
+/* Entry on SHIN buffer save stack */
+struct shinsaveentry {
+    /* Next entry on stack */
+    struct shinsaveentry *next;
+    /* Saved shinbuffer */
+    char *buffer;
+    /* Saved shinbufptr */
+    char *ptr;
+    /* Saved shinbufendptr */
+    char *endptr;
+};
+
+/* SHIN buffer save stack */
+struct shinsaveentry *shinsavestack;
+
+/* Reset the input buffer for SHIN, discarding any pending input */
+
+/**/
+void
+shinbufreset(void)
+{
+    shinbufendptr = shinbufptr = shinbuffer;
+}
+
+/* Allocate a new shinbuffer
+ *
+ * Only called at shell initialisation and when saving on the stack.
+ */
+
+/**/
+void
+shinbufalloc(void)
+{
+    shinbuffer = zalloc(SHINBUFSIZE);
+    shinbufreset();
+}
+
+/* Save entry on SHIN buffer save stack */
+
+/**/
+void
+shinbufsave(void)
+{
+    struct shinsaveentry *entry =
+	(struct shinsaveentry *)zalloc(sizeof(struct shinsaveentry));
+
+    entry->next = shinsavestack;
+    entry->buffer = shinbuffer;
+    entry->ptr = shinbufptr;
+    entry->endptr = shinbufendptr;
+
+    shinsavestack = entry;
+
+    shinbufalloc();
+}
+
+/* Restore entry from SHIN buffer save stack */
+
+/**/
+void
+shinbufrestore(void)
+{
+    struct shinsaveentry *entry = shinsavestack;
+
+    zfree(shinbuffer, SHINBUFSIZE);
+
+    shinbuffer = entry->buffer;
+    shinbufptr = entry->ptr;
+    shinbufendptr = entry->endptr;
+
+    shinsavestack = entry->next;
+    zfree(entry, sizeof(struct shinsaveentry));
+}
+
+/* Get a character from SHIN, -1 if none available */
+
+/**/
+static int
+shingetchar(void)
+{
+    int nread;
+
+    if (shinbufptr < shinbufendptr)
+	return STOUC(*shinbufptr++);
+
+    shinbufreset();
+    do {
+	errno = 0;
+	nread = read(SHIN, shinbuffer, SHINBUFSIZE);
+    } while (nread < 0 && errno == EINTR);
+    if (nread <= 0)
+	return -1;
+    shinbufendptr = shinbuffer + nread;
+    return STOUC(*shinbufptr++);
+}
+
+/* Read a line from SHIN.  Convert tokens and   *
  * null characters to Meta c^32 character pairs. */
 
 /**/
@@ -147,11 +251,7 @@ shingetline(void)
     winch_unblock();
     dont_queue_signals();
     for (;;) {
-	/* Can't fgets() here because we need to accept '\0' bytes */
-	do {
-	    errno = 0;
-	    c = fgetc(bshin);
-	} while (c < 0 && errno == EINTR);
+	c = shingetchar();
 	if (c < 0 || c == '\n') {
 	    winch_block();
 	    restore_queue_signals(q);