(root)/
glibc-2.38/
debug/
tst-chk-cancel.c
       1  /* Test for required cancellation points in fortified functions (BZ #29274)
       2     Copyright (C) 2022-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  #include <array_length.h>
      20  #include <errno.h>
      21  #include <poll.h>
      22  #include <stdbool.h>
      23  #include <stdint.h>
      24  #include <stdio.h>
      25  #include <support/check.h>
      26  #include <support/xthread.h>
      27  #include <support/xunistd.h>
      28  #include <sys/socket.h>
      29  
      30  /* Cleanup handling test.  */
      31  static int cl_called;
      32  
      33  static void
      34  cl (void *arg)
      35  {
      36    ++cl_called;
      37  }
      38  
      39  static int fds[2];
      40  static pthread_barrier_t barrier;
      41  
      42  static void *
      43  tf_read (void *n)
      44  {
      45    pthread_cleanup_push (cl, NULL);
      46  
      47    xpthread_barrier_wait (&barrier);
      48  
      49    /* This call should be forwarded to __read_chk because the buffer size
      50       is known, but the read length is non-constant.  */
      51    char c;
      52    if (read (fds[0], &c, (uintptr_t) n) != 1)
      53      return (void *) -1L;
      54  
      55    pthread_cleanup_pop (0);
      56    return 0;
      57  }
      58  
      59  static void *
      60  tf_pread (void *n)
      61  {
      62    pthread_cleanup_push (cl, NULL);
      63  
      64    xpthread_barrier_wait (&barrier);
      65  
      66    /* This call should be forwarded to __pread_chk because the buffer size
      67       is known, but the read length is non-constant.  */
      68    char c;
      69    if (pread (fds[0], &c, (uintptr_t) n, 0) != 1)
      70      return (void *) -1L;
      71  
      72    pthread_cleanup_pop (0);
      73    return 0;
      74  }
      75  
      76  static void *
      77  tf_pread64 (void *n)
      78  {
      79    pthread_cleanup_push (cl, NULL);
      80  
      81    xpthread_barrier_wait (&barrier);
      82  
      83    /* This call should be forwarded to __pread64_chk because the buffer size
      84       is known, but the read length is non-constant.  */
      85    char c;
      86    if (pread64 (fds[0], &c, (uintptr_t) n, 0) != 1)
      87      return (void *) -1L;
      88  
      89    pthread_cleanup_pop (0);
      90    return 0;
      91  }
      92  
      93  static void *
      94  tf_poll (void *n)
      95  {
      96    pthread_cleanup_push (cl, NULL);
      97  
      98    xpthread_barrier_wait (&barrier);
      99  
     100    /* This call should be forwarded to __poll_chk because the pollfd size
     101       is known, but the number of entries is non-constant.  */
     102    struct pollfd pfd = { fds[0], POLLIN, 0 };
     103    if (poll (&pfd, (uintptr_t) n, -1) != 1)
     104      return (void *) -1L;
     105  
     106    pthread_cleanup_pop (0);
     107    return 0;
     108  }
     109  
     110  static void *
     111  tf_ppoll (void *n)
     112  {
     113    pthread_cleanup_push (cl, NULL);
     114  
     115    xpthread_barrier_wait (&barrier);
     116  
     117    /* This call should be forwarded to __ppoll_chk because the pollfd size
     118       is known, but the number of entries is non-constant.  */
     119    struct pollfd pfd = { fds[0], POLLIN, 0 };
     120    if (ppoll (&pfd, (uintptr_t) n, 0, 0) != 1)
     121      return (void *) -1L;
     122  
     123    pthread_cleanup_pop (0);
     124    return 0;
     125  }
     126  
     127  static void *
     128  tf_recv (void *n)
     129  {
     130    pthread_cleanup_push (cl, NULL);
     131  
     132    xpthread_barrier_wait (&barrier);
     133  
     134    /* This call should be forwarded to __ppoll_chk because the pollfd size
     135       is known, but the number of entries is non-constant.  */
     136    char c;
     137    if (recv (fds[0], &c, (uintptr_t) n, 0) != 1)
     138      return (void *) -1L;
     139  
     140    pthread_cleanup_pop (0);
     141    return 0;
     142  }
     143  
     144  static void *
     145  tf_recvfrom (void *n)
     146  {
     147    pthread_cleanup_push (cl, NULL);
     148  
     149    xpthread_barrier_wait (&barrier);
     150  
     151    /* This call should be forwarded to __ppoll_chk because the pollfd size
     152       is known, but the number of entries is non-constant.  */
     153    char c;
     154    if (recvfrom (fds[0], &c, (uintptr_t) n, 0, NULL, NULL) != 1)
     155      return (void *) -1L;
     156  
     157    pthread_cleanup_pop (0);
     158    return 0;
     159  }
     160  
     161  static struct cancel_tests
     162  {
     163    const char *name;
     164    void *(*tf) (void *);
     165    bool only_early;
     166  #define ADD_TEST(name, early) { #name, tf_##name, early }
     167  } tests[] =
     168  {
     169    ADD_TEST (poll,     false),
     170    ADD_TEST (ppoll,    false),
     171    ADD_TEST (pread,    true),
     172    ADD_TEST (pread64,  true),
     173    ADD_TEST (read,     false),
     174    ADD_TEST (recv,     false),
     175    ADD_TEST (recvfrom, false),
     176  };
     177  
     178  /* Set the send buffer of socket S to 1 byte so any send operation
     179     done with WRITE_BUFFER_SIZE bytes will force syscall blocking.  */
     180  static void
     181  set_socket_buffer (int s)
     182  {
     183    int val = 1;
     184    socklen_t len = sizeof (val);
     185  
     186    TEST_VERIFY_EXIT (setsockopt (s, SOL_SOCKET, SO_SNDBUF, &val,
     187                      sizeof (val)) == 0);
     188    TEST_VERIFY_EXIT (getsockopt (s, SOL_SOCKET, SO_SNDBUF, &val, &len) == 0);
     189    printf ("%s: got size %d\n", __func__, val);
     190  }
     191  
     192  static int
     193  do_test (void)
     194  {
     195    xpthread_barrier_init (&barrier, 0, 2);
     196  
     197    if (socketpair (AF_UNIX, SOCK_STREAM, 0, fds) != 0)
     198      FAIL_EXIT1 ("socketpair: %m");
     199    set_socket_buffer (fds[1]);
     200  
     201    /* This is the !only_early test.  It is a late cancel test that has a sleep
     202       in the main thread in an attempt to allow the child thread to reach and
     203       block on the syscall.  The cancellation should happen with high
     204       probability when the child thread blocked on the syscall, and that is
     205       the intent of the test (syscall cancellation registration complete).  */
     206    for (int i = 0; i < array_length (tests); i++)
     207      {
     208        if (tests[i].only_early)
     209  	continue;
     210  
     211        xpthread_barrier_init (&barrier, NULL, 2);
     212        /* Reset the counter for the cleanup handler.  */
     213        cl_called = 0;
     214  
     215        pthread_t thr = xpthread_create (0, tests[i].tf, (void *) 1L);
     216        /* After this wait the threads cancellation handler is installed.  */
     217        xpthread_barrier_wait (&barrier);
     218  
     219        struct timespec ts = { .tv_sec = 0, .tv_nsec = 100000000 };
     220        TEMP_FAILURE_RETRY (clock_nanosleep (CLOCK_REALTIME, 0, &ts, &ts));
     221  
     222        xpthread_cancel (thr);
     223  
     224        void *status = xpthread_join (thr);
     225        TEST_VERIFY (status == PTHREAD_CANCELED);
     226        TEST_COMPARE (cl_called, 1);
     227  
     228        printf ("in-time cancel test of '%s' successful\n", tests[i].name);
     229      }
     230  
     231    /* This is a early cancel test that happens before the syscall is issued.
     232       In this case there is no signal involved, pthread_cancel will just mark
     233       the target thread canceled, since asynchronous mode is not set, and the
     234       cancellable entrypoint will check if the thread is set as cancelled and
     235       exit early.
     236  
     237       Keep in mind that neither pthread_barrier_wait nor pthread_cleanup_push
     238       act as cancellation entrypoints.  */
     239    for (int i = 0; i < array_length (tests); i++)
     240      {
     241        xpthread_barrier_init (&barrier, NULL, 2);
     242        /* Reset the counter for the cleanup handler.  */
     243        cl_called = 0;
     244  
     245        /* After this wait the cancellation handler is in place.  */
     246        pthread_t thr = xpthread_create (0, tests[i].tf, NULL);
     247  
     248        xpthread_cancel (thr);
     249        xpthread_barrier_wait (&barrier);
     250  
     251        void *status = xpthread_join (thr);
     252        TEST_VERIFY (status == PTHREAD_CANCELED);
     253        TEST_COMPARE (cl_called, 1);
     254  
     255        printf ("early cancel test of '%s' successful\n", tests[i].name);
     256      }
     257  
     258    xpthread_barrier_destroy (&barrier);
     259  
     260    return 0;
     261  }
     262  
     263  #include <support/test-driver.c>