[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bash signal during wait + read causes unkillable 100% CPU usage
From: |
hackerb9 |
Subject: |
bash signal during wait + read causes unkillable 100% CPU usage |
Date: |
Sun, 16 Sep 2018 20:47:09 -0700 (PDT) |
Configuration Information [Automatically generated, do not change]:
Machine: i686
OS: linux-gnu
Compiler: gcc
Compilation CFLAGS: -DPROGRAM='bash' -DCONF_HOSTTYPE='i686'
-DCONF_OSTYPE='linux-gnu' -DCONF_MACHTYPE='i686-pc-linux-gnu'
-DCONF_VENDOR='pc' -DLOCALEDIR='/usr/share/locale' -DPACKAGE='bash' -DSHELL
-DHAVE_CONFIG_H -I. -I../. -I.././include -I.././lib -Wdate-time
-D_FORTIFY_SOURCE=2 -g -O2 -fdebug-prefix-map=/build/bash-u3hlEB/bash-4.4.18=.
-fstack-protector-strong -Wformat -Werror=format-security -Wall
-Wno-parentheses -Wno-format-security
uname output: Linux thomas 4.18.0-7-generic #8-Ubuntu SMP Tue Aug 28 18:22:50
UTC 2018 i686 i686 i686 GNU/Linux
Machine Type: i686-pc-linux-gnu
Bash Version: 4.4
Patch Level: 19
Release Status: release
Description:
I was trying to work around the problem of bash not being able to
cancel the read builtin command when a signal comes in and I stumbled
upon this (perhaps unrelated) bug. I'm not sure exactly what's going
on but I have a simple script that demonstrates the problem.
Repeat-By:
Run the following script. Quickly hit a key, then wait to be prompted
to hit Control C. When you hit Control C, the process should stop
responding and your CPU usage should go to 100%. The HUP, TERM and
QUIT signals are ignored.
----- 8< ----- 8< ----- 8< ----- CUT HERE ----- 8< ----- 8< ----- 8< -----
#!/bin/bash
trap cleanup EXIT
cleanup() {
echo "Huh. There was supposed to be an earth-shattering kaboom."
echo "The bug wasn't triggered. Did you hit the first key quickly?"
echo "Or, perhaps Bash $BASH_VERSION isn't buggy?"
echo "This test was designed for Bash 4.4.19(1) on Ubuntu Cosmic
Cuttlefish."
}
trap huphandler SIGHUP
huphandler() {
# This handler doesn't actually do anything.
# The important thing is that HUP interrupts the wait.
return
}
# Send a SIGHUP to ourselves to interrupt the wait and start "read".
(sleep 2; kill -1 $$; sleep 2; echo -e "\nB: Now try ^C and see what breaks.")
&
echo "A: Quickly press a key before part B! "
while true; do
wait
read -p "Kaboom? " -n1
echo
done
----- 8< ----- 8< ----- 8< ----- CUT HERE ----- 8< ----- 8< ----- 8< -----
I tried attaching to the process with GDB. The backtrace showed it
being in jobs.c:wait_sigint_handler() or zread.c:zread(). This may be
a red herring, but there is a comment in zread.c with an XXX and a
question that may be worth investigating:
60 check_signals_and_traps (); /* XXX - should it be
check_signals()? */
Here is a more extensive log of what gdb said:
$ gdb -p 27088
Attaching to process 27088
Reading symbols from /src/bash-4.4.18/bash...done.
Reading symbols from /lib/i386-linux-gnu/libtinfo.so.6...(no debugging symbols
found)...done.
Reading symbols from /lib/i386-linux-gnu/libdl.so.2...Reading symbols from
/usr/lib/debug//lib/i386-linux-gnu/libdl-2.28.so...done.
done.
Reading symbols from /lib/i386-linux-gnu/libc.so.6...Reading symbols from
/usr/lib/debug//lib/i386-linux-gnu/libc-2.28.so...done.
done.
Reading symbols from /lib/ld-linux.so.2...Reading symbols from
/usr/lib/debug//lib/i386-linux-gnu/ld-2.28.so...done.
done.
wait_sigint_handler (sig=2) at jobs.c:2476
2476 if (interrupt_immediately ||
(gdb) list
2471 wait_sigint_handler (sig)
2472 int sig;
2473 {
2474 SigHandler *sigint_handler;
2475
2476 if (interrupt_immediately ||
2477 (this_shell_builtin && this_shell_builtin == wait_builtin))
2478 {
2479 last_command_exit_value = 128+SIGINT;
2480 restore_sigint_handler ();
2481 /* If we got a SIGINT while in `wait', and SIGINT is trapped, do
2482 what POSIX.2 says (see builtins/wait.def for more info). */
2483 if (this_shell_builtin && this_shell_builtin == wait_builtin &&
2484 signal_is_trapped (SIGINT) &&
2485 ((sigint_handler = trap_to_sighandler (SIGINT)) ==
trap_handler))
2486 {
2487 trap_handler (SIGINT); /* set pending_traps[SIGINT] */
2488 wait_signal_received = SIGINT;
2489 if (interrupt_immediately && wait_intr_flag)
2490 {
(gdb) bt
#0 wait_sigint_handler (sig=2) at jobs.c:2476
#1 <signal handler called>
#2 0xb7f09d41 in __kernel_vsyscall ()
#3 0xb7dbc5a7 in __GI___libc_read (fd=0, buf=0xbf956a56, nbytes=1)
at ../sysdeps/unix/sysv/linux/read.c:26
#4 0x0052dce1 in read (__nbytes=1, __buf=0xbf956a56, __fd=0)
at /usr/include/i386-linux-gnu/bits/unistd.h:44
#5 zread (fd=0, buf=0xbf956a56 "V", len=1) at zread.c:56
#6 0x00519c23 in read_builtin (list=0x0) at ./read.def:585
#7 0x004bacc0 in execute_builtin (
builtin=builtin@entry=0x519360 <read_builtin>, flags=flags@entry=0,
subshell=0, words=<optimized out>) at execute_cmd.c:4535
#8 0x004bd75a in execute_builtin_or_function (flags=<optimized out>,
fds_to_close=<optimized out>, redirects=<optimized out>, var=0x0,
builtin=0x519360 <read_builtin>, words=0x13367c8) at execute_cmd.c:5028
#9 execute_simple_command (simple_command=<optimized out>,
pipe_in=<optimized out>, pipe_in@entry=-1, pipe_out=<optimized out>,
pipe_out@entry=-1, async=0, fds_to_close=0x1335f68) at execute_cmd.c:4330
#10 0x004bf0e6 in execute_command_internal (command=0x1333be8,
asynchronous=<optimized out>, pipe_in=<optimized out>,
pipe_out=<optimized out>, fds_to_close=<optimized out>)
at execute_cmd.c:807
#11 0x004bfb6f in execute_connection (fds_to_close=0x1335f68,
pipe_out=<optimized out>, pipe_in=<optimized out>,
asynchronous=<optimized out>, command=<optimized out>) at execute_cmd.c:2600
#12 execute_command_internal (command=<optimized out>, asynchronous=<optimized
out>, pipe_in=<optimized out>, pipe_out=<optimized out>,
fds_to_close=<optimized out>) at execute_cmd.c:976
#13 0x004c0ba4 in execute_command (command=0x13325a8) at execute_cmd.c:405
#14 0x004bfb28 in execute_connection (fds_to_close=0x1333de8,
pipe_out=<optimized out>, pipe_in=<optimized out>, asynchronous=<optimized
out>, command=<optimized out>) at execute_cmd.c:2598
#15 execute_command_internal (command=<optimized out>, asynchronous=<optimized
out>, pipe_in=<optimized out>, pipe_out=<optimized out>,
fds_to_close=0x1333de8) at execute_cmd.c:976
#16 0x004c0ba4 in execute_command (command=0x1332aa8) at execute_cmd.c:405
#17 0x004c0ccb in execute_while_or_until (while_command=0x1332a28,
type=type@entry=0) at execute_cmd.c:3515
#18 0x004be819 in execute_while_command (while_command=<optimized out>) at
execute_cmd.c:3456
#19 execute_command_internal (command=0x13326e8, asynchronous=<optimized out>,
pipe_in=<optimized out>, pipe_out=<optimized out>, fds_to_close=0x1332748) at
execute_cmd.c:916
#20 0x004c0ba4 in execute_command (command=0x13326e8) at execute_cmd.c:405
#21 0x004a617e in reader_loop () at eval.c:180
#22 0x004a42b5 in main (argc=<optimized out>, argv=<optimized out>,
env=<optimized out>) at shell.c:792
(gdb) c
Continuing.
Program received signal SIGINT, Interrupt.
0xb7f09d41 in __kernel_vsyscall ()
(gdb) up
#1 0xb7dbc5a7 in __GI___libc_read (fd=0, buf=0xbf956a56, nbytes=1)
at ../sysdeps/unix/sysv/linux/read.c:26
26 ../sysdeps/unix/sysv/linux/read.c: No such file or directory.
(gdb) up
#2 0x0052dce1 in read (__nbytes=1, __buf=0xbf956a56, __fd=0)
at /usr/include/i386-linux-gnu/bits/unistd.h:44
44 return __read_alias (__fd, __buf, __nbytes);
(gdb) up
#3 zread (fd=0, buf=0xbf956a56 "V", len=1) at zread.c:56
56 while ((r = read (fd, buf, len)) < 0 && errno == EINTR)
(gdb) list
51 char *buf;
52 size_t len;
53 {
54 ssize_t r;
55
56 while ((r = read (fd, buf, len)) < 0 && errno == EINTR)
57 /* XXX - bash-5.0 */
58 /* We check executing_builtin and run traps here for backwards
compatibility */
59 if (executing_builtin)
60 check_signals_and_traps (); /* XXX - should it be
check_signals()? */
61 else
62 check_signals ();
63
64 return r;
65 }
- bash signal during wait + read causes unkillable 100% CPU usage,
hackerb9 <=