From: Christoph Hellwig <hch@lst.de>
To: Nick Piggin <npiggin@kernel.dk>
Cc: Linus Torvalds <torvalds@linux-foundation.org>,
linux-fsdevel@vger.kernel.org, Al Viro <viro@ZenIV.linux.org.uk>,
Christoph Hellwig <hch@lst.de>,
petr@vandrovec.name
Subject: Re: [patch] fs: fix d_validate
Date: Tue, 16 Nov 2010 11:20:45 +0100 [thread overview]
Message-ID: <20101116102045.GA22329@lst.de> (raw)
In-Reply-To: <20101116062319.GA3242@amd>
On Tue, Nov 16, 2010 at 05:23:19PM +1100, Nick Piggin wrote:
>
> fs: d_validate fixes
>
> d_validate has been broken for a long time.
>
> kmem_ptr_validate does not guarantee that a pointer can be dereferenced
> if it can go away at any time. Even rcu_read_lock doesn't help, because
> the pointer might be queued in RCU callbacks but not executed yet.
>
> So the parent cannot be checked, nor the name hashed. The dentry pointer
> can not be touched until it can be verified under lock. Hashing simply
> cannot be used.
>
> Instead, verify the parent/child relationship by traversing parent's
> d_child list. It's slow, but only ncpfs and the destaged smbfs care
> about it, at this point.
That's what both callers already do when d_validate fails, so if we want
to go down that route I'd suggest to just remove it, like in the patch
below. Note that there's probably a lot more caching infrastrucuture
in ncpfs/smbfs that becomes dead by this as well.
Index: linux-2.6/drivers/staging/smbfs/cache.c
===================================================================
--- linux-2.6.orig/drivers/staging/smbfs/cache.c 2010-11-16 10:49:55.915263307 +0100
+++ linux-2.6/drivers/staging/smbfs/cache.c 2010-11-16 11:12:17.091018582 +0100
@@ -73,33 +73,13 @@ smb_invalidate_dircache_entries(struct d
spin_unlock(&dcache_lock);
}
-/*
- * dget, but require that fpos and parent matches what the dentry contains.
- * dentry is not known to be a valid pointer at entry.
- */
struct dentry *
-smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
+smb_dget_fpos(struct dentry *parent, unsigned long fpos)
{
- struct dentry *dent = dentry;
- struct list_head *next;
+ struct dentry *dent;
- if (d_validate(dent, parent)) {
- if (dent->d_name.len <= SMB_MAXNAMELEN &&
- (unsigned long)dent->d_fsdata == fpos) {
- if (!dent->d_inode) {
- dput(dent);
- dent = NULL;
- }
- return dent;
- }
- dput(dent);
- }
-
- /* If a pointer is invalid, we search the dentry. */
spin_lock(&dcache_lock);
- next = parent->d_subdirs.next;
- while (next != &parent->d_subdirs) {
- dent = list_entry(next, struct dentry, d_u.d_child);
+ list_for_each_entry(dent, &parent->d_subdirs, d_u.d_child) {
if ((unsigned long)dent->d_fsdata == fpos) {
if (dent->d_inode)
dget_locked(dent);
@@ -107,7 +87,6 @@ smb_dget_fpos(struct dentry *dentry, str
dent = NULL;
goto out_unlock;
}
- next = next->next;
}
dent = NULL;
out_unlock:
Index: linux-2.6/drivers/staging/smbfs/dir.c
===================================================================
--- linux-2.6.orig/drivers/staging/smbfs/dir.c 2010-11-16 10:52:14.577003425 +0100
+++ linux-2.6/drivers/staging/smbfs/dir.c 2010-11-16 11:11:29.420254992 +0100
@@ -166,8 +166,7 @@ smb_readdir(struct file *filp, void *dir
struct dentry *dent;
int res;
- dent = smb_dget_fpos(ctl.cache->dentry[ctl.idx],
- dentry, filp->f_pos);
+ dent = smb_dget_fpos(dentry, filp->f_pos);
if (!dent)
goto invalid_cache;
Index: linux-2.6/drivers/staging/smbfs/proto.h
===================================================================
--- linux-2.6.orig/drivers/staging/smbfs/proto.h 2010-11-16 10:52:00.746253110 +0100
+++ linux-2.6/drivers/staging/smbfs/proto.h 2010-11-16 10:52:06.439266241 +0100
@@ -43,7 +43,7 @@ extern void smb_renew_times(struct dentr
/* cache.c */
extern void smb_invalid_dir_cache(struct inode *dir);
extern void smb_invalidate_dircache_entries(struct dentry *parent);
-extern struct dentry *smb_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos);
+extern struct dentry *smb_dget_fpos(struct dentry *parent, unsigned long fpos);
extern int smb_fill_cache(struct file *filp, void *dirent, filldir_t filldir, struct smb_cache_control *ctrl, struct qstr *qname, struct smb_fattr *entry);
/* sock.c */
extern void smb_data_ready(struct sock *sk, int len);
Index: linux-2.6/fs/ncpfs/dir.c
===================================================================
--- linux-2.6.orig/fs/ncpfs/dir.c 2010-11-16 10:49:04.988254089 +0100
+++ linux-2.6/fs/ncpfs/dir.c 2010-11-16 11:03:55.184018020 +0100
@@ -367,28 +367,12 @@ finished:
}
static struct dentry *
-ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
+ncp_dget_fpos(struct dentry *parent, unsigned long fpos)
{
- struct dentry *dent = dentry;
- struct list_head *next;
+ struct dentry *dent;
- if (d_validate(dent, parent)) {
- if (dent->d_name.len <= NCP_MAXPATHLEN &&
- (unsigned long)dent->d_fsdata == fpos) {
- if (!dent->d_inode) {
- dput(dent);
- dent = NULL;
- }
- return dent;
- }
- dput(dent);
- }
-
- /* If a pointer is invalid, we search the dentry. */
spin_lock(&dcache_lock);
- next = parent->d_subdirs.next;
- while (next != &parent->d_subdirs) {
- dent = list_entry(next, struct dentry, d_u.d_child);
+ list_for_each_entry(dent, &parent->d_subdirs, d_u.d_child) {
if ((unsigned long)dent->d_fsdata == fpos) {
if (dent->d_inode)
dget_locked(dent);
@@ -397,11 +381,9 @@ ncp_dget_fpos(struct dentry *dentry, str
spin_unlock(&dcache_lock);
goto out;
}
- next = next->next;
}
spin_unlock(&dcache_lock);
return NULL;
-
out:
return dent;
}
@@ -496,8 +478,7 @@ static int ncp_readdir(struct file *filp
struct dentry *dent;
int res;
- dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx],
- dentry, filp->f_pos);
+ dent = ncp_dget_fpos(dentry, filp->f_pos);
if (!dent)
goto invalid_cache;
res = filldir(dirent, dent->d_name.name,
Index: linux-2.6/fs/dcache.c
===================================================================
--- linux-2.6.orig/fs/dcache.c 2010-11-16 10:57:20.960004264 +0100
+++ linux-2.6/fs/dcache.c 2010-11-16 10:57:25.790005239 +0100
@@ -1482,39 +1482,6 @@ out:
return dentry;
}
-/**
- * d_validate - verify dentry provided from insecure source
- * @dentry: The dentry alleged to be valid child of @dparent
- * @dparent: The parent dentry (known to be valid)
- *
- * An insecure source has sent us a dentry, here we verify it and dget() it.
- * This is used by ncpfs in its readdir implementation.
- * Zero is returned in the dentry is invalid.
- */
-int d_validate(struct dentry *dentry, struct dentry *parent)
-{
- struct hlist_head *head = d_hash(parent, dentry->d_name.hash);
- struct hlist_node *node;
- struct dentry *d;
-
- /* Check whether the ptr might be valid at all.. */
- if (!kmem_ptr_validate(dentry_cache, dentry))
- return 0;
- if (dentry->d_parent != parent)
- return 0;
-
- rcu_read_lock();
- hlist_for_each_entry_rcu(d, node, head, d_hash) {
- if (d == dentry) {
- dget(dentry);
- return 1;
- }
- }
- rcu_read_unlock();
- return 0;
-}
-EXPORT_SYMBOL(d_validate);
-
/*
* When a file is deleted, we have two options:
* - turn this dentry into a negative dentry
Index: linux-2.6/include/linux/dcache.h
===================================================================
--- linux-2.6.orig/include/linux/dcache.h 2010-11-16 10:57:14.736253110 +0100
+++ linux-2.6/include/linux/dcache.h 2010-11-16 10:57:18.474254784 +0100
@@ -305,9 +305,6 @@ extern struct dentry * d_lookup(struct d
extern struct dentry * __d_lookup(struct dentry *, struct qstr *);
extern struct dentry * d_hash_and_lookup(struct dentry *, struct qstr *);
-/* validate "insecure" dentry pointer */
-extern int d_validate(struct dentry *, struct dentry *);
-
/*
* helper function for dentry_operations.d_dname() members
*/
next prev parent reply other threads:[~2010-11-16 10:21 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2010-11-16 6:23 [patch] fs: fix d_validate Nick Piggin
2010-11-16 6:24 ` [patch] kernel: get rid of *_ptr_validate Nick Piggin
2010-11-16 10:21 ` Christoph Hellwig
2010-11-16 10:20 ` Christoph Hellwig [this message]
2010-11-16 10:25 ` [patch] fs: fix d_validate Nick Piggin
2010-11-16 16:28 ` Christoph Hellwig
2010-11-17 3:51 ` Nick Piggin
2010-11-16 16:20 ` Linus Torvalds
2010-11-16 16:25 ` Christoph Hellwig
2010-11-17 3:49 ` Nick Piggin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20101116102045.GA22329@lst.de \
--to=hch@lst.de \
--cc=linux-fsdevel@vger.kernel.org \
--cc=npiggin@kernel.dk \
--cc=petr@vandrovec.name \
--cc=torvalds@linux-foundation.org \
--cc=viro@ZenIV.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).