Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 8 Sep 2008 01:12:28 +0400
From:      Chagin Dmitry <dchagin@freebsd.org>
To:        MITA Yoshio <mita@ee.t.u-tokyo.ac.jp>
Cc:        freebsd-emulation@freebsd.org
Subject:   Re: kern/117010: [linux] linux_getdents() get something like buffer overflow or else
Message-ID:  <20080907211228.GA51816@dchagin.dialup.corbina.ru>
In-Reply-To: <200809072030.m87KU44I064646@freefall.freebsd.org>
References:  <200809072030.m87KU44I064646@freefall.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
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@ee.t.u-tokyo.ac.jp>
> To: bug-followup@FreeBSD.org,samflanker@gmail.com,
>     Chagin Dmitry <chagin.dmitry@gmail.com>,
>     beech@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@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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20080907211228.GA51816>