1  /* Basic tests for Linux process_madvise.
       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 <limits.h>
      22  #include <stdlib.h>
      23  #include <support/check.h>
      24  #include <support/process_state.h>
      25  #include <support/support.h>
      26  #include <support/xsocket.h>
      27  #include <support/xunistd.h>
      28  #include <sys/mman.h>
      29  #include <sys/pidfd.h>
      30  #include <sys/wait.h>
      31  
      32  /* The pair of sockets used for coordination.  The subprocess uses
      33     sockets[1].  */
      34  static int sockets[2];
      35  
      36  static long int page_size;
      37  
      38  static void
      39  exit_subprocess (int dummy)
      40  {
      41    exit (EXIT_FAILURE);
      42  }
      43  
      44  static void
      45  subprocess (void)
      46  {
      47    /* In case something goes wrong with parent before pidfd_send_signal.  */
      48    support_create_timer (5, 0, false, exit_subprocess);
      49  
      50    void *p1 = xmmap (NULL, page_size * 2, PROT_READ | PROT_WRITE,
      51  		    MAP_PRIVATE | MAP_ANONYMOUS, -1);
      52  
      53    void *p2 = xmmap (NULL, page_size, PROT_READ | PROT_WRITE,
      54  		    MAP_PRIVATE | MAP_ANONYMOUS, -1);
      55    xmunmap(p2, page_size);
      56  
      57    xsendto (sockets[1], &(struct iovec) { p1, page_size * 2 },
      58  	   sizeof (struct iovec), 0, NULL, 0);
      59  
      60    xsendto (sockets[1], &(struct iovec) { p2, page_size },
      61  	   sizeof (struct iovec), 0, NULL, 0);
      62  
      63    pause ();
      64  
      65    _exit (0);
      66  }
      67  
      68  static int
      69  do_test (void)
      70  {
      71    page_size = sysconf (_SC_PAGE_SIZE);
      72  
      73    {
      74      int r = pidfd_open (-1, 0);
      75      TEST_COMPARE (r, -1);
      76      if (errno == ENOSYS)
      77        FAIL_UNSUPPORTED ("kernel does not support pidfd_open, skipping test");
      78  
      79      TEST_COMPARE (errno, EINVAL);
      80    }
      81  
      82    TEST_COMPARE (socketpair (AF_UNIX, SOCK_STREAM, 0, sockets), 0);
      83  
      84    pid_t pid = xfork ();
      85    if (pid == 0)
      86      {
      87        xclose (sockets[0]);
      88        subprocess ();
      89      }
      90    xclose (sockets[1]);
      91  
      92    int pidfd = pidfd_open (pid, 0);
      93    TEST_VERIFY (pidfd != -1);
      94  
      95    /* The target process is going to send us two iovec's.  The first one points
      96       to a valid mapping, the other points to a previously valid mapping which
      97       has now been unmapped.  */
      98    {
      99      struct iovec iv;
     100      xrecvfrom (sockets[0], &iv, sizeof (iv), 0, NULL, 0);
     101  
     102      /* We expect this to succeed in the target process because the mapping
     103         is valid.  */
     104      ssize_t ret = process_madvise (pidfd, &iv, 1, MADV_COLD, 0);
     105      if (ret == -1 && errno == ENOSYS)
     106        FAIL_UNSUPPORTED ("kernel does not support process_madvise, skipping"
     107  			"test");
     108      TEST_COMPARE (ret, 2 * page_size);
     109    }
     110  
     111    {
     112      struct iovec iv;
     113      xrecvfrom (sockets[0], &iv, sizeof (iv), 0, NULL, 0);
     114  
     115      /* We expect this to fail in the target process because the second iovec
     116         points to an unmapped region.  The target process arranges for this to
     117         be the case.  */
     118      TEST_COMPARE (process_madvise (pidfd, &iv, 1, MADV_COLD, 0), -1);
     119      TEST_COMPARE (errno, ENOMEM);
     120    }
     121  
     122    {
     123      struct iovec iv[IOV_MAX + 1];
     124      TEST_COMPARE (process_madvise (pidfd, iv, array_length (iv), MADV_COLD,
     125  				   0), -1);
     126      TEST_COMPARE (errno, EINVAL);
     127    }
     128  
     129    TEST_COMPARE (pidfd_send_signal (pidfd, SIGKILL, NULL, 0), 0);
     130    {
     131      siginfo_t info;
     132      int r = waitid (P_PIDFD, pidfd, &info, WEXITED);
     133      TEST_COMPARE (r, 0);
     134      TEST_COMPARE (info.si_status, SIGKILL);
     135      TEST_COMPARE (info.si_code, CLD_KILLED);
     136    }
     137  
     138    TEST_COMPARE (pidfd_send_signal (pidfd, SIGKILL, NULL, 0), -1);
     139    TEST_COMPARE (errno, ESRCH);
     140  
     141    return 0;
     142  }
     143  
     144  #include <support/test-driver.c>