Skip to content

Commit

Permalink
SELinux: delay initialization of audit data in selinux_inode_permission
Browse files Browse the repository at this point in the history
We pay a rather large overhead initializing the common_audit_data.
Since we only need this information if we actually emit an audit
message there is little need to set it up in the hot path.  This patch
splits the functionality of avc_has_perm() into avc_has_perm_noaudit(),
avc_audit_required() and slow_avc_audit().  But we take care of setting
up to audit between required() and the actual audit call.  Thus saving
measurable time in a hot path.

Signed-off-by: Stephen Smalley <[email protected]>
Signed-off-by: Eric Paris <[email protected]>
  • Loading branch information
eparis committed Apr 9, 2012
1 parent 154c50c commit 2e33405
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 70 deletions.
63 changes: 1 addition & 62 deletions security/selinux/avc.c
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
}

/* This is the slow part of avc audit with big stack footprint */
static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
u32 requested, u32 audited, u32 denied,
struct common_audit_data *a,
unsigned flags)
Expand Down Expand Up @@ -496,67 +496,6 @@ static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
return 0;
}

/**
* avc_audit - Audit the granting or denial of permissions.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @requested: requested permissions
* @avd: access vector decisions
* @result: result from avc_has_perm_noaudit
* @a: auxiliary audit data
* @flags: VFS walk flags
*
* Audit the granting or denial of permissions in accordance
* with the policy. This function is typically called by
* avc_has_perm() after a permission check, but can also be
* called directly by callers who use avc_has_perm_noaudit()
* in order to separate the permission check from the auditing.
* For example, this separation is useful when the permission check must
* be performed under a lock, to allow the lock to be released
* before calling the auditing code.
*/
inline int avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd, int result, struct common_audit_data *a,
unsigned flags)
{
u32 denied, audited;
denied = requested & ~avd->allowed;
if (unlikely(denied)) {
audited = denied & avd->auditdeny;
/*
* a->selinux_audit_data->auditdeny is TRICKY! Setting a bit in
* this field means that ANY denials should NOT be audited if
* the policy contains an explicit dontaudit rule for that
* permission. Take notice that this is unrelated to the
* actual permissions that were denied. As an example lets
* assume:
*
* denied == READ
* avd.auditdeny & ACCESS == 0 (not set means explicit rule)
* selinux_audit_data->auditdeny & ACCESS == 1
*
* We will NOT audit the denial even though the denied
* permission was READ and the auditdeny checks were for
* ACCESS
*/
if (a &&
a->selinux_audit_data->auditdeny &&
!(a->selinux_audit_data->auditdeny & avd->auditdeny))
audited = 0;
} else if (result)
audited = denied = requested;
else
audited = requested & avd->auditallow;
if (likely(!audited))
return 0;

return slow_avc_audit(ssid, tsid, tclass,
requested, audited, denied,
a, flags);
}

/**
* avc_add_callback - Register a callback for security events.
* @callback: callback function
Expand Down
30 changes: 27 additions & 3 deletions security/selinux/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -2684,6 +2684,11 @@ static int selinux_inode_permission(struct inode *inode, int mask)
u32 perms;
bool from_access;
unsigned flags = mask & MAY_NOT_BLOCK;
struct inode_security_struct *isec;
u32 sid;
struct av_decision avd;
int rc, rc2;
u32 audited, denied;

from_access = mask & MAY_ACCESS;
mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND);
Expand All @@ -2692,16 +2697,35 @@ static int selinux_inode_permission(struct inode *inode, int mask)
if (!mask)
return 0;

validate_creds(cred);

if (unlikely(IS_PRIVATE(inode)))
return 0;

perms = file_mask_to_av(inode->i_mode, mask);

sid = cred_sid(cred);
isec = inode->i_security;

rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd);
audited = avc_audit_required(perms, &avd, rc,
from_access ? FILE__AUDIT_ACCESS : 0,
&denied);
if (likely(!audited))
return rc;

COMMON_AUDIT_DATA_INIT(&ad, INODE);
ad.selinux_audit_data = &sad;
ad.u.inode = inode;

if (from_access)
ad.selinux_audit_data->auditdeny |= FILE__AUDIT_ACCESS;

perms = file_mask_to_av(inode->i_mode, mask);

return inode_has_perm(cred, inode, perms, &ad, flags);
rc2 = slow_avc_audit(sid, isec->sid, isec->sclass, perms,
audited, denied, &ad, flags);
if (rc2)
return rc2;
return rc;
}

static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
Expand Down
82 changes: 77 additions & 5 deletions security/selinux/include/avc.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,83 @@ struct selinux_audit_data {

void __init avc_init(void);

int avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd,
int result,
struct common_audit_data *a, unsigned flags);
static inline u32 avc_audit_required(u32 requested,
struct av_decision *avd,
int result,
u32 auditdeny,
u32 *deniedp)
{
u32 denied, audited;
denied = requested & ~avd->allowed;
if (unlikely(denied)) {
audited = denied & avd->auditdeny;
/*
* auditdeny is TRICKY! Setting a bit in
* this field means that ANY denials should NOT be audited if
* the policy contains an explicit dontaudit rule for that
* permission. Take notice that this is unrelated to the
* actual permissions that were denied. As an example lets
* assume:
*
* denied == READ
* avd.auditdeny & ACCESS == 0 (not set means explicit rule)
* auditdeny & ACCESS == 1
*
* We will NOT audit the denial even though the denied
* permission was READ and the auditdeny checks were for
* ACCESS
*/
if (auditdeny && !(auditdeny & avd->auditdeny))
audited = 0;
} else if (result)
audited = denied = requested;
else
audited = requested & avd->auditallow;
*deniedp = denied;
return audited;
}

int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
u32 requested, u32 audited, u32 denied,
struct common_audit_data *a,
unsigned flags);

/**
* avc_audit - Audit the granting or denial of permissions.
* @ssid: source security identifier
* @tsid: target security identifier
* @tclass: target security class
* @requested: requested permissions
* @avd: access vector decisions
* @result: result from avc_has_perm_noaudit
* @a: auxiliary audit data
* @flags: VFS walk flags
*
* Audit the granting or denial of permissions in accordance
* with the policy. This function is typically called by
* avc_has_perm() after a permission check, but can also be
* called directly by callers who use avc_has_perm_noaudit()
* in order to separate the permission check from the auditing.
* For example, this separation is useful when the permission check must
* be performed under a lock, to allow the lock to be released
* before calling the auditing code.
*/
static inline int avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested,
struct av_decision *avd,
int result,
struct common_audit_data *a, unsigned flags)
{
u32 audited, denied;
audited = avc_audit_required(requested, avd, result,
a ? a->selinux_audit_data->auditdeny : 0,
&denied);
if (likely(!audited))
return 0;
return slow_avc_audit(ssid, tsid, tclass,
requested, audited, denied,
a, flags);
}

#define AVC_STRICT 1 /* Ignore permissive mode. */
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
Expand Down

0 comments on commit 2e33405

Please sign in to comment.