1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2020 FUJITSU LIMITED. All rights reserved. 4 * Author: Feiyu Zhu <zhufy.jy@cn.fujitsu.com> 5 */ 6/*\ 7 * [Description] 8 * 9 * Call semctl() with SEM_INFO flag and check that: 10 * 11 * * The returned index points to a valid SEM by calling SEM_STAT_ANY 12 * * Also count that valid indexes < returned max index sums up to semusz 13 * * And the data are consistent with /proc/sysvipc/sem 14 * 15 * There is a possible race between the call to the semctl() and read from the 16 * proc file so this test cannot be run in parallel with any IPC testcases that 17 * adds or removes semaphore set. 18 * 19 * Note what we create a semaphore set in the test setup to make sure 20 * that there is at least one during the testrun. 21 * 22 * Also note that for SEM_INFO the members of the seminfo structure have 23 * completely different meaning than their names seems to suggest. 24 * 25 * We also calling semctl() directly by syscall(), because of a glibc bug: 26 * 27 * semctl SEM_STAT_ANY fails to pass the buffer specified by the caller 28 * to the kernel. 29 * 30 * https://sourceware.org/bugzilla/show_bug.cgi?id=26637 31 */ 32 33/* 34 * The glibc bug was fixed in: 35 * 36 * * commit 574500a108be1d2a6a0dc97a075c9e0a98371aba 37 * * Author: Dmitry V. Levin <ldv@altlinux.org> 38 * * Date: Tue, 29 Sep 2020 17:10:20 +0000 (14:10 -0300) 39 */ 40 41#include <stdio.h> 42#include <pwd.h> 43#include "tst_test.h" 44#include "tst_safe_sysv_ipc.h" 45#include "libnewipc.h" 46#include "lapi/sem.h" 47#include "lapi/syscalls.h" 48 49static int sem_id = -1; 50static uid_t nobody_uid, root_uid; 51static union semun un; 52 53/* 54 * Note: semctl man-pages may have wrong description. We should use sem_ds 55 * struct(un.buf) instead of seminfo struct(un.__buf). 56 */ 57static inline int do_semctl(int semid, int semnum, int cmd) 58{ 59 struct semid_ds info; 60 61 un.buf = &info; 62 63 switch (tst_variant) { 64 case 0: 65 return tst_syscall(__NR_semctl, semid, semnum, cmd, un); 66 case 1: 67 return semctl(semid, semnum, cmd, un); 68 } 69 return -1; 70} 71 72static void test_info(void) 73{ 74 switch (tst_variant) { 75 case 0: 76 tst_res(TINFO, "Test SYS_semctl syscall"); 77 break; 78 case 1: 79 tst_res(TINFO, "Test libc semctl()"); 80 break; 81 } 82} 83 84static struct tcases { 85 uid_t *uid; 86 char *desc; 87} tests[] = { 88 {&nobody_uid, "with nobody user",}, 89 {&root_uid, "with root user",}, 90}; 91 92static void parse_proc_sysvipc(struct seminfo *info) 93{ 94 FILE *f = fopen("/proc/sysvipc/sem", "r"); 95 int semset_cnt = 0; 96 int sem_cnt = 0; 97 98 /* Eat header */ 99 for (;;) { 100 int c = fgetc(f); 101 102 if (c == '\n' || c == EOF) 103 break; 104 } 105 106 int nsems; 107 /* 108 * Sum sem set, nsems for all elements listed, which should equal 109 * the data returned in the seminfo structure. 110 */ 111 while (fscanf(f, "%*i %*i %*i %i %*i %*i %*i %*i %*i %*i", 112 &nsems) > 0){ 113 semset_cnt++; 114 sem_cnt += nsems; 115 } 116 117 if (info->semusz != semset_cnt) { 118 tst_res(TFAIL, "semusz = %i, expected %i", 119 info->semusz, semset_cnt); 120 } else { 121 tst_res(TPASS, "semset_cnt = %i", semset_cnt); 122 } 123 124 if (info->semaem != sem_cnt) { 125 tst_res(TFAIL, "semaem = %i, expected %i", 126 info->semaem, sem_cnt); 127 } else { 128 tst_res(TPASS, "sen_cnt = %i", sem_cnt); 129 } 130 131 fclose(f); 132} 133 134static void verify_semctl(unsigned int n) 135{ 136 struct tcases *tc = &tests[n]; 137 int i, semid, cnt = 0; 138 struct seminfo info; 139 union semun arg; 140 141 tst_res(TINFO, "Test SEM_STAT_ANY %s", tc->desc); 142 143 SAFE_SETEUID(*tc->uid); 144 145 arg.__buf = &info; 146 147 TEST(semctl(sem_id, 0, SEM_INFO, arg)); 148 149 if (TST_RET == -1) { 150 tst_res(TFAIL | TTERRNO, "semctl(sem_id, 0, SEM_INFO, ...)"); 151 return; 152 } 153 154 semid = do_semctl(TST_RET, 0, SEM_STAT_ANY); 155 156 if (errno == EFAULT) { 157 tst_res(TFAIL, "SEM_STAT_ANY doesn't pass the buffer " 158 "specified by the caller to kernel"); 159 return; 160 } else if (semid == -1) { 161 tst_res(TFAIL | TERRNO, "SEM_INFO haven't returned a valid index"); 162 } else { 163 tst_res(TPASS, "SEM_INFO returned valid index %li to semid %i", 164 TST_RET, semid); 165 } 166 167 for (i = 0; i <= TST_RET; i++) { 168 if ((do_semctl(i, 0, SEM_STAT_ANY)) != -1) 169 cnt++; 170 } 171 172 if (cnt == info.semusz) { 173 tst_res(TPASS, "Counted used = %i", cnt); 174 } else { 175 tst_res(TFAIL, "Counted used = %i, semuse = %i", 176 cnt, info.semusz); 177 } 178 179 parse_proc_sysvipc(&info); 180} 181 182static void setup(void) 183{ 184 struct passwd *ltpuser = SAFE_GETPWNAM("nobody"); 185 186 nobody_uid = ltpuser->pw_uid; 187 root_uid = 0; 188 test_info(); 189 190#if !HAVE_DECL_SEM_STAT_ANY 191 if (tst_variant == 1) 192 tst_brk(TCONF, "libc does not support semctl(SEM_STAT_ANY)"); 193#endif 194 195 sem_id = SAFE_SEMGET(IPC_PRIVATE, 2, IPC_CREAT | 0600); 196 197 TEST(do_semctl(sem_id, 0, SEM_STAT_ANY)); 198 if (TST_RET == -1) { 199 if (TST_ERR == EFAULT) 200 tst_brk(TFAIL, 201 "SEM_STAT_ANY doesn't pass the buffer specified by the caller to kernel"); 202 if (TST_ERR == EINVAL) 203 tst_brk(TCONF, "kernel doesn't support SEM_STAT_ANY"); 204 else 205 tst_brk(TBROK | TTERRNO, 206 "Current environment doesn't permit SEM_STAT_ANY"); 207 } 208} 209 210static void cleanup(void) 211{ 212 SAFE_SETEUID(root_uid); 213 214 if (sem_id >= 0) 215 SAFE_SEMCTL(sem_id, 0, IPC_RMID); 216} 217 218static struct tst_test test = { 219 .setup = setup, 220 .cleanup = cleanup, 221 .test = verify_semctl, 222 .tcnt = ARRAY_SIZE(tests), 223 .test_variants = 2, 224 .needs_root = 1, 225 .tags = (const struct tst_tag[]) { 226 {"glibc-git", "574500a108be"}, 227 {} 228 } 229}; 230