• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
49 static int sem_id = -1;
50 static uid_t nobody_uid, root_uid;
51 static 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  */
do_semctl(int semid,int semnum,int cmd)57 static 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 
test_info(void)72 static 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 
84 static 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 
parse_proc_sysvipc(struct seminfo * info)92 static 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 
verify_semctl(unsigned int n)134 static 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 
setup(void)182 static 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 
cleanup(void)210 static 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 
218 static 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