audit: Fix use after free in audit_remove_watch_rule()
authorJan Kara <jack@suse.cz>
Tue, 15 Aug 2017 11:00:36 +0000 (13:00 +0200)
committerPaul Moore <paul@paul-moore.com>
Tue, 15 Aug 2017 19:58:17 +0000 (15:58 -0400)
audit_remove_watch_rule() drops watch's reference to parent but then
continues to work with it. That is not safe as parent can get freed once
we drop our reference. The following is a trivial reproducer:

mount -o loop image /mnt
touch /mnt/file
auditctl -w /mnt/file -p wax
umount /mnt
auditctl -D
<crash in fsnotify_destroy_mark()>

Grab our own reference in audit_remove_watch_rule() earlier to make sure
mark does not get freed under us.

CC: stable@vger.kernel.org
Reported-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Jan Kara <jack@suse.cz>
Tested-by: Tony Jones <tonyj@suse.de>
Signed-off-by: Paul Moore <paul@paul-moore.com>
kernel/audit_watch.c

index e0656bd6303684fcc49561b94065a709efc8b031..1c7ded42f82f0e8130b9cbf6b45b67648736a0f7 100644 (file)
@@ -457,13 +457,15 @@ void audit_remove_watch_rule(struct audit_krule *krule)
        list_del(&krule->rlist);
 
        if (list_empty(&watch->rules)) {
        list_del(&krule->rlist);
 
        if (list_empty(&watch->rules)) {
+               /*
+                * audit_remove_watch() drops our reference to 'parent' which
+                * can get freed. Grab our own reference to be safe.
+                */
+               audit_get_parent(parent);
                audit_remove_watch(watch);
                audit_remove_watch(watch);
-
-               if (list_empty(&parent->watches)) {
-                       audit_get_parent(parent);
+               if (list_empty(&parent->watches))
                        fsnotify_destroy_mark(&parent->mark, audit_watch_group);
                        fsnotify_destroy_mark(&parent->mark, audit_watch_group);
-                       audit_put_parent(parent);
-               }
+               audit_put_parent(parent);
        }
 }
 
        }
 }