1 | /* Basic tests for Linux SYSV shared memory extensions. |
2 | Copyright (C) 2020-2024 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/shm.h> |
21 | #include <errno.h> |
22 | #include <stdlib.h> |
23 | #include <stdbool.h> |
24 | #include <stdio.h> |
25 | #include <unistd.h> |
26 | #include <inttypes.h> |
27 | #include <limits.h> |
28 | |
29 | #include <support/check.h> |
30 | #include <support/temp_file.h> |
31 | |
32 | #define SHM_MODE 0644 |
33 | |
34 | /* These are for the temporary file we generate. */ |
35 | static char *name; |
36 | static int shmid; |
37 | static long int pgsz; |
38 | |
39 | static void |
40 | remove_shm (void) |
41 | { |
42 | /* Enforce message queue removal in case of early test failure. |
43 | Ignore error since the shm may already have being removed. */ |
44 | shmctl (shmid: shmid, IPC_RMID, NULL); |
45 | } |
46 | |
47 | static void |
48 | do_prepare (int argc, char *argv[]) |
49 | { |
50 | TEST_VERIFY_EXIT (create_temp_file ("tst-sysvshm." , &name) != -1); |
51 | } |
52 | |
53 | #define PREPARE do_prepare |
54 | |
55 | struct test_shminfo |
56 | { |
57 | __syscall_ulong_t shmall; |
58 | __syscall_ulong_t shmmax; |
59 | __syscall_ulong_t shmmni; |
60 | }; |
61 | |
62 | /* It tries to obtain some system-wide SysV shared memory information from |
63 | /proc to check against IPC_INFO/SHM_INFO. The /proc only returns the |
64 | tunables value of SHMALL, SHMMAX, and SHMMNI. */ |
65 | |
66 | static uint64_t |
67 | read_proc_file (const char *file) |
68 | { |
69 | FILE *f = fopen (file, "r" ); |
70 | if (f == NULL) |
71 | FAIL_UNSUPPORTED ("/proc is not mounted or %s is not available" , file); |
72 | |
73 | /* Handle 32-bit binaries running on 64-bit kernels. */ |
74 | uint64_t v; |
75 | int r = fscanf (stream: f, format: "%" SCNu64, &v); |
76 | TEST_VERIFY_EXIT (r == 1); |
77 | |
78 | fclose (f); |
79 | return v; |
80 | } |
81 | |
82 | |
83 | /* Check if the message queue with IDX (index into the kernel's internal |
84 | array) matches the one with KEY. The CMD is either SHM_STAT or |
85 | SHM_STAT_ANY. */ |
86 | |
87 | static bool |
88 | check_shminfo (int idx, key_t key, int cmd) |
89 | { |
90 | struct shmid_ds shminfo; |
91 | int sid = shmctl (shmid: idx, cmd: cmd, buf: &shminfo); |
92 | /* Ignore unused array slot returned by the kernel or information from |
93 | unknown message queue. */ |
94 | if ((sid == -1 && errno == EINVAL) || sid != shmid) |
95 | return false; |
96 | |
97 | if (sid == -1) |
98 | FAIL_EXIT1 ("shmctl with %s failed: %m" , |
99 | cmd == SHM_STAT ? "SHM_STAT" : "SHM_STAT_ANY" ); |
100 | |
101 | TEST_COMPARE (shminfo.shm_perm.__key, key); |
102 | TEST_COMPARE (shminfo.shm_perm.mode, SHM_MODE); |
103 | TEST_COMPARE (shminfo.shm_segsz, pgsz); |
104 | |
105 | return true; |
106 | } |
107 | |
108 | static int |
109 | do_test (void) |
110 | { |
111 | atexit (func: remove_shm); |
112 | |
113 | pgsz = sysconf (_SC_PAGESIZE); |
114 | if (pgsz == -1) |
115 | FAIL_EXIT1 ("sysconf (_SC_PAGESIZE) failed: %m" ); |
116 | |
117 | key_t key = ftok (pathname: name, proj_id: 'G'); |
118 | if (key == -1) |
119 | FAIL_EXIT1 ("ftok failed: %m" ); |
120 | |
121 | shmid = shmget (key: key, size: pgsz, IPC_CREAT | IPC_EXCL | SHM_MODE); |
122 | if (shmid == -1) |
123 | FAIL_EXIT1 ("shmget failed: %m" ); |
124 | |
125 | /* It does not check shmmax because kernel clamp its value to INT_MAX for: |
126 | |
127 | 1. Compat symbols with IPC_64, i.e, 32-bit binaries running on 64-bit |
128 | kernels. |
129 | |
130 | 2. Default symbol without IPC_64 (defined as IPC_OLD within Linux) and |
131 | glibc always use IPC_64 for 32-bit ABIs (to support 64-bit time_t). |
132 | It means that 32-bit binaries running on 32-bit kernels will not see |
133 | shmmax being clamped. |
134 | |
135 | And finding out whether the compat symbol is used would require checking |
136 | the underlying kernel against the current ABI. The shmall and shmmni |
137 | already provided enough coverage. */ |
138 | |
139 | struct test_shminfo tipcinfo; |
140 | tipcinfo.shmall = read_proc_file (file: "/proc/sys/kernel/shmall" ); |
141 | tipcinfo.shmmni = read_proc_file (file: "/proc/sys/kernel/shmmni" ); |
142 | |
143 | int shmidx; |
144 | |
145 | /* Note: SHM_INFO does not return a shminfo, but rather a 'struct shm_info'. |
146 | It is tricky to verify its values since the syscall returns system wide |
147 | resources consumed by shared memory. The shmctl implementation handles |
148 | SHM_INFO as IPC_INFO, so the IPC_INFO test should validate SHM_INFO as |
149 | well. */ |
150 | |
151 | { |
152 | struct shminfo ipcinfo; |
153 | shmidx = shmctl (shmid: shmid, IPC_INFO, buf: (struct shmid_ds *) &ipcinfo); |
154 | if (shmidx == -1) |
155 | FAIL_EXIT1 ("shmctl with IPC_INFO failed: %m" ); |
156 | |
157 | TEST_COMPARE (ipcinfo.shmall, tipcinfo.shmall); |
158 | TEST_COMPARE (ipcinfo.shmmni, tipcinfo.shmmni); |
159 | } |
160 | |
161 | /* We check if the created shared memory shows in the global list. */ |
162 | bool found = false; |
163 | for (int i = 0; i <= shmidx; i++) |
164 | { |
165 | /* We can't tell apart if SHM_STAT_ANY is not supported (kernel older |
166 | than 4.17) or if the index used is invalid. So it just check if |
167 | value returned from a valid call matches the created message |
168 | queue. */ |
169 | check_shminfo (idx: i, key, SHM_STAT_ANY); |
170 | |
171 | if (check_shminfo (idx: i, key, SHM_STAT)) |
172 | { |
173 | found = true; |
174 | break; |
175 | } |
176 | } |
177 | |
178 | if (!found) |
179 | FAIL_EXIT1 ("shmctl with SHM_STAT/SHM_STAT_ANY could not find the " |
180 | "created shared memory" ); |
181 | |
182 | if (shmctl (shmid: shmid, IPC_RMID, NULL) == -1) |
183 | FAIL_EXIT1 ("shmctl failed" ); |
184 | |
185 | return 0; |
186 | } |
187 | |
188 | #include <support/test-driver.c> |
189 | |