1 /* Basic tests for Linux SYSV semaphore extensions.
2    Copyright (C) 2020-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 <sys/ipc.h>
20 #include <sys/sem.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <stdbool.h>
24 #include <stdio.h>
25 
26 #include <support/check.h>
27 #include <support/temp_file.h>
28 
29 /* These are for the temporary file we generate.  */
30 static char *name;
31 static int semid;
32 
33 static void
remove_sem(void)34 remove_sem (void)
35 {
36   /* Enforce message queue removal in case of early test failure.
37      Ignore error since the sem may already have being removed.  */
38   semctl (semid, 0, IPC_RMID, 0);
39 }
40 
41 static void
do_prepare(int argc,char * argv[])42 do_prepare (int argc, char *argv[])
43 {
44   TEST_VERIFY_EXIT (create_temp_file ("tst-sysvsem.", &name) != -1);
45 }
46 
47 #define PREPARE do_prepare
48 
49 #define SEM_MODE 0644
50 
51 union semun
52 {
53   int val;
54   struct semid_ds *buf;
55   unsigned short  *array;
56   struct seminfo *__buf;
57 };
58 
59 struct test_seminfo
60 {
61   int semmsl;
62   int semmns;
63   int semopm;
64   int semmni;
65 };
66 
67 /* It tries to obtain some system-wide SysV semaphore information from /proc
68    to check against IPC_INFO/SEM_INFO.  The /proc only returns the tunables
69    value of SEMMSL, SEMMNS, SEMOPM, and SEMMNI.
70 
71    The kernel also returns constant value for SEMVMX, SEMMNU, SEMMAP, SEMUME,
72    and also SEMUSZ and SEMAEM (for IPC_INFO).  The issue to check them is they
73    might change over kernel releases.  */
74 
75 static void
read_sem_stat(struct test_seminfo * tseminfo)76 read_sem_stat (struct test_seminfo *tseminfo)
77 {
78   FILE *f = fopen ("/proc/sys/kernel/sem", "r");
79   if (f == NULL)
80     FAIL_UNSUPPORTED ("/proc is not mounted or /proc/sys/kernel/sem is not "
81 		      "available");
82 
83   int r = fscanf (f, "%d %d %d %d",
84 		  &tseminfo->semmsl, &tseminfo->semmns, &tseminfo->semopm,
85 		  &tseminfo->semmni);
86   TEST_VERIFY_EXIT (r == 4);
87 
88   fclose (f);
89 }
90 
91 
92 /* Check if the semaphore with IDX (index into the kernel's internal array)
93    matches the one with KEY.  The CMD is either SEM_STAT or SEM_STAT_ANY.  */
94 
95 static bool
check_seminfo(int idx,key_t key,int cmd)96 check_seminfo (int idx, key_t key, int cmd)
97 {
98   struct semid_ds seminfo;
99   int sid = semctl (idx, 0, cmd, (union semun) { .buf = &seminfo });
100   /* Ignore unused array slot returned by the kernel or information from
101      unknown semaphores.  */
102   if ((sid == -1 && errno == EINVAL) || sid != semid)
103     return false;
104 
105   if (sid == -1)
106     FAIL_EXIT1 ("semctl with SEM_STAT failed (errno=%d)", errno);
107 
108   TEST_COMPARE (seminfo.sem_perm.__key, key);
109   TEST_COMPARE (seminfo.sem_perm.mode, SEM_MODE);
110   TEST_COMPARE (seminfo.sem_nsems, 1);
111 
112   return true;
113 }
114 
115 static int
do_test(void)116 do_test (void)
117 {
118   atexit (remove_sem);
119 
120   key_t key = ftok (name, 'G');
121   if (key == -1)
122     FAIL_EXIT1 ("ftok failed: %m");
123 
124   semid = semget (key, 1, IPC_CREAT | IPC_EXCL | SEM_MODE);
125   if (semid == -1)
126     FAIL_EXIT1 ("semget failed: %m");
127 
128   struct test_seminfo tipcinfo;
129   read_sem_stat (&tipcinfo);
130 
131   int semidx;
132 
133   {
134     struct seminfo ipcinfo;
135     semidx = semctl (semid, 0, IPC_INFO, (union semun) { .__buf = &ipcinfo });
136     if (semidx == -1)
137       FAIL_EXIT1 ("semctl with IPC_INFO failed: %m");
138 
139     TEST_COMPARE (ipcinfo.semmsl, tipcinfo.semmsl);
140     TEST_COMPARE (ipcinfo.semmns, tipcinfo.semmns);
141     TEST_COMPARE (ipcinfo.semopm, tipcinfo.semopm);
142     TEST_COMPARE (ipcinfo.semmni, tipcinfo.semmni);
143   }
144 
145   /* Same as before but with SEM_INFO.  */
146   {
147     struct seminfo ipcinfo;
148     semidx = semctl (semid, 0, SEM_INFO, (union semun) { .__buf = &ipcinfo });
149     if (semidx == -1)
150       FAIL_EXIT1 ("semctl with IPC_INFO failed: %m");
151 
152     TEST_COMPARE (ipcinfo.semmsl, tipcinfo.semmsl);
153     TEST_COMPARE (ipcinfo.semmns, tipcinfo.semmns);
154     TEST_COMPARE (ipcinfo.semopm, tipcinfo.semopm);
155     TEST_COMPARE (ipcinfo.semmni, tipcinfo.semmni);
156   }
157 
158   /* We check if the created semaphore shows in the system-wide status.  */
159   bool found = false;
160   for (int i = 0; i <= semidx; i++)
161     {
162       /* We can't tell apart if SEM_STAT_ANY is not supported (kernel older
163 	 than 4.17) or if the index used is invalid.  So it just check if
164 	 value returned from a valid call matches the created semaphore.  */
165       check_seminfo (i, key, SEM_STAT_ANY);
166 
167       if (check_seminfo (i, key, SEM_STAT))
168 	{
169 	  found = true;
170 	  break;
171 	}
172     }
173 
174   if (!found)
175     FAIL_EXIT1 ("semctl with SEM_STAT/SEM_STAT_ANY could not find the "
176 		"created  semaphore");
177 
178   if (semctl (semid, 0, IPC_RMID, 0) == -1)
179     FAIL_EXIT1 ("semctl failed: %m");
180 
181   return 0;
182 }
183 
184 #include <support/test-driver.c>
185