diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2009-06-24 00:02:38 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-06-24 00:02:38 -0400 |
commit | 916d75761c971b6e630a26bd4ba472e90ac9a4b9 (patch) | |
tree | 3a4b18d0d29c1d12f64fefbb2bc5559813a686f7 /kernel/audit_tree.c | |
parent | 9d9609851003ebed15957f0f2ce18492739ee124 (diff) |
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>
Diffstat (limited to 'kernel/audit_tree.c')
-rw-r--r-- | kernel/audit_tree.c | 56 |
1 files changed, 51 insertions, 5 deletions
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 3ff0731284a1..2451dc6f3282 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c | |||
@@ -2,6 +2,7 @@ | |||
2 | #include <linux/inotify.h> | 2 | #include <linux/inotify.h> |
3 | #include <linux/namei.h> | 3 | #include <linux/namei.h> |
4 | #include <linux/mount.h> | 4 | #include <linux/mount.h> |
5 | #include <linux/kthread.h> | ||
5 | 6 | ||
6 | struct audit_tree; | 7 | struct audit_tree; |
7 | struct audit_chunk; | 8 | struct audit_chunk; |
@@ -517,6 +518,8 @@ static void trim_marked(struct audit_tree *tree) | |||
517 | } | 518 | } |
518 | } | 519 | } |
519 | 520 | ||
521 | static void audit_schedule_prune(void); | ||
522 | |||
520 | /* called with audit_filter_mutex */ | 523 | /* called with audit_filter_mutex */ |
521 | int audit_remove_tree_rule(struct audit_krule *rule) | 524 | int audit_remove_tree_rule(struct audit_krule *rule) |
522 | { | 525 | { |
@@ -822,10 +825,11 @@ int audit_tag_tree(char *old, char *new) | |||
822 | 825 | ||
823 | /* | 826 | /* |
824 | * That gets run when evict_chunk() ends up needing to kill audit_tree. | 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 | mutex_lock(&audit_filter_mutex); | 833 | mutex_lock(&audit_filter_mutex); |
830 | 834 | ||
831 | while (!list_empty(&prune_list)) { | 835 | while (!list_empty(&prune_list)) { |
@@ -842,6 +846,40 @@ void audit_prune_trees(void) | |||
842 | } | 846 | } |
843 | 847 | ||
844 | mutex_unlock(&audit_filter_mutex); | 848 | mutex_unlock(&audit_filter_mutex); |
849 | mutex_unlock(&audit_cmd_mutex); | ||
850 | return 0; | ||
851 | } | ||
852 | |||
853 | static void audit_schedule_prune(void) | ||
854 | { | ||
855 | kthread_run(prune_tree_thread, NULL, "audit_prune_tree"); | ||
856 | } | ||
857 | |||
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); | ||
845 | } | 883 | } |
846 | 884 | ||
847 | /* | 885 | /* |
@@ -852,6 +890,8 @@ void audit_prune_trees(void) | |||
852 | static void evict_chunk(struct audit_chunk *chunk) | 890 | static void evict_chunk(struct audit_chunk *chunk) |
853 | { | 891 | { |
854 | struct audit_tree *owner; | 892 | struct audit_tree *owner; |
893 | struct list_head *postponed = audit_killed_trees(); | ||
894 | int need_prune = 0; | ||
855 | int n; | 895 | int n; |
856 | 896 | ||
857 | if (chunk->dead) | 897 | if (chunk->dead) |
@@ -867,15 +907,21 @@ static void evict_chunk(struct audit_chunk *chunk) | |||
867 | owner->root = NULL; | 907 | owner->root = NULL; |
868 | list_del_init(&owner->same_root); | 908 | list_del_init(&owner->same_root); |
869 | spin_unlock(&hash_lock); | 909 | spin_unlock(&hash_lock); |
870 | kill_rules(owner); | 910 | if (!postponed) { |
871 | list_move(&owner->list, &prune_list); | 911 | kill_rules(owner); |
872 | audit_schedule_prune(); | 912 | list_move(&owner->list, &prune_list); |
913 | need_prune = 1; | ||
914 | } else { | ||
915 | list_move(&owner->list, postponed); | ||
916 | } | ||
873 | spin_lock(&hash_lock); | 917 | spin_lock(&hash_lock); |
874 | } | 918 | } |
875 | list_del_rcu(&chunk->hash); | 919 | list_del_rcu(&chunk->hash); |
876 | for (n = 0; n < chunk->count; n++) | 920 | for (n = 0; n < chunk->count; n++) |
877 | list_del_init(&chunk->owners[n].list); | 921 | list_del_init(&chunk->owners[n].list); |
878 | spin_unlock(&hash_lock); | 922 | spin_unlock(&hash_lock); |
923 | if (need_prune) | ||
924 | audit_schedule_prune(); | ||
879 | mutex_unlock(&audit_filter_mutex); | 925 | mutex_unlock(&audit_filter_mutex); |
880 | } | 926 | } |
881 | 927 | ||