1 /* Test program for POSIX shm_* functions.
2    Copyright (C) 2000-2022 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
init_shm_test_names(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
worker(int write_now)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
do_test(void)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
cleanup_handler(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>
226