fix minor errors uncovered by address sanitizer; work around android issue with read -e -u; fix minor memory leaks; make spell-correct-shellword work with negative arguments; fix readline change-case on invalid multibyte character

This commit is contained in:
Chet Ramey
2024-05-28 09:19:03 -04:00
parent dbd27e54cc
commit 9cea457aef
16 changed files with 1071 additions and 1325 deletions
+67
View File
@@ -9454,3 +9454,70 @@ pathexp.c
integer underflow
From a report from Grisha Levit <grishalevit@gmail.com>
5/20
----
lib/readline/kill.c
- _rl_bracketed_text: make sure buf is null-terminated even if
rl_read_key() returns an error
From a report from Grisha Levit <grishalevit@gmail.com>
builtins/read.def
- read_builtin: if -u and -e are both supplied, dup the file descriptor
supplied as an argument to -u and use it in the new FILE * to pass
to readline as rl_instream. Works around an android problem with
stdio and application-managed file descriptors
Report and patch from Grisha Levit <grishalevit@gmail.com>
5/21
----
lib/sh/spell.c
- mindist: don't check best unless we set it
Report and patch from Grisha Levit <grishalevit@gmail.com>
jobs.c
- make_child: if FORK_NOJOB is in the flags argument, don't call
setpgid to set the child's process group in either the parent or
child
- alloc_process,dispose_process: allocate and deallocate a PROCESS;
changed callers
subst.c
- command_substitute: call cleanup_the_pipeline after waiting for
the command substitution process, since we allocated it
5/22
----
lib/readline/text.c
- rl_execute_named_command: fix a leak if the command name is null or
the bound function doesn't return (rl_abort)
Report and patch from Grisha Levit <grishalevit@gmail.com>
bashline.c
- command_word_completion_function: free directory_part if it's left
over from a previous completion
- command_subst_completion_function: free contents of match list left
over from previous completion
- bash_spell_correct_shellword: free text if it's "" before returning
- build_history_completion_array: only call qsort if there's actually
something in the array to sort
Report and patches from Grisha Levit <grishalevit@gmail.com>
- bash_spell_correct_shellword: fix bug where we would correct the
previous word if we start on the first character of a word
- bash_spell_correct_shellword: make negative argument counts work
backwards, correcting words before point
Report from Grisha Levit <grishalevit@gmail.com>
5/23
----
lib/readline/text.c
- rl_change_case: if mbrtowc returns -1 or -2, jump to changing case
for a single character, since _rl_find_next_mbchar_internal() will
treat an invalid multibyte character as a sequence of bytes
Report from Grisha Levit <grishalevit@gmail.com>
5/25
----
execute_cmd.c
- shell_execve: fix typo in code that chops \r off the end of the #!
interpreter
Report and fix from Collin Funk <collin.funk1@gmail.com>
+47 -7
View File
@@ -1328,14 +1328,45 @@ bash_transpose_shellwords (int count, int key)
return 0;
}
/* Directory name spelling correction on the current word (not shellword).
COUNT > 1 is not exactly correct yet. */
/* Directory name spelling correction on the current or previous shellword. */
static int
bash_spell_correct_shellword (int count, int key)
{
int wbeg, wend;
int wbeg, wend, n, p;
char *text, *newdir;
/* If we have a negative count, move back that many shellwords and then
move forward. Do it one at a time so we get an accurate count of the
number of words we moved back -- we only want to correct that many. */
if (count < 0)
{
n = 0;
while (rl_point > 0 && count++)
{
p = rl_point;
bash_backward_shellword (1, key);
/* We probably moved to column 0 with leading spaces on the line */
if (rl_point == 0 && WORDDELIM (rl_line_buffer[rl_point]))
{
rl_point = p;
break;
}
n++;
}
count = n;
}
else if (WORDDELIM (rl_line_buffer[rl_point])) /* count > 0 */
{
/* between words with a positive count, move forward to word start */
while (rl_point < rl_end && WORDDELIM (rl_line_buffer[rl_point]))
rl_point++; /* word delims are single-byte characters */
}
/* First make sure we're at the end of the word we want to begin with
so the initial bash_backward_shellword works right. */
if (rl_point < rl_end && WORDDELIM (rl_line_buffer[rl_point]) == 0)
bash_forward_shellword (1, key);
while (count)
{
bash_backward_shellword (1, key);
@@ -1348,7 +1379,10 @@ bash_spell_correct_shellword (int count, int key)
text = rl_copy_text (wbeg, wend);
if (text == 0 || *text == 0)
break;
{
FREE (text);
break;
}
newdir = dirspell (text);
if (newdir)
@@ -1371,7 +1405,7 @@ bash_spell_correct_shellword (int count, int key)
count--;
if (count)
bash_forward_shellword (1, key); /* XXX */
bash_forward_shellword (1, key); /* XXX */
}
return 0;
@@ -2002,6 +2036,12 @@ command_word_completion_function (const char *hint_text, int state)
glob_matches = (char **)NULL;
}
if (directory_part)
{
free (directory_part);
directory_part = (char *)NULL;
}
globpat = completion_glob_pattern (hint_text);
/* If this is an absolute program name, do not check it against
@@ -2451,7 +2491,7 @@ command_subst_completion_function (const char *text, int state)
filename_text = savestring (text);
if (matches)
{
free (matches);
strvec_dispose (matches);
matches = (char **)NULL;
}
@@ -3716,7 +3756,7 @@ build_history_completion_array (void)
}
/* Sort the complete list of tokens. */
if (dabbrev_expand_active == 0)
if (harry_len > 1 && dabbrev_expand_active == 0)
qsort (history_completion_array, harry_len, sizeof (char *), (QSFUNC *)strvec_strcmp);
}
}
+30 -3
View File
@@ -201,6 +201,19 @@ read_builtin_timeout (int fd)
#endif
}
void
reset_rl_instream (FILE *save_instream)
{
fclose (rl_instream);
rl_instream = save_instream;
}
void
uw_reset_rl_instream (void *fp)
{
reset_rl_instream (fp);
}
/* Read the value of the shell variables whose names follow.
The reading is done from the current input stream, whatever
that may be. Successive words of the input line are assigned
@@ -613,12 +626,26 @@ read_builtin (WORD_LIST *list)
save_instream = 0;
if (edit && fd != 0)
{
int fd2;
FILE *fp2;
if ((fd2 = dup (fd)) < 0 || (fp2 = fdopen (fd2, "r")) == 0)
{
builtin_error ("%d: %s", fd, strerror (errno));
if (fd2 >= 0)
close (fd2);
run_unwind_frame ("read_builtin");
return (EXECUTION_FAILURE);
}
if (bash_readline_initialized == 0)
initialize_readline ();
unwind_protect_var (rl_instream);
save_instream = rl_instream;
rl_instream = fdopen (fd, "r");
rl_instream = fp2;
add_unwind_protect (uw_reset_rl_instream, save_instream);
fd = fd2;
}
#endif
@@ -896,7 +923,7 @@ add_char:
#if defined (READLINE)
if (save_instream)
rl_instream = save_instream; /* can't portably free it */
reset_rl_instream (save_instream);
#endif
discard_unwind_frame ("read_builtin");
+4 -1
View File
@@ -154,7 +154,10 @@ source_builtin (WORD_LIST *list)
#else
if (posixly_correct == 0 && (spath = path_value ("BASH_SOURCE_PATH", 1)))
#endif
filename = find_in_path (list->word->word, spath, FS_READABLE);
{
filename = find_in_path (list->word->word, spath, FS_READABLE);
search_cwd = 0;
}
else
#endif
filename = find_path_file (list->word->word);
+2 -1
View File
@@ -2326,7 +2326,8 @@ inode change time, and number of blocks, respectively.
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.
are returned in the order they are read from the file system,
and any leading \fI+\fP or \fI\-\fP is ignored.
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.
+2 -1
View File
@@ -6694,7 +6694,8 @@ 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.
are returned in the order they are read from the file system,
and any leading @samp{-} is ignored.
If the sort specifier is missing, it defaults to @var{name},
so a value of @samp{+} is equivalent to the null string,
+3 -3
View File
@@ -1392,13 +1392,13 @@ print_formatted_time (FILE *fp, char *format,
static int
time_command (COMMAND *command, int asynchronous, int pipe_in, int pipe_out, struct fd_bitmap *fds_to_close)
{
int rv, posix_time, old_flags, nullcmd, code;
int rv, posix_time, nullcmd, code;
time_t rs, us, ss; /* seconds */
long rsf, usf, ssf; /* microseconds */
int cpu;
char *time_format;
volatile procenv_t save_top_level;
volatile int old_subshell;
volatile int old_subshell, old_flags;
#if defined (HAVE_GETRUSAGE) && defined (HAVE_GETTIMEOFDAY)
struct timeval real, user, sys;
@@ -6128,7 +6128,7 @@ shell_execve (char *command, char **args, char **env)
interp = getinterp (sample, sample_len, (int *)NULL);
ilen = strlen (interp);
errno = i;
if (interp > 0 && interp[ilen - 1] == '\r')
if (ilen > 0 && interp[ilen - 1] == '\r')
{
interp = xrealloc (interp, ilen + 2);
interp[ilen - 1] = '^';
+42 -16
View File
@@ -216,6 +216,9 @@ volatile pid_t last_asynchronous_pid = NO_PID;
/* The pipeline currently being built. */
PROCESS *the_pipeline = (PROCESS *)NULL;
/* We are forking this pipeline (process) to perform a command substitution. */
PROCESS *comsub_pipeline = (PROCESS *)NULL;
/* If this is non-zero, do job control. */
int job_control = 1;
@@ -643,8 +646,9 @@ stop_pipeline (int async, COMMAND *deferred)
the_pipeline = (PROCESS *)NULL;
newjob->pgrp = pipeline_pgrp;
/* Invariant: if the shell is executing a command substitution,
pipeline_pgrp == shell_pgrp. Other parts of the shell assume this. */
/* Invariant: if the shell is executing a command substitution when
job control is enabled, pipeline_pgrp == shell_pgrp.
Other parts of the shell assume this. */
if (pipeline_pgrp != shell_pgrp)
pipeline_pgrp = 0;
@@ -1473,6 +1477,28 @@ nohup_job (int job_index)
temp->flags |= J_NOHUP;
}
PROCESS *
alloc_process (char *name, pid_t pid)
{
PROCESS *t;
t = (PROCESS *)xmalloc (sizeof (PROCESS));
t->pid = pid;
WSTATUS (t->status) = 0;
t->running = PS_RUNNING; /* default */
t->command = name;
t->next = (PROCESS *)0;
return (t);
}
void
dispose_process (PROCESS *t)
{
FREE (t->command);
free (t);
}
/* Get rid of the data structure associated with a process chain. */
int
discard_pipeline (PROCESS *chain)
@@ -1485,8 +1511,7 @@ discard_pipeline (PROCESS *chain)
do
{
next = this->next;
FREE (this->command);
free (this);
dispose_process (this);
n++;
this = next;
}
@@ -1516,12 +1541,8 @@ add_process (char *name, pid_t pid)
}
#endif
t = (PROCESS *)xmalloc (sizeof (PROCESS));
t = alloc_process (name, pid);
t->next = the_pipeline;
t->pid = pid;
WSTATUS (t->status) = 0;
t->running = PS_RUNNING;
t->command = name;
the_pipeline = t;
if (t->next == 0)
@@ -1542,13 +1563,11 @@ append_process (char *name, pid_t pid, int status, int jid)
{
PROCESS *t, *p;
t = (PROCESS *)xmalloc (sizeof (PROCESS));
t->next = (PROCESS *)NULL;
t->pid = pid;
t = alloc_process (name, pid);
/* set process exit status using offset discovered by configure */
t->status = (status & 0xff) << WEXITSTATUS_OFFSET;
t->running = PS_DONE;
t->command = name;
js.c_reaped++; /* XXX */
@@ -2267,6 +2286,7 @@ make_child (char *command, int flags)
pipeline_pgrp = mypid;
/* Check for running command in backquotes. */
/* XXX - do this if (flags & FORK_NOJOB)? */
if (pipeline_pgrp == shell_pgrp)
ignore_tty_job_signals ();
else
@@ -2280,7 +2300,7 @@ make_child (char *command, int flags)
this would have for the first child) is an error. Section
B.4.3.3, p. 237 also covers this, in the context of job control
shells. */
if (setpgid (mypid, pipeline_pgrp) < 0)
if ((flags & FORK_NOJOB) == 0 && setpgid (mypid, pipeline_pgrp) < 0)
sys_error (_("child setpgid (%ld to %ld)"), (long)mypid, (long)pipeline_pgrp);
/* By convention (and assumption above), if
@@ -2344,7 +2364,8 @@ make_child (char *command, int flags)
the POSIX 1003.1 standard, where it discusses job control and
shells. It is done to avoid possible race conditions. (Ref.
1003.1 Rationale, section B.4.3.3, page 236). */
setpgid (pid, pipeline_pgrp);
if ((flags & FORK_NOJOB) == 0)
setpgid (pid, pipeline_pgrp);
}
else
{
@@ -3964,7 +3985,12 @@ itrace("waitchld: waitpid returns %d block = %d children_exited = %d", pid, bloc
if (child == 0)
{
if (WIFEXITED (status) || WIFSIGNALED (status))
js.c_reaped++;
{
js.c_reaped++;
js.c_totreaped++;
if (pid == wpid) /* but we're waiting for it?? */
internal_debug ("waitchld: pid == wpid but child == 0");
}
continue;
}
+3
View File
@@ -210,6 +210,7 @@ struct procchain {
#define FORK_ASYNC 1 /* background process */
#define FORK_NOJOB 2 /* don't put process in separate pgrp */
#define FORK_NOTERM 4 /* don't give terminal to any pgrp */
#define FORK_COMSUB 8 /* command or process substitution */
/* System calls. */
#if !defined (HAVE_UNISTD_H)
@@ -239,6 +240,8 @@ extern void save_pipeline (int);
extern PROCESS *restore_pipeline (int);
extern void start_pipeline (void);
extern int stop_pipeline (int, COMMAND *);
extern PROCESS *alloc_process (char *, pid_t);
extern void dispose_process (PROCESS *);
extern int discard_pipeline (PROCESS *);
extern void append_process (char *, pid_t, int, int);
+3 -6
View File
@@ -737,12 +737,9 @@ _rl_bracketed_text (size_t *lenp)
}
RL_UNSETSTATE (RL_STATE_MOREINPUT);
if (c >= 0)
{
if (len == cap)
buf = xrealloc (buf, cap + 1);
buf[len] = '\0';
}
if (len == cap)
buf = xrealloc (buf, cap + 1);
buf[len] = '\0';
if (lenp)
*lenp = len;
+20 -6
View File
@@ -1449,6 +1449,7 @@ rl_change_case (int count, int op)
int start, next, end;
int inword, nc, nop;
WCHAR_T c;
unsigned char uc;
#if defined (HANDLE_MULTIBYTE)
WCHAR_T wc, nwc;
char mb[MB_LEN_MAX+1];
@@ -1503,7 +1504,9 @@ rl_change_case (int count, int op)
characters */
if (MB_CUR_MAX == 1 || rl_byte_oriented)
{
nc = (nop == UpCase) ? _rl_to_upper (c) : _rl_to_lower (c);
change_singlebyte:
uc = c;
nc = (nop == UpCase) ? _rl_to_upper (uc) : _rl_to_lower (uc);
rl_line_buffer[start] = nc;
}
#if defined (HANDLE_MULTIBYTE)
@@ -1511,9 +1514,16 @@ rl_change_case (int count, int op)
{
m = MBRTOWC (&wc, rl_line_buffer + start, end - start, &mps);
if (MB_INVALIDCH (m))
wc = (WCHAR_T)rl_line_buffer[start];
{
c = rl_line_buffer[start];
next = start + 1; /* potentially redundant */
goto change_singlebyte;
}
else if (MB_NULLWCH (m))
wc = L'\0';
{
start = next; /* don't bother with null wide characters */
continue;
}
nwc = (nop == UpCase) ? _rl_to_wupper (wc) : _rl_to_wlower (wc);
if (nwc != wc) /* just skip unchanged characters */
{
@@ -2355,8 +2365,13 @@ rl_execute_named_command (int count, int key)
command = _rl_read_command_name ();
if (command == 0 || *command == '\0')
return 1;
if (func = rl_named_function (command))
{
free (command);
return 1;
}
func = rl_named_function (command);
free (command);
if (func)
{
int prev, ostate;
@@ -2375,6 +2390,5 @@ rl_execute_named_command (int count, int key)
r = 1;
}
free (command);
return r;
}
+1 -1
View File
@@ -135,7 +135,7 @@ mindist(const char *dir, char *guess, char *best)
(void)closedir(fd);
/* Don't return `.' */
if (best[0] == '.' && best[1] == '\0')
if (dist != 3 && best[0] == '.' && best[1] == '\0')
dist = 3;
return dist;
}
+3 -1
View File
@@ -751,7 +751,9 @@ globsort_namecmp (char **s1, char **s2)
}
/* Generic transitive comparison of two numeric values for qsort */
#define GENCMP(a,b) (a < b ? -1 : (a > b ? 1 : 0))
/* #define GENCMP(a,b) ((a) < (b) ? -1 : ((a) > (b) ? 1 : 0)) */
/* A clever idea from gnulib */
#define GENCMP(a,b) (((a) > (b)) - ((a) < (b)))
static int
globsort_sizecmp (struct globsort_t *g1, struct globsort_t *g2)
BIN
View File
Binary file not shown.
+836 -1279
View File
File diff suppressed because it is too large Load Diff
+8
View File
@@ -7278,7 +7278,10 @@ command_substitute (char *string, int quoted, int flags)
for example). */
if ((subshell_environment & (SUBSHELL_FORK|SUBSHELL_PIPE)) == 0)
pipeline_pgrp = shell_pgrp;
/* this can happen if we're performing word expansion in the second or
subsequent commands in a pipeline */
cleanup_the_pipeline ();
/* at this point, the_pipeline is NULL */
#endif /* JOB_CONTROL */
old_async_pid = last_asynchronous_pid;
@@ -7491,6 +7494,11 @@ command_substitute (char *string, int quoted, int flags)
CHECK_TERMSIG;
#if defined (JOB_CONTROL)
/* this is the pipeline we allocated for this command substitution */
cleanup_the_pipeline ();
#endif
ret = alloc_word_desc ();
ret->word = istring;
ret->flags = tflag;