setxid_error() crash triggered by setgroups()

Problem description

virtiofsd may crash inside glibc's setxid_error() when a thread calls setgroups() while another thread is temporarily running under a unprivileged effective uid.

Example stack traces

Thread 1 (Thread 0x7fd559879640 (LWP 3)):
#0  __GI_abort () at ./stdlib/abort.c:107
#1  0x00007fd559aa7895 in setxid_error (error=1, cmdp=<optimized out>) at ./nptl/nptl_setxid.c:43
#2  __GI___nptl_setxid_sighandler (sig=33, si=0x7fd5594772f0, ctx=<optimized out>) at ./nptl/nptl_setxid.c:74
#3  __GI___nptl_setxid_sighandler (sig=<optimized out>, si=0x7fd5594772f0, ctx=<optimized out>) at ./nptl/nptl_setxid.c:56
#4  <signal handler called>
#5  syscall () at ../sysdeps/unix/sysv/linux/x86_64/syscall.S:38
#6  0x00005559643b208e in virtiofsd::oslib::do_open_relative_to<virtiofsd::passthrough::inode_store::InodeFile> (pathname=..., mode=<optimized out>, dir=<optimized out>, flags=<optimized out>) at src/oslib.rs:229
#7  virtiofsd::passthrough::PassthroughFs::open_relative_to<virtiofsd::passthrough::inode_store::InodeFile> (self=0x5559a27d8d90, pathname=..., mode=..., dir=<optimized out>, flags=<optimized out>) at src/passthrough/mod.rs:585
#8  virtiofsd::passthrough::PassthroughFs::do_create (self=0x5559a27d8d90, name=..., mode=<optimized out>, flags=<optimized out>, umask=<optimized out>, ctx=<optimized out>, parent_file=<optimized out>, extensions=<error reading variable: Cannot access memory at address 0x0>) at src/passthrough/mod.rs:1157
#9  virtiofsd::passthrough::{impl#7}::create (self=0x5559a27d8d90, ctx=..., parent=77037126, name=..., mode=33204, kill_priv=<optimized out>, flags=33346, umask=2, extensions=...) at src/passthrough/mod.rs:1730
#10 0x00005559643eb12f in virtiofsd::server::Server<virtiofsd::passthrough::PassthroughFs>::create<virtiofsd::passthrough::PassthroughFs> (self=<optimized out>, in_header=..., r=..., w=...) at src/server.rs:1265
#11 0x00005559643e49eb in virtiofsd::server::Server<virtiofsd::passthrough::PassthroughFs>::handle_message<virtiofsd::passthrough::PassthroughFs, vhost::vhost_user::backend_req::Backend> (self=0x5559a27d8d90, r=..., w=..., vu_req=<error reading variable: Cannot access memory at address 0x0>) at src/server.rs:151
#12 0x0000555964411d0c in virtiofsd::vhost_user::{impl#3}::process_queue_pool::{async_block#1}<virtiofsd::passthrough::PassthroughFs> () at src/vhost_user.rs:232
#13 0x00005559642e5995 in futures_task::future_obj::{impl#4}::poll<()> (cx=0x7fd559878ac0, self=...) at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-task-0.3.21/src/future_obj.rs:84
#14 futures_task::future_obj::{impl#10}::poll<()> (cx=0x7fd559878ac0, self=...) at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-task-0.3.21/src/future_obj.rs:127
#15 futures_util::future::future::FutureExt::poll_unpin<futures_task::future_obj::FutureObj<()>> (cx=0x7fd559878ac0, self=<optimized out>) at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.21/src/future/future/mod.rs:562
#16 futures_executor::thread_pool::Task::run (self=...) at src/thread_pool.rs:322
#17 futures_executor::thread_pool::PoolState::work (self=0x5559a27c54c0, idx=1, after_start=..., before_stop=...) at src/thread_pool.rs:154
#18 futures_executor::thread_pool::{impl#8}::create::{closure#0} () at src/thread_pool.rs:284
#19 std::sys::backtrace::__rust_begin_short_backtrace<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()> (f=...) at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/backtrace.rs:152
#20 0x00005559642e7304 in std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure#0}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()> () at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/thread/mod.rs:559
#21 core::panic::unwind_safe::{impl#23}::call_once<(), std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()>> (self=<error reading variable: Cannot access memory at address 0x18>) at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/panic/unwind_safe.rs:272
#22 std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()>>, ()> (data=<optimized out>) at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:587
#23 std::panicking::try<(), core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()>>> (f=...) at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:550
#24 std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()>>, ()> (f=...) at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panic.rs:358
#25 std::thread::{impl#0}::spawn_unchecked_::{closure#1}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()> () at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/thread/mod.rs:557
#26 core::ops::function::FnOnce::call_once<std::thread::{impl#0}::spawn_unchecked_::{closure_env#1}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()>, ()> () at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/ops/function.rs:250
#27 0x0000555964387f0b in alloc::boxed::{impl#28}::call_once<(), dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global> () at library/alloc/src/boxed.rs:1976
#28 alloc::boxed::{impl#28}::call_once<(), alloc::boxed::Box<dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global>, alloc::alloc::Global> () at library/alloc/src/boxed.rs:1976
#29 std::sys::pal::unix::thread::{impl#2}::new::thread_start () at library/std/src/sys/pal/unix/thread.rs:106
#30 0x00007fd559b12b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#31 0x00007fd559ba4a00 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81

Thread 5 (Thread 0x7fd55926a640 (LWP 7)):
#0  futex_wait (private=0, expected=5, futex_word=0x7fd5592691a0) at ../sysdeps/nptl/futex-internal.h:146
#1  futex_wait_simple (private=0, expected=5, futex_word=0x7fd5592691a0) at ../sysdeps/nptl/futex-internal.h:177
#2  __nptl_setxid (cmdp=cmdp@entry=0x7fd559269180) at ./nptl/nptl_setxid.c:236
#3  0x00007fd559b65782 in __GI_setgroups (groups=<optimized out>, n=<optimized out>) at ../sysdeps/unix/sysv/linux/setgroups.c:33
#4  __GI_setgroups (n=<optimized out>, groups=<optimized out>) at ../sysdeps/unix/sysv/linux/setgroups.c:28
#5  0x00005559643dc88c in virtiofsd::oslib::dropsupgroups () at src/oslib.rs:542
#6  virtiofsd::passthrough::credentials::{impl#1}::drop (self=0x7fd5592693a0) at src/passthrough/credentials.rs:109
#7  0x00005559643b2298 in core::ptr::drop_in_place<virtiofsd::passthrough::credentials::UnixCredentialsGuard> () at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/ptr/mod.rs:523
#8  core::ptr::drop_in_place<core::option::Option<virtiofsd::passthrough::credentials::UnixCredentialsGuard>> () at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/ptr/mod.rs:523
#9  virtiofsd::passthrough::PassthroughFs::do_create (self=0x5559a27d8d90, name=..., mode=<optimized out>, flags=<optimized out>, umask=<optimized out>, ctx=<optimized out>, parent_file=<optimized out>, extensions=<error reading variable: Cannot access memory at address 0x0>) at src/passthrough/mod.rs:1163
#10 virtiofsd::passthrough::{impl#7}::create (self=0x5559a27d8d90, ctx=..., parent=52, name=..., mode=33152, kill_priv=<optimized out>, flags=33345, umask=2, extensions=...) at src/passthrough/mod.rs:1730
#11 0x00005559643eb12f in virtiofsd::server::Server<virtiofsd::passthrough::PassthroughFs>::create<virtiofsd::passthrough::PassthroughFs> (self=<optimized out>, in_header=..., r=..., w=...) at src/server.rs:1265
#12 0x00005559643e49eb in virtiofsd::server::Server<virtiofsd::passthrough::PassthroughFs>::handle_message<virtiofsd::passthrough::PassthroughFs, vhost::vhost_user::backend_req::Backend> (self=0x5559a27d8d90, r=..., w=..., vu_req=<error reading variable: Cannot access memory at address 0x0>) at src/server.rs:151
#13 0x0000555964411d0c in virtiofsd::vhost_user::{impl#3}::process_queue_pool::{async_block#1}<virtiofsd::passthrough::PassthroughFs> () at src/vhost_user.rs:232
#14 0x00005559642e5995 in futures_task::future_obj::{impl#4}::poll<()> (cx=0x7fd559269ac0, self=...) at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-task-0.3.21/src/future_obj.rs:84
#15 futures_task::future_obj::{impl#10}::poll<()> (cx=0x7fd559269ac0, self=...) at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-task-0.3.21/src/future_obj.rs:127
#16 futures_util::future::future::FutureExt::poll_unpin<futures_task::future_obj::FutureObj<()>> (cx=0x7fd559269ac0, self=<optimized out>) at /root/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/futures-util-0.3.21/src/future/future/mod.rs:562
#17 futures_executor::thread_pool::Task::run (self=...) at src/thread_pool.rs:322
#18 futures_executor::thread_pool::PoolState::work (self=0x5559a27c54c0, idx=5, after_start=..., before_stop=...) at src/thread_pool.rs:154
#19 futures_executor::thread_pool::{impl#8}::create::{closure#0} () at src/thread_pool.rs:284
#20 std::sys::backtrace::__rust_begin_short_backtrace<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()> (f=...) at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/sys/backtrace.rs:152
#21 0x00005559642e7304 in std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure#0}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()> () at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/thread/mod.rs:559
#22 core::panic::unwind_safe::{impl#23}::call_once<(), std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()>> (self=<error reading variable: Cannot access memory at address 0x18>) at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/panic/unwind_safe.rs:272
#23 std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()>>, ()> (data=<optimized out>) at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:587
#24 std::panicking::try<(), core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()>>> (f=...) at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panicking.rs:550
#25 std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()>>, ()> (f=...) at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/panic.rs:358
#26 std::thread::{impl#0}::spawn_unchecked_::{closure#1}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()> () at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/std/src/thread/mod.rs:557
#27 core::ops::function::FnOnce::call_once<std::thread::{impl#0}::spawn_unchecked_::{closure_env#1}<futures_executor::thread_pool::{impl#8}::create::{closure_env#0}, ()>, ()> () at /rustc/05f9846f893b09a1be1fc8560e33fc3c815cfecb/library/core/src/ops/function.rs:250
#28 0x0000555964387f0b in alloc::boxed::{impl#28}::call_once<(), dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global> () at library/alloc/src/boxed.rs:1976
#29 alloc::boxed::{impl#28}::call_once<(), alloc::boxed::Box<dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global>, alloc::alloc::Global> () at library/alloc/src/boxed.rs:1976
#30 std::sys::pal::unix::thread::{impl#2}::new::thread_start () at library/std/src/sys/pal/unix/thread.rs:106
#31 0x00007fd559b12b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#32 0x00007fd559ba4a00 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb) thread 1
[Switching to thread 1 (Thread 0x7fd559879640 (LWP 3))]
#0  __GI_abort () at ./stdlib/abort.c:107
107	./stdlib/abort.c: No such file or directory.
(gdb) up
#1  0x00007fd559aa7895 in setxid_error (error=1, cmdp=<optimized out>) at ./nptl/nptl_setxid.c:43
43	./nptl/nptl_setxid.c: No such file or directory.
(gdb) p xid_err
$7 = 1
(gdb) p *xidcmd
$8 = {syscall_no = 116, id = {0, 0, 140554309883778}, cntr = 1, error = 0}
(gdb)

Reproducer

This can be reproduced by creating files on a directory owned by a supplementary gid while creating directories in a loop from multiple processes.

ubuntu@hostname:~/testfs$ ls -l
total 1
drwxrwxr-x 3 root   admin  1 May 28 18:22 admin-dir
drwxrwxr-x 3 ubuntu ubuntu 1 May 28 18:22 regular-dir
ubuntu@hostname:~/testfs$ id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),100(users),118(admin)
ubuntu@hostname:~/testfs$ while :;do f=regular-dir/$RANDOM;mkdir $f;rmdir $f;done &
[1] 3391
ubuntu@hostname:~/testfs$ while :;do f=regular-dir/$RANDOM;mkdir $f;rmdir $f;done &
[2] 3554
ubuntu@hostname:~/testfs$ while :;do f=regular-dir/$RANDOM;mkdir $f;rmdir $f;done &
[3] 3785
ubuntu@hostname:~/testfs$ while :;do f=regular-dir/$RANDOM;mkdir $f;rmdir $f;done &
[4] 3910
ubuntu@hostname:~/testfs$ while :;do f=regular-dir/$RANDOM;mkdir $f;rmdir $f;done &
[5] 3912
ubuntu@hostname:~/testfs$ while :;do f=regular-dir/$RANDOM;mkdir $f;rmdir $f;done &
[6] 3914
ubuntu@hostname:~/testfs$ ls -l *
admin-dir:
total 0
drwxrwxr-x 2 ubuntu ubuntu 0 May 28 18:22 22219

regular-dir:
total 0
drwxrwxr-x 2 ubuntu ubuntu 0 May 28 18:49 11969
drwxrwxr-x 2 ubuntu ubuntu 0 May 28 18:22 23083
drwxrwxr-x 2 ubuntu ubuntu 0 May 28 18:49 25557
drwxrwxr-x 2 ubuntu ubuntu 0 May 28 18:49 5590
ubuntu@hostname:~/testfs$
ubuntu@hostname:~/testfs$ while :;do f=admin-dir/$RANDOM;mkdir $f;rmdir $f;done &
[7] 10452
ubuntu@hostname:~/testfs$ ls -l *
admin-dir:

[virtiofsd crashes here, and guest filesystem operations freeze]

Software versions

Reproduced using:

  • virtiofsd 1.13.1
  • glibc 2.35-0ubuntu3.1
  • Guest kernel 6.8.0-52-generic (Ubuntu Jammy)