xref: /illumos-gate/usr/src/test/os-tests/tests/ksid/ksid.c (revision 7f3d7c9289dee6488b3cd2848a68c0b8580d750c)
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 2020 Tintri by DDN, Inc. All rights reserved.
14  * Copyright 2025 RackTop Systems, Inc.
15  */
16 
17 /*
18  * Test the kernel ksid interfaces used by ZFS and SMB
19  */
20 
21 #include <sys/sid.h>
22 #include <sys/time.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <strings.h>
26 #include <errno.h>
27 
28 extern int ksl_bin_search_cutoff;
29 
30 #define	MAX_DOMAINS 8
31 #define	DEFAULT_NUMSIDS 128
32 #define	DEFAULT_ITERS 1
33 
34 char ksid_sids[MAX_DOMAINS+2][256];
35 ksid_t *bad_ksids;
36 
37 boolean_t
38 run_test(ksidlist_t *ksl, uint32_t idx, uint32_t numsids,
39     uint32_t iters, boolean_t pid)
40 {
41 	uint64_t savenum = ksl->ksl_nsid;
42 	uint64_t success = 0;
43 	hrtime_t start, end;
44 	uint32_t i, id;
45 	boolean_t expect_success = (idx < numsids);
46 	ksid_t *ks;
47 
48 	ksl->ksl_nsid = numsids;
49 
50 	start = gethrtime();
51 	if (pid) {
52 		/*
53 		 * Only the first savenum entries are sorted,
54 		 * but ksl_sorted is only used when numsids > cutoff.
55 		 * Use ksl_sorted when idx is among them
56 		 */
57 		if (numsids <= ksl_bin_search_cutoff)
58 			id = ksl->ksl_sids[idx].ks_id;
59 		else if (idx >= savenum)
60 			id = bad_ksids[idx - savenum].ks_id;
61 		else
62 			id = ksl->ksl_sorted[idx]->ks_id;
63 
64 		for (i = 0; i < iters; i++) {
65 			success += (ksidlist_has_pid(ksl, id) ==
66 			    expect_success) ? 1 : 0;
67 		}
68 	} else {
69 		if (idx >= savenum)
70 			ks = &bad_ksids[idx - savenum];
71 		else
72 			ks = &ksl->ksl_sids[idx];
73 
74 		for (i = 0; i < iters; i++) {
75 			success += (ksidlist_has_sid(ksl,
76 			    ksid_getdomain(ks), ks->ks_rid) ==
77 			    expect_success) ? 1 : 0;
78 		}
79 	}
80 	end = gethrtime();
81 
82 	if (iters > 1) {
83 		printf("avg time to %s %s in %d sids "
84 		    "over %d iterations: %llu\n",
85 		    (expect_success) ? "find" : "not find",
86 		    (pid) ? "pid" : "sid", numsids, iters,
87 		    (end - start) / iters);
88 	}
89 
90 	ksl->ksl_nsid = savenum;
91 	return (success == iters);
92 }
93 
94 void
95 usage(char *prog)
96 {
97 	fprintf(stderr, "usage: %s [num sids] [num iters]\n", prog);
98 }
99 
100 int
101 main(int argc, char *argv[])
102 {
103 	credsid_t *kcr;
104 	ksidlist_t *ksl;
105 	uint64_t num_failures = 0;
106 	uint32_t i, j, numsids, iters;
107 	boolean_t retry;
108 
109 	if (argc > 1) {
110 		errno = 0;
111 		numsids = strtoul(argv[1], NULL, 0);
112 		if (errno != 0) {
113 			fprintf(stderr, "error decoding numsids (%s): \n",
114 			    argv[1]);
115 			usage(argv[0]);
116 			perror(argv[0]);
117 			return (errno);
118 		}
119 	} else {
120 		numsids = DEFAULT_NUMSIDS;
121 	}
122 
123 	if (argc > 2) {
124 		iters = strtoul(argv[2], NULL, 0);
125 		if (errno != 0) {
126 			fprintf(stderr, "error decoding iters (%s): \n",
127 			    argv[2]);
128 			usage(argv[0]);
129 			perror(argv[0]);
130 			return (errno);
131 		}
132 	} else {
133 		iters = DEFAULT_ITERS;
134 	}
135 
136 	if (numsids < 1 || iters < 1) {
137 		fprintf(stderr, "both numsids and iters "
138 		    "need to be at least 1\n");
139 		usage(argv[0]);
140 		return (2);
141 	}
142 
143 	/* create MAX_DOMAINS random SIDs */
144 	for (i = 0; i < MAX_DOMAINS; i++) {
145 		(void) snprintf(ksid_sids[i], sizeof (ksid_sids[0]),
146 		    "S-1-5-21-%u-%u-%u",
147 		    arc4random(), arc4random(), arc4random());
148 	}
149 
150 	/* create two unique SIDs for negative testing */
151 	for (j = MAX_DOMAINS; j < MAX_DOMAINS+2; j++) {
152 		do {
153 			retry = B_FALSE;
154 			(void) snprintf(ksid_sids[j], sizeof (ksid_sids[0]),
155 			    "S-1-5-21-%u-%u-%u",
156 			    arc4random(), arc4random(), arc4random());
157 			for (i = 0; i < MAX_DOMAINS; i++) {
158 				if (strncmp(ksid_sids[i], ksid_sids[j],
159 				    sizeof (ksid_sids[0])) == 0) {
160 					retry = B_TRUE;
161 					break;
162 				}
163 			}
164 		} while (retry);
165 	}
166 
167 	ksl = calloc(1, KSIDLIST_MEM(numsids));
168 	ksl->ksl_ref = 1;
169 	ksl->ksl_nsid = numsids;
170 	ksl->ksl_neid = 0;
171 
172 	bad_ksids = malloc(sizeof (ksid_t) * numsids);
173 
174 	/* initialize numsids random sids and ids in the sidlist */
175 	for (i = 0; i < numsids; i++) {
176 		uint32_t idx = arc4random_uniform(MAX_DOMAINS);
177 
178 		ksl->ksl_sids[i].ks_id = arc4random();
179 		ksl->ksl_sids[i].ks_rid = arc4random();
180 		ksl->ksl_sids[i].ks_domain = ksid_lookupdomain(ksid_sids[idx]);
181 		ksl->ksl_sids[i].ks_attr = 0;
182 	}
183 
184 	/*
185 	 * create numsids random sids, whose sids and ids aren't in
186 	 * the sidlist for negative testing
187 	 */
188 	for (i = 0; i < numsids; i++) {
189 		bad_ksids[i].ks_attr = 0;
190 		bad_ksids[i].ks_rid = arc4random();
191 		bad_ksids[i].ks_domain =
192 		    ksid_lookupdomain(ksid_sids[MAX_DOMAINS +
193 		    (arc4random() % 2)]);
194 
195 		do {
196 			retry = B_FALSE;
197 			bad_ksids[i].ks_id = arc4random();
198 			for (j = 0; j < numsids; j++) {
199 				if (ksl->ksl_sids[j].ks_id ==
200 				    bad_ksids[i].ks_id) {
201 					retry = B_TRUE;
202 					break;
203 				}
204 			}
205 		} while (retry);
206 	}
207 
208 	kcr = kcrsid_setsidlist(NULL, ksl);
209 
210 	/* run tests */
211 	for (i = 1; i <= numsids; i++) {
212 		uint32_t s_idx = arc4random_uniform(i);
213 		uint32_t f_idx = numsids + i - 1;
214 
215 		if (!run_test(ksl, s_idx, i, iters, B_FALSE)) {
216 			fprintf(stderr, "Sid search failed unexpectedly: "
217 			    "numsids %u\n", i);
218 			fprintf(stderr, "Bad SID: id %u rid %u domain %s\n",
219 			    ksl->ksl_sids[s_idx].ks_id,
220 			    ksl->ksl_sids[s_idx].ks_rid,
221 			    ksid_getdomain(&ksl->ksl_sids[s_idx]));
222 			num_failures++;
223 		}
224 		if (!run_test(ksl, s_idx, i, iters, B_TRUE)) {
225 			fprintf(stderr, "Pid search failed unexpectedly: "
226 			    "numsids %u\n", i);
227 			fprintf(stderr, "Bad PID: id %u rid %u domain %s\n",
228 			    ksl->ksl_sorted[s_idx]->ks_id,
229 			    ksl->ksl_sorted[s_idx]->ks_rid,
230 			    ksid_getdomain(ksl->ksl_sorted[s_idx]));
231 			num_failures++;
232 		}
233 
234 		if (!run_test(ksl, f_idx, i, iters, B_FALSE)) {
235 			fprintf(stderr, "Sid search succeeded unexpectedly: "
236 			    "numsids %u\n", i);
237 			fprintf(stderr, "Bad SID: id %u rid %u domain %s\n",
238 			    ksl->ksl_sids[f_idx].ks_id,
239 			    ksl->ksl_sids[f_idx].ks_rid,
240 			    ksid_getdomain(&ksl->ksl_sids[f_idx]));
241 			num_failures++;
242 		}
243 		if (!run_test(ksl, f_idx, i, iters, B_TRUE)) {
244 			fprintf(stderr, "Pid search succeeded unexpectedly: "
245 			    "numsids %u\n", i);
246 			fprintf(stderr, "Bad PID: id %u rid %u domain %s\n",
247 			    ksl->ksl_sids[f_idx].ks_id,
248 			    ksl->ksl_sids[f_idx].ks_rid,
249 			    ksid_getdomain(&ksl->ksl_sids[f_idx]));
250 			num_failures++;
251 		}
252 	}
253 
254 	for (i = 0; i < numsids - 1; i++) {
255 		if (ksl->ksl_sorted[i]->ks_id > ksl->ksl_sorted[i + 1]->ks_id) {
256 			fprintf(stderr, "PID %u is not sorted correctly: "
257 			    "%u %u\n", i, ksl->ksl_sorted[i]->ks_id,
258 			    ksl->ksl_sorted[i + 1]->ks_id);
259 			num_failures++;
260 		}
261 		if (ksl->ksl_sids[i].ks_rid > ksl->ksl_sids[i + 1].ks_rid) {
262 			fprintf(stderr, "RID %u is not sorted correctly: "
263 			    "%u %u\n", i, ksl->ksl_sids[i].ks_rid,
264 			    ksl->ksl_sids[i + 1].ks_rid);
265 			num_failures++;
266 		} else if (ksl->ksl_sids[i].ks_rid ==
267 		    ksl->ksl_sids[i + 1].ks_rid &&
268 		    strcmp(ksid_getdomain(&ksl->ksl_sids[i]),
269 		    ksid_getdomain(&ksl->ksl_sids[i + 1])) > 0) {
270 			fprintf(stderr, "SID %u is not sorted correctly: "
271 			    "%s %s\n", i, ksl->ksl_sids[i].ks_rid,
272 			    ksl->ksl_sids[i + 1].ks_rid);
273 			num_failures++;
274 		}
275 	}
276 
277 	if (num_failures != 0) {
278 		fprintf(stderr, "%d failures detected; dumping SID table\n",
279 		    num_failures);
280 		for (i = 0; i < numsids; i++) {
281 			fprintf(stderr, "SID %u: %s-%u -> %u\n", i,
282 			    ksid_getdomain(&ksl->ksl_sids[i]),
283 			    ksl->ksl_sids[i].ks_rid,
284 			    ksl->ksl_sids[i].ks_id);
285 		}
286 
287 		for (i = 0; i < numsids; i++) {
288 			fprintf(stderr, "SID %u: %s-%u -> %u\n",
289 			    i + numsids,
290 			    ksid_getdomain(&bad_ksids[i]),
291 			    bad_ksids[i].ks_rid,
292 			    bad_ksids[i].ks_id);
293 		}
294 
295 		for (i = 0; i < numsids; i++) {
296 			fprintf(stderr, "PID %u: %u\n", i,
297 			    ksl->ksl_sorted[i]->ks_id);
298 		}
299 	} else {
300 		printf("all tests completed successfully!\n");
301 	}
302 
303 	/* Clear ksidlist. This should free the credsid_t. */
304 	kcr = kcrsid_setsidlist(kcr, NULL);
305 	/* Add a core ksid. This should allocate a credsid_t. */
306 	kcr = kcrsid_setsid(kcr, &ksl->ksl_sids[0], KSID_GROUP);
307 	/* Set the ksidlist to NULL again. This should be a no-op. */
308 	kcr = kcrsid_setsidlist(kcr, NULL);
309 	kcrsid_rele(kcr);
310 
311 	return (num_failures);
312 }
313