* [PATCH 1/3] audit: don't free_chunk() after fsnotify_add_mark() [not found] <1340646378-5169-1-git-send-email-miklos@szeredi.hu> @ 2012-06-25 17:46 ` Miklos Szeredi 2012-07-06 17:43 ` Eric Paris 2012-06-25 17:46 ` [PATCH 2/3] audit: fix refcounting in audit-tree Miklos Szeredi 1 sibling, 1 reply; 5+ messages in thread From: Miklos Szeredi @ 2012-06-25 17:46 UTC (permalink / raw) To: eparis; +Cc: viro, linux-kernel, mszeredi, stable From: Miklos Szeredi <mszeredi@suse.cz> Don't do free_chunk() after fsnotify_add_mark(). That one does a delayed unref via the destroy list and this results in use-after-free. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> CC: stable@vger.kernel.org --- kernel/audit_tree.c | 6 +++--- 1 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 5bf0790..d52d247 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -259,7 +259,7 @@ static void untag_chunk(struct node *p) fsnotify_duplicate_mark(&new->mark, entry); if (fsnotify_add_mark(&new->mark, new->mark.group, new->mark.i.inode, NULL, 1)) { - free_chunk(new); + fsnotify_put_mark(&new->mark); goto Fallback; } @@ -322,7 +322,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree) entry = &chunk->mark; if (fsnotify_add_mark(entry, audit_tree_group, inode, NULL, 0)) { - free_chunk(chunk); + fsnotify_put_mark(entry); return -ENOSPC; } @@ -396,7 +396,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) fsnotify_duplicate_mark(chunk_entry, old_entry); if (fsnotify_add_mark(chunk_entry, chunk_entry->group, chunk_entry->i.inode, NULL, 1)) { spin_unlock(&old_entry->lock); - free_chunk(chunk); + fsnotify_put_mark(chunk_entry); fsnotify_put_mark(old_entry); return -ENOSPC; } -- 1.7.7 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/3] audit: don't free_chunk() after fsnotify_add_mark() 2012-06-25 17:46 ` [PATCH 1/3] audit: don't free_chunk() after fsnotify_add_mark() Miklos Szeredi @ 2012-07-06 17:43 ` Eric Paris 0 siblings, 0 replies; 5+ messages in thread From: Eric Paris @ 2012-07-06 17:43 UTC (permalink / raw) To: Miklos Szeredi; +Cc: viro, linux-kernel, mszeredi, stable On Mon, 2012-06-25 at 19:46 +0200, Miklos Szeredi wrote: > From: Miklos Szeredi <mszeredi@suse.cz> > > Don't do free_chunk() after fsnotify_add_mark(). That one does a delayed unref > via the destroy list and this results in use-after-free. > > Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> > CC: stable@vger.kernel.org Al, can you send these along? I am not going to see a computer again for a month. Hurray! Acked-by: Eric Paris <eparis@redhat.com> > --- > kernel/audit_tree.c | 6 +++--- > 1 files changed, 3 insertions(+), 3 deletions(-) > > diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c > index 5bf0790..d52d247 100644 > --- a/kernel/audit_tree.c > +++ b/kernel/audit_tree.c > @@ -259,7 +259,7 @@ static void untag_chunk(struct node *p) > > fsnotify_duplicate_mark(&new->mark, entry); > if (fsnotify_add_mark(&new->mark, new->mark.group, new->mark.i.inode, NULL, 1)) { > - free_chunk(new); > + fsnotify_put_mark(&new->mark); > goto Fallback; > } > > @@ -322,7 +322,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree) > > entry = &chunk->mark; > if (fsnotify_add_mark(entry, audit_tree_group, inode, NULL, 0)) { > - free_chunk(chunk); > + fsnotify_put_mark(entry); > return -ENOSPC; > } > > @@ -396,7 +396,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) > fsnotify_duplicate_mark(chunk_entry, old_entry); > if (fsnotify_add_mark(chunk_entry, chunk_entry->group, chunk_entry->i.inode, NULL, 1)) { > spin_unlock(&old_entry->lock); > - free_chunk(chunk); > + fsnotify_put_mark(chunk_entry); > fsnotify_put_mark(old_entry); > return -ENOSPC; > } ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/3] audit: fix refcounting in audit-tree [not found] <1340646378-5169-1-git-send-email-miklos@szeredi.hu> 2012-06-25 17:46 ` [PATCH 1/3] audit: don't free_chunk() after fsnotify_add_mark() Miklos Szeredi @ 2012-06-25 17:46 ` Miklos Szeredi 2012-07-06 17:52 ` Eric Paris 1 sibling, 1 reply; 5+ messages in thread From: Miklos Szeredi @ 2012-06-25 17:46 UTC (permalink / raw) To: eparis; +Cc: viro, linux-kernel, mszeredi, stable From: Miklos Szeredi <mszeredi@suse.cz> Refcounting of fsnotify_mark in audit tree is broken. E.g: refcount create_chunk alloc_chunk 1 fsnotify_add_mark 2 untag_chunk fsnotify_get_mark 3 fsnotify_destroy_mark audit_tree_freeing_mark 2 fsnotify_put_mark 1 fsnotify_put_mark 0 via destroy_list fsnotify_mark_destroy -1 This was reported by various people as triggering Oops when stopping auditd. We could just remove the put_mark from audit_tree_freeing_mark() but that would break freeing via inode destruction. So this patch simply omits a put_mark after calling destroy_mark (or adds a get_mark before). Next patch will clean up the remaining mess. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> CC: stable@vger.kernel.org --- kernel/audit_tree.c | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index d52d247..31fdc48 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -250,7 +250,6 @@ static void untag_chunk(struct node *p) spin_unlock(&hash_lock); spin_unlock(&entry->lock); fsnotify_destroy_mark(entry); - fsnotify_put_mark(entry); goto out; } @@ -293,7 +292,6 @@ static void untag_chunk(struct node *p) spin_unlock(&hash_lock); spin_unlock(&entry->lock); fsnotify_destroy_mark(entry); - fsnotify_put_mark(entry); goto out; Fallback: @@ -332,6 +330,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree) spin_unlock(&hash_lock); chunk->dead = 1; spin_unlock(&entry->lock); + fsnotify_get_mark(entry); fsnotify_destroy_mark(entry); fsnotify_put_mark(entry); return 0; @@ -412,6 +411,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) spin_unlock(&chunk_entry->lock); spin_unlock(&old_entry->lock); + fsnotify_get_mark(chunk_entry); fsnotify_destroy_mark(chunk_entry); fsnotify_put_mark(chunk_entry); @@ -445,7 +445,6 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) spin_unlock(&old_entry->lock); fsnotify_destroy_mark(old_entry); fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */ - fsnotify_put_mark(old_entry); /* and kill it */ return 0; } -- 1.7.7 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 2/3] audit: fix refcounting in audit-tree 2012-06-25 17:46 ` [PATCH 2/3] audit: fix refcounting in audit-tree Miklos Szeredi @ 2012-07-06 17:52 ` Eric Paris 2012-07-11 22:24 ` Miklos Szeredi 0 siblings, 1 reply; 5+ messages in thread From: Eric Paris @ 2012-07-06 17:52 UTC (permalink / raw) To: Miklos Szeredi; +Cc: viro, linux-kernel, mszeredi, stable On Mon, 2012-06-25 at 19:46 +0200, Miklos Szeredi wrote: > From: Miklos Szeredi <mszeredi@suse.cz> > > Refcounting of fsnotify_mark in audit tree is broken. E.g: > > refcount > create_chunk > alloc_chunk 1 > fsnotify_add_mark 2 > > untag_chunk > fsnotify_get_mark 3 > fsnotify_destroy_mark > audit_tree_freeing_mark 2 > fsnotify_put_mark 1 > fsnotify_put_mark 0 > via destroy_list > fsnotify_mark_destroy -1 > > This was reported by various people as triggering Oops when stopping auditd. > > We could just remove the put_mark from audit_tree_freeing_mark() but that would > break freeing via inode destruction. So this patch simply omits a put_mark > after calling destroy_mark (or adds a get_mark before). > > Next patch will clean up the remaining mess. > > Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> > CC: stable@vger.kernel.org Agreed this is needed. My changelog was: audit: fix ref count problems in audit trees Before the switch from in kernel inotify to fsnotify for audit trees the code regularly did: inotify_evict_watch(&chunk->watch); put_inotify_watch(&chunk->watch); I translated this in fsnotify_speak into: fsnotify_destroy_mark_by_entry(chunk_entry); fsnotify_put_mark(chunk_entry); The problem is that the inotify_evict_watch function actually took a reference on chunk->watch, which is what was being dropped by put_inotify_watch(). The fsnotify code does not take such a reference during fsnotify_destroy_mark_by_entry(). Thus we are dropping reference counts prematurely and eventually we hit a use after free! Whoops! Fix these call sites to not drop the extra reference. Reported-by: Valentin Avram <aval13@gmail.com> Reported-by: Peter Moody <pmoody@google.com> Partial-patch-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> Signed-off-by: Eric Paris <eparis@redhat.com> Maybe you can use some of that changelog in your next post? (If you do one?) The only reason you would repost is because I don't understand why you took a ref in some places instead of just not dropping it everywhere... > --- > kernel/audit_tree.c | 5 ++--- > 1 files changed, 2 insertions(+), 3 deletions(-) > > diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c > index d52d247..31fdc48 100644 > --- a/kernel/audit_tree.c > +++ b/kernel/audit_tree.c > @@ -250,7 +250,6 @@ static void untag_chunk(struct node *p) > spin_unlock(&hash_lock); > spin_unlock(&entry->lock); > fsnotify_destroy_mark(entry); > - fsnotify_put_mark(entry); > goto out; > } > > @@ -293,7 +292,6 @@ static void untag_chunk(struct node *p) > spin_unlock(&hash_lock); > spin_unlock(&entry->lock); > fsnotify_destroy_mark(entry); > - fsnotify_put_mark(entry); > goto out; > > Fallback: > @@ -332,6 +330,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree) > spin_unlock(&hash_lock); > chunk->dead = 1; > spin_unlock(&entry->lock); > + fsnotify_get_mark(entry); > fsnotify_destroy_mark(entry); > fsnotify_put_mark(entry); > return 0; Like here? Why not just avoid the atomic op altogether? > @@ -412,6 +411,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) > spin_unlock(&chunk_entry->lock); > spin_unlock(&old_entry->lock); > > + fsnotify_get_mark(chunk_entry); > fsnotify_destroy_mark(chunk_entry); > > fsnotify_put_mark(chunk_entry); > @@ -445,7 +445,6 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) > spin_unlock(&old_entry->lock); > fsnotify_destroy_mark(old_entry); > fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */ > - fsnotify_put_mark(old_entry); /* and kill it */ > return 0; > } > ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH 2/3] audit: fix refcounting in audit-tree 2012-07-06 17:52 ` Eric Paris @ 2012-07-11 22:24 ` Miklos Szeredi 0 siblings, 0 replies; 5+ messages in thread From: Miklos Szeredi @ 2012-07-11 22:24 UTC (permalink / raw) To: Eric Paris; +Cc: Miklos Szeredi, viro, linux-kernel, stable On Fri, 2012-07-06 at 13:52 -0400, Eric Paris wrote: > On Mon, 2012-06-25 at 19:46 +0200, Miklos Szeredi wrote: > > From: Miklos Szeredi <mszeredi@suse.cz> > > > > Refcounting of fsnotify_mark in audit tree is broken. E.g: > > > > refcount > > create_chunk > > alloc_chunk 1 > > fsnotify_add_mark 2 > > > > untag_chunk > > fsnotify_get_mark 3 > > fsnotify_destroy_mark > > audit_tree_freeing_mark 2 > > fsnotify_put_mark 1 > > fsnotify_put_mark 0 > > via destroy_list > > fsnotify_mark_destroy -1 > > > > This was reported by various people as triggering Oops when stopping auditd. > > > > We could just remove the put_mark from audit_tree_freeing_mark() but that would > > break freeing via inode destruction. So this patch simply omits a put_mark > > after calling destroy_mark (or adds a get_mark before). > > > > Next patch will clean up the remaining mess. > > > > Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> > > CC: stable@vger.kernel.org > > Agreed this is needed. My changelog was: > > audit: fix ref count problems in audit trees > > Before the switch from in kernel inotify to fsnotify for audit trees the > code regularly did: > inotify_evict_watch(&chunk->watch); > put_inotify_watch(&chunk->watch); > > I translated this in fsnotify_speak into: > fsnotify_destroy_mark_by_entry(chunk_entry); > fsnotify_put_mark(chunk_entry); > > The problem is that the inotify_evict_watch function actually took a > reference on chunk->watch, which is what was being dropped by > put_inotify_watch(). The fsnotify code does not take such a reference > during fsnotify_destroy_mark_by_entry(). Thus we are dropping reference > counts prematurely and eventually we hit a use after free! Whoops! > > Fix these call sites to not drop the extra reference. > > Reported-by: Valentin Avram <aval13@gmail.com> > Reported-by: Peter Moody <pmoody@google.com> > Partial-patch-by: Marcelo Cerri <mhcerri@linux.vnet.ibm.com> > Signed-off-by: Eric Paris <eparis@redhat.com> > > Maybe you can use some of that changelog in your next post? (If you do > one?) The only reason you would repost is because I don't understand > why you took a ref in some places instead of just not dropping it > everywhere... > > > --- > > kernel/audit_tree.c | 5 ++--- > > 1 files changed, 2 insertions(+), 3 deletions(-) > > > > diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c > > index d52d247..31fdc48 100644 > > --- a/kernel/audit_tree.c > > +++ b/kernel/audit_tree.c > > @@ -250,7 +250,6 @@ static void untag_chunk(struct node *p) > > spin_unlock(&hash_lock); > > spin_unlock(&entry->lock); > > fsnotify_destroy_mark(entry); > > - fsnotify_put_mark(entry); > > goto out; > > } > > > > @@ -293,7 +292,6 @@ static void untag_chunk(struct node *p) > > spin_unlock(&hash_lock); > > spin_unlock(&entry->lock); > > fsnotify_destroy_mark(entry); > > - fsnotify_put_mark(entry); > > goto out; > > > > Fallback: > > @@ -332,6 +330,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree) > > spin_unlock(&hash_lock); > > chunk->dead = 1; > > spin_unlock(&entry->lock); > > + fsnotify_get_mark(entry); > > fsnotify_destroy_mark(entry); > > fsnotify_put_mark(entry); > > return 0; > > Like here? Why not just avoid the atomic op altogether? Simply because fsnotify_destroy_mark() assumes that the caller is holding a reference to the mark (or the inode is keeping the mark pinned, not the case here AFAICS). Without that ref it can Oops. > > > @@ -412,6 +411,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) > > spin_unlock(&chunk_entry->lock); > > spin_unlock(&old_entry->lock); > > > > + fsnotify_get_mark(chunk_entry); > > fsnotify_destroy_mark(chunk_entry); > > > > fsnotify_put_mark(chunk_entry); > > @@ -445,7 +445,6 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) > > spin_unlock(&old_entry->lock); > > fsnotify_destroy_mark(old_entry); > > fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */ > > - fsnotify_put_mark(old_entry); /* and kill it */ > > return 0; > > } > > > > ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2012-07-11 22:24 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- [not found] <1340646378-5169-1-git-send-email-miklos@szeredi.hu> 2012-06-25 17:46 ` [PATCH 1/3] audit: don't free_chunk() after fsnotify_add_mark() Miklos Szeredi 2012-07-06 17:43 ` Eric Paris 2012-06-25 17:46 ` [PATCH 2/3] audit: fix refcounting in audit-tree Miklos Szeredi 2012-07-06 17:52 ` Eric Paris 2012-07-11 22:24 ` Miklos Szeredi
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).