Commit 916d75761c971b6e630a26bd4ba472e90ac9a4b9

Authored by Al Viro
1 parent 9d96098510

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>

Showing 4 changed files with 72 additions and 23 deletions Side-by-side Diff

... ... @@ -133,7 +133,7 @@
133 133 static DECLARE_WAIT_QUEUE_HEAD(audit_backlog_wait);
134 134  
135 135 /* Serialize requests from userspace. */
136   -static DEFINE_MUTEX(audit_cmd_mutex);
  136 +DEFINE_MUTEX(audit_cmd_mutex);
137 137  
138 138 /* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
139 139 * audit records. Since printk uses a 1024 byte buffer, this buffer
... ... @@ -504,21 +504,6 @@
504 504  
505 505 return 0;
506 506 }
507   -
508   -#ifdef CONFIG_AUDIT_TREE
509   -static int prune_tree_thread(void *unused)
510   -{
511   - mutex_lock(&audit_cmd_mutex);
512   - audit_prune_trees();
513   - mutex_unlock(&audit_cmd_mutex);
514   - return 0;
515   -}
516   -
517   -void audit_schedule_prune(void)
518   -{
519   - kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
520   -}
521   -#endif
522 507  
523 508 struct sk_buff *audit_make_reply(int pid, int seq, int type, int done,
524 509 int multi, void *payload, int size)
... ... @@ -128,10 +128,9 @@
128 128 extern int audit_remove_tree_rule(struct audit_krule *);
129 129 extern void audit_trim_trees(void);
130 130 extern int audit_tag_tree(char *old, char *new);
131   -extern void audit_schedule_prune(void);
132   -extern void audit_prune_trees(void);
133 131 extern const char *audit_tree_path(struct audit_tree *);
134 132 extern void audit_put_tree(struct audit_tree *);
  133 +extern void audit_kill_trees(struct list_head *);
135 134 #else
136 135 #define audit_remove_tree_rule(rule) BUG()
137 136 #define audit_add_tree_rule(rule) -EINVAL
... ... @@ -140,6 +139,7 @@
140 139 #define audit_put_tree(tree) (void)0
141 140 #define audit_tag_tree(old, new) -EINVAL
142 141 #define audit_tree_path(rule) "" /* never called */
  142 +#define audit_kill_trees(list) BUG()
143 143 #endif
144 144  
145 145 extern char *audit_unpack_string(void **, size_t *, size_t);
146 146  
... ... @@ -158,8 +158,11 @@
158 158 return 0;
159 159 }
160 160 extern void audit_filter_inodes(struct task_struct *, struct audit_context *);
  161 +extern struct list_head *audit_killed_trees(void);
161 162 #else
162 163 #define audit_signal_info(s,t) AUDIT_DISABLED
163 164 #define audit_filter_inodes(t,c) AUDIT_DISABLED
164 165 #endif
  166 +
  167 +extern struct mutex audit_cmd_mutex;
... ... @@ -2,6 +2,7 @@
2 2 #include <linux/inotify.h>
3 3 #include <linux/namei.h>
4 4 #include <linux/mount.h>
  5 +#include <linux/kthread.h>
5 6  
6 7 struct audit_tree;
7 8 struct audit_chunk;
... ... @@ -517,6 +518,8 @@
517 518 }
518 519 }
519 520  
  521 +static void audit_schedule_prune(void);
  522 +
520 523 /* called with audit_filter_mutex */
521 524 int audit_remove_tree_rule(struct audit_krule *rule)
522 525 {
523 526  
524 527  
... ... @@ -822,10 +825,11 @@
822 825  
823 826 /*
824 827 * That gets run when evict_chunk() ends up needing to kill audit_tree.
825   - * Runs from a separate thread, with audit_cmd_mutex held.
  828 + * Runs from a separate thread.
826 829 */
827   -void audit_prune_trees(void)
  830 +static int prune_tree_thread(void *unused)
828 831 {
  832 + mutex_lock(&audit_cmd_mutex);
829 833 mutex_lock(&audit_filter_mutex);
830 834  
831 835 while (!list_empty(&prune_list)) {
832 836  
833 837  
... ... @@ -842,9 +846,43 @@
842 846 }
843 847  
844 848 mutex_unlock(&audit_filter_mutex);
  849 + mutex_unlock(&audit_cmd_mutex);
  850 + return 0;
845 851 }
846 852  
  853 +static void audit_schedule_prune(void)
  854 +{
  855 + kthread_run(prune_tree_thread, NULL, "audit_prune_tree");
  856 +}
  857 +
847 858 /*
  859 + * ... and that one is done if evict_chunk() decides to delay until the end
  860 + * of syscall. Runs synchronously.
  861 + */
  862 +void audit_kill_trees(struct list_head *list)
  863 +{
  864 + mutex_lock(&audit_cmd_mutex);
  865 + mutex_lock(&audit_filter_mutex);
  866 +
  867 + while (!list_empty(list)) {
  868 + struct audit_tree *victim;
  869 +
  870 + victim = list_entry(list->next, struct audit_tree, list);
  871 + kill_rules(victim);
  872 + list_del_init(&victim->list);
  873 +
  874 + mutex_unlock(&audit_filter_mutex);
  875 +
  876 + prune_one(victim);
  877 +
  878 + mutex_lock(&audit_filter_mutex);
  879 + }
  880 +
  881 + mutex_unlock(&audit_filter_mutex);
  882 + mutex_unlock(&audit_cmd_mutex);
  883 +}
  884 +
  885 +/*
848 886 * Here comes the stuff asynchronous to auditctl operations
849 887 */
850 888  
... ... @@ -852,6 +890,8 @@
852 890 static void evict_chunk(struct audit_chunk *chunk)
853 891 {
854 892 struct audit_tree *owner;
  893 + struct list_head *postponed = audit_killed_trees();
  894 + int need_prune = 0;
855 895 int n;
856 896  
857 897 if (chunk->dead)
858 898  
... ... @@ -867,15 +907,21 @@
867 907 owner->root = NULL;
868 908 list_del_init(&owner->same_root);
869 909 spin_unlock(&hash_lock);
870   - kill_rules(owner);
871   - list_move(&owner->list, &prune_list);
872   - audit_schedule_prune();
  910 + if (!postponed) {
  911 + kill_rules(owner);
  912 + list_move(&owner->list, &prune_list);
  913 + need_prune = 1;
  914 + } else {
  915 + list_move(&owner->list, postponed);
  916 + }
873 917 spin_lock(&hash_lock);
874 918 }
875 919 list_del_rcu(&chunk->hash);
876 920 for (n = 0; n < chunk->count; n++)
877 921 list_del_init(&chunk->owners[n].list);
878 922 spin_unlock(&hash_lock);
  923 + if (need_prune)
  924 + audit_schedule_prune();
879 925 mutex_unlock(&audit_filter_mutex);
880 926 }
881 927  
... ... @@ -199,6 +199,7 @@
199 199  
200 200 struct audit_tree_refs *trees, *first_trees;
201 201 int tree_count;
  202 + struct list_head killed_trees;
202 203  
203 204 int type;
204 205 union {
... ... @@ -853,6 +854,7 @@
853 854 if (!(context = kmalloc(sizeof(*context), GFP_KERNEL)))
854 855 return NULL;
855 856 audit_zero_context(context, state);
  857 + INIT_LIST_HEAD(&context->killed_trees);
856 858 return context;
857 859 }
858 860  
... ... @@ -1545,6 +1547,8 @@
1545 1547 /* that can happen only if we are called from do_exit() */
1546 1548 if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
1547 1549 audit_log_exit(context, tsk);
  1550 + if (!list_empty(&context->killed_trees))
  1551 + audit_kill_trees(&context->killed_trees);
1548 1552  
1549 1553 audit_free_context(context);
1550 1554 }
... ... @@ -1688,6 +1692,9 @@
1688 1692 context->in_syscall = 0;
1689 1693 context->prio = context->state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;
1690 1694  
  1695 + if (!list_empty(&context->killed_trees))
  1696 + audit_kill_trees(&context->killed_trees);
  1697 +
1691 1698 if (context->previous) {
1692 1699 struct audit_context *new_context = context->previous;
1693 1700 context->previous = NULL;
... ... @@ -2520,5 +2527,13 @@
2520 2527 audit_log_untrustedstring(ab, current->comm);
2521 2528 audit_log_format(ab, " sig=%ld", signr);
2522 2529 audit_log_end(ab);
  2530 +}
  2531 +
  2532 +struct list_head *audit_killed_trees(void)
  2533 +{
  2534 + struct audit_context *ctx = current->audit_context;
  2535 + if (likely(!ctx || !ctx->in_syscall))
  2536 + return NULL;
  2537 + return &ctx->killed_trees;
2523 2538 }