Skip to content

Commit

Permalink
Merge branch 'selinux' ("struct common_audit_data" sanitizer)
Browse files Browse the repository at this point in the history
Merge common_audit_data cleanup patches from Eric Paris.

This is really too late, but it's a long-overdue cleanup of the costly
wrapper functions for the security layer.

The "struct common_audit_data" is used all over in critical paths,
allocated and initialized on the stack.  And used to be much too large,
causing not only unnecessarily big stack frames but the clearing of the
(mostly useless) data was also very visible in profiles.

As a particular example, in one microbenchmark for just doing "stat()"
over files a lot, selinux_inode_permission() used 7% of the CPU time.
That's despite the fact that it doesn't actually *do* anything: it is
just a helper wrapper function in the selinux security layer.

This patch-series shrinks "struct common_audit_data" sufficiently that
code generation for these kinds of wrapper functions is improved
noticeably, and we spend much less time just initializing data that we
will never use.

The functions still get called all the time, and it still shows up at
3.5+% in my microbenchmark, but it's quite a bit lower down the list,
and much less noticeable.

* Emailed patches from Eric Paris <eparis@redhat.com>:
  lsm_audit: don't specify the audit pre/post callbacks in 'struct common_audit_data'
  SELinux: do not allocate stack space for AVC data unless needed
  SELinux: remove avd from slow_avc_audit()
  SELinux: remove avd from selinux_audit_data
  LSM: shrink the common_audit_data data union
  LSM: shrink sizeof LSM specific portion of common_audit_data
  • Loading branch information
Linus Torvalds committed Apr 4, 2012
2 parents 3ff8f93 + b61c37f commit a5149bf
Show file tree
Hide file tree
Showing 18 changed files with 411 additions and 271 deletions.
96 changes: 25 additions & 71 deletions include/linux/lsm_audit.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@
#include <linux/key.h>
#include <linux/skbuff.h>

struct lsm_network_audit {
int netif;
struct sock *sk;
u16 family;
__be16 dport;
__be16 sport;
union {
struct {
__be32 daddr;
__be32 saddr;
} v4;
struct {
struct in6_addr daddr;
struct in6_addr saddr;
} v6;
} fam;
};

/* Auxiliary data to use in generating the audit record. */
struct common_audit_data {
Expand All @@ -41,23 +58,7 @@ struct common_audit_data {
struct path path;
struct dentry *dentry;
struct inode *inode;
struct {
int netif;
struct sock *sk;
u16 family;
__be16 dport;
__be16 sport;
union {
struct {
__be32 daddr;
__be32 saddr;
} v4;
struct {
struct in6_addr daddr;
struct in6_addr saddr;
} v6;
} fam;
} net;
struct lsm_network_audit *net;
int cap;
int ipc_id;
struct task_struct *tsk;
Expand All @@ -72,64 +73,15 @@ struct common_audit_data {
/* this union contains LSM specific data */
union {
#ifdef CONFIG_SECURITY_SMACK
/* SMACK data */
struct smack_audit_data {
const char *function;
char *subject;
char *object;
char *request;
int result;
} smack_audit_data;
struct smack_audit_data *smack_audit_data;
#endif
#ifdef CONFIG_SECURITY_SELINUX
/* SELinux data */
struct {
u32 ssid;
u32 tsid;
u16 tclass;
u32 requested;
u32 audited;
u32 denied;
/*
* auditdeny is a bit tricky and unintuitive. See the
* comments in avc.c for it's meaning and usage.
*/
u32 auditdeny;
struct av_decision *avd;
int result;
} selinux_audit_data;
struct selinux_audit_data *selinux_audit_data;
#endif
#ifdef CONFIG_SECURITY_APPARMOR
struct {
int error;
int op;
int type;
void *profile;
const char *name;
const char *info;
union {
void *target;
struct {
long pos;
void *target;
} iface;
struct {
int rlim;
unsigned long max;
} rlim;
struct {
const char *target;
u32 request;
u32 denied;
uid_t ouid;
} fs;
};
} apparmor_audit_data;
struct apparmor_audit_data *apparmor_audit_data;
#endif
};
/* these callback will be implemented by a specific LSM */
void (*lsm_pre_audit)(struct audit_buffer *, void *);
void (*lsm_post_audit)(struct audit_buffer *, void *);
}; /* per LSM data pointer union */
};

#define v4info fam.v4
Expand All @@ -146,6 +98,8 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,
{ memset((_d), 0, sizeof(struct common_audit_data)); \
(_d)->type = LSM_AUDIT_DATA_##_t; }

void common_lsm_audit(struct common_audit_data *a);
void common_lsm_audit(struct common_audit_data *a,
void (*pre_audit)(struct audit_buffer *, void *),
void (*post_audit)(struct audit_buffer *, void *));

#endif
42 changes: 20 additions & 22 deletions security/apparmor/audit.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,23 +115,23 @@ static void audit_pre(struct audit_buffer *ab, void *ca)

if (aa_g_audit_header) {
audit_log_format(ab, "apparmor=");
audit_log_string(ab, aa_audit_type[sa->aad.type]);
audit_log_string(ab, aa_audit_type[sa->aad->type]);
}

if (sa->aad.op) {
if (sa->aad->op) {
audit_log_format(ab, " operation=");
audit_log_string(ab, op_table[sa->aad.op]);
audit_log_string(ab, op_table[sa->aad->op]);
}

if (sa->aad.info) {
if (sa->aad->info) {
audit_log_format(ab, " info=");
audit_log_string(ab, sa->aad.info);
if (sa->aad.error)
audit_log_format(ab, " error=%d", sa->aad.error);
audit_log_string(ab, sa->aad->info);
if (sa->aad->error)
audit_log_format(ab, " error=%d", sa->aad->error);
}

if (sa->aad.profile) {
struct aa_profile *profile = sa->aad.profile;
if (sa->aad->profile) {
struct aa_profile *profile = sa->aad->profile;
pid_t pid;
rcu_read_lock();
pid = rcu_dereference(tsk->real_parent)->pid;
Expand All @@ -145,9 +145,9 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
audit_log_untrustedstring(ab, profile->base.hname);
}

if (sa->aad.name) {
if (sa->aad->name) {
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, sa->aad.name);
audit_log_untrustedstring(ab, sa->aad->name);
}
}

Expand All @@ -159,10 +159,8 @@ static void audit_pre(struct audit_buffer *ab, void *ca)
void aa_audit_msg(int type, struct common_audit_data *sa,
void (*cb) (struct audit_buffer *, void *))
{
sa->aad.type = type;
sa->lsm_pre_audit = audit_pre;
sa->lsm_post_audit = cb;
common_lsm_audit(sa);
sa->aad->type = type;
common_lsm_audit(sa, audit_pre, cb);
}

/**
Expand All @@ -184,7 +182,7 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
BUG_ON(!profile);

if (type == AUDIT_APPARMOR_AUTO) {
if (likely(!sa->aad.error)) {
if (likely(!sa->aad->error)) {
if (AUDIT_MODE(profile) != AUDIT_ALL)
return 0;
type = AUDIT_APPARMOR_AUDIT;
Expand All @@ -196,21 +194,21 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp,
if (AUDIT_MODE(profile) == AUDIT_QUIET ||
(type == AUDIT_APPARMOR_DENIED &&
AUDIT_MODE(profile) == AUDIT_QUIET))
return sa->aad.error;
return sa->aad->error;

if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED)
type = AUDIT_APPARMOR_KILL;

if (!unconfined(profile))
sa->aad.profile = profile;
sa->aad->profile = profile;

aa_audit_msg(type, sa, cb);

if (sa->aad.type == AUDIT_APPARMOR_KILL)
if (sa->aad->type == AUDIT_APPARMOR_KILL)
(void)send_sig_info(SIGKILL, NULL, sa->tsk ? sa->tsk : current);

if (sa->aad.type == AUDIT_APPARMOR_ALLOWED)
return complain_error(sa->aad.error);
if (sa->aad->type == AUDIT_APPARMOR_ALLOWED)
return complain_error(sa->aad->error);

return sa->aad.error;
return sa->aad->error;
}
6 changes: 4 additions & 2 deletions security/apparmor/capability.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,13 @@ static int audit_caps(struct aa_profile *profile, struct task_struct *task,
struct audit_cache *ent;
int type = AUDIT_APPARMOR_AUTO;
struct common_audit_data sa;
struct apparmor_audit_data aad = {0,};
COMMON_AUDIT_DATA_INIT(&sa, CAP);
sa.aad = &aad;
sa.tsk = task;
sa.u.cap = cap;
sa.aad.op = OP_CAPABLE;
sa.aad.error = error;
sa.aad->op = OP_CAPABLE;
sa.aad->error = error;

if (likely(!error)) {
/* test if auditing is being forced */
Expand Down
54 changes: 28 additions & 26 deletions security/apparmor/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,22 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
struct common_audit_data *sa = va;
uid_t fsuid = current_fsuid();

if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) {
if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " requested_mask=");
audit_file_mask(ab, sa->aad.fs.request);
audit_file_mask(ab, sa->aad->fs.request);
}
if (sa->aad.fs.denied & AA_AUDIT_FILE_MASK) {
if (sa->aad->fs.denied & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " denied_mask=");
audit_file_mask(ab, sa->aad.fs.denied);
audit_file_mask(ab, sa->aad->fs.denied);
}
if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) {
if (sa->aad->fs.request & AA_AUDIT_FILE_MASK) {
audit_log_format(ab, " fsuid=%d", fsuid);
audit_log_format(ab, " ouid=%d", sa->aad.fs.ouid);
audit_log_format(ab, " ouid=%d", sa->aad->fs.ouid);
}

if (sa->aad.fs.target) {
if (sa->aad->fs.target) {
audit_log_format(ab, " target=");
audit_log_untrustedstring(ab, sa->aad.fs.target);
audit_log_untrustedstring(ab, sa->aad->fs.target);
}
}

Expand All @@ -107,45 +107,47 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms,
{
int type = AUDIT_APPARMOR_AUTO;
struct common_audit_data sa;
struct apparmor_audit_data aad = {0,};
COMMON_AUDIT_DATA_INIT(&sa, NONE);
sa.aad.op = op,
sa.aad.fs.request = request;
sa.aad.name = name;
sa.aad.fs.target = target;
sa.aad.fs.ouid = ouid;
sa.aad.info = info;
sa.aad.error = error;

if (likely(!sa.aad.error)) {
sa.aad = &aad;
aad.op = op,
aad.fs.request = request;
aad.name = name;
aad.fs.target = target;
aad.fs.ouid = ouid;
aad.info = info;
aad.error = error;

if (likely(!sa.aad->error)) {
u32 mask = perms->audit;

if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
mask = 0xffff;

/* mask off perms that are not being force audited */
sa.aad.fs.request &= mask;
sa.aad->fs.request &= mask;

if (likely(!sa.aad.fs.request))
if (likely(!sa.aad->fs.request))
return 0;
type = AUDIT_APPARMOR_AUDIT;
} else {
/* only report permissions that were denied */
sa.aad.fs.request = sa.aad.fs.request & ~perms->allow;
sa.aad->fs.request = sa.aad->fs.request & ~perms->allow;

if (sa.aad.fs.request & perms->kill)
if (sa.aad->fs.request & perms->kill)
type = AUDIT_APPARMOR_KILL;

/* quiet known rejects, assumes quiet and kill do not overlap */
if ((sa.aad.fs.request & perms->quiet) &&
if ((sa.aad->fs.request & perms->quiet) &&
AUDIT_MODE(profile) != AUDIT_NOQUIET &&
AUDIT_MODE(profile) != AUDIT_ALL)
sa.aad.fs.request &= ~perms->quiet;
sa.aad->fs.request &= ~perms->quiet;

if (!sa.aad.fs.request)
return COMPLAIN_MODE(profile) ? 0 : sa.aad.error;
if (!sa.aad->fs.request)
return COMPLAIN_MODE(profile) ? 0 : sa.aad->error;
}

sa.aad.fs.denied = sa.aad.fs.request & ~perms->allow;
sa.aad->fs.denied = sa.aad->fs.request & ~perms->allow;
return aa_audit(type, profile, gfp, &sa, file_audit_cb);
}

Expand Down
28 changes: 27 additions & 1 deletion security/apparmor/include/audit.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,33 @@ enum aa_ops {
};


/* define a short hand for apparmor_audit_data portion of common_audit_data */
struct apparmor_audit_data {
int error;
int op;
int type;
void *profile;
const char *name;
const char *info;
union {
void *target;
struct {
long pos;
void *target;
} iface;
struct {
int rlim;
unsigned long max;
} rlim;
struct {
const char *target;
u32 request;
u32 denied;
uid_t ouid;
} fs;
};
};

/* define a short hand for apparmor_audit_data structure */
#define aad apparmor_audit_data

void aa_audit_msg(int type, struct common_audit_data *sa,
Expand Down
10 changes: 6 additions & 4 deletions security/apparmor/ipc.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ static void audit_cb(struct audit_buffer *ab, void *va)
{
struct common_audit_data *sa = va;
audit_log_format(ab, " target=");
audit_log_untrustedstring(ab, sa->aad.target);
audit_log_untrustedstring(ab, sa->aad->target);
}

/**
Expand All @@ -41,10 +41,12 @@ static int aa_audit_ptrace(struct aa_profile *profile,
struct aa_profile *target, int error)
{
struct common_audit_data sa;
struct apparmor_audit_data aad = {0,};
COMMON_AUDIT_DATA_INIT(&sa, NONE);
sa.aad.op = OP_PTRACE;
sa.aad.target = target;
sa.aad.error = error;
sa.aad = &aad;
aad.op = OP_PTRACE;
aad.target = target;
aad.error = error;

return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa,
audit_cb);
Expand Down
Loading

0 comments on commit a5149bf

Please sign in to comment.