Dear Maintainer, Using history-search-backward and -forward can cause bash to die with an error: free(): double free detected in tcache 2 Aborted (core dumped) This is easily replicated by binding keys to run the history-search- functions. Start up a fresh bash shell and type the following. bind '"\C-p": history-search-backward' bind '"\C-n": history-search-forward' ^P^U^P^U^N^J Bash will immediately crash. Note ^ means hold down the control key while pressing the next letter. So, in the above example, you'd by hitting: Control-P # history-search-backward Control-U # unix-line-discard Control-P # history-search-backward Control-U # unix-line-discard Control-N # history-search-forward Control-J # accept-line
Hello, below is the top of a valgrind run with dbgsym package installed. Kind regards, Bernhard benutzer@debian:~$ valgrind bash ==1114== Memcheck, a memory error detector ==1114== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==1114== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info ==1114== Command: bash ==1114== benutzer@debian:~$ bind '"\C-p": history-search-backward' benutzer@debian:~$ bind '"\C-n": history-search-forward' benutzer@debian:~$ Control-P # history-search-backward Control-U # unix-line-discard Control-P # history-search-backward Control-U # unix-line-discard Control-N # history-search-forward Control-J # accept-line ==1114== Invalid read of size 4 ==1114== at 0x1E9D04: rl_do_undo (undo.c:186) ==1114== by 0x1EA0B4: rl_revert_line (undo.c:337) ==1114== by 0x1CD86C: readline_internal_teardown (readline.c:498) ==1114== by 0x1CEC7B: readline_internal (readline.c:734) ==1114== by 0x1CEC7B: readline (readline.c:387) ==1114== by 0x13AFE1: yy_readline_get (parse.y:1528) ==1114== by 0x13DAE0: yy_getc (parse.y:1462) ==1114== by 0x13DAE0: shell_getc (parse.y:2393) ==1114== by 0x13FF5A: read_token.constprop.0 (parse.y:3402) ==1114== by 0x1440BA: yylex (parse.y:2890) ==1114== by 0x1440BA: yyparse (y.tab.c:1854) ==1114== by 0x13A5B5: parse_command (eval.c:348) ==1114== by 0x13A743: read_command (eval.c:392) ==1114== by 0x13A8F5: reader_loop (eval.c:139) ==1114== by 0x1393D8: main (shell.c:833) ==1114== Address 0x53ba808 is 24 bytes inside a block of size 32 free'd ==1114== at 0x484317B: free (vg_replace_malloc.c:872) ==1114== by 0x1E9ABA: _rl_free_undo_list (undo.c:111) ==1114== by 0x1F03DF: _rl_free_saved_history_line (misc.c:396) ==1114== by 0x1D4286: rl_history_search_forward (search.c:647) ==1114== by 0x1CDD96: _rl_dispatch_subseq (readline.c:916) ==1114== by 0x1CE371: _rl_dispatch (readline.c:860) ==1114== by 0x1CE371: readline_internal_char (readline.c:675) ==1114== by 0x1CEC64: readline_internal_charloop (readline.c:721) ==1114== by 0x1CEC64: readline_internal (readline.c:733) ==1114== by 0x1CEC64: readline (readline.c:387) ==1114== by 0x13AFE1: yy_readline_get (parse.y:1528) ==1114== by 0x13DAE0: yy_getc (parse.y:1462) ==1114== by 0x13DAE0: shell_getc (parse.y:2393) ==1114== by 0x13FF5A: read_token.constprop.0 (parse.y:3402) ==1114== by 0x1440BA: yylex (parse.y:2890) ==1114== by 0x1440BA: yyparse (y.tab.c:1854) ==1114== by 0x13A5B5: parse_command (eval.c:348) ==1114== Block was alloc'd at ==1114== at 0x48407B4: malloc (vg_replace_malloc.c:381) ==1114== by 0x1A2C8D: xmalloc (xmalloc.c:114) ==1114== by 0x1E9A4E: alloc_undo_entry (undo.c:75) ==1114== by 0x1E9A4E: rl_add_undo (undo.c:92) ==1114== by 0x1EDB41: rl_delete_text (text.c:152) ==1114== by 0x1E8E4C: rl_kill_text (kill.c:177) ==1114== by 0x1E9466: rl_unix_line_discard (kill.c:412) ==1114== by 0x1CDD96: _rl_dispatch_subseq (readline.c:916) ==1114== by 0x1CE371: _rl_dispatch (readline.c:860) ==1114== by 0x1CE371: readline_internal_char (readline.c:675) ==1114== by 0x1CEC64: readline_internal_charloop (readline.c:721) ==1114== by 0x1CEC64: readline_internal (readline.c:733) ==1114== by 0x1CEC64: readline (readline.c:387) ==1114== by 0x13AFE1: yy_readline_get (parse.y:1528) ==1114== by 0x13DAE0: yy_getc (parse.y:1462) ==1114== by 0x13DAE0: shell_getc (parse.y:2393) ==1114== by 0x13FF5A: read_token.constprop.0 (parse.y:3402) ==1114== ==1114== Invalid read of size 4 ... ==1114== benutzer@debian:~$ exit ... ==1114== ERROR SUMMARY: 85 errors from 13 contexts (suppressed: 0 from 0) benutzer@debian:~$
Thank you for narrowing it down, Bernhard. I’ve further isolated the
problem to libreadline. I have attached a program based on the sample code
from the libreadline documentation which exhibits the same behaviour.
Example run:
$ make
cc rlgets.c -lreadline -o rlgets
$ make test
bash -c "echo $'foo\nbar\n\cp\cu\cp\cu\cn\cj' | ./rlgets"
prompt> foo
prompt> bar
prompt>
free(): double free detected in tcache 2
bash: line 1: 556449 Done echo 'foo
bar
'
556450 Aborted (core dumped) | ./rlgets
make: *** [Makefile:5: test] Error 134
Ben Wong
rlgets.c
#include <readline/readline.h> /* readline() */#include
<readline/history.h> /* add_history() */#include <stdlib.h>
/* free() */
/* A static variable for holding the line. */static char *line_read =
(char *)NULL;
/* Read a string, and return a pointer to it.
Returns NULL on EOF. */char *
rl_gets (char *prompt)
{
/* If the buffer has already been allocated,
return the memory to the free pool. */
if (line_read)
{
free (line_read);
line_read = (char *)NULL;
}
/* Get a line from the user. */
line_read = readline (prompt);
/* If the line has any text in it,
save it on the history. */
if (line_read && *line_read)
add_history (line_read);
return (line_read);
}
int main(int argc, char *argv[]) {
char *buf;
rl_bind_key ('p'-'a'+1, rl_history_search_backward);
rl_bind_key ('n'-'a'+1, rl_history_search_forward);
read_history("history_file");
while (1) {
buf = rl_gets("prompt> ");
if (!buf) { break; }
if ( !strncmp(buf, "exit", 4) || !strncmp(buf, "quit", 4) ) { break; }
}
write_history ("history_file");
return 0;
}
Makefile
LDLIBS+=-lreadline
rlgets: rlgets.c
test: rlgets
bash -c "echo $$'foo\nbar\n\cp\cu\cp\cu\cn\cj' | ./rlgets"
clean:
rm rlgets history_file core *~ 2>/dev/null || true
On Fri, Jun 2, 2023 at 3:01 AM Bernhard Übelacker bernhardu@mailbox.org <http://mailto:bernhardu@mailbox.org> wrote:
Hello,
Despite Bernhard's efforts, this is still not fixed. Plus there's a bunch of similar sounding reports, a few on Debian's BTS alone, even if unclear whether it's the exact same issue. This one, probably connected to readline and for what I believe ancient, persists at any rate. I'm seeing it apparently randomly about once per week (then usually my xterm just disappears, session history lost) and it's getting hard to bear. Also hard to believe something so basic and still popular as bash/readline could be so buggy for so long. Do other people/devs just never run into it? Are we others doing something wrong? I read in some places, perhaps in connection with similar bugs, that it seems to be related to "hard" bash or history usage; when you're working fast that is, and I think I can about confirm as much. It apparently only happens when I'm busy, but certainly never in the background or when I'm doing nothing. Still, bash should be able to handle this. Or else maybe not allow us to bind history search to keys to begin with, and this is a serious sugggestion. Or at least warn us against possible consequences. It has to be fixed in some way, any way. As it is however it's not good. Oliver