Date: Wed, 10 Sep 2008 11:44:41 +0400 From: sam <samflanker@gmail.com> To: chagin.dmitry@gmail.com Cc: beech@FreeBSD.org, 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: <48C77AE9.90700@gmail.com>
next in thread | raw e-mail | index | archive | help
On Mon, Sep 08, 2008 at 01:12:28AM +0400, Chagin Dmitry wrote: >/ 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! Hello Iam tested this patch on my old testing pack (with source) http://cs.udmvt.ru/files/temp/linux_getdents.tar.bz2 (bin.file linux_getdents_static compiled on system Linux 2.6.9 with old glibc version) # uname -a FreeBSD damascus 7.0-STABLE FreeBSD 7.0-STABLE #0: Mon Aug 25 12:41:55 MSD 2008 root@static:/usr/obj/usr/src/sys/DAMASCUS i386 # sysctl compat.linux.osrelease compat.linux.osrelease: 2.6.16 # pkg_info|grep linux linux_base-fc-4_13 Base set of packages needed in Linux mode (for i386/amd64) /before patch ------------------------------------------------------------------- # ./linux_getdents_static ./temp/ Reading... . .. ak47-1.wav ak47-2.wav *** sliderelease1.wav Closing... *** glibc detected *** ./linux_getdents_static: double free or corruption (!prev): 0x080c7688 *** ======= Backtrace: ========= [0x80515fe] [0x8054cdb] [0x80564b8] [0x804828b] [0x80484ab] [0x8048151] ======= Memory map: ======== 08048000-080c3000 r-xp 0008d000 00:00 4168734 /usr/home/venom/temp/temp/devel/linux/linux_getdents_static 080c3000-080c6000 rw-p 00025000 00:00 0 080c6000-080e8000 rwxp 00025000 00:00 0 480c3000-480c4000 rwxp 0013d000 00:00 0 48100000-48121000 rwxp 0013d000 00:00 0 48121000-48200000 ---p 0013d000 00:00 0 bfbe0000-bfc00000 rwxp 00020000 00:00 0 Abort trap: 6 (core dumped) ------------------------------------------------------------------- after patch ------------------------------------------------------------------- # ./linux_getdents_static ./temp/ Reading... . .. ak47-1.wav ak47-2.wav *** sliderelease1.wav Closing...Done! ------------------------------------------------------------------- thanks /Vladimir Ermakov
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?48C77AE9.90700>