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