linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Nick Piggin <npiggin@kernel.dk>
To: Christoph Hellwig <hch@lst.de>
Cc: Nick Piggin <npiggin@kernel.dk>,
	Linus Torvalds <torvalds@linux-foundation.org>,
	linux-fsdevel@vger.kernel.org, Al Viro <viro@ZenIV.linux.org.uk>,
	petr@vandrovec.name
Subject: Re: [patch] fs: fix d_validate
Date: Tue, 16 Nov 2010 21:25:33 +1100	[thread overview]
Message-ID: <20101116102533.GA4139@amd> (raw)
In-Reply-To: <20101116102045.GA22329@lst.de>

On Tue, Nov 16, 2010 at 11:20:45AM +0100, Christoph Hellwig wrote:
> 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.

Right, I depreated it, making it an easy backport. Your next patch
and then removing it completely would be the next step.

> 
> 
> 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
>   */

  reply	other threads:[~2010-11-16 10:25 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 ` [patch] fs: fix d_validate Christoph Hellwig
2010-11-16 10:25   ` Nick Piggin [this message]
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=20101116102533.GA4139@amd \
    --to=npiggin@kernel.dk \
    --cc=hch@lst.de \
    --cc=linux-fsdevel@vger.kernel.org \
    --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).