When the monitoring directory exceeds 128, dbus-daemon crashes after receiving the SIGHUP signal.
commit b551b3e9 has protected the scenario where XDG_DATA_DIRS
is configured with more than 128 directories; however, there is a problem with the access method of new_dir
in _set_watched_dirs_internal
. When i
exceed 128, the loop does not stop, so the function continue to try to access the out-of-bounds new_dirs[i]; This will cause the value of the global variable num_wds
to be abnormal (exceeding 128); if the process enters the _set_watched_dirs_internal
function again (for example, receiving the SIGHUP signal and executing the reload process), the abnormal num_wds
will be used as the upper limit of the loop, triggering a SIGSEGV exception. We have reproduced this problem on the operating system of EulerOS version 2.10 (based on Linux 4.19 kernel).
The following is a reproducible demonstration that we simulated with gdb
/* set XDG_DATA_DIRS=$(seq -f "/foo/%g" -s ':' 129) */
(gdb) set environment XDG_DATA_DIRS=/foo/1:/foo/2:/foo/3:/foo/4:/foo/5:/foo/6:/foo/7:/foo/8:/foo/9:/foo/10:/foo/11:/foo/12:/foo/13:/foo/14:/foo/15:/foo/16:/foo/17:/foo/18:/foo/19:/foo/20:/foo/21:/foo/22:/foo/23:/foo/24:/foo/25:/foo/26:/foo/27:/foo/28:/foo/29:/foo/30:/foo/31:/foo/32:/foo/33:/foo/34:/foo/35:/foo/36:/foo/37:/foo/38:/foo/39:/foo/40:/foo/41:/foo/42:/foo/43:/foo/44:/foo/45:/foo/46:/foo/47:/foo/48:/foo/49:/foo/50:/foo/51:/foo/52:/foo/53:/foo/54:/foo/55:/foo/56:/foo/57:/foo/58:/foo/59:/foo/60:/foo/61:/foo/62:/foo/63:/foo/64:/foo/65:/foo/66:/foo/67:/foo/68:/foo/69:/foo/70:/foo/71:/foo/72:/foo/73:/foo/74:/foo/75:/foo/76:/foo/77:/foo/78:/foo/79:/foo/80:/foo/81:/foo/82:/foo/83:/foo/84:/foo/85:/foo/86:/foo/87:/foo/88:/foo/89:/foo/90:/foo/91:/foo/92:/foo/93:/foo/94:/foo/95:/foo/96:/foo/97:/foo/98:/foo/99:/foo/100:/foo/101:/foo/102:/foo/103:/foo/104:/foo/105:/foo/106:/foo/107:/foo/108:/foo/109:/foo/110:/foo/111:/foo/112:/foo/113:/foo/114:/foo/115:/foo/116:/foo/117:/foo/118:/foo/119:/foo/120:/foo/121:/foo/122:/foo/123:/foo/124:/foo/125:/foo/126:/foo/127:/foo/128:/foo/129
(gdb) set args --session
(gdb) r
Starting program: /usr/bin/dbus-daemon --session
warning: File "/usr/lib64/libthread_db-1.0.so" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
add-auto-load-safe-path /usr/lib64/libthread_db-1.0.so
line to your configuration file "/root/.gdbinit".
To completely disable this security protection add
set auto-load safe-path /
line to your configuration file "/root/.gdbinit".
For more information about this security protection see the
"Auto-loading safe path" section in the GDB manual. E.g., run from the shell:
info "(gdb)Auto-loading safe path"
warning: Unable to find libthread_db matching inferior's thread library, thread debugging will not be available.
[New LWP 20725]
dbus-daemon[20721]: Too many directories to watch them all, only watching first 128.
/* send SIGHUP signal to dbus-daemon process */
Thread 1 "dbus-daemon" received signal SIGHUP, Hangup.
0x0000ffff90db5da4 in epoll_pwait () from /usr/lib64/libc.so.6
/* check the value of num_wds, already exceeded 128 */
(gdb) p num_wds
$1 = 162
(gdb) c
Continuing.
dbus-daemon[20721]: Too many directories to watch them all, only watching first 128.
/* Enter the reload process ,and then trigger SIGSEGV */
Thread 1 "dbus-daemon" received signal SIGSEGV, Segmentation fault.
0x0000ffff90d5dd54 in strcmp () from /usr/lib64/libc.so.6
(gdb) bt
#0 0x0000ffff90d5dd54 in strcmp () from /usr/lib64/libc.so.6
#1 0x0000aaaab58b756c in _set_watched_dirs_internal (directories=directories@entry=0xffffea08ce90) at dir-watch-inotify.c:129
#2 0x0000aaaab58b793c in bus_set_watched_dirs (context=context@entry=0xaaaabb563ce0, directories=directories@entry=0xffffea08ce90) at dir-watch-inotify.c:293
#3 0x0000aaaab58add60 in process_config_postinit (context=0xaaaabb563ce0, parser=0xaaaabb56ce20, error=0xffffea08cf48) at bus.c:737
#4 0x0000aaaab58aec28 in bus_context_reload_config (context=0xaaaabb563ce0, error=0xffffea08cf48) at bus.c:1056
#5 0x0000aaaab58c385c in handle_reload_watch (watch=0xaaaabb585b30, flags=<optimized out>, data=<optimized out>) at main.c:300
#6 0x0000aaaab58c4880 in _dbus_loop_iterate (loop=loop@entry=0xaaaabb563e10, block=block@entry=1) at dbus-mainloop.c:824
#7 0x0000aaaab58c4cf4 in _dbus_loop_run (loop=0xaaaabb563e10) at dbus-mainloop.c:888
#8 0x0000aaaab58aa128 in main (argc=<optimized out>, argv=<optimized out>) at main.c:722
(gdb) f 1
#1 0x0000aaaab58b756c in _set_watched_dirs_internal (directories=directories@entry=0xffffea08ce90) at dir-watch-inotify.c:129
129 if (dirs[j] && strcmp (new_dirs[i], dirs[j]) == 0)
/* j should not exceed 128 */
(gdb) p j
$3 = 128