kern/117010: [linux] linux_getdents() get something like buffer
overflow or else
Chagin Dmitry
dchagin at freebsd.org
Sun Sep 7 21:12:37 UTC 2008
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!
--
Have fun!
chd
More information about the freebsd-emulation
mailing list