Skip to content

Commit

Permalink
Fix rule eviction order for AUDIT_DIR
Browse files Browse the repository at this point in the history
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: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
Al Viro committed Jun 24, 2009
1 parent 9d96098 commit 916d757
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 23 deletions.
17 changes: 1 addition & 16 deletions kernel/audit.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);

/* 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 records. Since printk uses a 1024 byte buffer, this buffer
Expand Down Expand Up @@ -505,21 +505,6 @@ int audit_send_list(void *_dest)
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,
int multi, void *payload, int size)
{
Expand Down
7 changes: 5 additions & 2 deletions kernel/audit.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,9 @@ extern int audit_add_tree_rule(struct audit_krule *);
extern int audit_remove_tree_rule(struct audit_krule *);
extern void audit_trim_trees(void);
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 void audit_put_tree(struct audit_tree *);
extern void audit_kill_trees(struct list_head *);
#else
#define audit_remove_tree_rule(rule) BUG()
#define audit_add_tree_rule(rule) -EINVAL
Expand All @@ -140,6 +139,7 @@ extern void audit_put_tree(struct audit_tree *);
#define audit_put_tree(tree) (void)0
#define audit_tag_tree(old, new) -EINVAL
#define audit_tree_path(rule) "" /* never called */
#define audit_kill_trees(list) BUG()
#endif

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

extern struct mutex audit_cmd_mutex;
56 changes: 51 additions & 5 deletions kernel/audit_tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <linux/inotify.h>
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/kthread.h>

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

static void audit_schedule_prune(void);

/* called with audit_filter_mutex */
int audit_remove_tree_rule(struct audit_krule *rule)
{
Expand Down Expand Up @@ -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.
* 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);

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

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

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

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

Expand Down
15 changes: 15 additions & 0 deletions kernel/auditsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ struct audit_context {

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

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

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

audit_free_context(context);
}
Expand Down Expand Up @@ -1688,6 +1692,9 @@ void audit_syscall_exit(int valid, long return_code)
context->in_syscall = 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) {
struct audit_context *new_context = context->previous;
context->previous = NULL;
Expand Down Expand Up @@ -2521,3 +2528,11 @@ void audit_core_dumps(long signr)
audit_log_format(ab, " sig=%ld", signr);
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;
}

0 comments on commit 916d757

Please sign in to comment.