From 1baf0d1f553631ecb641e98f4bf48bc2a44e5b82 Mon Sep 17 00:00:00 2001
From: Peter Stephenson
Date: Sun, 15 Dec 2019 19:04:04 +0000
Subject: 45025: fix re-entrancy problem with memory management in
readoutput().
This could cause a signal received during $(...) to corrupt memory.
---
ChangeLog | 6 ++++++
Src/exec.c | 61 ++++++++++++++++++++++++++++++++++++-------------------------
2 files changed, 42 insertions(+), 25 deletions(-)
diff --git a/ChangeLog b/ChangeLog
index 3b3b9c65b..d241323b2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2019-12-15 Peter Stephenson
+
+ * 45025: Src/exec.c: fix re-entrancy problem with memory
+ management in readoutput(). This could cause a signal
+ received during $(...) to corrupt memory.
+
2019-12-14 dana
* unposted: Config/version.mk, Etc/FAQ.yo, README: Update for
diff --git a/Src/exec.c b/Src/exec.c
index 9dc91a71e..64eee7dc4 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -4646,19 +4646,25 @@ getoutput(char *cmd, int qt)
return NULL;
}
-/* read output of command substitution */
+/* read output of command substitution
+ *
+ * The file descriptor "in" is closed by the function.
+ *
+ * "qt" indicates if the substitution was in double quotes.
+ *
+ * "readerror", if not NULL, is used to return any error that
+ * occurred during the read.
+ */
/**/
mod_export LinkList
readoutput(int in, int qt, int *readerror)
{
LinkList ret;
- char *buf, *ptr;
- int bsiz, c, cnt = 0;
- FILE *fin;
+ char *buf, *bufptr, *ptr, inbuf[64];
+ int bsiz, c, cnt = 0, readret;
int q = queue_signal_level();
- fin = fdopen(in, "r");
ret = newlinklist();
ptr = buf = (char *) hcalloc(bsiz = 64);
/*
@@ -4670,33 +4676,38 @@ readoutput(int in, int qt, int *readerror)
*/
dont_queue_signals();
child_unblock();
- while ((c = fgetc(fin)) != EOF || errno == EINTR) {
- if (c == EOF) {
- errno = 0;
- clearerr(fin);
- continue;
- }
- if (imeta(c)) {
- *ptr++ = Meta;
- c ^= 32;
- cnt++;
+ for (;;) {
+ readret = read(in, inbuf, 64);
+ if (readret <= 0) {
+ if (readret < 0 && errno == EINTR)
+ continue;
+ else
+ break;
}
- if (++cnt >= bsiz) {
- char *pp;
- queue_signals();
- pp = (char *) hcalloc(bsiz *= 2);
- dont_queue_signals();
+ for (bufptr = inbuf; bufptr < inbuf + readret; bufptr++) {
+ c = *bufptr;
+ if (imeta(c)) {
+ *ptr++ = Meta;
+ c ^= 32;
+ cnt++;
+ }
+ if (++cnt >= bsiz) {
+ char *pp;
+ queue_signals();
+ pp = (char *) hcalloc(bsiz *= 2);
+ dont_queue_signals();
- memcpy(pp, buf, cnt - 1);
- ptr = (buf = pp) + cnt - 1;
+ memcpy(pp, buf, cnt - 1);
+ ptr = (buf = pp) + cnt - 1;
+ }
+ *ptr++ = c;
}
- *ptr++ = c;
}
child_block();
restore_queue_signals(q);
if (readerror)
- *readerror = ferror(fin) ? errno : 0;
- fclose(fin);
+ *readerror = readret < 0 ? errno : 0;
+ close(in);
while (cnt && ptr[-1] == '\n')
ptr--, cnt--;
*ptr = '\0';
--
cgit 1.4.1