From 23935dbe8513437e69ca14d6b0890067dddceba3 Mon Sep 17 00:00:00 2001 From: Chet Ramey Date: Mon, 1 May 2023 09:59:55 -0400 Subject: [PATCH] asan fuzzing fixes; fix incomplete multibyte chars in history expansion; new nosort GLOBSORT value --- CWRU/CWRU.chlog | 31 ++++++++++++++++++++- bashline.c | 13 +++++++-- doc/bash.1 | 4 ++- doc/bashref.texi | 3 ++ lib/readline/histexpand.c | 2 +- lib/readline/misc.c | 4 +++ lib/readline/text.c | 4 ++- pathexp.c | 58 +++++++++++++++++++++------------------ pathexp.h | 17 ++++++------ 9 files changed, 95 insertions(+), 41 deletions(-) diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index a306fa15..49734eb5 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -6040,7 +6040,8 @@ builtins/complete.def variable in which to store the completions. - compgen_builtin: if the -V option is supplied, store the possible completions into VARNAME instead of printing them on stdout. From a - patch from Grisha Levit + patch from Grisha Levit , idea originally + from konsolebox back in 2/2022 doc/bash.1,lib/readline/doc/rluser.texi - compgen: document new -V option @@ -6150,6 +6151,7 @@ lib/readline/display.c - _rl_move_cursor_relative: when checking to see whether data is within the invisible line, make sure to stay within the invisible line line break boundaries + From a report by minipython <599192367@qq.com> lib/readline/search.c - dispose_saved_search_line: new function, either unsave the saved @@ -6163,3 +6165,30 @@ lib/readline/search.c - noninc_dosearch,rl_history_search_internal: call dispose_saved_search_line before calling make_history_line_current +lib/readline/misc.c + - _rl_start_using_history: free the undo list associated with + _rl_saved_line_for_history, if one exists. Have to watch out for + this causing pointer aliasing problems, maybe add a call to + _hs_search_history_data() + + 4/28 + ---- +pathexp.c + - sh_globsort: a sort specifier of `nosort' disables sorting completely + +doc/bash.1,doc/bashref.texi + - GLOBSORT: document `nosort' sort specifier + +bashline.c + - attempt_shell_completion: attempt completion with the empty command + for a null command word on a line with only whitespace following any + optional assignment statements + - attempt_shell_completion: make sure that the start of the command + word is not after rl_point before calling the programmable completion + for the initial word. + From a report by Grisha Levit + +lib/readline/histexpand.c + - history_expand: don't read past the end of the string if it ends with + an incomplete multibyte character + From a report by Grisha Levit diff --git a/bashline.c b/bashline.c index d0d5bd48..6adc548e 100644 --- a/bashline.c +++ b/bashline.c @@ -1695,10 +1695,12 @@ attempt_shell_completion (const char *text, int start, int end) end == index of where word to be completed ends if (s == start) we are doing command word completion for sure if (e1 == end) we are at the end of the command name and completing it */ - if (start == 0 && end == 0 && e != 0 && text[0] == '\0') /* beginning of non-empty line */ + if (start == 0 && end == 0 && e != 0 && e1 < rl_end && text[0] == '\0') /* beginning of non-empty line */ foundcs = 0; else if (start == end && start == s1 && e != 0 && e1 > end) /* beginning of command name, leading whitespace */ foundcs = 0; + else if (start == 0 && start == end && start < s1 && e != 0 && e1 > end && text[0] == '\0' && have_progcomps) /* no command name, leading whitespace only */ + prog_complete_matches = programmable_completions (EMPTYCMD, text, s, e, &foundcs); else if (e == 0 && e == s && text[0] == '\0' && have_progcomps) /* beginning of empty line */ prog_complete_matches = programmable_completions (EMPTYCMD, text, s, e, &foundcs); else if (start == end && text[0] == '\0' && s1 > start && whitespace (rl_line_buffer[start])) @@ -1742,7 +1744,14 @@ attempt_shell_completion (const char *text, int start, int end) /* If we have defined a compspec for the initial (command) word, call it and process the results like any other programmable completion. */ if (in_command_position && have_progcomps && foundcs == 0 && iw_compspec) - prog_complete_matches = programmable_completions (INITIALWORD, text, s, e, &foundcs); + { + /* Do some sanity checking on S and E. Room for more here. */ + if (s > rl_point) + s = rl_point; + if (e > rl_end) + e = rl_end; + prog_complete_matches = programmable_completions (INITIALWORD, text, s, e, &foundcs); + } FREE (n); /* XXX - if we found a COMPSPEC for the command, just return whatever diff --git a/doc/bash.1 b/doc/bash.1 index 47753df6..bee814ae 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -2267,8 +2267,10 @@ and .IR blocks , which sort the files on name, file size, modification time, access time, inode change time, and number of blocks, respectively. -For example, a value of \fB\-mtime\fP sorts the results in descending +For example, a value of \fI\-mtime\fP sorts the results in descending order by modification time (newest first). +A sort specifier of \fInosort\fP disables sorting completely; the results +are returned in the order they are read from the file system,. If the sort specifier is missing, it defaults to \fIname\fP, so a value of \fI+\fP is equivalent to the null string, and a value of \fI-\fP sorts by name in descending order. diff --git a/doc/bashref.texi b/doc/bashref.texi index d56eb55a..3263a73c 100644 --- a/doc/bashref.texi +++ b/doc/bashref.texi @@ -6494,6 +6494,9 @@ inode change time, and number of blocks, respectively. For example, a value of @code{-mtime} sorts the results in descending order by modification time (newest first). +A sort specifier of @samp{nosort} disables sorting completely; the results +are returned in the order they are read from the file system,. + If the sort specifier is missing, it defaults to @var{name}, so a value of @samp{+} is equivalent to the null string, and a value of @samp{-} sorts by name in descending order. diff --git a/lib/readline/histexpand.c b/lib/readline/histexpand.c index db344b49..425ea7cf 100644 --- a/lib/readline/histexpand.c +++ b/lib/readline/histexpand.c @@ -1121,7 +1121,7 @@ history_expand (const char *hstring, char **output) c = tchar; memset (mb, 0, sizeof (mb)); - for (k = 0; k < MB_LEN_MAX; k++) + for (k = 0; k < MB_LEN_MAX && i < l; k++) { mb[k] = (char)c; memset (&ps, 0, sizeof (mbstate_t)); diff --git a/lib/readline/misc.c b/lib/readline/misc.c index 6e9e0305..7e3efca4 100644 --- a/lib/readline/misc.c +++ b/lib/readline/misc.c @@ -307,6 +307,10 @@ void _rl_start_using_history (void) { using_history (); +#if 1 + if (_rl_saved_line_for_history && _rl_saved_line_for_history->data) + _rl_free_undo_list ((UNDO_LIST *)_rl_saved_line_for_history->data); +#endif _rl_free_saved_history_line (); _rl_history_search_pos = -99; /* some random invalid history position */ } diff --git a/lib/readline/text.c b/lib/readline/text.c index 356cac5f..c7c12646 100644 --- a/lib/readline/text.c +++ b/lib/readline/text.c @@ -983,6 +983,9 @@ rl_insert (int count, int c) break; } + /* If we didn't insert n and there are pending bytes, we need to insert + them if _rl_insert_char didn't do that on its own. */ + if (n != (unsigned short)-2) /* -2 = sentinel value for having inserted N */ { /* setting rl_pending_input inhibits setting rl_last_func so we do it @@ -992,7 +995,6 @@ rl_insert (int count, int c) rl_executing_keyseq[rl_key_sequence_length = 0] = '\0'; r = rl_execute_next (n); } - return r; } diff --git a/pathexp.c b/pathexp.c index 21eed383..c7ca6df7 100644 --- a/pathexp.c +++ b/pathexp.c @@ -647,15 +647,16 @@ setup_ignore_patterns (struct ignorevar *ivp) /* Functions to handle sorting glob results in different ways depending on the value of the GLOBSORT variable. */ -static int glob_sorttype = STAT_NONE; +static int glob_sorttype = SORT_NONE; static STRING_INT_ALIST sorttypes[] = { - { "name", STAT_NAME }, - { "size", STAT_SIZE }, - { "mtime", STAT_MTIME }, - { "atime", STAT_ATIME }, - { "ctime", STAT_CTIME }, - { "blocks", STAT_BLOCKS }, + { "name", SORT_NAME }, + { "size", SORT_SIZE }, + { "mtime", SORT_MTIME }, + { "atime", SORT_ATIME }, + { "ctime", SORT_CTIME }, + { "blocks", SORT_BLOCKS }, + { "nosort", SORT_NOSORT }, { (char *)NULL, -1 } }; @@ -682,7 +683,7 @@ glob_findtype (char *t) int type; type = find_string_in_alist (t, sorttypes, 0); - return (type == -1 ? STAT_NONE : type); + return (type == -1 ? SORT_NONE : type); } void @@ -691,7 +692,7 @@ setup_globsort (const char *varname) char *val; int r, t; - glob_sorttype = STAT_NONE; + glob_sorttype = SORT_NONE; val = get_string_value (varname); if (val == 0 || *val == 0) return; @@ -703,7 +704,7 @@ setup_globsort (const char *varname) val++; /* allow leading `+' but ignore it */ else if (*val == '-') { - r = STAT_REVERSE; /* leading `-' reverses sort order */ + r = SORT_REVERSE; /* leading `-' reverses sort order */ val++; } @@ -711,25 +712,25 @@ setup_globsort (const char *varname) { /* A bare `+' means the default sort by name in ascending order; a bare `-' means to sort by name in descending order. */ - glob_sorttype = STAT_NAME | r; + glob_sorttype = SORT_NAME | r; return; } t = glob_findtype (val); /* any other value is equivalent to the historical behavior */ - glob_sorttype = (t == STAT_NONE) ? t : t | r; + glob_sorttype = (t == SORT_NONE) ? t : t | r; } static int globsort_namecmp (char **s1, char **s2) { - return ((glob_sorttype < STAT_REVERSE) ? strvec_posixcmp (s1, s2) : strvec_posixcmp (s2, s1)); + return ((glob_sorttype < SORT_REVERSE) ? strvec_posixcmp (s1, s2) : strvec_posixcmp (s2, s1)); } static int globsort_sizecmp (struct globsort_t *g1, struct globsort_t *g2) { - return ((glob_sorttype < STAT_REVERSE) ? g1->st.size - g2->st.size : g2->st.size - g1->st.size); + return ((glob_sorttype < SORT_REVERSE) ? g1->st.size - g2->st.size : g2->st.size - g1->st.size); } static int @@ -738,13 +739,13 @@ globsort_timecmp (struct globsort_t *g1, struct globsort_t *g2) int t; struct timespec t1, t2; - t = (glob_sorttype < STAT_REVERSE) ? glob_sorttype : glob_sorttype - STAT_REVERSE; - if (t == STAT_MTIME) + t = (glob_sorttype < SORT_REVERSE) ? glob_sorttype : glob_sorttype - SORT_REVERSE; + if (t == SORT_MTIME) { t1 = g1->st.mtime; t2 = g2->st.mtime; } - else if (t == STAT_ATIME) + else if (t == SORT_ATIME) { t1 = g1->st.atime; t2 = g2->st.atime; @@ -755,13 +756,13 @@ globsort_timecmp (struct globsort_t *g1, struct globsort_t *g2) t2 = g2->st.ctime; } - return ((glob_sorttype < STAT_REVERSE) ? timespec_cmp (t1, t2) : timespec_cmp (t2, t1)); + return ((glob_sorttype < SORT_REVERSE) ? timespec_cmp (t1, t2) : timespec_cmp (t2, t1)); } static int globsort_blockscmp (struct globsort_t *g1, struct globsort_t *g2) { - return ((glob_sorttype < STAT_REVERSE) ? g1->st.blocks - g2->st.blocks : g2->st.blocks - g1->st.blocks); + return ((glob_sorttype < SORT_REVERSE) ? g1->st.blocks - g2->st.blocks : g2->st.blocks - g1->st.blocks); } static struct globsort_t * @@ -803,19 +804,19 @@ globsort_sortarray (struct globsort_t *garray, size_t len) int t; QSFUNC *sortfunc; - t = (glob_sorttype < STAT_REVERSE) ? glob_sorttype : glob_sorttype - STAT_REVERSE; + t = (glob_sorttype < SORT_REVERSE) ? glob_sorttype : glob_sorttype - SORT_REVERSE; switch (t) { - case STAT_SIZE: + case SORT_SIZE: sortfunc = (QSFUNC *)globsort_sizecmp; break; - case STAT_ATIME: - case STAT_MTIME: - case STAT_CTIME: + case SORT_ATIME: + case SORT_MTIME: + case SORT_CTIME: sortfunc = (QSFUNC *)globsort_timecmp; break; - case STAT_BLOCKS: + case SORT_BLOCKS: sortfunc = (QSFUNC *)globsort_blockscmp; break; default: @@ -832,9 +833,12 @@ sh_sortglob (char **results) size_t rlen; struct globsort_t *garray; - if (glob_sorttype == STAT_NONE || glob_sorttype == STAT_NAME) + if (glob_sorttype == SORT_NOSORT || glob_sorttype == (SORT_NOSORT|SORT_REVERSE)) + return; + + if (glob_sorttype == SORT_NONE || glob_sorttype == SORT_NAME) globsort_sortbyname (results); /* posix sort */ - else if (glob_sorttype == (STAT_NAME|STAT_REVERSE)) + else if (glob_sorttype == (SORT_NAME|SORT_REVERSE)) globsort_sortbyname (results); /* posix sort reverse order */ else { diff --git a/pathexp.h b/pathexp.h index 87da5f9e..6f80ca08 100644 --- a/pathexp.h +++ b/pathexp.h @@ -105,15 +105,16 @@ extern int should_ignore_glob_matches (void); extern void ignore_glob_matches (char **); /* Definitions for glob sorting */ -#define STAT_NONE 0 -#define STAT_NAME 1 -#define STAT_SIZE 2 -#define STAT_MTIME 3 -#define STAT_ATIME 4 -#define STAT_CTIME 5 -#define STAT_BLOCKS 6 +#define SORT_NONE 0 +#define SORT_NAME 1 +#define SORT_SIZE 2 +#define SORT_MTIME 3 +#define SORT_ATIME 4 +#define SORT_CTIME 5 +#define SORT_BLOCKS 6 +#define SORT_NOSORT 7 -#define STAT_REVERSE 128 +#define SORT_REVERSE 128 extern void setup_globsort (const char *);