Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a5149bf3 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'selinux' ("struct common_audit_data" sanitizer)

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
parents 3ff8f932 b61c37f5
Loading
Loading
Loading
Loading
+25 −71
Original line number Diff line number Diff line
@@ -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 {
@@ -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;
@@ -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
@@ -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
+20 −22
Original line number Diff line number Diff line
@@ -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;
@@ -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);
	}
}

@@ -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);
}

/**
@@ -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;
@@ -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;
}
+4 −2
Original line number Diff line number Diff line
@@ -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 */
+28 −26
Original line number Diff line number Diff line
@@ -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);
	}
}

@@ -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);
}

+27 −1
Original line number Diff line number Diff line
@@ -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,
Loading