From 8a100514480a55ad73966e516e38778509f6ace6 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Thu, 11 May 2017 14:45:50 -0400 Subject: [PATCH] commit bash-20170511 snapshot --- CWRU/CWRU.chlog | 41 +++++++++++++++++++++++++++++++++++++ builtins/read.def | 34 ++++++++++++++++++++++++------- lib/readline/input.c | 1 + lib/readline/text.c | 5 +++-- parse.y | 48 +++++++++++++++++++++++--------------------- 5 files changed, 97 insertions(+), 32 deletions(-) diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 7892ce94..8ea60a39 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -13840,3 +13840,44 @@ builtins/read.def a CTLESC the last time through the loop (skip_ctlesc == 0), especially if i == 0. Another fuzzing bug from Eduardo Bustamante + + 5/8 + --- +builtins/read.def + - read_mbchar: handle zreadn/zreadc/zread returning EOF in the middle + of an incomplete multibyte sequence. Fixes another fuzzing bug + - read_builtin: use mb_cur_max instead of constant 4 when deciding + whether the next character can exceed the number of bytes available + in input_string + +lib/readline/input.c + - MinGW: include before . Fix from Eli Zaretskii + + +builtins/read.def + - read_builtin: if we get input from readline, we need to get the + remainder of a multibyte character from rlbuf instead of calling + read_mbchar. Bug reported by Eduardo Bustamante + + 5/9 + --- +parse.y + - token_is_assignment: use the allocated buffer approach in all cases, + not just if we're not using bash malloc. This avoids the assignment + to t[i+1] writing beyond the end of the allocated token if + i == token_buffer_size - 1. Another fuzzing bug + - xparse_dolparen: if parse_string returns < 0, we clear out the + current shell_input_line before performing a longjmp, since we're + abandoning parsing of this command. This is consistent with how + the parser resynchronizes after other syntax errors + - GRAMMAR: add 'error yacc_EOF' production to handle a syntax error + that's immediately followed by an EOF after resynchronization. + Fixes another fuzzing bug + + 5/10 + ---- +lib/readline/text.c + - _rl_set_mark_at_pos: don't let the mark be set to a position < 0. + Fixes a fuzzing bug + - rl_exchange_point_and_mark: don't do anything if the mark is already + less than 0 diff --git a/builtins/read.def b/builtins/read.def index 14da6a2f..9f64406e 100644 --- a/builtins/read.def +++ b/builtins/read.def @@ -381,7 +381,11 @@ read_builtin (list) sync_buffered_stream (default_buffered_input); #endif +#if 1 input_is_tty = isatty (fd); +#else + input_is_tty = 1; +#endif if (input_is_tty == 0) #ifndef __CYGWIN__ input_is_pipe = (lseek (fd, 0L, SEEK_CUR) < 0) && (errno == ESPIPE); @@ -606,7 +610,8 @@ read_builtin (list) #endif CHECK_ALRM; - if (i + 4 >= size) /* XXX was i + 2; use i + 4 for multibyte/read_mbchar */ + /* XXX -- use i + mb_cur_max (at least 4) for multibyte/read_mbchar */ + if (i + (mb_cur_max > 4 ? mb_cur_max : 4) >= size) { char *t; t = (char *)xrealloc (input_string, size += 128); @@ -666,14 +671,28 @@ add_char: CHECK_ALRM; #if defined (HANDLE_MULTIBYTE) -# if 0 - if (nchars > 0 && mb_cur_max > 1 && is_basic (c) == 0) -# else + /* XXX - what if C == 127? Can DEL introduce a multibyte sequence? */ if (mb_cur_max > 1 && is_basic (c) == 0) -# endif { input_string[i] = '\0'; /* for simplicity and debugging */ - i += read_mbchar (fd, input_string, i, c, unbuffered_read); + /* If we got input from readline, grab the next multibyte char from + rlbuf. */ + if (edit) + { + size_t clen; + clen = mbrlen (rlbuf + rlind - 1, mb_cur_max, (mbstate_t *)NULL); + /* We only deal with valid multibyte sequences longer than one + byte. If we get anything else, we leave the one character + copied and move on to the next. */ + if ((int)clen > 1) + { + memcpy (input_string+i, rlbuf+rlind, clen-1); + i += clen - 1; + rlind += clen - 1; + } + } + else + i += read_mbchar (fd, input_string, i, c, unbuffered_read); } #endif @@ -968,6 +987,7 @@ read_mbchar (fd, string, ind, ch, unbuffered) if (ret == (size_t)-2) { ps = ps_back; + /* We don't want to be interrupted during a multibyte char read */ if (unbuffered == 2) r = zreadn (fd, &c, 1); @@ -975,7 +995,7 @@ read_mbchar (fd, string, ind, ch, unbuffered) r = zread (fd, &c, 1); else r = zreadc (fd, &c); - if (r < 0) + if (r <= 0) goto mbchar_return; mbchar[i++] = c; continue; diff --git a/lib/readline/input.c b/lib/readline/input.c index 8c66137d..b57bba61 100644 --- a/lib/readline/input.c +++ b/lib/readline/input.c @@ -102,6 +102,7 @@ static int rl_gather_tyi PARAMS((void)); /* Windows isatty returns true for every character device, including the null device, so we need to perform additional checks. */ #if defined (_WIN32) && !defined (__CYGWIN__) +#include #include #define WIN32_LEAN_AND_MEAN 1 #include diff --git a/lib/readline/text.c b/lib/readline/text.c index 095c0ef3..e4328588 100644 --- a/lib/readline/text.c +++ b/lib/readline/text.c @@ -1699,7 +1699,7 @@ rl_backward_char_search (int count, int key) int _rl_set_mark_at_pos (int position) { - if (position > rl_end) + if (position < 0 || position > rl_end) return 1; rl_mark = position; @@ -1720,9 +1720,10 @@ rl_exchange_point_and_mark (int count, int key) if (rl_mark > rl_end) rl_mark = -1; - if (rl_mark == -1) + if (rl_mark < 0) { rl_ding (); + rl_mark = 0; /* like _RL_FIX_POINT */ return 1; } else diff --git a/parse.y b/parse.y index 7ca4a64e..dcc628e7 100644 --- a/parse.y +++ b/parse.y @@ -411,6 +411,14 @@ inputunit: simple_list simple_list_terminator YYABORT; } } + | error yacc_EOF + { + /* EOF after an error. Do ignoreeof or not. Really + only interesting in non-interactive shells */ + global_command = (COMMAND *)NULL; + handle_eof_input_unit (); + YYACCEPT; + } | yacc_EOF { /* Case of EOF seen by itself. Do ignoreeof or @@ -4313,9 +4321,13 @@ xparse_dolparen (base, string, indp, flags) token_to_read = 0; /* If parse_string returns < 0, we need to jump to top level with the - negative of the return value */ + negative of the return value. We abandon the rest of this input line + first */ if (nc < 0) - jump_to_top_level (-nc); /* XXX */ + { + clear_shell_input_line (); /* XXX */ + jump_to_top_level (-nc); /* XXX */ + } /* Need to find how many characters parse_and_execute consumed, update *indp, if flags != 0, copy the portion of the string parsed into RET @@ -4714,37 +4726,27 @@ parse_cond_command () #if defined (ARRAY_VARS) /* When this is called, it's guaranteed that we don't care about anything - in t beyond i. We do save and restore the chars, though. */ + in t beyond i. We use a buffer with room for the characters we add just + in case assignment() ends up doing something like parsing a command + substitution that will reallocate atoken. We don't want to write beyond + the end of an allocated buffer. */ static int token_is_assignment (t, i) char *t; int i; { int r; + char *atoken; -#if !defined (USING_BASH_MALLOC) - static char *token = 0; + atoken = xmalloc (i + 3); + memcpy (atoken, t, i); + atoken[i] = '='; + atoken[i+1] = '\0'; - token = xrealloc (token, i + 3); - memcpy (token, t, i); - token[i] = '='; - token[i+1] = '\0'; + r = assignment (atoken, (parser_state & PST_COMPASSIGN) != 0); - r = assignment (token, (parser_state & PST_COMPASSIGN) != 0); + free (atoken); - if (i + 3 > 4096) /* keep it from getting too big */ - { - free (token); - token = 0; - } -#else - unsigned char c, c1; - - c = t[i]; c1 = t[i+1]; - t[i] = '='; t[i+1] = '\0'; - r = assignment (t, (parser_state & PST_COMPASSIGN) != 0); - t[i] = c; t[i+1] = c1; -#endif /* XXX - check that r == i to avoid returning false positive for t containing `=' before t[i]. */ return (r > 0 && r == i);