kern/117010: [linux] linux_getdents() get something like buffer overflow or else

Chagin Dmitry dchagin at freebsd.org
Mon Sep 8 07:30:23 UTC 2008


On Mon, Sep 08, 2008 at 09:05:05AM +0200, Roman Divacky wrote:
> 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 at ee.t.u-tokyo.ac.jp>
> > > To: bug-followup at FreeBSD.org,samflanker at gmail.com,
> > >     Chagin Dmitry <chagin.dmitry at gmail.com>,
> > >     beech at 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 at 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?

yes

-- 
Have fun!
chd


More information about the freebsd-emulation mailing list