Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selinux: store role transitions in a hash table

Currently, they are stored in a linked list, which adds significant
overhead to security_transition_sid(). On Fedora, with 428 role
transitions in policy, converting this list to a hash table cuts down
its run time by about 50%. This was measured by running 'stress-ng --msg
1 --msg-ops 100000' under perf with and without this patch.

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>

authored by

Ondrej Mosnacek and committed by
Paul Moore
e67b2ec9 433e3aa3

+109 -62
+94 -48
security/selinux/ss/policydb.c
··· 352 352 return 0; 353 353 } 354 354 355 + static int role_tr_destroy(void *key, void *datum, void *p) 356 + { 357 + kfree(key); 358 + kfree(datum); 359 + return 0; 360 + } 361 + 355 362 static void ocontext_destroy(struct ocontext *c, int i) 356 363 { 357 364 if (!c) ··· 463 456 v = key1->target_class - key2->target_class; 464 457 465 458 return v; 459 + } 460 + 461 + static u32 role_trans_hash(struct hashtab *h, const void *k) 462 + { 463 + const struct role_trans_key *key = k; 464 + 465 + return (key->role + (key->type << 3) + (key->tclass << 5)) & 466 + (h->size - 1); 467 + } 468 + 469 + static int role_trans_cmp(struct hashtab *h, const void *k1, const void *k2) 470 + { 471 + const struct role_trans_key *key1 = k1, *key2 = k2; 472 + int v; 473 + 474 + v = key1->role - key2->role; 475 + if (v) 476 + return v; 477 + 478 + v = key1->type - key2->type; 479 + if (v) 480 + return v; 481 + 482 + return key1->tclass - key2->tclass; 466 483 } 467 484 468 485 /* ··· 759 728 struct genfs *g, *gtmp; 760 729 int i; 761 730 struct role_allow *ra, *lra = NULL; 762 - struct role_trans *tr, *ltr = NULL; 763 731 764 732 for (i = 0; i < SYM_NUM; i++) { 765 733 cond_resched(); ··· 805 775 806 776 cond_policydb_destroy(p); 807 777 808 - for (tr = p->role_tr; tr; tr = tr->next) { 809 - cond_resched(); 810 - kfree(ltr); 811 - ltr = tr; 812 - } 813 - kfree(ltr); 778 + hashtab_map(p->role_tr, role_tr_destroy, NULL); 779 + hashtab_destroy(p->role_tr); 814 780 815 781 for (ra = p->role_allow; ra; ra = ra->next) { 816 782 cond_resched(); ··· 2277 2251 int policydb_read(struct policydb *p, void *fp) 2278 2252 { 2279 2253 struct role_allow *ra, *lra; 2280 - struct role_trans *tr, *ltr; 2254 + struct role_trans_key *rtk = NULL; 2255 + struct role_trans_datum *rtd = NULL; 2281 2256 int i, j, rc; 2282 2257 __le32 buf[4]; 2283 2258 u32 len, nprim, nel; ··· 2443 2416 if (rc) 2444 2417 goto bad; 2445 2418 nel = le32_to_cpu(buf[0]); 2446 - ltr = NULL; 2419 + 2420 + p->role_tr = hashtab_create(role_trans_hash, role_trans_cmp, nel); 2421 + if (!p->role_tr) 2422 + goto bad; 2447 2423 for (i = 0; i < nel; i++) { 2448 2424 rc = -ENOMEM; 2449 - tr = kzalloc(sizeof(*tr), GFP_KERNEL); 2450 - if (!tr) 2425 + rtk = kmalloc(sizeof(*rtk), GFP_KERNEL); 2426 + if (!rtk) 2451 2427 goto bad; 2452 - if (ltr) 2453 - ltr->next = tr; 2454 - else 2455 - p->role_tr = tr; 2428 + 2429 + rc = -ENOMEM; 2430 + rtd = kmalloc(sizeof(*rtd), GFP_KERNEL); 2431 + if (!rtd) 2432 + goto bad; 2433 + 2456 2434 rc = next_entry(buf, fp, sizeof(u32)*3); 2457 2435 if (rc) 2458 2436 goto bad; 2459 2437 2460 2438 rc = -EINVAL; 2461 - tr->role = le32_to_cpu(buf[0]); 2462 - tr->type = le32_to_cpu(buf[1]); 2463 - tr->new_role = le32_to_cpu(buf[2]); 2439 + rtk->role = le32_to_cpu(buf[0]); 2440 + rtk->type = le32_to_cpu(buf[1]); 2441 + rtd->new_role = le32_to_cpu(buf[2]); 2464 2442 if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { 2465 2443 rc = next_entry(buf, fp, sizeof(u32)); 2466 2444 if (rc) 2467 2445 goto bad; 2468 - tr->tclass = le32_to_cpu(buf[0]); 2446 + rtk->tclass = le32_to_cpu(buf[0]); 2469 2447 } else 2470 - tr->tclass = p->process_class; 2448 + rtk->tclass = p->process_class; 2471 2449 2472 2450 rc = -EINVAL; 2473 - if (!policydb_role_isvalid(p, tr->role) || 2474 - !policydb_type_isvalid(p, tr->type) || 2475 - !policydb_class_isvalid(p, tr->tclass) || 2476 - !policydb_role_isvalid(p, tr->new_role)) 2451 + if (!policydb_role_isvalid(p, rtk->role) || 2452 + !policydb_type_isvalid(p, rtk->type) || 2453 + !policydb_class_isvalid(p, rtk->tclass) || 2454 + !policydb_role_isvalid(p, rtd->new_role)) 2477 2455 goto bad; 2478 - ltr = tr; 2456 + 2457 + rc = hashtab_insert(p->role_tr, rtk, rtd); 2458 + if (rc) 2459 + goto bad; 2460 + 2461 + rtk = NULL; 2462 + rtd = NULL; 2479 2463 } 2480 2464 2481 2465 rc = next_entry(buf, fp, sizeof(u32)); ··· 2574 2536 out: 2575 2537 return rc; 2576 2538 bad: 2539 + kfree(rtk); 2540 + kfree(rtd); 2577 2541 policydb_destroy(p); 2578 2542 goto out; 2579 2543 } ··· 2693 2653 return 0; 2694 2654 } 2695 2655 2696 - static int role_trans_write(struct policydb *p, void *fp) 2656 + static int role_trans_write_one(void *key, void *datum, void *ptr) 2697 2657 { 2698 - struct role_trans *r = p->role_tr; 2699 - struct role_trans *tr; 2658 + struct role_trans_key *rtk = key; 2659 + struct role_trans_datum *rtd = datum; 2660 + struct policy_data *pd = ptr; 2661 + void *fp = pd->fp; 2662 + struct policydb *p = pd->p; 2700 2663 __le32 buf[3]; 2701 - size_t nel; 2702 2664 int rc; 2703 2665 2704 - nel = 0; 2705 - for (tr = r; tr; tr = tr->next) 2706 - nel++; 2707 - buf[0] = cpu_to_le32(nel); 2666 + buf[0] = cpu_to_le32(rtk->role); 2667 + buf[1] = cpu_to_le32(rtk->type); 2668 + buf[2] = cpu_to_le32(rtd->new_role); 2669 + rc = put_entry(buf, sizeof(u32), 3, fp); 2670 + if (rc) 2671 + return rc; 2672 + if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { 2673 + buf[0] = cpu_to_le32(rtk->tclass); 2674 + rc = put_entry(buf, sizeof(u32), 1, fp); 2675 + if (rc) 2676 + return rc; 2677 + } 2678 + return 0; 2679 + } 2680 + 2681 + static int role_trans_write(struct policydb *p, void *fp) 2682 + { 2683 + struct policy_data pd = { .p = p, .fp = fp }; 2684 + __le32 buf[1]; 2685 + int rc; 2686 + 2687 + buf[0] = cpu_to_le32(p->role_tr->nel); 2708 2688 rc = put_entry(buf, sizeof(u32), 1, fp); 2709 2689 if (rc) 2710 2690 return rc; 2711 - for (tr = r; tr; tr = tr->next) { 2712 - buf[0] = cpu_to_le32(tr->role); 2713 - buf[1] = cpu_to_le32(tr->type); 2714 - buf[2] = cpu_to_le32(tr->new_role); 2715 - rc = put_entry(buf, sizeof(u32), 3, fp); 2716 - if (rc) 2717 - return rc; 2718 - if (p->policyvers >= POLICYDB_VERSION_ROLETRANS) { 2719 - buf[0] = cpu_to_le32(tr->tclass); 2720 - rc = put_entry(buf, sizeof(u32), 1, fp); 2721 - if (rc) 2722 - return rc; 2723 - } 2724 - } 2725 2691 2726 - return 0; 2692 + return hashtab_map(p->role_tr, role_trans_write_one, &pd); 2727 2693 } 2728 2694 2729 2695 static int role_allow_write(struct role_allow *r, void *fp)
+5 -3
security/selinux/ss/policydb.h
··· 81 81 struct ebitmap types; /* set of authorized types for role */ 82 82 }; 83 83 84 - struct role_trans { 84 + struct role_trans_key { 85 85 u32 role; /* current role */ 86 86 u32 type; /* program executable type, or new object type */ 87 87 u32 tclass; /* process class, or new object class */ 88 + }; 89 + 90 + struct role_trans_datum { 88 91 u32 new_role; /* new role */ 89 - struct role_trans *next; 90 92 }; 91 93 92 94 struct filename_trans_key { ··· 263 261 struct avtab te_avtab; 264 262 265 263 /* role transitions */ 266 - struct role_trans *role_tr; 264 + struct hashtab *role_tr; 267 265 268 266 /* file transitions with the last path component */ 269 267 /* quickly exclude lookups when parent ttype has no rules */
+10 -11
security/selinux/ss/services.c
··· 1731 1731 struct class_datum *cladatum = NULL; 1732 1732 struct context *scontext, *tcontext, newcontext; 1733 1733 struct sidtab_entry *sentry, *tentry; 1734 - struct role_trans *roletr = NULL; 1735 1734 struct avtab_key avkey; 1736 1735 struct avtab_datum *avdatum; 1737 1736 struct avtab_node *node; ··· 1863 1864 /* Check for class-specific changes. */ 1864 1865 if (specified & AVTAB_TRANSITION) { 1865 1866 /* Look for a role transition rule. */ 1866 - for (roletr = policydb->role_tr; roletr; 1867 - roletr = roletr->next) { 1868 - if ((roletr->role == scontext->role) && 1869 - (roletr->type == tcontext->type) && 1870 - (roletr->tclass == tclass)) { 1871 - /* Use the role transition rule. */ 1872 - newcontext.role = roletr->new_role; 1873 - break; 1874 - } 1875 - } 1867 + struct role_trans_datum *rtd; 1868 + struct role_trans_key rtk = { 1869 + .role = scontext->role, 1870 + .type = tcontext->type, 1871 + .tclass = tclass, 1872 + }; 1873 + 1874 + rtd = hashtab_search(policydb->role_tr, &rtk); 1875 + if (rtd) 1876 + newcontext.role = rtd->new_role; 1876 1877 } 1877 1878 1878 1879 /* Set the MLS attributes.