This patch fixes the following issues in the latest filesystem audit
patch:

    - fix error handling on call to audit_init_watch()
    - remove unnecessary check for watch in audit_add_rule()
    - in audit_del_rule(), don't check e->rule.watch after calling
      audit_free_rule_rcu
    - always update watch filter fields in audit_add_watch(), in case
      we don't find an existing watch struct
    - don't drop audit_filter_mutex between adding to existing
      watches/parents and adding the rule to the filterlist

If these fixes look correct, please fold them in with lspp.b7
f11fcf7556067b41d592d970f5f5c6ffbdd1e8fd on next rebase.

Signed-off-by: Amy Griffis <[EMAIL PROTECTED]>

---

 auditfilter.c |  144 ++++++++++++++++++++++++++++++++++------------------------
 1 files changed, 86 insertions(+), 58 deletions(-)

diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index f1151a2..eb102ff 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -585,9 +585,9 @@ static inline struct audit_watch *audit_
                return ERR_PTR(-ENOMEM);
 
        new = audit_init_watch(path);
-       if (unlikely(!new)) {
+       if (unlikely(IS_ERR(new))) {
                kfree(path);
-               return ERR_PTR(-ENOMEM);
+               goto out;
        }
 
        new->dev = old->dev;
@@ -595,6 +595,7 @@ static inline struct audit_watch *audit_
        audit_get_parent(old->parent);
        new->parent = old->parent;
 
+out:
        return new;
 }
 
@@ -853,77 +854,103 @@ static inline void audit_put_nd(struct n
        }
 }
 
-/* Find a matching watch entry, or add this one. */
-static inline int audit_add_watch(struct audit_krule *krule,
-                                 struct nameidata *ndp, struct nameidata *ndw,
-                                 struct list_head *inotify_list)
+/* Add a parent inotify_watch for the given rule. */
+static int audit_add_parent(struct audit_krule *krule,
+                           struct list_head *inotify_list)
+{
+       struct audit_parent *parent;
+       struct audit_watch *watch = krule->watch;
+
+       parent = audit_init_parent();
+       if (IS_ERR(parent))
+               return PTR_ERR(parent);
+
+       audit_get_parent(parent);
+       watch->parent = parent;
+
+       /* krule, watch and parent have not been added to any global
+        * lists, so we don't need to take audit_filter_mutex. */
+       list_add(&watch->wlist, &parent->watches);
+       list_add(&krule->rlist, &watch->rules);
+
+       /* add parent to inotify registration list */
+       list_add(&parent->ilist, inotify_list);
+
+       return 0;
+}
+
+/* Associate the given rule with an existing parent inotify_watch.
+ * Caller must hold audit_filter_mutex. */
+static int audit_add_to_parent(struct audit_krule *krule,
+                              struct inotify_watch *iwatch)
 {
-       int ret;
-       struct inotify_watch *iwatch;
        struct audit_parent *parent;
        struct audit_watch *w, *watch = krule->watch;
        int watch_found = 0;
 
-       ret = inotify_find_watch(audit_ih, ndp->dentry->d_inode, &iwatch);
-       if (ret < 0) {
-               parent = audit_init_parent();
-               if (IS_ERR(parent))
-                       return PTR_ERR(parent);
+       parent = container_of(iwatch, struct audit_parent, wdata);
 
-               audit_get_parent(parent);
-               watch->parent = parent;
+       /* parent was moved before we took audit_filter_mutex */
+       if (parent->flags & AUDIT_PARENT_INVALID)
+               return -ENOENT;
 
-               /* krule, watch and parent have not been added to any global
-                * lists, so we don't need to take audit_filter_mutex.
-                */
-               list_add(&watch->wlist, &parent->watches);
-               list_add(&krule->rlist, &watch->rules);
+       list_for_each_entry(w, &parent->watches, wlist) {
+               if (strcmp(watch->path, w->path))
+                       continue;
 
-               /* update watch filter fields */
-               if (ndw) {
-                       watch->dev = ndw->dentry->d_inode->i_sb->s_dev;
-                       watch->ino = ndw->dentry->d_inode->i_ino;
-               }
-               
-               /* add parent to inotify registration list */
-               list_add(&parent->ilist, inotify_list);
-       } else {
-               parent = container_of(iwatch, struct audit_parent, wdata);
+               watch_found = 1;
 
-               mutex_lock(&audit_filter_mutex);
-               if (parent->flags & AUDIT_PARENT_INVALID) {
-                       /* parent was moved before we took the lock */
-                       mutex_unlock(&audit_filter_mutex);
-                       return -ENOENT;
-               }
+               /* put krule's and initial refs to temporary watch */
+               audit_put_watch(watch);
+               audit_put_watch(watch);
 
-               list_for_each_entry(w, &parent->watches, wlist) {
-                       if (strcmp(watch->path, w->path))
-                               continue;
+               audit_get_watch(w);
+               krule->watch = watch = w;
+               break;
+       }
 
-                       watch_found = 1;
+       if (!watch_found) {
+               audit_get_parent(parent);
+               watch->parent = parent;
 
-                       /* put krule's and initial refs to temporary watch */
-                       audit_put_watch(watch);
-                       audit_put_watch(watch);
+               list_add(&watch->wlist, &parent->watches);
+       }
 
-                       audit_get_watch(w);
-                       krule->watch = watch = w;
-                       break;
-               }
+       list_add(&krule->rlist, &watch->rules);
 
-               if (!watch_found) {
-                       audit_get_parent(parent);
-                       watch->parent = parent;
+       return 0;
+}
 
-                       list_add(&watch->wlist, &parent->watches);
-               }
+/* Find a matching watch entry, or add this one.
+ * Caller must hold audit_filter_mutex. */
+static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp,
+                          struct nameidata *ndw,
+                          struct list_head *inotify_list)
+{
+       struct audit_watch *watch = krule->watch;
+       struct inotify_watch *iwatch;
+       int ret;
 
-               list_add(&krule->rlist, &watch->rules);
-               mutex_unlock(&audit_filter_mutex);
+       /* update watch filter fields */
+       if (ndw) {
+               watch->dev = ndw->dentry->d_inode->i_sb->s_dev;
+               watch->ino = ndw->dentry->d_inode->i_ino;
        }
 
-       return 0;
+       /* The audit_filter_mutex must not be held during inotify calls because
+        * we hold it during inotify event callback processing.
+        * We can trust iwatch to stick around because we hold nameidata (ndp). 
*/
+       mutex_unlock(&audit_filter_mutex);
+
+       if (inotify_find_watch(audit_ih, ndp->dentry->d_inode, &iwatch) < 0) {
+               ret = audit_add_parent(krule, inotify_list);
+               mutex_lock(&audit_filter_mutex);
+       } else {
+               mutex_lock(&audit_filter_mutex);
+               ret = audit_add_to_parent(krule, iwatch);
+       }
+
+       return ret;
 }
 
 /* Add rule to given filterlist if not a duplicate. */
@@ -954,7 +981,9 @@ static inline int audit_add_rule(struct 
                        goto error;
        }
 
+       mutex_lock(&audit_filter_mutex);
        if (watch) {
+               /* audit_filter_mutex is dropped and re-taken during this call 
*/
                err = audit_add_watch(&entry->rule, ndp, ndw, &inotify_list);
                if (err) {
                        audit_put_nd(ndp, ndw);
@@ -962,7 +991,6 @@ static inline int audit_add_rule(struct 
                }
        }
 
-       mutex_lock(&audit_filter_mutex);
        if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
                list_add_rcu(&entry->list, list);
        } else {
@@ -970,7 +998,7 @@ static inline int audit_add_rule(struct 
        }
        mutex_unlock(&audit_filter_mutex);
 
-       if (watch && !list_empty(&inotify_list)) {
+       if (!list_empty(&inotify_list)) {
                err = audit_inotify_register(ndp, &inotify_list);
                if (err)
                        goto error;
@@ -1031,7 +1059,7 @@ static inline int audit_del_rule(struct 
                call_rcu(&e->rcu, audit_free_rule_rcu);
                mutex_unlock(&audit_filter_mutex);
 
-               if (e->rule.watch && !list_empty(&inotify_list))
+               if (!list_empty(&inotify_list))
                        audit_inotify_unregister(&inotify_list);
 
                return 0;


--
Linux-audit mailing list
Linux-audit@redhat.com
https://www.redhat.com/mailman/listinfo/linux-audit

Reply via email to