Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 8 Sep 2008 09:05:05 +0200
From:      Roman Divacky <rdivacky@freebsd.org>
To:        MITA Yoshio <mita@ee.t.u-tokyo.ac.jp>, freebsd-emulation@freebsd.org
Subject:   Re: kern/117010: [linux] linux_getdents() get something like buffer overflow or else
Message-ID:  <20080908070505.GA20963@freebsd.org>
In-Reply-To: <20080907211228.GA51816@dchagin.dialup.corbina.ru>
References:  <200809072030.m87KU44I064646@freefall.freebsd.org> <20080907211228.GA51816@dchagin.dialup.corbina.ru>

next in thread | previous in thread | raw e-mail | index | archive | help
On Mon, Sep 08, 2008 at 01:12:28AM +0400, Chagin Dmitry wrote:
> On Sun, Sep 07, 2008 at 08:30:04PM +0000, MITA Yoshio wrote:
> > The following reply was made to PR kern/117010; it has been noted by GNATS.
> > 
> > From: MITA Yoshio <mita@ee.t.u-tokyo.ac.jp>
> > To: bug-followup@FreeBSD.org,samflanker@gmail.com,
> >     Chagin Dmitry <chagin.dmitry@gmail.com>,
> >     beech@FreeBSD.org
> > Cc:  
> > Subject: Re: kern/117010: [linux] linux_getdents() get something like buffer overflow or else
> > Date: Sun, 07 Sep 2008 21:42:15 +0200
> > 
> >  Hello, 
> >  
> >  I've tested a patch from Mr. Dmitry concerning Mr. Ermakov's PR:
> >  
> >  http://www.freebsd.org/cgi/query-pr.cgi?pr=117010
> >  
> >  Patch:
> >  >From:	Chagin Dmitry <chagin.dmitry@gmail.com>
> >  >Date:	Fri, 25 Jul 2008 10:22:46 +0400 (MSD)
> >  
> >  This worked!!! to make skype2 work.  
> >  Otherwise skype2 dumped core as Mr.  reported. 
> >  
> >  Regards,
> >  -----
> >  Tested Environment: 
> >  FreeBSD 7.0-RELEASE
> >  linux_base-fc6-6_5
> >  linux-glib2-2.6.6
> >  skype-2.0.0.68,1 
> >  
> >  /etc/sysctl.conf:
> >  compat.linux.osrelease=2.6.16
> >  
> >  /etc/make.conf:
> >  OVERRIDE_LINUX_BASE_PORT=fc6 
> 
> Please, try a patch bellow:
> 
> diff --git a/src/sys/compat/linux/linux_file.c b/src/sys/compat/linux/linux_file.c
> index 303bc3f..413e597 100644
> --- a/src/sys/compat/linux/linux_file.c
> +++ b/src/sys/compat/linux/linux_file.c
> @@ -303,9 +303,20 @@ struct l_dirent64 {
>  	char		d_name[LINUX_NAME_MAX + 1];
>  };
>  
> -#define LINUX_RECLEN(de,namlen) \
> -    ALIGN((((char *)&(de)->d_name - (char *)de) + (namlen) + 1))
> +/*
> + * Linux uses the last byte in the dirent buffer to store d_type,
> + * at least glibc-2.7 requires it. For what l_dirent padded on 2 bytes.
> + */
> +#define LINUX_RECLEN(namlen)						\
> +    roundup((offsetof(struct l_dirent, d_name) + (namlen) + 2),		\
> +    sizeof(l_ulong))
> +
> +#define LINUX_RECLEN64(namlen)						\
> +    roundup((offsetof(struct l_dirent64, d_name) + (namlen) + 1),	\
> +    sizeof(uint64_t))
>  
> +#define LINUX_MAXRECLEN		max(LINUX_RECLEN(LINUX_NAME_MAX),	\
> +				    LINUX_RECLEN64(LINUX_NAME_MAX))
>  #define	LINUX_DIRBLKSIZ		512
>  
>  static int
> @@ -318,12 +329,13 @@ getdents_common(struct thread *td, struct linux_getdents64_args *args,
>  	int len, reclen;		/* BSD-format */
>  	caddr_t outp;			/* Linux-format */
>  	int resid, linuxreclen=0;	/* Linux-format */
> +	caddr_t lbuf;			/* Linux-format */
>  	struct file *fp;
>  	struct uio auio;
>  	struct iovec aiov;
>  	off_t off;
> -	struct l_dirent linux_dirent;
> -	struct l_dirent64 linux_dirent64;
> +	struct l_dirent *linux_dirent;
> +	struct l_dirent64 *linux_dirent64;
>  	int buflen, error, eofflag, nbytes, justone;
>  	u_long *cookies = NULL, *cookiep;
>  	int ncookies, vfslocked;
> @@ -359,6 +371,7 @@ getdents_common(struct thread *td, struct linux_getdents64_args *args,
>  	buflen = max(LINUX_DIRBLKSIZ, nbytes);
>  	buflen = min(buflen, MAXBSIZE);
>  	buf = malloc(buflen, M_TEMP, M_WAITOK);
> +	lbuf = malloc(LINUX_MAXRECLEN, M_TEMP, M_WAITOK | M_ZERO);
>  	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
>  
>  again:
> @@ -436,8 +449,8 @@ again:
>  		}
>  
>  		linuxreclen = (is64bit)
> -		    ? LINUX_RECLEN(&linux_dirent64, bdp->d_namlen)
> -		    : LINUX_RECLEN(&linux_dirent, bdp->d_namlen);
> +		    ? LINUX_RECLEN64(bdp->d_namlen)
> +		    : LINUX_RECLEN(bdp->d_namlen);
>  
>  		if (reclen > len || resid < linuxreclen) {
>  			outp++;
> @@ -446,34 +459,41 @@ again:
>  
>  		if (justone) {
>  			/* readdir(2) case. */
> -			linux_dirent.d_ino = bdp->d_fileno;
> -			linux_dirent.d_off = (l_off_t)linuxreclen;
> -			linux_dirent.d_reclen = (l_ushort)bdp->d_namlen;
> -			strcpy(linux_dirent.d_name, bdp->d_name);
> -			error = copyout(&linux_dirent, outp, linuxreclen);
> -		} else {
> -			if (is64bit) {
> -				linux_dirent64.d_ino = bdp->d_fileno;
> -				linux_dirent64.d_off = (cookiep)
> -				    ? (l_off_t)*cookiep
> -				    : (l_off_t)(off + reclen);
> -				linux_dirent64.d_reclen =
> -				    (l_ushort)linuxreclen;
> -				linux_dirent64.d_type = bdp->d_type;
> -				strcpy(linux_dirent64.d_name, bdp->d_name);
> -				error = copyout(&linux_dirent64, outp,
> -				    linuxreclen);
> -			} else {
> -				linux_dirent.d_ino = bdp->d_fileno;
> -				linux_dirent.d_off = (cookiep)
> -				    ? (l_off_t)*cookiep
> -				    : (l_off_t)(off + reclen);
> -				linux_dirent.d_reclen = (l_ushort)linuxreclen;
> -				strcpy(linux_dirent.d_name, bdp->d_name);
> -				error = copyout(&linux_dirent, outp,
> -				    linuxreclen);
> -			}
> +			linux_dirent = (struct l_dirent*)lbuf;
> +			linux_dirent->d_ino = bdp->d_fileno;
> +			linux_dirent->d_off = (l_off_t)linuxreclen;
> +			linux_dirent->d_reclen = (l_ushort)bdp->d_namlen;
> +			strlcpy(linux_dirent->d_name, bdp->d_name,
> +			    linuxreclen - offsetof(struct l_dirent, d_name));
> +			error = copyout(linux_dirent, outp, linuxreclen);
>  		}
> +		if (is64bit) {
> +			linux_dirent64 = (struct l_dirent64*)lbuf;
> +			linux_dirent64->d_ino = bdp->d_fileno;
> +			linux_dirent64->d_off = (cookiep)
> +			    ? (l_off_t)*cookiep
> +			    : (l_off_t)(off + reclen);
> +			linux_dirent64->d_reclen = (l_ushort)linuxreclen;
> +			linux_dirent64->d_type = bdp->d_type;
> +			strlcpy(linux_dirent64->d_name, bdp->d_name,
> +			    linuxreclen - offsetof(struct l_dirent64, d_name));
> +			error = copyout(linux_dirent64, outp, linuxreclen);
> +		} else if (!justone) {
> +			linux_dirent = (struct l_dirent*)lbuf;
> +			linux_dirent->d_ino = bdp->d_fileno;
> +			linux_dirent->d_off = (cookiep)
> +			    ? (l_off_t)*cookiep
> +			    : (l_off_t)(off + reclen);
> +			linux_dirent->d_reclen = (l_ushort)linuxreclen;
> +			/*
> +			 * Copy d_type to last byte of l_dirent buffer
> +			 */
> +			lbuf[linuxreclen-1] = bdp->d_type;
> +			strlcpy(linux_dirent->d_name, bdp->d_name,
> +			    linuxreclen - offsetof(struct l_dirent, d_name)-1);
> +			error = copyout(linux_dirent, outp, linuxreclen);
> +		}
> +
>  		if (error)
>  			goto out;
>  
> @@ -509,6 +529,7 @@ out:
>  	VFS_UNLOCK_GIANT(vfslocked);
>  	fdrop(fp, td);
>  	free(buf, M_TEMP);
> +	free(lbuf, M_TEMP);
>  	return (error);
>  }
>  
> 
> Roman, I think that this patch can be commited (if testing passes :))
> thnx!

yes.... this is the version of the patch I posted to you + the comment changed, right?



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20080908070505.GA20963>