Re: [PATCH v2] cifs: fix composing of mount options for DFS referrals

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

 



On Thu, 23 May 2013 14:47:44 -0400
Jeff Layton <jlayton@xxxxxxxxxx> wrote:

> With the change to ignore the unc= and prefixpath= mount options, there
> is no longer any need to add them to the options string when mounting.
> By the same token, we now need to build a device name that includes the
> prefixpath when mounting.
> 
> To make things neater, the delimiters on the devicename are changed
> to '/' since that's preferred when mounting anyway.
> 
> While we're at it, fix a potential buffer overrun when building the
> mount options string, if the new ip= option is much longer than the
> original one.
> 
> v2: fix some comments and don't bother looking at whether there is
>     a prepath in the ref->node_name when deciding whether to pass
>     a prepath to cifs_build_devname.
> 
> Signed-off-by: Jeff Layton <jlayton@xxxxxxxxxx>
> ---
>  fs/cifs/cifs_dfs_ref.c | 141 +++++++++++++++++++++++++------------------------
>  fs/cifs/dns_resolve.c  |   4 +-
>  2 files changed, 74 insertions(+), 71 deletions(-)
> 
> diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
> index 8e33ec6..43feb8d 100644
> --- a/fs/cifs/cifs_dfs_ref.c
> +++ b/fs/cifs/cifs_dfs_ref.c
> @@ -18,6 +18,7 @@
>  #include <linux/slab.h>
>  #include <linux/vfs.h>
>  #include <linux/fs.h>
> +#include <linux/inet.h>
>  #include "cifsglob.h"
>  #include "cifsproto.h"
>  #include "cifsfs.h"
> @@ -48,58 +49,74 @@ void cifs_dfs_release_automount_timer(void)
>  }
>  
>  /**
> - * cifs_get_share_name	-	extracts share name from UNC
> - * @node_name:	pointer to UNC string
> + * cifs_build_devname - build a devicename from a UNC and optional prepath
> + * @nodename:	pointer to UNC string
> + * @prepath:	pointer to prefixpath (or NULL if there isn't one)
>   *
> - * Extracts sharename form full UNC.
> - * i.e. strips from UNC trailing path that is not part of share
> - * name and fixup missing '\' in the beginning of DFS node refferal
> - * if necessary.
> - * Returns pointer to share name on success or ERR_PTR on error.
> - * Caller is responsible for freeing returned string.
> + * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer
> + * big enough to hold the final thing. Copy the UNC from the nodename, and
> + * concatenate the prepath onto the end of it if there is one.
> + *
> + * Returns pointer to the built string, or a ERR_PTR. Caller is responsible
> + * for freeing the returned string.
>   */
> -static char *cifs_get_share_name(const char *node_name)
> +static char *
> +cifs_build_devname(char *nodename, const char *prepath)
>  {
> -	int len;
> -	char *UNC;
> -	char *pSep;
> -
> -	len = strlen(node_name);
> -	UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */,
> -			 GFP_KERNEL);
> -	if (!UNC)
> -		return ERR_PTR(-ENOMEM);
> +	size_t pplen;
> +	size_t unclen;
> +	char *dev;
> +	char *pos;
> +
> +	/* skip over any preceding delimiters */
> +	nodename += strspn(nodename, "\\");
> +	if (!*nodename)
> +		return ERR_PTR(-EINVAL);
>  
> -	/* get share name and server name */
> -	if (node_name[1] != '\\') {
> -		UNC[0] = '\\';
> -		strncpy(UNC+1, node_name, len);
> -		len++;
> -		UNC[len] = 0;
> -	} else {
> -		strncpy(UNC, node_name, len);
> -		UNC[len] = 0;
> -	}
> +	/* get length of UNC and set pos to last char */
> +	unclen = strlen(nodename);
> +	pos = nodename + unclen - 1;
>  
> -	/* find server name end */
> -	pSep = memchr(UNC+2, '\\', len-2);
> -	if (!pSep) {
> -		cifs_dbg(VFS, "%s: no server name end in node name: %s\n",
> -			 __func__, node_name);
> -		kfree(UNC);
> -		return ERR_PTR(-EINVAL);
> +	/* trim off any trailing delimiters */
> +	while(*pos == '\\') {
> +		--pos;
> +		--unclen;
>  	}
>  
> -	/* find sharename end */
> -	pSep++;
> -	pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC));
> -	if (pSep) {
> -		/* trim path up to sharename end
> -		 * now we have share name in UNC */
> -		*pSep = 0;
> +	/* allocate a buffer:
> +	 * +2 for preceding "//"
> +	 * +1 for delimiter between UNC and prepath
> +	 * +1 for trailing NULL
> +	 */
> +	pplen = prepath ? strlen(prepath) : 0;
> +	dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL);
> +	if (!dev)
> +		return ERR_PTR(-ENOMEM);
> +
> +	pos = dev;
> +	/* add the initial "//" */
> +	*pos = '/';
> +	++pos;
> +	*pos = '/';
> +	++pos;
> +
> +	/* copy in the UNC portion from referral */
> +	memcpy(pos, nodename, unclen);
> +	pos += unclen;
> +
> +	/* copy the prefixpath remainder (if there is one) */
> +	if (pplen) {
> +		*pos = '/';
> +		++pos;
> +		memcpy(pos, prepath, pplen);
> +		pos += pplen;
>  	}
>  
> -	return UNC;
> +	/* NULL terminator */
> +	*pos = '\0';
> +
> +	convert_delimiter(dev, '/');
> +	return dev;
>  }
>  
>  
> @@ -123,6 +140,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
>  {
>  	int rc;
>  	char *mountdata = NULL;
> +	const char *prepath = NULL;
>  	int md_len;
>  	char *tkn_e;
>  	char *srvIP = NULL;
> @@ -132,7 +150,10 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
>  	if (sb_mountdata == NULL)
>  		return ERR_PTR(-EINVAL);
>  
> -	*devname = cifs_get_share_name(ref->node_name);
> +	if (strlen(fullpath) - ref->path_consumed)
> +		prepath = fullpath + ref->path_consumed;
> +
> +	*devname = cifs_build_devname(ref->node_name, prepath);
>  	if (IS_ERR(*devname)) {
>  		rc = PTR_ERR(*devname);
>  		*devname = NULL;
> @@ -146,12 +167,14 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
>  		goto compose_mount_options_err;
>  	}
>  
> -	/* md_len = strlen(...) + 12 for 'sep+prefixpath='
> -	 * assuming that we have 'unc=' and 'ip=' in
> -	 * the original sb_mountdata
> +	/*
> +	 * In most cases, we'll be building a shorter string than the original,
> +	 * but we do have to assume that the address in the ip= option may be
> +	 * much longer than the original. Add the max length of an address
> +	 * string to the length of the original string to allow for worst case.
>  	 */
> -	md_len = strlen(sb_mountdata) + rc + strlen(ref->node_name) + 12;
> -	mountdata = kzalloc(md_len+1, GFP_KERNEL);
> +	md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN;
> +	mountdata = kzalloc(md_len + 1, GFP_KERNEL);

Hmm... on third thought, I'll go ahead and spin up a smaller patch that
just fixes this potential buffer overrun so we can send it to stable,
and re-base this patch on top of that.

Sorry for the noise...

-- 
Jeff Layton <jlayton@xxxxxxxxxx>
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux