1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) International Business Machines Corp., 2001 4 * Copyright (C) 2020 Cyril Hrubis <chrubis@suse.cz> 5 */ 6 7/*\ 8 * [Description] 9 * 10 * Verify that shmctl() IPC_STAT and SHM_STAT reports correct data. 11 * 12 * The shm_nattach is excercised by: 13 * 14 * - forking() children that attach and detach SHM 15 * - attaching the SHM before fork and letting the children detach it 16 * 17 * We check that the number shm_nattach is correct after each step we do. 18 */ 19 20#define _GNU_SOURCE 21#include <stdlib.h> 22#include "tst_test.h" 23#include "tst_safe_sysv_ipc.h" 24#include "tst_clocks.h" 25#include "libnewipc.h" 26 27#define NCHILD 20 28 29static pid_t children[NCHILD]; 30 31static int shm_id; 32static int shm_idx; 33static time_t ctime_min, ctime_max; 34 35static void *addr; 36 37static void attach_child(void) 38{ 39 pause(); 40 41 addr = SAFE_SHMAT(shm_id, NULL, 0); 42 43 pause(); 44 45 SAFE_SHMDT(addr); 46 47 pause(); 48 49 exit(0); 50} 51 52static void detach_child(void) 53{ 54 pause(); 55 56 SAFE_SHMDT(addr); 57 58 pause(); 59 60 exit(0); 61} 62 63static void fork_children(void (*child_func)(void)) 64{ 65 unsigned int i; 66 67 for (i = 0; i < NCHILD; i++) { 68 pid_t pid = SAFE_FORK(); 69 70 if (!pid) 71 child_func(); 72 73 children[i] = pid; 74 } 75} 76 77static void wait_for_children(void) 78{ 79 unsigned int i; 80 81 for (i = 0; i < NCHILD; i++) 82 TST_PROCESS_STATE_WAIT(children[i], 'S', 0); 83} 84 85static void signal_children(void) 86{ 87 unsigned int i; 88 89 for (i = 0; i < NCHILD; i++) 90 SAFE_KILL(children[i], SIGUSR1); 91} 92 93static void reap_children(void) 94{ 95 unsigned int i; 96 97 for (i = 0; i < NCHILD; i++) 98 SAFE_WAITPID(children[i], NULL, 0); 99} 100 101static void check_nattch(int exp_nattch, const char *msg) 102{ 103 struct shmid_ds ds1; 104 struct shmid_ds ds2; 105 106 SAFE_SHMCTL(shm_id, IPC_STAT, &ds1); 107 SAFE_SHMCTL(shm_idx, SHM_STAT, &ds2); 108 109 if (ds1.shm_nattch != ds2.shm_nattch) { 110 tst_res(TFAIL, "IPC_STAT nattch=%li SHM_STAT nattch=%li", 111 (long)ds1.shm_nattch, (long)ds2.shm_nattch); 112 return; 113 } 114 115 if ((int)ds1.shm_nattch == exp_nattch) { 116 tst_res(TPASS, "%s shm_nattch=%i", msg, exp_nattch); 117 return; 118 } 119 120 tst_res(TFAIL, "%s shm_nattcg=%li expected %i", 121 msg, (long)ds1.shm_nattch, exp_nattch); 122} 123 124static void verify_shmstat_attach(void) 125{ 126 fork_children(attach_child); 127 wait_for_children(); 128 129 check_nattch(0, "before child shmat()"); 130 131 signal_children(); 132 wait_for_children(); 133 134 check_nattch(NCHILD, "after child shmat()"); 135 136 signal_children(); 137 wait_for_children(); 138 139 check_nattch(0, "after child shmdt()"); 140 141 signal_children(); 142 reap_children(); 143} 144 145static void verify_shmstat_inherit(void) 146{ 147 addr = SAFE_SHMAT(shm_id, NULL, 0); 148 149 fork_children(detach_child); 150 wait_for_children(); 151 152 check_nattch(NCHILD+1, "inherited after fork()"); 153 154 signal_children(); 155 wait_for_children(); 156 157 check_nattch(1, "after child shmdt()"); 158 159 SAFE_SHMDT(addr); 160 161 check_nattch(0, "after parent shmdt()"); 162 163 signal_children(); 164 reap_children(); 165} 166 167static void check_ds(struct shmid_ds *ds, const char *desc) 168{ 169 pid_t pid = getpid(); 170 171 if (ds->shm_segsz != SHM_SIZE) { 172 tst_res(TFAIL, "%s: shm_segsz=%zu, expected %i", 173 desc, ds->shm_segsz, SHM_SIZE); 174 } else { 175 tst_res(TPASS, "%s: shm_segsz=%i", desc, SHM_SIZE); 176 } 177 178 if (ds->shm_cpid != pid) { 179 tst_res(TFAIL, "%s: shm_cpid=%i, expected %i", 180 desc, ds->shm_cpid, pid); 181 } else { 182 tst_res(TPASS, "%s: shm_cpid=%i", desc, pid); 183 } 184 185 if (ds->shm_ctime < ctime_min || ds->shm_ctime > ctime_max) { 186 tst_res(TFAIL, "%s: shm_ctime=%li, expected <%li,%li>", 187 desc, ds->shm_ctime, ctime_min, ctime_max); 188 } else { 189 tst_res(TPASS, "%s: shm_ctime=%li in range <%li,%li>", 190 desc, ds->shm_ctime, ctime_min, ctime_max); 191 } 192} 193 194static void shmstat_basic_check(void) 195{ 196 struct shmid_ds ds; 197 198 memset(&ds, 0, sizeof(ds)); 199 SAFE_SHMCTL(shm_id, IPC_STAT, &ds); 200 201 check_ds(&ds, "IPC_STAT"); 202 203 memset(&ds, 0, sizeof(ds)); 204 SAFE_SHMCTL(shm_idx, SHM_STAT, &ds); 205 206 check_ds(&ds, "SHM_STAT"); 207} 208 209static struct tcase { 210 void (*func)(void); 211 const char *desc; 212} tcases[] = { 213 {shmstat_basic_check, "Basic checks"}, 214 {verify_shmstat_attach, "Children attach SHM"}, 215 {verify_shmstat_inherit, "Chidlren inherit SHM"}, 216}; 217 218static void verify_shmstat(unsigned int n) 219{ 220 tst_res(TINFO, "%s", tcases[n].desc); 221 tcases[n].func(); 222} 223 224static void dummy_sighandler(int sig) 225{ 226 (void)sig; 227} 228 229static int get_shm_idx_from_id(int shm_id) 230{ 231 struct shm_info dummy; 232 struct shmid_ds dummy_ds; 233 int max_idx, i; 234 235 max_idx = SAFE_SHMCTL(shm_id, SHM_INFO, (void *)&dummy); 236 237 for (i = 0; i <= max_idx; i++) { 238 if (shmctl(i, SHM_STAT, &dummy_ds) == shm_id) 239 return i; 240 } 241 242 return -1; 243} 244 245static void setup(void) 246{ 247 ctime_min = tst_get_fs_timestamp(); 248 shm_id = SAFE_SHMGET(IPC_PRIVATE, SHM_SIZE, IPC_CREAT | SHM_RW); 249 ctime_max = tst_get_fs_timestamp(); 250 251 shm_idx = get_shm_idx_from_id(shm_id); 252 253 if (shm_idx < 0) 254 tst_brk(TBROK, "Failed to get shm_id to idx mapping"); 255 256 tst_res(TINFO, "shm_id=%i maps to kernel index=%i", shm_id, shm_idx); 257 258 SAFE_SIGNAL(SIGUSR1, dummy_sighandler); 259} 260 261static void cleanup(void) 262{ 263 if (shm_id >= 0) 264 SAFE_SHMCTL(shm_id, IPC_RMID, NULL); 265} 266 267static struct tst_test test = { 268 .setup = setup, 269 .cleanup = cleanup, 270 .forks_child = 1, 271 .test = verify_shmstat, 272 .tcnt = ARRAY_SIZE(tcases), 273}; 274