Subject: Re: infinite recursion when handling the "out of memory" state
From: Kamil Dudka <[email-masked]>
To: Peter Stephenson <[email-masked]>
Cc: [email-masked]
Date: Mon, 25 Jan 2016 18:01:07 +0100
List-Id: Zsh Workers List <zsh-workers.zsh.org>

On Monday 25 January 2016 15:31:25 Peter Stephenson wrote:
> On Mon, 25 Jan 2016 16:02:50 +0100
> 
> Kamil Dudka <[email-masked]> wrote:
> > If zsh is compiled with multibyte support, handling of the "out of memory"
> > state does not work well in certain cases -- instead of printing the error
> > message and exiting, zsh ends up in an infinite recursion and crashes due
> > to stack overflow.
> > 
> > The memory allocation functions in mem.c use zerr() to print the fatal
> > error messages.  However, zerr() calls zwarning() and transitively
> > mb_niceformat(), which allocates heap memory (and may call zerr() on
> > failure).
> > 
> > 3. introduce a flag that would prevent zerr() from recurring into itself
> 
> I think this is easy --- we already do this except that the flag is set
> after the warning is printed rather than before.  The zwarning()
> function that is called in the middle isn't directly sensitive to the
> flag, so moving the code up should have the desired effect and is the
> sane thing to do with error messages anyway.

Thanks!  Your patch indeed prevented the infinite recursion from occurring.
I am attaching one more OOM fix to make zerr() work in case argzero is NULL,
like in the following backtrace:

#0  0x000000000041ebe2 in untokenize (s=0x0) at exec.c:1857
No locals.
#1  0x000000000049971e in mb_niceformat (s=0x0, stream=0x880580 <_IO_2_1_stderr_>, outstrp=0x0, flags=0) at utils.c:4994
        l = 0
        newl = 5598153
        umlen = 0
        outalloc = 0
        outleft = 0
        eol = 0
        c = 22 L'\026'
        ums = 0x0
        ptr = 0x16 <error: Cannot access memory at address 0x16>
        fmt = 0x170f12001a131100 <error: Cannot access memory at address 0x170f12001a131100>
        outstr = 0x0
        outptr = 0x0
        mbs = {__count = 2132542208, __value = {__wch = 16778261, __wchb = "\025\004\000\001"}}
#2  0x000000000048fc15 in zwarning (cmd=0x0, fmt=0x5f6228 "fatal error: out of memory", ap=0x7fff1b83b5f8) at utils.c:150
No locals.
#3  0x000000000048fd26 in zerr (fmt=0x5f6228 "fatal error: out of memory") at utils.c:176
        ap = {{gp_offset = 8, fp_offset = 48, overflow_arg_area = 0x7fff1b83b6d0, reg_save_area = 0x7fff1b83b610}}
#4  0x00000000004559af in zshcalloc (size=64) at mem.c:975
        ptr = 0x0
#5  0x00000000004415d7 in zsh_main (argc=3, argv=0x7fff1b83b958) at init.c:1651
        t = 0x7fff1b83b970
        runscript = 0x0
        cmd = 0x7fff1b83b978 ",\301\203\033\377\177"
        t0 = 162
#6  0x0000000000400bbe in main (argc=3, argv=0x7fff1b83b958) at ./main.c:93
No locals.

>From 8636c26cfb8d2b4b61b7bbd802042910b707ebc2 Mon Sep 17 00:00:00 2001
From: Kamil Dudka <[email-masked]>
Date: Mon, 25 Jan 2016 17:53:41 +0100
Subject: [PATCH] prevent zerr() from crashing when printing "out of memory"

... in case argzero is NULL
---
 Src/utils.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/Src/utils.c b/Src/utils.c
index 17ebfeb..053731c 100644
--- a/Src/utils.c
+++ b/Src/utils.c
@@ -133,9 +133,11 @@ zwarning(const char *cmd, const char *fmt, va_list ap)
     if (isatty(2))
 	zleentry(ZLE_CMD_TRASH);
 
+    char *prefix = scriptname ? scriptname : (argzero ? argzero : "");
+
     if (cmd) {
 	if (unset(SHINSTDIN) || locallevel) {
-	    nicezputs(scriptname ? scriptname : argzero, stderr);
+	    nicezputs(prefix, stderr);
 	    fputc((unsigned char)':', stderr);
 	}
 	nicezputs(cmd, stderr);
@@ -147,8 +149,7 @@ zwarning(const char *cmd, const char *fmt, va_list ap)
 	 * program/script is running.  It's also set in shell functions,
 	 * so test locallevel, too.
 	 */
-	nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" :
-		  scriptname ? scriptname : argzero, stderr);
+	nicezputs((isset(SHINSTDIN) && !locallevel) ? "zsh" : prefix, stderr);
 	fputc((unsigned char)':', stderr);
     }
 
-- 
2.5.0