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 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 94 usage(char *prog) 95 { 96 fprintf(stderr, "usage: %s [num sids] [num iters]\n", prog); 97 } 98 99 int 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