Skip to content

shell: Do not allocate memory or call async-signal-unsafe code during GSpawnChildSetupFunc

Marco Trevisan requested to merge 3v1n0/gnome-shell:safer-child-spawn into main

In various places in the shell we were using GSpawnChildSetupFunc implementations that are not async-signal-safe as it should be, and even a single malloc can be dangerous during fork/exec, and this was causing the shell to hang on startup during ibus-daemon launch.

As per this, replace all the code that may allocate data, however since we were doing this quite a lot in javascript callbacks, making it even more unsafe (since we can't exactly control what will happen), add Shell wrappers that are only in C, ad do not expose any GSpawnChildSetupFunc by only calling the simple mutter code that we require.

Note that this is something I am basically experiencing always when running the shell in my jhbuild setup and this is the obvious stacktrace:

__futex_abstimed_wait_common (cancel=false, private=<optimized out>, abstime=0x0, clockid=0, 
    expected=3, futex_word=0x56046445c028) at ./nptl/futex-internal.c:103
103	./nptl/futex-internal.c: No such file or directory.
(gdb) info threads 
  Id   Target Id                                        Frame 
* 1    Thread 0x7f03263ccdc0 (LWP 339734) "gnome-shell" __futex_abstimed_wait_common (
    cancel=false, private=<optimized out>, abstime=0x0, clockid=0, expected=3, 
    futex_word=0x56046445c028) at ./nptl/futex-internal.c:103
(gdb) bt
#0  __futex_abstimed_wait_common (cancel=false, private=<optimized out>, abstime=0x0, 
    clockid=0, expected=3, futex_word=0x56046445c028) at ./nptl/futex-internal.c:103
#1  __GI___futex_abstimed_wait64 (futex_word=futex_word@entry=0x56046445c028, 
    expected=expected@entry=3, clockid=clockid@entry=0, abstime=abstime@entry=0x0, 
    private=<optimized out>) at ./nptl/futex-internal.c:128
#2  0x00007f032a49d6a6 in __pthread_rwlock_rdlock_full64 (abstime=0x0, clockid=0, 
    rwlock=0x56046445c020) at ./nptl/pthread_rwlock_common.c:460
#3  ___pthread_rwlock_rdlock (rwlock=rwlock@entry=0x56046445c020)
    at ./nptl/pthread_rwlock_rdlock.c:26
#4  0x00007f032b17fd2b in g_rw_lock_reader_lock (
    rw_lock=rw_lock@entry=0x7f032b7ffff0 <type_rw_lock>) at ../../glib/glib/gthread-posix.c:478
#5  0x00007f032b7d6ba9 in g_type_get_qdata (type=<optimized out>, quark=1157)
    at ../../glib/gobject/gtype.c:3786
#6  0x00007f032aeb11fe in ObjectBase::is_custom_js_class (this=0x560464a3b0b0)
    at ../../gjs/gi/object.cpp:114
#7  0x00007f032aeb4bf1 in ObjectPrototype::resolve_no_info (this=0x560464a3b0b0, 
    cx=0x5604644921d0, obj=..., id=..., resolved=0x7fff836642ef, 
    name=0x560466315db0 "restore_rlimit_nofile", 
    resolve_props=ObjectPrototype::ConsiderMethodsAndProperties) at ../../gjs/gi/object.cpp:808
#8  0x00007f032aeb5570 in ObjectPrototype::uncached_resolve (this=0x560464a3b0b0, 
    context=0x5604644921d0, obj=..., id=..., name=0x560466315db0 "restore_rlimit_nofile", 
    resolved=0x7fff836642ef) at ../../gjs/gi/object.cpp:947
#9  0x00007f032aeb5442 in ObjectPrototype::resolve_impl (this=0x560464a3b0b0, 
    context=0x5604644921d0, obj=..., id=..., resolved=0x7fff836642ef)
    at ../../gjs/gi/object.cpp:928
#10 0x00007f032aec6181 in GIWrapperBase<ObjectBase, ObjectPrototype, ObjectInstance>::resolve (
    cx=0x5604644921d0, obj=..., id=..., resolved=0x7fff836642ef)
    at ../../gjs/gi/wrapperutils.h:388
#11 0x00007f032808248d in ?? () from /lib/x86_64-linux-gnu/libmozjs-115.so.0
#12 0x00007f0327f6928b in ?? () from /lib/x86_64-linux-gnu/libmozjs-115.so.0
#13 0x00007f0327f5c9eb in ?? () from /lib/x86_64-linux-gnu/libmozjs-115.so.0
#14 0x00007f0327f63437 in ?? () from /lib/x86_64-linux-gnu/libmozjs-115.so.0
#15 0x00007f0327f63847 in ?? () from /lib/x86_64-linux-gnu/libmozjs-115.so.0
#16 0x00007f0327f63db9 in ?? () from /lib/x86_64-linux-gnu/libmozjs-115.so.0
#17 0x00007f0327fd8219 in JS_CallFunctionValue(JSContext*, JS::Handle<JSObject*>, JS::Handle<JS::Value>, JS::HandleValueArray const&, JS::MutableHandle<JS::Value>) ()
   from /lib/x86_64-linux-gnu/libmozjs-115.so.0
#18 0x00007f032ae8eddf in JS::Call (cx=0x5604644921d0, thisObj=..., fun=..., args=..., 
    rval=...) at /usr/include/mozjs-115/js/CallAndConstruct.h:92
#19 0x00007f032ae8f39b in Gjs::Closure::invoke (this=0x56046631ac60, this_obj=..., args=..., 
    retval=...) at ../../gjs/gi/closure.cpp:184
#20 0x00007f032ae98a8b in GjsCallbackTrampoline::callback_closure_inner (this=0x56046631ac60, 
    context=0x5604644921d0, this_object=..., gobject=0x0, rval=..., args=0x7fff83665050, 
    ret_type=0x7fff83664f50, n_args=1, c_args_offset=0, result=0x7fff836651e0)
    at ../../gjs/gi/function.cpp:515
#21 0x00007f032ae980f0 in GjsCallbackTrampoline::callback_closure (this=0x56046631ac60, 
    args=0x7fff83665050, result=0x7fff836651e0) at ../../gjs/gi/function.cpp:381
#22 0x00007f032ae9985a in operator() (__closure=0x0, result=0x7fff836651e0, 
    ffi_args=0x7fff83665050, data=0x56046631ac60) at ../../gjs/gi/function.cpp:741
--Type <RET> for more, q to quit, c to continue without paging--
#23 0x00007f032ae998b9 in _FUN () at ../../gjs/gi/function.cpp:742
#24 0x00007f032b080492 in ?? () from /lib/x86_64-linux-gnu/libffi.so.8
#25 0x00007f032b080ad8 in ?? () from /lib/x86_64-linux-gnu/libffi.so.8
#26 0x00007f032b17c106 in do_exec (child_err_report_fd=44, stdin_fd=<optimized out>, 
    stdin_fd@entry=-1, stdout_fd=<optimized out>, stdout_fd@entry=-1, 
    stderr_fd=<optimized out>, stderr_fd@entry=-1, source_fds=source_fds@entry=0x0, 
    target_fds=target_fds@entry=0x0, n_fds=0, working_directory=0x0, argv=0x560466315d20, 
    argv_buffer=0x7fff83665350, argv_buffer_len=5, envp=0x5604663170e0, close_descriptors=1, 
    search_path=0x7fff8366aee0 "/opt/dev/GNOME/bin:/home/marco/Dev/Script:/home/marco/Software/Script:/home/marco/Software/bin:/home/marco/.local/bin:/home/marco/.zinit/plugins/jesseduffield---lazygit:/opt/dev/sbin:/opt/dev/bin:/hom"..., 
    search_path_buffer=0x7fff83665380 "\300Sf\203\377\177", search_path_buffer_len=338, 
    stdout_to_null=0, stderr_to_null=0, child_inherits_stdin=0, file_and_argv_zero=0, 
    child_setup=0x7f031b8036a0, user_data=0x56046631ac60) at ../../glib/glib/gspawn.c:1576
#27 0x00007f032b17ddde in fork_exec (intermediate_child=intermediate_child@entry=0, 
    working_directory=<optimized out>, argv=0x560466315d20, envp=<optimized out>, 
    close_descriptors=close_descriptors@entry=1, search_path=search_path@entry=1, 
    search_path_from_envp=<optimized out>, stdout_to_null=<optimized out>, 
    stderr_to_null=<optimized out>, child_inherits_stdin=<optimized out>, 
    file_and_argv_zero=<optimized out>, cloexec_pipes=<optimized out>, 
    child_setup=<optimized out>, user_data=<optimized out>, child_pid=<optimized out>, 
    stdin_pipe_out=<optimized out>, stdout_pipe_out=<optimized out>, 
    stderr_pipe_out=<optimized out>, stdin_fd=<optimized out>, stdout_fd=<optimized out>, 
    stderr_fd=<optimized out>, source_fds=<optimized out>, target_fds=<optimized out>, 
    n_fds=<optimized out>, error=<optimized out>) at ../../glib/glib/gspawn.c:2220
#28 0x00007f032b17e71b in g_spawn_async_with_pipes_and_fds (working_directory=<optimized out>, 
    argv=<optimized out>, envp=<optimized out>, 
    flags=(G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH), child_setup=<optimized out>, 
    user_data=<optimized out>, stdin_fd=-1, stdout_fd=-1, stderr_fd=-1, source_fds=0x0, 
    target_fds=0x0, n_fds=0, child_pid_out=0x560466316d08, stdin_pipe_out=0x0, 
    stdout_pipe_out=0x0, stderr_pipe_out=0x0, error=0x7fff83665de0)
    at ../../glib/glib/gspawn.c:819
#29 0x00007f032b17e88d in g_spawn_async_with_pipes (working_directory=<optimized out>, 
    argv=<optimized out>, envp=<optimized out>, flags=<optimized out>, 
    child_setup=<optimized out>, user_data=<optimized out>, child_pid=0x560466316d08, 
    standard_input=0x0, standard_output=0x0, standard_error=0x0, error=0x7fff83665de0)
    at ../../glib/glib/gspawn.c:542
#30 0x00007f032b17e8bb in g_spawn_async (working_directory=<optimized out>, 
    argv=<optimized out>, envp=<optimized out>, flags=<optimized out>, 
    child_setup=<optimized out>, user_data=<optimized out>, child_pid=0x560466316d08, 
    error=0x7fff83665de0) at ../../glib/glib/gspawn.c:195
#31 0x00007f032b0808b6 in ?? () from /lib/x86_64-linux-gnu/libffi.so.8
#32 0x00007f032b07d34d in ?? () from /lib/x86_64-linux-gnu/libffi.so.8
#33 0x00007f032b07ff33 in ffi_call () from /lib/x86_64-linux-gnu/libffi.so.8
#34 0x00007f032ae9ad07 in Gjs::Function::invoke (this=0x560466315c50, context=0x5604644921d0, 
    args=..., this_obj=..., r_value=0x0) at ../../gjs/gi/function.cpp:1054
#35 0x00007f032ae9b836 in Gjs::Function::call (context=0x5604644921d0, js_argc=5, 
    vp=0x56046455f488) at ../../gjs/gi/function.cpp:1236
#36 0x00007f0327f63970 in ?? () from /lib/x86_64-linux-gnu/libmozjs-115.so.0
== Stack trace for context 0x560464492120 ==
#0   56046455f4c0 i   resource:///org/gnome/shell/misc/ibusManager.js:123 (3912bae58e20 @ 12) -> global.context.restore_rlimit_nofile()
#1   56046455f3f0 i   resource:///org/gnome/shell/misc/ibusManager.js:118 (3912bae58dd0 @ 287)
#2   56046455f350 i   resource:///org/gnome/shell/misc/ibusManager.js:102 (3912bae58d30 @ 162)
#3   56046455f2b8 i   self-hosted:1461 (1129836a58d0 @ 30)
#4   7fff83666630 b   self-hosted:852 (1129836a5d30 @ 15)
#5   56046455f228 i   resource:///org/gnome/shell/ui/init.js:21 (5239f270bf0 @ 48)

Fixes: #6698

Merge request reports