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
run_test(ksidlist_t * ksl,uint32_t idx,uint32_t numsids,uint32_t iters,boolean_t pid)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
usage(char * prog)95 usage(char *prog)
96 {
97 fprintf(stderr, "usage: %s [num sids] [num iters]\n", prog);
98 }
99
100 int
main(int argc,char * argv[])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