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