1  /* Test program for POSIX shm_* functions.
       2     Copyright (C) 2000-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 <errno.h>
      20  #include <error.h>
      21  #include <fcntl.h>
      22  #include <signal.h>
      23  #include <stdio.h>
      24  #include <stdlib.h>
      25  #include <string.h>
      26  #include <time.h>
      27  #include <unistd.h>
      28  #include <sys/mman.h>
      29  #include <sys/stat.h>
      30  #include <sys/wait.h>
      31  
      32  
      33  /* We want to see output immediately.  */
      34  #define STDOUT_UNBUFFERED
      35  
      36  static char shm_test_name[sizeof "/glibc-shm-test-" + sizeof (pid_t) * 3];
      37  static char shm_escape_name[sizeof "/../escaped-" + sizeof (pid_t) * 3];
      38  
      39  static void
      40  init_shm_test_names (void)
      41  {
      42    snprintf (shm_test_name, sizeof (shm_test_name), "/glibc-shm-test-%u",
      43  	    getpid ());
      44    snprintf (shm_escape_name, sizeof (shm_escape_name), "/../escaped-%u",
      45  	    getpid ());
      46  }
      47  
      48  static void
      49  worker (int write_now)
      50  {
      51    struct timespec ts;
      52    struct stat64 st;
      53    int i;
      54  
      55    int fd = shm_open (shm_test_name, O_RDWR, 0600);
      56  
      57    if (fd == -1)
      58      error (EXIT_FAILURE, 0, "failed to open shared memory object: shm_open");
      59  
      60    char *mem;
      61  
      62    if (fd == -1)
      63      exit (fd);
      64  
      65    if (fstat64 (fd, &st) == -1)
      66      error (EXIT_FAILURE, 0, "stat failed");
      67    if (st.st_size != 4000)
      68      error (EXIT_FAILURE, 0, "size incorrect");
      69  
      70    mem = mmap (NULL, 4000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
      71    if (mem == MAP_FAILED)
      72      error (EXIT_FAILURE, 0, "mmap failed");
      73  
      74    ts.tv_sec = 0;
      75    ts.tv_nsec = 500000000;
      76  
      77    if (write_now)
      78      for (i = 0; i <= 4; ++i)
      79        mem[i] = i;
      80    else
      81      /* Wait until the first bytes of the memory region are 0, 1, 2, 3, 4.  */
      82      while (1)
      83        {
      84  	for (i = 0; i <= 4; ++i)
      85  	  if (mem[i] != i)
      86  	    break;
      87  
      88  	if (i > 4)
      89  	  /* OK, that's done.  */
      90  	  break;
      91  
      92  	nanosleep (&ts, NULL);
      93        }
      94  
      95    if (!write_now)
      96      for (i = 0; i <= 4; ++i)
      97        mem[i] = 4 + i;
      98    else
      99      /* Wait until the first bytes of the memory region are 4, 5, 6, 7, 8.  */
     100      while (1)
     101        {
     102  	for (i = 0; i <= 4; ++i)
     103  	  if (mem[i] != 4 + i)
     104  	    break;
     105  
     106  	if (i > 4)
     107  	  /* OK, that's done.  */
     108  	  break;
     109  
     110  	nanosleep (&ts, NULL);
     111        }
     112  
     113    if (munmap (mem, 4000) == -1)
     114      error (EXIT_FAILURE, errno, "munmap");
     115  
     116    close (fd);
     117  
     118    exit (0);
     119  }
     120  
     121  
     122  static int
     123  do_test (void)
     124  {
     125    int fd;
     126    pid_t pid1;
     127    pid_t pid2;
     128    int status1;
     129    int status2;
     130    struct stat64 st;
     131  
     132    init_shm_test_names ();
     133  
     134    fd = shm_open (shm_escape_name, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
     135    if (fd != -1)
     136      {
     137        perror ("read file outside of SHMDIR directory");
     138        return 1;
     139      }
     140  
     141  
     142    /* Create the shared memory object.  */
     143    fd = shm_open (shm_test_name, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600);
     144    if (fd == -1)
     145      {
     146        /* If shm_open is unimplemented we skip the test.  */
     147        if (errno == ENOSYS)
     148          {
     149  	  perror ("shm_open unimplemented.  Test skipped.");
     150            return 0;
     151          }
     152        else
     153          error (EXIT_FAILURE, 0, "failed to create shared memory object: shm_open");
     154      }
     155  
     156    /* Size the object.  We make it 4000 bytes long.  */
     157    if (ftruncate (fd, 4000) == -1)
     158      {
     159        /* This failed.  Must be a bug in the implementation of the
     160           shared memory itself.  */
     161        perror ("failed to size of shared memory object: ftruncate");
     162        close (fd);
     163        shm_unlink (shm_test_name);
     164        return 0;
     165      }
     166  
     167    if (fstat64 (fd, &st) == -1)
     168      {
     169        shm_unlink (shm_test_name);
     170        error (EXIT_FAILURE, 0, "initial stat failed");
     171      }
     172    if (st.st_size != 4000)
     173      {
     174        shm_unlink (shm_test_name);
     175        error (EXIT_FAILURE, 0, "initial size not correct");
     176      }
     177  
     178    /* Spawn to processes which will do the work.  */
     179    pid1 = fork ();
     180    if (pid1 == 0)
     181      worker (0);
     182    else if (pid1 == -1)
     183      {
     184        /* Couldn't create a second process.  */
     185        perror ("fork");
     186        close (fd);
     187        shm_unlink (shm_test_name);
     188        return 0;
     189      }
     190  
     191    pid2 = fork ();
     192    if (pid2 == 0)
     193      worker (1);
     194    else if (pid2 == -1)
     195      {
     196        /* Couldn't create a second process.  */
     197        int ignore;
     198        perror ("fork");
     199        kill (pid1, SIGTERM);
     200        waitpid (pid1, &ignore, 0);
     201        close (fd);
     202        shm_unlink (shm_test_name);
     203        return 0;
     204      }
     205  
     206    /* Wait until the two processes are finished.  */
     207    waitpid (pid1, &status1, 0);
     208    waitpid (pid2, &status2, 0);
     209  
     210    /* Now we can unlink the shared object.  */
     211    shm_unlink (shm_test_name);
     212  
     213    return (!WIFEXITED (status1) || WEXITSTATUS (status1) != 0
     214  	  || !WIFEXITED (status2) || WEXITSTATUS (status2) != 0);
     215  }
     216  
     217  static void
     218  cleanup_handler (void)
     219  {
     220    shm_unlink (shm_test_name);
     221  }
     222  
     223  #define CLEANUP_HANDLER cleanup_handler
     224  
     225  #include <support/test-driver.c>