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