| From 1da3bfb4b1a74d37d6d87f9c914f59017e38374f Mon Sep 17 00:00:00 2001 |
| From: David Howells <dhowells@redhat.com> |
| Date: Tue, 14 Jan 2020 16:16:25 +0000 |
| Subject: [PATCH] afs: Fix use-after-loss-of-ref |
| |
| commit 40a708bd622b78582ae3d280de29b09b50bd04c0 upstream. |
| |
| afs_lookup() has a tracepoint to indicate the outcome of |
| d_splice_alias(), passing it the inode to retrieve the fid from. |
| However, the function gave up its ref on that inode when it called |
| d_splice_alias(), which may have failed and dropped the inode. |
| |
| Fix this by caching the fid. |
| |
| Fixes: 80548b03991f ("afs: Add more tracepoints") |
| Reported-by: Al Viro <viro@zeniv.linux.org.uk> |
| Signed-off-by: David Howells <dhowells@redhat.com> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/afs/dir.c b/fs/afs/dir.c |
| index e26c901877ea..d25c80bdd5de 100644 |
| --- a/fs/afs/dir.c |
| +++ b/fs/afs/dir.c |
| @@ -909,6 +909,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, |
| unsigned int flags) |
| { |
| struct afs_vnode *dvnode = AFS_FS_I(dir); |
| + struct afs_fid fid = {}; |
| struct inode *inode; |
| struct dentry *d; |
| struct key *key; |
| @@ -958,15 +959,16 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, |
| dentry->d_fsdata = |
| (void *)(unsigned long)dvnode->status.data_version; |
| } |
| + |
| + if (!IS_ERR_OR_NULL(inode)) |
| + fid = AFS_FS_I(inode)->fid; |
| + |
| d = d_splice_alias(inode, dentry); |
| if (!IS_ERR_OR_NULL(d)) { |
| d->d_fsdata = dentry->d_fsdata; |
| - trace_afs_lookup(dvnode, &d->d_name, |
| - inode ? AFS_FS_I(inode) : NULL); |
| + trace_afs_lookup(dvnode, &d->d_name, &fid); |
| } else { |
| - trace_afs_lookup(dvnode, &dentry->d_name, |
| - IS_ERR_OR_NULL(inode) ? NULL |
| - : AFS_FS_I(inode)); |
| + trace_afs_lookup(dvnode, &dentry->d_name, &fid); |
| } |
| return d; |
| } |
| diff --git a/include/trace/events/afs.h b/include/trace/events/afs.h |
| index 51b1e0da2efc..051612b4654b 100644 |
| --- a/include/trace/events/afs.h |
| +++ b/include/trace/events/afs.h |
| @@ -853,9 +853,9 @@ TRACE_EVENT(afs_call_state, |
| |
| TRACE_EVENT(afs_lookup, |
| TP_PROTO(struct afs_vnode *dvnode, const struct qstr *name, |
| - struct afs_vnode *vnode), |
| + struct afs_fid *fid), |
| |
| - TP_ARGS(dvnode, name, vnode), |
| + TP_ARGS(dvnode, name, fid), |
| |
| TP_STRUCT__entry( |
| __field_struct(struct afs_fid, dfid ) |
| @@ -866,13 +866,7 @@ TRACE_EVENT(afs_lookup, |
| TP_fast_assign( |
| int __len = min_t(int, name->len, 23); |
| __entry->dfid = dvnode->fid; |
| - if (vnode) { |
| - __entry->fid = vnode->fid; |
| - } else { |
| - __entry->fid.vid = 0; |
| - __entry->fid.vnode = 0; |
| - __entry->fid.unique = 0; |
| - } |
| + __entry->fid = *fid; |
| memcpy(__entry->name, name->name, __len); |
| __entry->name[__len] = 0; |
| ), |
| -- |
| 2.7.4 |
| |