Commit 916d75761c971b6e630a26bd4ba472e90ac9a4b9
1 parent
9d96098510
Exists in
master
and in
4 other branches
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
kernel/audit.c
... | ... | @@ -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) |
kernel/audit.h
... | ... | @@ -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; |
kernel/audit_tree.c
... | ... | @@ -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 |
kernel/auditsc.c
... | ... | @@ -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 | } |