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: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2009-06-24 00:02:38 -04:00
parent 9d96098510
commit 916d75761c
4 changed files with 72 additions and 23 deletions

View file

@ -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)
{ {

View file

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

View file

@ -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);
kill_rules(owner); if (!postponed) {
list_move(&owner->list, &prune_list); kill_rules(owner);
audit_schedule_prune(); list_move(&owner->list, &prune_list);
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);
} }

View file

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