Commit 0dc1ba24f7fff659725eecbba2c9ad679a0954cd

Authored by Eric Paris
1 parent 1c99042974

SELINUX: Make selinux cache VFS RCU walks safe

Now that the security modules can decide whether they support the
dcache RCU walk or not it's possible to make selinux a bit more
RCU friendly.  The SELinux AVC and security server access decision
code is RCU safe.  A specific piece of the LSM audit code may not
be RCU safe.

This patch makes the VFS RCU walk retry if it would hit the non RCU
safe chunk of code.  It will normally just work under RCU.  This is
done simply by passing the VFS RCU state as a flag down into the
avc_audit() code and returning ECHILD there if it would have an issue.

Based-on-patch-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Eric Paris <eparis@redhat.com>

Showing 3 changed files with 55 additions and 25 deletions Side-by-side Diff

security/selinux/avc.c
... ... @@ -471,6 +471,7 @@
471 471 * @avd: access vector decisions
472 472 * @result: result from avc_has_perm_noaudit
473 473 * @a: auxiliary audit data
  474 + * @flags: VFS walk flags
474 475 *
475 476 * Audit the granting or denial of permissions in accordance
476 477 * with the policy. This function is typically called by
477 478  
... ... @@ -481,9 +482,10 @@
481 482 * be performed under a lock, to allow the lock to be released
482 483 * before calling the auditing code.
483 484 */
484   -void avc_audit(u32 ssid, u32 tsid,
  485 +int avc_audit(u32 ssid, u32 tsid,
485 486 u16 tclass, u32 requested,
486   - struct av_decision *avd, int result, struct common_audit_data *a)
  487 + struct av_decision *avd, int result, struct common_audit_data *a,
  488 + unsigned flags)
487 489 {
488 490 struct common_audit_data stack_data;
489 491 u32 denied, audited;
490 492  
... ... @@ -515,11 +517,24 @@
515 517 else
516 518 audited = requested & avd->auditallow;
517 519 if (!audited)
518   - return;
  520 + return 0;
  521 +
519 522 if (!a) {
520 523 a = &stack_data;
521 524 COMMON_AUDIT_DATA_INIT(a, NONE);
522 525 }
  526 +
  527 + /*
  528 + * When in a RCU walk do the audit on the RCU retry. This is because
  529 + * the collection of the dname in an inode audit message is not RCU
  530 + * safe. Note this may drop some audits when the situation changes
  531 + * during retry. However this is logically just as if the operation
  532 + * happened a little later.
  533 + */
  534 + if ((a->type == LSM_AUDIT_DATA_FS) &&
  535 + (flags & IPERM_FLAG_RCU))
  536 + return -ECHILD;
  537 +
523 538 a->selinux_audit_data.tclass = tclass;
524 539 a->selinux_audit_data.requested = requested;
525 540 a->selinux_audit_data.ssid = ssid;
... ... @@ -529,6 +544,7 @@
529 544 a->lsm_pre_audit = avc_audit_pre_callback;
530 545 a->lsm_post_audit = avc_audit_post_callback;
531 546 common_lsm_audit(a);
  547 + return 0;
532 548 }
533 549  
534 550 /**
... ... @@ -793,6 +809,7 @@
793 809 * @tclass: target security class
794 810 * @requested: requested permissions, interpreted based on @tclass
795 811 * @auditdata: auxiliary audit data
  812 + * @flags: VFS walk flags
796 813 *
797 814 * Check the AVC to determine whether the @requested permissions are granted
798 815 * for the SID pair (@ssid, @tsid), interpreting the permissions
799 816  
800 817  
... ... @@ -802,14 +819,19 @@
802 819 * permissions are granted, -%EACCES if any permissions are denied, or
803 820 * another -errno upon other errors.
804 821 */
805   -int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
806   - u32 requested, struct common_audit_data *auditdata)
  822 +int avc_has_perm_flags(u32 ssid, u32 tsid, u16 tclass,
  823 + u32 requested, struct common_audit_data *auditdata,
  824 + unsigned flags)
807 825 {
808 826 struct av_decision avd;
809   - int rc;
  827 + int rc, rc2;
810 828  
811 829 rc = avc_has_perm_noaudit(ssid, tsid, tclass, requested, 0, &avd);
812   - avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
  830 +
  831 + rc2 = avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata,
  832 + flags);
  833 + if (rc2)
  834 + return rc2;
813 835 return rc;
814 836 }
815 837  
security/selinux/hooks.c
... ... @@ -1447,8 +1447,11 @@
1447 1447 }
1448 1448  
1449 1449 rc = avc_has_perm_noaudit(sid, sid, sclass, av, 0, &avd);
1450   - if (audit == SECURITY_CAP_AUDIT)
1451   - avc_audit(sid, sid, sclass, av, &avd, rc, &ad);
  1450 + if (audit == SECURITY_CAP_AUDIT) {
  1451 + int rc2 = avc_audit(sid, sid, sclass, av, &avd, rc, &ad, 0);
  1452 + if (rc2)
  1453 + return rc2;
  1454 + }
1452 1455 return rc;
1453 1456 }
1454 1457  
... ... @@ -1468,7 +1471,8 @@
1468 1471 static int inode_has_perm(const struct cred *cred,
1469 1472 struct inode *inode,
1470 1473 u32 perms,
1471   - struct common_audit_data *adp)
  1474 + struct common_audit_data *adp,
  1475 + unsigned flags)
1472 1476 {
1473 1477 struct inode_security_struct *isec;
1474 1478 struct common_audit_data ad;
... ... @@ -1488,7 +1492,7 @@
1488 1492 ad.u.fs.inode = inode;
1489 1493 }
1490 1494  
1491   - return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
  1495 + return avc_has_perm_flags(sid, isec->sid, isec->sclass, perms, adp, flags);
1492 1496 }
1493 1497  
1494 1498 /* Same as inode_has_perm, but pass explicit audit data containing
... ... @@ -1505,7 +1509,7 @@
1505 1509 COMMON_AUDIT_DATA_INIT(&ad, FS);
1506 1510 ad.u.fs.path.mnt = mnt;
1507 1511 ad.u.fs.path.dentry = dentry;
1508   - return inode_has_perm(cred, inode, av, &ad);
  1512 + return inode_has_perm(cred, inode, av, &ad, 0);
1509 1513 }
1510 1514  
1511 1515 /* Check whether a task can use an open file descriptor to
... ... @@ -1541,7 +1545,7 @@
1541 1545 /* av is zero if only checking access to the descriptor. */
1542 1546 rc = 0;
1543 1547 if (av)
1544   - rc = inode_has_perm(cred, inode, av, &ad);
  1548 + rc = inode_has_perm(cred, inode, av, &ad, 0);
1545 1549  
1546 1550 out:
1547 1551 return rc;
... ... @@ -2103,7 +2107,7 @@
2103 2107 file = file_priv->file;
2104 2108 inode = file->f_path.dentry->d_inode;
2105 2109 if (inode_has_perm(cred, inode,
2106   - FILE__READ | FILE__WRITE, NULL)) {
  2110 + FILE__READ | FILE__WRITE, NULL, 0)) {
2107 2111 drop_tty = 1;
2108 2112 }
2109 2113 }
... ... @@ -2649,10 +2653,6 @@
2649 2653 if (!mask)
2650 2654 return 0;
2651 2655  
2652   - /* May be droppable after audit */
2653   - if (flags & IPERM_FLAG_RCU)
2654   - return -ECHILD;
2655   -
2656 2656 COMMON_AUDIT_DATA_INIT(&ad, FS);
2657 2657 ad.u.fs.inode = inode;
2658 2658  
... ... @@ -2661,7 +2661,7 @@
2661 2661  
2662 2662 perms = file_mask_to_av(inode->i_mode, mask);
2663 2663  
2664   - return inode_has_perm(cred, inode, perms, &ad);
  2664 + return inode_has_perm(cred, inode, perms, &ad, flags);
2665 2665 }
2666 2666  
2667 2667 static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
... ... @@ -3208,7 +3208,7 @@
3208 3208 * new inode label or new policy.
3209 3209 * This check is not redundant - do not remove.
3210 3210 */
3211   - return inode_has_perm(cred, inode, open_file_to_av(file), NULL);
  3211 + return inode_has_perm(cred, inode, open_file_to_av(file), NULL, 0);
3212 3212 }
3213 3213  
3214 3214 /* task security operations */
security/selinux/include/avc.h
... ... @@ -54,11 +54,11 @@
54 54  
55 55 void __init avc_init(void);
56 56  
57   -void avc_audit(u32 ssid, u32 tsid,
  57 +int avc_audit(u32 ssid, u32 tsid,
58 58 u16 tclass, u32 requested,
59 59 struct av_decision *avd,
60 60 int result,
61   - struct common_audit_data *a);
  61 + struct common_audit_data *a, unsigned flags);
62 62  
63 63 #define AVC_STRICT 1 /* Ignore permissive mode. */
64 64 int avc_has_perm_noaudit(u32 ssid, u32 tsid,
... ... @@ -66,9 +66,17 @@
66 66 unsigned flags,
67 67 struct av_decision *avd);
68 68  
69   -int avc_has_perm(u32 ssid, u32 tsid,
70   - u16 tclass, u32 requested,
71   - struct common_audit_data *auditdata);
  69 +int avc_has_perm_flags(u32 ssid, u32 tsid,
  70 + u16 tclass, u32 requested,
  71 + struct common_audit_data *auditdata,
  72 + unsigned);
  73 +
  74 +static inline int avc_has_perm(u32 ssid, u32 tsid,
  75 + u16 tclass, u32 requested,
  76 + struct common_audit_data *auditdata)
  77 +{
  78 + return avc_has_perm_flags(ssid, tsid, tclass, requested, auditdata, 0);
  79 +}
72 80  
73 81 u32 avc_policy_seqno(void);
74 82