(root)/
glibc-2.38/
sysdeps/
unix/
sysv/
linux/
i386/
tst-bz21269.c
       1  /* Test for i386 sigaction sa_restorer handling (BZ#21269)
       2     Copyright (C) 2017-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  /* This is based on Linux test tools/testing/selftests/x86/ldt_gdt.c,
      20     more specifically in do_multicpu_tests function.  The main changes
      21     are:
      22  
      23     - C11 atomics instead of plain access.
      24     - Remove x86_64 support which simplifies the syscall handling
      25       and fallbacks.
      26     - Replicate only the test required to trigger the issue for the
      27       BZ#21269.  */
      28  
      29  #include <stdatomic.h>
      30  
      31  #include <asm/ldt.h>
      32  #include <linux/futex.h>
      33  
      34  #include <setjmp.h>
      35  #include <signal.h>
      36  #include <errno.h>
      37  #include <sys/syscall.h>
      38  #include <sys/mman.h>
      39  
      40  #include <support/xunistd.h>
      41  #include <support/check.h>
      42  #include <support/xthread.h>
      43  
      44  static int
      45  xset_thread_area (struct user_desc *u_info)
      46  {
      47    long ret = syscall (SYS_set_thread_area, u_info);
      48    TEST_VERIFY_EXIT (ret == 0);
      49    return ret;
      50  }
      51  
      52  static void
      53  xmodify_ldt (int func, const void *ptr, unsigned long bytecount)
      54  {
      55    TEST_VERIFY_EXIT (syscall (SYS_modify_ldt, 1, ptr, bytecount) == 0);
      56  }
      57  
      58  static int
      59  futex (int *uaddr, int futex_op, int val, void *timeout, int *uaddr2,
      60  	int val3)
      61  {
      62    return syscall (SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
      63  }
      64  
      65  static void
      66  xsethandler (int sig, void (*handler)(int, siginfo_t *, void *), int flags)
      67  {
      68    struct sigaction sa = { 0 };
      69    sa.sa_sigaction = handler;
      70    sa.sa_flags = SA_SIGINFO | flags;
      71    TEST_VERIFY_EXIT (sigemptyset (&sa.sa_mask) == 0);
      72    TEST_VERIFY_EXIT (sigaction (sig, &sa, 0) == 0);
      73  }
      74  
      75  static jmp_buf jmpbuf;
      76  
      77  static void
      78  sigsegv_handler (int sig, siginfo_t *info, void *ctx_void)
      79  {
      80    siglongjmp (jmpbuf, 1);
      81  }
      82  
      83  /* Points to an array of 1024 ints, each holding its own index.  */
      84  static const unsigned int *counter_page;
      85  static struct user_desc *low_user_desc;
      86  static struct user_desc *low_user_desc_clear; /* Used to delete GDT entry.  */
      87  static int gdt_entry_num;
      88  
      89  static void
      90  setup_counter_page (void)
      91  {
      92    long page_size = sysconf (_SC_PAGE_SIZE);
      93    TEST_VERIFY_EXIT (page_size > 0);
      94    unsigned int *page = xmmap (NULL, page_size, PROT_READ | PROT_WRITE,
      95  			      MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1);
      96    for (int i = 0; i < (page_size / sizeof (unsigned int)); i++)
      97      page[i] = i;
      98    counter_page = page;
      99  }
     100  
     101  static void
     102  setup_low_user_desc (void)
     103  {
     104    low_user_desc = xmmap (NULL, 2 * sizeof (struct user_desc),
     105  			 PROT_READ | PROT_WRITE,
     106  			 MAP_ANONYMOUS | MAP_PRIVATE | MAP_32BIT, -1);
     107  
     108    low_user_desc->entry_number    = -1;
     109    low_user_desc->base_addr       = (unsigned long) &counter_page[1];
     110    low_user_desc->limit           = 0xffff;
     111    low_user_desc->seg_32bit       = 1;
     112    low_user_desc->contents        = 0;
     113    low_user_desc->read_exec_only  = 0;
     114    low_user_desc->limit_in_pages  = 1;
     115    low_user_desc->seg_not_present = 0;
     116    low_user_desc->useable         = 0;
     117  
     118    xset_thread_area (low_user_desc);
     119  
     120    low_user_desc_clear = low_user_desc + 1;
     121    low_user_desc_clear->entry_number = gdt_entry_num;
     122    low_user_desc_clear->read_exec_only = 1;
     123    low_user_desc_clear->seg_not_present = 1;
     124  }
     125  
     126  /* Possible values of futex:
     127     0: thread is idle.
     128     1: thread armed.
     129     2: thread should clear LDT entry 0.
     130     3: thread should exit.  */
     131  static atomic_uint ftx;
     132  
     133  static void *
     134  threadproc (void *ctx)
     135  {
     136    while (1)
     137      {
     138        /* Continue to wait here until we've successfully waited, unless
     139  	 we're supposed to be clearing the LDT already.  */
     140        while (futex ((int *) &ftx, FUTEX_WAIT, 1, NULL, NULL, 0) < 0)
     141  	if (atomic_load (&ftx) >= 2)
     142  	  break;
     143  
     144        /* Normally there's time to hit this busy loop and wait for ftx
     145  	 to be set to 2.  */
     146        while (atomic_load (&ftx) != 2)
     147  	{
     148  	  if (atomic_load (&ftx) >= 3)
     149  	    return NULL;
     150  	}
     151  
     152        /* clear LDT entry 0.  */
     153        const struct user_desc desc = { 0 };
     154        xmodify_ldt (1, &desc, sizeof (desc));
     155  
     156        /* If ftx == 2, set it to zero,  If ftx == 100, quit.  */
     157        if (atomic_fetch_add (&ftx, -2) != 2)
     158  	return NULL;
     159      }
     160  }
     161  
     162  
     163  /* As described in testcase, for historical reasons x86_32 Linux (and compat
     164     on x86_64) interprets SA_RESTORER clear with nonzero sa_restorer as a
     165     request for stack switching if the SS segment is 'funny' (this is default
     166     scenario for vDSO system).  This means that anything that tries to mix
     167     signal handling with segmentation should explicit clear the sa_restorer.
     168  
     169     This testcase check if sigaction in fact does it by changing the local
     170     descriptor table (LDT) through the modify_ldt syscall and triggering
     171     a synchronous segfault on iret fault by trying to install an invalid
     172     segment.  With a correct zeroed sa_restorer it should not trigger an
     173     'real' SEGSEGV and allows the siglongjmp in signal handler.  */
     174  
     175  static int
     176  do_test (void)
     177  {
     178    setup_counter_page ();
     179    setup_low_user_desc ();
     180  
     181    pthread_t thread;
     182    unsigned short orig_ss;
     183  
     184    xsethandler (SIGSEGV, sigsegv_handler, 0);
     185    /* 32-bit kernels send SIGILL instead of SIGSEGV on IRET faults.  */
     186    xsethandler (SIGILL, sigsegv_handler, 0);
     187    /* Some kernels send SIGBUS instead.  */
     188    xsethandler (SIGBUS, sigsegv_handler, 0);
     189  
     190    thread = xpthread_create (0, threadproc, 0);
     191  
     192    asm volatile ("mov %%ss, %0" : "=rm" (orig_ss));
     193  
     194    for (int i = 0; i < 5; i++)
     195      {
     196        if (sigsetjmp (jmpbuf, 1) != 0)
     197  	continue;
     198  
     199        /* We may have longjmp'd before triggering the thread.  If so,
     200  	 trigger the thread now and wait for it.  */
     201        if (atomic_load (&ftx) == 1)
     202  	atomic_store (&ftx, 2);
     203  
     204        /* Make sure the thread is ready after the last test.  FTX is
     205  	 initially zero for the first loop, and set to zero each time
     206  	 the thread clears the LDT.  */
     207        while (atomic_load (&ftx) != 0)
     208  	;
     209  
     210        struct user_desc desc = {
     211  	.entry_number       = 0,
     212  	.base_addr          = 0,
     213  	.limit              = 0xffff,
     214  	.seg_32bit          = 1,
     215  	.contents           = 0,
     216  	.read_exec_only     = 0,
     217  	.limit_in_pages     = 1,
     218  	.seg_not_present    = 0,
     219  	.useable            = 0
     220        };
     221  
     222        xmodify_ldt (0x11, &desc, sizeof (desc));
     223  
     224        /* Arm the thread.  We loop here until we've woken up one thread.  */
     225        atomic_store (&ftx, 1);
     226        while (futex ((int*) &ftx, FUTEX_WAKE, 1, NULL, NULL, 0) < 1)
     227  	;
     228  
     229        /* Give the thread a chance to get into it's busy loop.  */
     230        usleep (5);
     231  
     232        /* At *ANY* point after this instruction, we may segfault and
     233  	 longjump back to the top of the loop.  The intention is to
     234  	 have this happen when the thread clears the LDT, but it could
     235  	 happen elsewhen.  */
     236        asm volatile ("mov %0, %%ss" : : "r" (0x7));
     237  
     238        /* Fire up thread modify_ldt call.  */
     239        atomic_store (&ftx, 2);
     240  
     241        /* And wait for it.  */
     242        while (atomic_load (&ftx) != 0)
     243  	;
     244  
     245        /* On success, modify_ldt will segfault us synchronously and we will
     246  	 escape via siglongjmp.  */
     247        support_record_failure ();
     248      }
     249  
     250    atomic_store (&ftx, 100);
     251    futex ((int*) &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
     252  
     253    xpthread_join (thread);
     254  
     255    return 0;
     256  }
     257  
     258  #include <support/test-driver.c>