Skip site navigation (1)Skip section navigation (2)
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>