apparmor/kernel-patches/for-mainline/rcu-stale-forward-ptr-2.diff
2007-03-26 19:37:35 +00:00

158 lines
5.4 KiB
Diff

Index: linux-2.6-apparmor/security/apparmor/apparmor.h
===================================================================
--- linux-2.6-apparmor.orig/security/apparmor/apparmor.h
+++ linux-2.6-apparmor/security/apparmor/apparmor.h
@@ -257,6 +257,7 @@ extern ssize_t aa_file_prof_replace(void
extern ssize_t aa_file_prof_remove(const char *, size_t);
extern void free_aa_profile(struct aa_profile *profile);
extern void free_aa_profile_kref(struct kref *kref);
+extern void aa_unconfine_tasks(struct aa_profile *profile);
/* procattr.c */
extern int aa_getprocattr(struct aa_profile *profile, char **string,
Index: linux-2.6-apparmor/security/apparmor/lsm.c
===================================================================
--- linux-2.6-apparmor.orig/security/apparmor/lsm.c
+++ linux-2.6-apparmor/security/apparmor/lsm.c
@@ -807,7 +807,7 @@ static void __exit apparmor_exit(void)
/* Remove the profile from each task context it is on. */
lock_profile(profile);
profile->isstale = ERR_PTR(-ENOENT);
- remove_tasks_on_context_list(profile);
+ aa_unconfine_tasks(profile);
unlock_profile(profile);
/* Release the profile itself. */
Index: linux-2.6-apparmor/security/apparmor/module_interface.c
===================================================================
--- linux-2.6-apparmor.orig/security/apparmor/module_interface.c
+++ linux-2.6-apparmor/security/apparmor/module_interface.c
@@ -444,7 +444,9 @@ ssize_t aa_file_prof_replace(void *udata
write_lock(&profile_list_lock);
old_profile = __aa_find_profile(new_profile->name, &profile_list);
if (old_profile) {
- old_profile->isstale = aa_dup_profile(new_profile);
+ lock_profile(old_profile);
+ old_profile->isstale = new_profile;
+ unlock_profile(old_profile);
list_del_init(&old_profile->list);
}
aa_dup_profile(new_profile);
@@ -471,20 +473,14 @@ ssize_t aa_file_prof_replace(void *udata
do {
new_cxt = aa_alloc_task_context(GFP_KERNEL | __GFP_NOFAIL);
- /*
- * new_profile needs to be locked in the case that there
- * are multiple tasks on old_profile->list, this avoids
- * a race between an already replaced task changing its
- * profile (updating the list) and replacement updating
- * the list
- */
repeat:
lock_both_profiles(old_profile, new_profile);
if (new_profile->isstale) {
struct aa_profile *profile;
+
if (IS_ERR(new_profile->isstale)) {
/* new_profile was removed so become removal */
- remove_tasks_on_context_list(old_profile);
+ aa_unconfine_tasks(old_profile);
unlock_both_profiles(old_profile, new_profile);
break;
}
@@ -531,14 +527,17 @@ ssize_t aa_file_prof_remove(const char *
write_unlock(&profile_list_lock);
return -ENOENT;
}
- profile->isstale = ERR_PTR(-ENOENT);
- list_del_init(&profile->list);
- write_unlock(&profile_list_lock);
+ /* Remove the profile from each task context it is on. */
lock_profile(profile);
- remove_tasks_on_context_list(profile);
+ profile->isstale = ERR_PTR(-ENOENT);
+ aa_unconfine_tasks(profile);
unlock_profile(profile);
+
+ /* Release the profile itself. */
+ list_del_init(&profile->list);
aa_put_profile(profile);
+ write_unlock(&profile_list_lock);
return size;
}
@@ -581,8 +580,6 @@ void free_aa_profile(struct aa_profile *
BUG();
}
- if (!IS_ERR(profile->isstale))
- aa_put_profile(profile->isstale);
aa_match_free(profile->file_rules);
/* use free_aa_profile instead of aa_put_profile to destroy the
@@ -603,3 +600,21 @@ void free_aa_profile(struct aa_profile *
kfree(profile);
}
+
+/**
+ * aa_unconfine_tasks - remove tasks on @profiles task_contexts list
+ * @profile: profile to remove associated tasks
+ *
+ * Assumes that @profile lock is held
+ */
+void aa_unconfine_tasks(struct aa_profile *profile)
+{
+ while (!list_empty(&profile->task_contexts)) {
+ struct task_struct *task =
+ list_entry(profile->task_contexts.next,
+ struct aa_task_context, list)->task;
+ task_lock(task);
+ aa_change_task_context(task, NULL, NULL, 0);
+ task_unlock(task);
+ }
+}
Index: linux-2.6-apparmor/security/apparmor/inline.h
===================================================================
--- linux-2.6-apparmor.orig/security/apparmor/inline.h
+++ linux-2.6-apparmor/security/apparmor/inline.h
@@ -104,24 +104,6 @@ static inline struct aa_profile *alloc_a
}
/**
- * remove_tasks_on_context_list - remove tasks on @profiles task_contexts list
- * @profile: profile to remove associated tasks
- *
- * Assumes that @profile lock is held
- */
-static inline void remove_tasks_on_context_list(struct aa_profile *profile)
-{
- while (!list_empty(&profile->task_contexts)) {
- struct task_struct *task =
- list_entry(profile->task_contexts.next,
- struct aa_task_context, list)->task;
- task_lock(task);
- aa_change_task_context(task, NULL, NULL, 0);
- task_unlock(task);
- }
-}
-
-/**
* lock_profile - lock a profile
* @profile: the profile to lock
*
Index: linux-2.6-apparmor/security/apparmor/main.c
===================================================================
--- linux-2.6-apparmor.orig/security/apparmor/main.c
+++ linux-2.6-apparmor/security/apparmor/main.c
@@ -1201,7 +1201,7 @@ struct aa_profile *aa_replace_profile(st
}
cxt = lock_task_and_profiles(task, profile);
- if (profile && profile->isstale) {
+ if (unlikely(profile && profile->isstale)) {
task_unlock(task);
unlock_both_profiles(profile, cxt ? cxt->profile : NULL);
aa_free_task_context(new_cxt);