(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
tst-rseq-nptl.c
       1  /* Restartable Sequences NPTL test.
       2     Copyright (C) 2021-2023 Free Software Foundation, Inc.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  /* These tests validate that rseq is registered from various execution
      19     contexts (main thread, destructor, other threads, other threads created
      20     from destructor, forked process (without exec), pthread_atfork handlers,
      21     pthread setspecific destructors, signal handlers, atexit handlers).
      22  
      23     See the Linux kernel selftests for extensive rseq stress-tests.  */
      24  
      25  #include <stdio.h>
      26  #include <support/check.h>
      27  #include <support/xthread.h>
      28  #include <sys/rseq.h>
      29  #include <unistd.h>
      30  
      31  #ifdef RSEQ_SIG
      32  # include <array_length.h>
      33  # include <errno.h>
      34  # include <error.h>
      35  # include <pthread.h>
      36  # include <signal.h>
      37  # include <stdlib.h>
      38  # include <string.h>
      39  # include <support/namespace.h>
      40  # include <support/xsignal.h>
      41  # include <syscall.h>
      42  # include <sys/types.h>
      43  # include <sys/wait.h>
      44  # include "tst-rseq.h"
      45  
      46  static pthread_key_t rseq_test_key;
      47  
      48  static void
      49  atfork_prepare (void)
      50  {
      51    if (!rseq_thread_registered ())
      52      {
      53        printf ("error: rseq not registered in pthread atfork prepare\n");
      54        support_record_failure ();
      55      }
      56  }
      57  
      58  static void
      59  atfork_parent (void)
      60  {
      61    if (!rseq_thread_registered ())
      62      {
      63        printf ("error: rseq not registered in pthread atfork parent\n");
      64        support_record_failure ();
      65      }
      66  }
      67  
      68  static void
      69  atfork_child (void)
      70  {
      71    if (!rseq_thread_registered ())
      72      {
      73        printf ("error: rseq not registered in pthread atfork child\n");
      74        support_record_failure ();
      75      }
      76  }
      77  
      78  static void
      79  rseq_key_destructor (void *arg)
      80  {
      81    /* Cannot use deferred failure reporting after main returns.  */
      82    if (!rseq_thread_registered ())
      83      FAIL_EXIT1 ("rseq not registered in pthread key destructor");
      84  }
      85  
      86  static void
      87  atexit_handler (void)
      88  {
      89    /* Cannot use deferred failure reporting after main returns.  */
      90    if (!rseq_thread_registered ())
      91      FAIL_EXIT1 ("rseq not registered in atexit handler");
      92  }
      93  
      94  /* Used to avoid -Werror=stringop-overread warning with
      95     pthread_setspecific and GCC 11.  */
      96  static char one = 1;
      97  
      98  static void
      99  do_rseq_main_test (void)
     100  {
     101    TEST_COMPARE (atexit (atexit_handler), 0);
     102    rseq_test_key = xpthread_key_create (rseq_key_destructor);
     103    TEST_COMPARE (pthread_atfork (atfork_prepare, atfork_parent, atfork_child), 0);
     104    xraise (SIGUSR1);
     105    TEST_COMPARE (pthread_setspecific (rseq_test_key, &one), 0);
     106    TEST_VERIFY_EXIT (rseq_thread_registered ());
     107  }
     108  
     109  static void
     110  cancel_routine (void *arg)
     111  {
     112    if (!rseq_thread_registered ())
     113      {
     114        printf ("error: rseq not registered in cancel routine\n");
     115        support_record_failure ();
     116      }
     117  }
     118  
     119  static pthread_barrier_t cancel_thread_barrier;
     120  static pthread_cond_t cancel_thread_cond = PTHREAD_COND_INITIALIZER;
     121  static pthread_mutex_t cancel_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
     122  
     123  static void
     124  test_cancel_thread (void)
     125  {
     126    pthread_cleanup_push (cancel_routine, NULL);
     127    (void) xpthread_barrier_wait (&cancel_thread_barrier);
     128    /* Wait forever until cancellation.  */
     129    xpthread_cond_wait (&cancel_thread_cond, &cancel_thread_mutex);
     130    pthread_cleanup_pop (0);
     131  }
     132  
     133  static void *
     134  thread_function (void * arg)
     135  {
     136    int i = (int) (intptr_t) arg;
     137  
     138    xraise (SIGUSR1);
     139    if (i == 0)
     140      test_cancel_thread ();
     141    TEST_COMPARE (pthread_setspecific (rseq_test_key, &one), 0);
     142    return rseq_thread_registered () ? NULL : (void *) 1l;
     143  }
     144  
     145  static void
     146  sighandler (int sig)
     147  {
     148    if (!rseq_thread_registered ())
     149      {
     150        printf ("error: rseq not registered in signal handler\n");
     151        support_record_failure ();
     152      }
     153  }
     154  
     155  static void
     156  setup_signals (void)
     157  {
     158    struct sigaction sa;
     159  
     160    sigemptyset (&sa.sa_mask);
     161    sigaddset (&sa.sa_mask, SIGUSR1);
     162    sa.sa_flags = 0;
     163    sa.sa_handler = sighandler;
     164    xsigaction (SIGUSR1, &sa, NULL);
     165  }
     166  
     167  static int
     168  do_rseq_threads_test (int nr_threads)
     169  {
     170    pthread_t th[nr_threads];
     171    int i;
     172    int result = 0;
     173  
     174    xpthread_barrier_init (&cancel_thread_barrier, NULL, 2);
     175  
     176    for (i = 0; i < nr_threads; ++i)
     177      th[i] = xpthread_create (NULL, thread_function,
     178                               (void *) (intptr_t) i);
     179  
     180    (void) xpthread_barrier_wait (&cancel_thread_barrier);
     181  
     182    xpthread_cancel (th[0]);
     183  
     184    for (i = 0; i < nr_threads; ++i)
     185      {
     186        void *v;
     187  
     188        v = xpthread_join (th[i]);
     189        if (i != 0 && v != NULL)
     190          {
     191            printf ("error: join %d successful, but child failed\n", i);
     192            result = 1;
     193          }
     194        else if (i == 0 && v == NULL)
     195          {
     196            printf ("error: join %d successful, child did not fail as expected\n", i);
     197            result = 1;
     198          }
     199      }
     200  
     201    xpthread_barrier_destroy (&cancel_thread_barrier);
     202  
     203    return result;
     204  }
     205  
     206  static void
     207  subprocess_callback (void *closure)
     208  {
     209    do_rseq_main_test ();
     210  }
     211  
     212  static void
     213  do_rseq_fork_test (void)
     214  {
     215    support_isolate_in_subprocess (subprocess_callback, NULL);
     216  }
     217  
     218  static int
     219  do_rseq_test (void)
     220  {
     221    int t[] = { 1, 2, 6, 5, 4, 3, 50 };
     222    int i, result = 0;
     223  
     224    if (!rseq_available ())
     225      FAIL_UNSUPPORTED ("kernel does not support rseq, skipping test");
     226    setup_signals ();
     227    xraise (SIGUSR1);
     228    do_rseq_main_test ();
     229    for (i = 0; i < array_length (t); i++)
     230      if (do_rseq_threads_test (t[i]))
     231        result = 1;
     232    do_rseq_fork_test ();
     233    return result;
     234  }
     235  
     236  static void __attribute__ ((destructor))
     237  do_rseq_destructor_test (void)
     238  {
     239    /* Cannot use deferred failure reporting after main returns.  */
     240    if (do_rseq_test ())
     241      FAIL_EXIT1 ("rseq not registered within destructor");
     242    xpthread_key_delete (rseq_test_key);
     243  }
     244  
     245  #else /* RSEQ_SIG */
     246  static int
     247  do_rseq_test (void)
     248  {
     249    FAIL_UNSUPPORTED ("glibc does not define RSEQ_SIG, skipping test");
     250    return 0;
     251  }
     252  #endif /* RSEQ_SIG */
     253  
     254  static int
     255  do_test (void)
     256  {
     257    return do_rseq_test ();
     258  }
     259  
     260  #include <support/test-driver.c>