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>
...


You are receiving this mail because: