Re: [patch] fs: fix d_validate

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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
>   */
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux