Bug ID | 1206926 |
---|---|
Summary | process exit status not set by leader thread, when all threads exit using SYS_exit |
Classification | openSUSE |
Product | openSUSE Tumbleweed |
Version | Current |
Hardware | Other |
OS | Other |
Status | NEW |
Severity | Normal |
Priority | P5 - None |
Component | Kernel |
Assignee | kernel-bugs@opensuse.org |
Reporter | tdevries@suse.com |
QA Contact | qa-bugs@suse.de |
Found By | --- |
Blocker | --- |
[ Corresponding upstream gdb PR: https://sourceware.org/bugzilla/show_bug.cgi?id=29965 ] Consider the following gdb testsuite test-case: ... $ cat src/gdb/testsuite/gdb.threads/process-exit-status-is-leader-exit-status.c /* This testcase is part of GDB, the GNU debugger. Copyright 2022-2023 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <pthread.h> #include <unistd.h> #include <sys/syscall.h> #define NUM_THREADS 32 pthread_barrier_t barrier; static void do_exit (int exitcode) { /* Synchronize all threads up to here so that they all exit at roughly the same time. */ pthread_barrier_wait (&barrier); /* All threads exit with SYS_exit, even the main thread, to avoid exiting with a group-exit syscall, as that syscall changes the exit status of all still-alive threads, thus potentially masking a bug. */ syscall (SYS_exit, exitcode); } static void * start (void *arg) { int thread_return_value = *(int *) arg; do_exit (thread_return_value); } int main(void) { pthread_t threads[NUM_THREADS]; int thread_return_val[NUM_THREADS]; int i; pthread_barrier_init (&barrier, NULL, NUM_THREADS + 1); for (i = 0; i < NUM_THREADS; ++i) { thread_return_val[i] = i + 2; pthread_create (&threads[i], NULL, start, &thread_return_val[i]); } do_exit (1); } ... Compile like this: ... $ gcc -lpthread src/gdb/testsuite/gdb.threads/process-exit-status-is-leader-exit-status.c ... On x86_64 openSUSE Tumbleweed 20230102 with kernel 6.1.1-1-default, we have: ... $ ./a.out; echo $? 32 ... Expected value (which I get on say openSUSE Leap 15.4 with kernel 5.14.21) is 1. Possible kernel commit causing this change in behaviour: ... commit d80f7d7b2c75c5954d335dffbccca62a5002c3e0 Author: Eric W. Biederman <ebiederm@xmission.com> Date: Tue Jun 21 14:38:52 2022 -0500 signal: Guarantee that SIGNAL_GROUP_EXIT is set on process exit Track how many threads have not started exiting and when the last thread starts exiting set SIGNAL_GROUP_EXIT. This guarantees that SIGNAL_GROUP_EXIT will get set when a process exits. In practice this achieves nothing as glibc's implementation of _exit calls sys_group_exit then sys_exit. While glibc's implemenation of pthread_exit calls exit (which cleansup and calls _exit) if it is the last thread and sys_exit if it is the last thread. This means the only way the kernel might observe a process that does not set call exit_group is if the language runtime does not use glibc. With more cleanups I hope to move the decrement of quick_threads earlier. Link: https://lkml.kernel.org/r/87bkukd4tc.fsf_-_@email.froward.int.ebiederm.org Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> ...