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