diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index caf2e2d4..05ff6e46 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -5122,3 +5122,71 @@ builtins/complete.def mode characters and doesn't contain any shell break characters that would need to be quoted when defining a function. Fixes issue reported by Great Big Dot + + 1/28 + ---- +variables.c + - dispose_temporary_env: make sure to save temporary_env to a temp + pointer and set temporary_env to NULL before trying to dispose it, + so no flush function ever tries to add a temporary variable back + into the table (e.g., bind_variable()) + + 1/29 + ---- +builtins/evalstring.c + - can_optimize_connection,optimize_fork: add the last command in lists + separated by `;' to the list of candidates for fork optimization + + 1/30 + ---- +examples/loadables/strftime.c + - strftime_builtin: try to extend the buffer longer than tbsize*3, + which is a minimum of 24 characters, in case some of the formats + (e.g., %c) expand to something longer than that. Fixes bug + reported by Stan Marsh + + 1/31 + ---- +lib/readline/undo.c + - rl_do_undo: before inserting text while undoing UNDO_DELETE, or + performing a deletion while undoing UNDO_INSERT, make sure that + rl_point is valid by calling _rl_fix_point. Fuzzing bug and fix + from Eduardo Bustamante + +lib/readline/search.c + - _rl_nsearch_abort: validate new values for rl_point and rl_mark by + calling _rl_fix_point(). Fuzzing bug and fix from + Eduardo Bustamante + +subst.c + - string_extract_double_quoted: if we parse a syntactically-incorrect + $( expression while extracting a double-quoted string, si will + appear to go `backward'. Just skip over the rest of the string and + continue. Fuzzing bug from Eduardo Bustamante + +lib/readline/text.c + - rl_change_case: if towupper or towlower returns a valid wide char + that can't be converted back to a valid multibyte character, use + the original character and go on. Fuzzing bug from + Eduardo Bustamante + +lib/glob/glob.c + - wdequote_pathname: if there are no multibyte characters in pathname, + just call udequote_pathname and don't bother converting it to wide + characters + - glob_pattern_p: if there are no multibyte characters in the pattern, + just call internal_glob_pattern_p right away + +lib/glob/glob_loop.c + - INTERNAL_GLOB_PATTERN_P: return 2 if we see only backslash-quoted + characters without any other unquoted glob pattern characters, so + interested callers can shortcut and just dequote the pathname + +pathexp.c + - unquoted_glob_pattern_p: return 2 if we see only backslash-quoted + characters without any other unquoted glob pattern characters, + consistent with the glob library + - unquoted_glob_pattern_p: don't count a backslash quoting a slash as + a backslash that will trigger a call to shell_glob_filename, since + backslashes at the end of patterns (pathname components) will always + fail to match. XXX - this is provisional diff --git a/MANIFEST b/MANIFEST index 03de2210..dcfd192c 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1060,6 +1060,7 @@ tests/glob1.sub f tests/glob2.sub f tests/glob3.sub f tests/glob4.sub f +tests/glob5.sub f tests/glob.right f tests/globstar.tests f tests/globstar.right f diff --git a/builtins/evalstring.c b/builtins/evalstring.c index 7a110d95..cadc9bc0 100644 --- a/builtins/evalstring.c +++ b/builtins/evalstring.c @@ -105,7 +105,7 @@ can_optimize_connection (command) COMMAND *command; { return (*bash_input.location.string == '\0' && - (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR) && + (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR || command->value.Connection->connector == ';') && command->value.Connection->second->type == cm_simple); } @@ -114,7 +114,7 @@ optimize_fork (command) COMMAND *command; { if (command->type == cm_connection && - (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR) && + (command->value.Connection->connector == AND_AND || command->value.Connection->connector == OR_OR || command->value.Connection->connector == ';') && (command->value.Connection->second->flags & CMD_TRY_OPTIMIZING) && should_suppress_fork (command->value.Connection->second)) { diff --git a/examples/loadables/strftime.c b/examples/loadables/strftime.c index 2de09e34..a6190f1c 100644 --- a/examples/loadables/strftime.c +++ b/examples/loadables/strftime.c @@ -86,7 +86,7 @@ strftime_builtin (list) /* Now try to figure out how big the buffer should really be. strftime(3) will return the number of bytes placed in the buffer unless it's greater than MAXSIZE, in which case it returns 0. */ - for (n = 1; n < 4; n++) + for (n = 1; n <= 8; n++) { tbuf = xrealloc (tbuf, tbsize * n); tsize = strftime (tbuf, tbsize * n, format, t); @@ -94,7 +94,8 @@ strftime_builtin (list) break; } - printf ("%s\n", tbuf); + if (tsize) + printf ("%s\n", tbuf); free (tbuf); return (EXECUTION_SUCCESS); diff --git a/lib/glob/glob.c b/lib/glob/glob.c index 79ab9339..51c585ce 100644 --- a/lib/glob/glob.c +++ b/lib/glob/glob.c @@ -159,7 +159,7 @@ glob_pattern_p (pattern) wchar_t *wpattern; int r; - if (MB_CUR_MAX == 1) + if (MB_CUR_MAX == 1 || mbsmbchar (pattern) == 0) return (internal_glob_pattern_p ((unsigned char *)pattern)); /* Convert strings to wide chars, and call the multibyte version. */ @@ -173,7 +173,7 @@ glob_pattern_p (pattern) return r; #else - return (internal_glob_pattern_p (pattern)); + return (internal_glob_pattern_p ((unsigned char *)pattern)); #endif } @@ -431,6 +431,12 @@ wdequote_pathname (pathname) int i, j; wchar_t *orig_wpathname; + if (mbsmbchar (pathname) == 0) + { + udequote_pathname (pathname); + return; + } + len = strlen (pathname); /* Convert the strings into wide characters. */ n = xdupmbstowcs (&wpathname, NULL, pathname); @@ -1071,7 +1077,7 @@ glob_filename (pathname, flags) char *directory_name, *filename, *dname, *fn; unsigned int directory_len; int free_dirname; /* flag */ - int dflags; + int dflags, hasglob; result = (char **) malloc (sizeof (char *)); result_size = 1; @@ -1120,9 +1126,12 @@ glob_filename (pathname, flags) free_dirname = 1; } + hasglob = 0; /* If directory_name contains globbing characters, then we - have to expand the previous levels. Just recurse. */ - if (directory_len > 0 && glob_pattern_p (directory_name)) + have to expand the previous levels. Just recurse. + If glob_pattern_p returns != [0,1] we have a pattern that has backslash + quotes but no unquoted glob pattern characters. We dequote it below. */ + if (directory_len > 0 && (hasglob = glob_pattern_p (directory_name)) == 1) { char **directories, *d, *p; register unsigned int i; @@ -1342,6 +1351,11 @@ only_filename: free (directory_name); return (NULL); } + if (directory_len > 0 && hasglob == 2) /* need to dequote */ + { + dequote_pathname (directory_name); + directory_len = strlen (directory_name); + } /* Handle GX_MARKDIRS here. */ result[0] = (char *) malloc (directory_len + 1); if (result[0] == NULL) diff --git a/lib/glob/glob_loop.c b/lib/glob/glob_loop.c index 7d6ae211..6062f2f9 100644 --- a/lib/glob/glob_loop.c +++ b/lib/glob/glob_loop.c @@ -26,10 +26,10 @@ INTERNAL_GLOB_PATTERN_P (pattern) { register const GCHAR *p; register GCHAR c; - int bopen; + int bopen, bsquote; p = pattern; - bopen = 0; + bopen = bsquote = 0; while ((c = *p++) != L('\0')) switch (c) @@ -55,13 +55,22 @@ INTERNAL_GLOB_PATTERN_P (pattern) case L('\\'): /* Don't let the pattern end in a backslash (GMATCH returns no match - if the pattern ends in a backslash anyway), but otherwise return 1, - since the matching engine uses backslash as an escape character - and it can be removed. */ - return (*p != L('\0')); + if the pattern ends in a backslash anyway), but otherwise note that + we have seen this, since the matching engine uses backslash as an + escape character and it can be removed. We return 2 later if we + have seen only backslash-escaped characters, so interested callers + know they can shortcut and just dequote the pathname. */ + if (*p != L('\0')) + { + p++; + bsquote = 1; + } + else if (*p == L('\0')) + return 0; + continue; } - return 0; + return bsquote ? 2 : 0; } #undef INTERNAL_GLOB_PATTERN_P diff --git a/lib/glob/smatch.c b/lib/glob/smatch.c index 64fdbbb7..b2b8b35e 100644 --- a/lib/glob/smatch.c +++ b/lib/glob/smatch.c @@ -529,7 +529,7 @@ xstrmatch (pattern, string, flags) if (MB_CUR_MAX == 1) return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags)); - if (mbsmbchar (string) == 0 && mbsmbchar (pattern) == 0 && posix_cclass_only (pattern) ) + if (mbsmbchar (string) == 0 && mbsmbchar (pattern) == 0 && posix_cclass_only (pattern)) return (internal_strmatch ((unsigned char *)pattern, (unsigned char *)string, flags)); n = xdupmbstowcs (&wpattern, NULL, pattern); diff --git a/lib/readline/search.c b/lib/readline/search.c index c9c1f5d1..d3920d4b 100644 --- a/lib/readline/search.c +++ b/lib/readline/search.c @@ -256,6 +256,7 @@ _rl_nsearch_abort (_rl_search_cxt *cxt) rl_clear_message (); rl_point = cxt->save_point; rl_mark = cxt->save_mark; + _rl_fix_point (1); rl_restore_prompt (); RL_UNSETSTATE (RL_STATE_NSEARCH); diff --git a/lib/readline/text.c b/lib/readline/text.c index cddaeebd..43aa4d97 100644 --- a/lib/readline/text.c +++ b/lib/readline/text.c @@ -1452,7 +1452,18 @@ rl_change_case (int count, int op) if (nwc != wc) /* just skip unchanged characters */ { char *s, *e; - mlen = wcrtomb (mb, nwc, &mps); + mbstate_t ts; + + memset (&ts, 0, sizeof (mbstate_t)); + mlen = wcrtomb (mb, nwc, &ts); + if (mlen < 0) + { + nwc = wc; + memset (&ts, 0, sizeof (mbstate_t)); + mlen = wcrtomb (mb, nwc, &ts); + if (mlen < 0) /* should not happen */ + strncpy (mb, rl_line_buffer + start, mlen = m); + } if (mlen > 0) mb[mlen] = '\0'; /* what to do if m != mlen? adjust below */ diff --git a/lib/readline/undo.c b/lib/readline/undo.c index ae65d380..14799911 100644 --- a/lib/readline/undo.c +++ b/lib/readline/undo.c @@ -196,6 +196,7 @@ rl_do_undo (void) /* Undoing deletes means inserting some text. */ case UNDO_DELETE: rl_point = start; + _rl_fix_point (1); rl_insert_text (rl_undo_list->text); xfree (rl_undo_list->text); break; @@ -204,6 +205,7 @@ rl_do_undo (void) case UNDO_INSERT: rl_delete_text (start, end); rl_point = start; + _rl_fix_point (1); break; /* Undoing an END means undoing everything 'til we get to a BEGIN. */ diff --git a/pathexp.c b/pathexp.c index b51729a7..c1bf2d89 100644 --- a/pathexp.c +++ b/pathexp.c @@ -65,11 +65,11 @@ unquoted_glob_pattern_p (string) { register int c; char *send; - int open; + int open, bsquote; DECLARE_MBSTATE; - open = 0; + open = bsquote = 0; send = string + strlen (string); while (c = *string++) @@ -100,7 +100,14 @@ unquoted_glob_pattern_p (string) can be removed by the matching engine, so we have to run it through globbing. */ case '\\': - return (*string != 0); + if (*string != '\0' && *string != '/') + { + bsquote = 1; + string++; + continue; + } + else if (*string == 0) + return (0); case CTLESC: if (*string++ == '\0') @@ -117,7 +124,8 @@ unquoted_glob_pattern_p (string) ADVANCE_CHAR_P (string, send - string); #endif } - return (0); + + return (bsquote ? 2 : 0); } /* Return 1 if C is a character that is `special' in a POSIX ERE and needs to diff --git a/subst.c b/subst.c index 4173547d..f9d34ca7 100644 --- a/subst.c +++ b/subst.c @@ -964,7 +964,9 @@ add_one_character: temp[j] = ret[t]; temp[j] = string[si]; - if (string[si]) + if (si < i + 2) /* we went back? */ + i += 2; + else if (string[si]) { j++; i = si + 1; diff --git a/tests/RUN-ONE-TEST b/tests/RUN-ONE-TEST index 58c375b7..554f3d6e 100755 --- a/tests/RUN-ONE-TEST +++ b/tests/RUN-ONE-TEST @@ -1,4 +1,4 @@ -BUILD_DIR=/usr/local/build/chet/bash/bash-current +BUILD_DIR=/usr/local/build/bash/bash-current THIS_SH=$BUILD_DIR/bash PATH=$PATH:$BUILD_DIR diff --git a/tests/glob.right b/tests/glob.right index 964d83d1..3e3ee55c 100644 --- a/tests/glob.right +++ b/tests/glob.right @@ -63,6 +63,12 @@ a? aa +/tmp/a/b/c /tmp/a/b/c /tmp/a/b/c +/tmp/a/b/c /tmp/a/b/c /tmp/a/b/c +/tmp/a/b/c +/tmp/a/b/c +/tmp\/a/b/c +/tm[p]\/a/b/c argv[1] = argv[2] = argv[3] = @@ -77,7 +83,7 @@ argv[2] = argv[3] = argv[4] = tmp/l1 tmp/l2 tmp/*4 tmp/l3 -./glob.tests: line 47: no match: tmp/*4 +./glob.tests: line 48: no match: tmp/*4 argv[1] = argv[1] = <*> argv[1] = diff --git a/tests/glob.tests b/tests/glob.tests index 01913bbe..a40dbbbb 100644 --- a/tests/glob.tests +++ b/tests/glob.tests @@ -12,6 +12,7 @@ ${THIS_SH} ./glob1.sub ${THIS_SH} ./glob2.sub ${THIS_SH} ./glob3.sub ${THIS_SH} ./glob4.sub +${THIS_SH} ./glob5.sub MYDIR=$PWD # save where we are diff --git a/tests/glob5.sub b/tests/glob5.sub new file mode 100644 index 00000000..8075cb3c --- /dev/null +++ b/tests/glob5.sub @@ -0,0 +1,16 @@ +mkdir -m700 /tmp/a /tmp/a/b +touch /tmp/a/b/c + +echo /tmp/a/b/* "/tmp/a/"b/* "/tmp/a/b"/* + +chmod -r /tmp/a +echo /tmp/a/b/* "/tmp/a/"b/* "/tmp/a/b"/* +echo "/tmp/a/b"/* + +bs=\\ +echo /tmp${bs}/a/b/* +echo /tmp${bs}/a/b/c +echo /tm[p]${bs}/a/b/c + +chmod +r /tmp/a +rm -rf /tmp/a diff --git a/variables.c b/variables.c index f6346eb5..90c16d87 100644 --- a/variables.c +++ b/variables.c @@ -3270,12 +3270,7 @@ bind_variable (name, value, flags) else if (nv == &nameref_maxloop_value) { internal_warning (_("%s: circular name reference"), v->name); -#if 1 - /* TAG:bash-5.1 */ return (bind_global_variable (v->name, value, flags)); -#else - v = 0; /* backwards compat */ -#endif } else v = nv; @@ -3283,12 +3278,7 @@ bind_variable (name, value, flags) else if (nv == &nameref_maxloop_value) { internal_warning (_("%s: circular name reference"), v->name); -#if 1 - /* TAG:bash-5.1 */ return (bind_global_variable (v->name, value, flags)); -#else - v = 0; /* backwards compat */ -#endif } else v = nv; @@ -4472,16 +4462,37 @@ push_posix_temp_var (data) var = (SHELL_VAR *)data; +#if 0 /* TAG:bash-5.1 */ + /* Just like do_assignment_internal(). This makes assignments preceding + special builtins act like standalone assignment statements when in + posix mode. */ + v = bind_variable (var->name, value_cell (var), ASS_FORCE|ASS_NOLONGJMP); + + /* If this modifies an existing local variable, v->context will be non-zero. + If it comes back with v->context == 0, we bound at the global context. + Set binding_table appropriately. It doesn't matter whether it's correct + if the variable is local, only that it's not global_variables->table */ + binding_table = v->context ? shell_variables->table : global_variables->table; +#else binding_table = global_variables->table; if (binding_table == 0) binding_table = global_variables->table = hash_create (VARIABLES_HASH_BUCKETS); v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, ASS_FORCE|ASS_NOLONGJMP); +#endif /* global variables are no longer temporary and don't need propagating. */ - var->attributes &= ~(att_tempvar|att_propagate); + if (binding_table == global_variables->table) + var->attributes &= ~(att_tempvar|att_propagate); + if (v) - v->attributes |= var->attributes; + { + v->attributes |= var->attributes; + v->attributes &= ~att_tempvar; /* not a temp var now */ +#if 1 /* TAG:bash-5.1 code doesn't need this, disable for bash-5.1 */ + v->context = (binding_table == global_variables->table) ? 0 : shell_variables->scope; +#endif + } if (find_special_var (var->name) >= 0) tempvar_list[tvlist_ind++] = savestring (var->name); @@ -4575,14 +4586,17 @@ dispose_temporary_env (pushf) sh_free_func_t *pushf; { int i; + HASH_TABLE *disposer; tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1); tempvar_list[tvlist_ind = 0] = 0; - - hash_flush (temporary_env, pushf); - hash_dispose (temporary_env); + + disposer = temporary_env; temporary_env = (HASH_TABLE *)NULL; + hash_flush (disposer, pushf); + hash_dispose (disposer); + tempvar_list[tvlist_ind] = 0; array_needs_making = 1;