afs: Fix use-after-loss-of-ref
authorDavid Howells <dhowells@redhat.com>
Tue, 14 Jan 2020 16:16:25 +0000 (16:16 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 Jan 2020 18:48:41 +0000 (19:48 +0100)
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: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/afs/dir.c
include/trace/events/afs.h

index 497f979..813db17 100644 (file)
@@ -908,6 +908,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;
@@ -957,15 +958,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;
 }
index d5ec4fa..564ba1b 100644 (file)
@@ -915,9 +915,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            )
@@ -928,13 +928,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;
                           ),