xref: /illumos-gate/usr/src/test/os-tests/tests/idmap/idmaptest.c (revision 24987e92ed84693f7d1351de150743664658894f)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2025 Bill Sommerfeld
14  */
15 
16 /*
17  * Demonstration of idmap collisions due to missing locking and a
18  * fencepost error in get_next_eph_uid.  On a 24-core Zen 4 system
19  * (EPYC 8224P) with a batchsize of 1000 and 20 threads, I see a
20  * couple dups per run.  Dups ending in 001 are from the fencepost error.
21  *
22  * Build with: gcc -O2 -o idmaptest idmaptest.c -lidmap
23  */
24 #include <err.h>
25 #include <idmap.h>
26 #include <pthread.h>
27 #include <stdio.h>
28 #include <stdbool.h>
29 #include <time.h>
30 
31 struct batch {
32 	idmap_rid_t base;
33 	int count;
34 	uid_t *idbuf;
35 	idmap_stat *statbuf;
36 	idmap_stat *statbuf2;
37 };
38 
39 static volatile int go;
40 static char sid[32];
41 
42 static pthread_mutex_t m;
43 static int ready_count;
44 static bool test_gid;
45 
46 void *
get_idmap_batch(void * arg)47 get_idmap_batch(void *arg)
48 {
49 	idmap_rid_t rid;
50 	int i;
51 	struct batch *b = arg;
52 
53 	(void) pthread_mutex_lock(&m);
54 	ready_count += 1;
55 	(void) pthread_mutex_unlock(&m);
56 
57 	/* spinwait until all threads are created */
58 	while (!go)
59 		;
60 
61 	for (i = 0; i < b->count; i++) {
62 		idmap_get_handle_t *h;
63 		rid = b->base + i;
64 		(void) idmap_get_create(&h);
65 		if (test_gid) {
66 			(void) idmap_get_gidbysid(h, sid, rid,
67 			    IDMAP_REQ_FLG_USE_CACHE,
68 			    &b->idbuf[i], &b->statbuf[i]);
69 		} else {
70 			(void) idmap_get_uidbysid(h, sid, rid,
71 			    IDMAP_REQ_FLG_USE_CACHE,
72 			    &b->idbuf[i], &b->statbuf[i]);
73 		}
74 		b->statbuf2[i] = idmap_get_mappings(h);
75 		idmap_get_destroy(h);
76 	}
77 	return (NULL);
78 }
79 
80 #define	NTHREAD 20
81 #define	BATCHSIZE 1000
82 #define	RIDBASE 2000
83 
84 int
cmpugid(const void * a,const void * b)85 cmpugid(const void *a, const void *b)
86 {
87 	uid_t x = *(const uid_t *)a;
88 	uid_t y = *(const uid_t *)b;
89 	if (x > y)
90 		return (1);
91 	if (x < y)
92 		return (-1);
93 	return (0);
94 }
95 
96 static const struct timespec usec100 = { 0, 100000 };
97 
98 bool
test_idmap()99 test_idmap()
100 {
101 	int i, j, err;
102 	time_t now;
103 	bool fail = false;
104 	const char *whatsit = test_gid ? "gid" : "uid";
105 
106 	pthread_t thread[NTHREAD];
107 	struct batch b[NTHREAD];
108 	uid_t idbuf[NTHREAD*BATCHSIZE];
109 
110 	go = 0;
111 	ready_count = 0;
112 
113 	(void) time(&now);
114 	(void) snprintf(sid, sizeof (sid), "S-1-5-21-44444444-%ld", now);
115 
116 	printf("testing for dup %s\n", whatsit);
117 
118 	printf("base sid: %s\n", sid);
119 
120 	for (i = 0; i < NTHREAD; i++) {
121 		b[i].base = RIDBASE +  i * BATCHSIZE;
122 		b[i].count = BATCHSIZE;
123 		b[i].idbuf = &idbuf[i * BATCHSIZE];
124 		b[i].statbuf = calloc(BATCHSIZE, sizeof (idmap_stat));
125 		b[i].statbuf2 = calloc(BATCHSIZE, sizeof (idmap_stat));
126 	}
127 	for (i = 0; i < NTHREAD; i++) {
128 		err = pthread_create(&thread[i], NULL, get_idmap_batch, &b[i]);
129 		if (err) {
130 			errc(EXIT_FAILURE, err,
131 			    "Failed to create thread %d", i);
132 		}
133 	}
134 
135 	for (;;) {
136 		int n;
137 		(void) pthread_mutex_lock(&m);
138 		n = ready_count;
139 		(void) pthread_mutex_unlock(&m);
140 		if (n == NTHREAD) {
141 			go = 1;
142 			break;
143 		}
144 		(void) nanosleep(&usec100, NULL);
145 	}
146 
147 	go = 1;
148 	for (i = 0; i < NTHREAD; i++) {
149 		int err = pthread_join(thread[i], NULL);
150 		if (err != 0) {
151 			printf("thread %d error %d\n", i, err);
152 			fail = true;
153 		}
154 	}
155 	for (i = 0; i < NTHREAD; i++) {
156 		for (j = 0; j < BATCHSIZE; j++) {
157 			if (b[i].statbuf[j]) {
158 				printf("fail 1: %d,%d => %d\n",
159 				    i, j, b[i].statbuf[j]);
160 				fail = true;
161 			}
162 			if (b[i].statbuf2[j]) {
163 				printf("fail 2: %d,%d => %d\n",
164 				    i, j, b[i].statbuf2[j]);
165 				fail = true;
166 			}
167 		}
168 	}
169 	qsort(idbuf, NTHREAD*BATCHSIZE, sizeof (uid_t), cmpugid);
170 	for (i = 1; i < NTHREAD*BATCHSIZE; i++) {
171 		if (idbuf[i] == idbuf[i-1]) {
172 			printf("dup %s %x\n", whatsit, idbuf[i]);
173 			fail = true;
174 		}
175 	}
176 	return (fail);
177 }
178 
179 bool
idmapd_running()180 idmapd_running()
181 {
182 	bool running = false;
183 	idmap_get_handle_t *h;
184 	idmap_stat status;
185 	idmap_stat s = idmap_get_create(&h);
186 	idmap_rid_t rid;
187 	char *domain;
188 
189 	if (s != 0) {
190 		fprintf(stderr, "Can't create idmap handle: %s\n",
191 		    idmap_stat2string(s));
192 		return (false);
193 	}
194 
195 	s = idmap_get_sidbyuid(h, 0,
196 	    IDMAP_REQ_FLG_USE_CACHE, &domain, &rid, &status);
197 	if (s != IDMAP_SUCCESS) {
198 		fprintf(stderr, "Can't create queue map request: %s\n",
199 		    idmap_stat2string(s));
200 	} else if ((s = idmap_get_mappings(h)) != 0) {
201 		fprintf(stderr, "idmap_get_mappings failed: %s\n",
202 		    idmap_stat2string(s));
203 	} else if (status != IDMAP_SUCCESS) {
204 		fprintf(stderr, "mapping of 0 failed: %s\n",
205 		    idmap_stat2string(status));
206 	} else {
207 		running = true;
208 	}
209 	idmap_get_destroy(h);
210 	return (running);
211 }
212 
213 int
main(int argc,char ** argv)214 main(int argc, char **argv)
215 {
216 	bool fail = false;
217 
218 	if (!idmapd_running()) {
219 		fprintf(stderr, "Is idmapd running?\n");
220 		exit(4);	/* signal a "SKIP" to the testing framework */
221 	}
222 	fprintf(stderr, "idmapd is running\n");
223 
224 	(void) pthread_mutex_init(&m, NULL);
225 
226 	test_gid = false;
227 	if (test_idmap())
228 		fail = true;
229 
230 	test_gid = true;
231 	if (test_idmap())
232 		fail = true;
233 
234 	if (fail) {
235 		fprintf(stderr, "FAIL: test failed\n");
236 		exit(EXIT_FAILURE);
237 	}
238 	fprintf(stderr, "PASS: duplicate ids not detected\n");
239 	exit(EXIT_SUCCESS);
240 }
241