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

Commit 916d7576 authored by Al Viro's avatar Al Viro
Browse files

Fix rule eviction order for AUDIT_DIR



If syscall removes the root of subtree being watched, we
definitely do not want the rules refering that subtree
to be destroyed without the syscall in question having
a chance to match them.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 9d960985
Loading
Loading
Loading
Loading
+1 −16
Original line number Original line Diff line number Diff line
@@ -133,7 +133,7 @@ static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);


/* Serialize requests from userspace. */
/* Serialize requests from userspace. */
static DEFINE_MUTEX(audit_cmd_mutex);
DEFINE_MUTEX(audit_cmd_mutex);


/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
 * audit records.  Since printk uses a 1024 byte buffer, this buffer
 * audit records.  Since printk uses a 1024 byte buffer, this buffer
@@ -505,21 +505,6 @@ int audit_send_list(void *_dest)
	return 0;
	return 0;
}
}


#ifdef CONFIG_AUDIT_TREE
static int prune_tree_thread(void *unused)
{
	mutex_lock(&audit_cmd_mutex);
	audit_prune_trees();
	mutex_unlock(&audit_cmd_mutex);
	return 0;
}

void audit_schedule_prune(void)
{
	kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
}
#endif

struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
				 int multi, void *payload, int size)
				 int multi, void *payload, int size)
{
{
+5 −2
Original line number Original line Diff line number Diff line
@@ -128,10 +128,9 @@ extern int audit_add_tree_rule(struct audit_krule *);
extern int audit_remove_tree_rule(struct audit_krule *);
extern int audit_remove_tree_rule(struct audit_krule *);
extern void audit_trim_trees(void);
extern void audit_trim_trees(void);
extern int audit_tag_tree(char *old, char *new);
extern int audit_tag_tree(char *old, char *new);
extern void audit_schedule_prune(void);
extern void audit_prune_trees(void);
extern const char *audit_tree_path(struct audit_tree *);
extern const char *audit_tree_path(struct audit_tree *);
extern void audit_put_tree(struct audit_tree *);
extern void audit_put_tree(struct audit_tree *);
extern void audit_kill_trees(struct list_head *);
#else
#else
#define audit_remove_tree_rule(rule) BUG()
#define audit_remove_tree_rule(rule) BUG()
#define audit_add_tree_rule(rule) -EINVAL
#define audit_add_tree_rule(rule) -EINVAL
@@ -140,6 +139,7 @@ extern void audit_put_tree(struct audit_tree *);
#define audit_put_tree(tree) (void)0
#define audit_put_tree(tree) (void)0
#define audit_tag_tree(old, new) -EINVAL
#define audit_tag_tree(old, new) -EINVAL
#define audit_tree_path(rule) ""	/* never called */
#define audit_tree_path(rule) ""	/* never called */
#define audit_kill_trees(list) BUG()
#endif
#endif


extern char *audit_unpack_string(void **, size_t *, size_t);
extern char *audit_unpack_string(void **, size_t *, size_t);
@@ -158,7 +158,10 @@ static inline int audit_signal_info(int sig, struct task_struct *t)
	return 0;
	return 0;
}
}
extern void audit_filter_inodes(struct task_struct *, struct audit_context *);
extern void audit_filter_inodes(struct task_struct *, struct audit_context *);
extern struct list_head *audit_killed_trees(void);
#else
#else
#define audit_signal_info(s,t) AUDIT_DISABLED
#define audit_signal_info(s,t) AUDIT_DISABLED
#define audit_filter_inodes(t,c) AUDIT_DISABLED
#define audit_filter_inodes(t,c) AUDIT_DISABLED
#endif
#endif

extern struct mutex audit_cmd_mutex;
+51 −5
Original line number Original line Diff line number Diff line
@@ -2,6 +2,7 @@
#include <linux/inotify.h>
#include <linux/inotify.h>
#include <linux/namei.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/mount.h>
#include <linux/kthread.h>


struct audit_tree;
struct audit_tree;
struct audit_chunk;
struct audit_chunk;
@@ -517,6 +518,8 @@ static void trim_marked(struct audit_tree *tree)
	}
	}
}
}


static void audit_schedule_prune(void);

/* called with audit_filter_mutex */
/* called with audit_filter_mutex */
int audit_remove_tree_rule(struct audit_krule *rule)
int audit_remove_tree_rule(struct audit_krule *rule)
{
{
@@ -822,10 +825,11 @@ int audit_tag_tree(char *old, char *new)


/*
/*
 * That gets run when evict_chunk() ends up needing to kill audit_tree.
 * That gets run when evict_chunk() ends up needing to kill audit_tree.
 * Runs from a separate thread, with audit_cmd_mutex held.
 * Runs from a separate thread.
 */
 */
void audit_prune_trees(void)
static int prune_tree_thread(void *unused)
{
{
	mutex_lock(&audit_cmd_mutex);
	mutex_lock(&audit_filter_mutex);
	mutex_lock(&audit_filter_mutex);


	while (!list_empty(&prune_list)) {
	while (!list_empty(&prune_list)) {
@@ -842,6 +846,40 @@ void audit_prune_trees(void)
	}
	}


	mutex_unlock(&audit_filter_mutex);
	mutex_unlock(&audit_filter_mutex);
	mutex_unlock(&audit_cmd_mutex);
	return 0;
}

static void audit_schedule_prune(void)
{
	kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
}

/*
 * ... and that one is done if evict_chunk() decides to delay until the end
 * of syscall.  Runs synchronously.
 */
void audit_kill_trees(struct list_head *list)
{
	mutex_lock(&audit_cmd_mutex);
	mutex_lock(&audit_filter_mutex);

	while (!list_empty(list)) {
		struct audit_tree *victim;

		victim = list_entry(list->next, struct audit_tree, list);
		kill_rules(victim);
		list_del_init(&victim->list);

		mutex_unlock(&audit_filter_mutex);

		prune_one(victim);

		mutex_lock(&audit_filter_mutex);
	}

	mutex_unlock(&audit_filter_mutex);
	mutex_unlock(&audit_cmd_mutex);
}
}


/*
/*
@@ -852,6 +890,8 @@ void audit_prune_trees(void)
static void evict_chunk(struct audit_chunk *chunk)
static void evict_chunk(struct audit_chunk *chunk)
{
{
	struct audit_tree *owner;
	struct audit_tree *owner;
	struct list_head *postponed = audit_killed_trees();
	int need_prune = 0;
	int n;
	int n;


	if (chunk->dead)
	if (chunk->dead)
@@ -867,15 +907,21 @@ static void evict_chunk(struct audit_chunk *chunk)
		owner->root = NULL;
		owner->root = NULL;
		list_del_init(&owner->same_root);
		list_del_init(&owner->same_root);
		spin_unlock(&hash_lock);
		spin_unlock(&hash_lock);
		if (!postponed) {
			kill_rules(owner);
			kill_rules(owner);
			list_move(&owner->list, &prune_list);
			list_move(&owner->list, &prune_list);
		audit_schedule_prune();
			need_prune = 1;
		} else {
			list_move(&owner->list, postponed);
		}
		spin_lock(&hash_lock);
		spin_lock(&hash_lock);
	}
	}
	list_del_rcu(&chunk->hash);
	list_del_rcu(&chunk->hash);
	for (n = 0; n < chunk->count; n++)
	for (n = 0; n < chunk->count; n++)
		list_del_init(&chunk->owners[n].list);
		list_del_init(&chunk->owners[n].list);
	spin_unlock(&hash_lock);
	spin_unlock(&hash_lock);
	if (need_prune)
		audit_schedule_prune();
	mutex_unlock(&audit_filter_mutex);
	mutex_unlock(&audit_filter_mutex);
}
}


+15 −0
Original line number Original line Diff line number Diff line
@@ -199,6 +199,7 @@ struct audit_context {


	struct audit_tree_refs *trees, *first_trees;
	struct audit_tree_refs *trees, *first_trees;
	int tree_count;
	int tree_count;
	struct list_head killed_trees;


	int type;
	int type;
	union {
	union {
@@ -853,6 +854,7 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state)
	if (!(context = kmalloc(sizeof(*context), GFP_KERNEL)))
	if (!(context = kmalloc(sizeof(*context), GFP_KERNEL)))
		return NULL;
		return NULL;
	audit_zero_context(context, state);
	audit_zero_context(context, state);
	INIT_LIST_HEAD(&context->killed_trees);
	return context;
	return context;
}
}


@@ -1545,6 +1547,8 @@ void audit_free(struct task_struct *tsk)
	/* that can happen only if we are called from do_exit() */
	/* that can happen only if we are called from do_exit() */
	if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
	if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
		audit_log_exit(context, tsk);
		audit_log_exit(context, tsk);
	if (!list_empty(&context->killed_trees))
		audit_kill_trees(&context->killed_trees);


	audit_free_context(context);
	audit_free_context(context);
}
}
@@ -1688,6 +1692,9 @@ void audit_syscall_exit(int valid, long return_code)
	context->in_syscall = 0;
	context->in_syscall = 0;
	context->prio = context->state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;
	context->prio = context->state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;


	if (!list_empty(&context->killed_trees))
		audit_kill_trees(&context->killed_trees);

	if (context->previous) {
	if (context->previous) {
		struct audit_context *new_context = context->previous;
		struct audit_context *new_context = context->previous;
		context->previous  = NULL;
		context->previous  = NULL;
@@ -2521,3 +2528,11 @@ void audit_core_dumps(long signr)
	audit_log_format(ab, " sig=%ld", signr);
	audit_log_format(ab, " sig=%ld", signr);
	audit_log_end(ab);
	audit_log_end(ab);
}
}

struct list_head *audit_killed_trees(void)
{
	struct audit_context *ctx = current->audit_context;
	if (likely(!ctx || !ctx->in_syscall))
		return NULL;
	return &ctx->killed_trees;
}