Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 27 Jul 2008 20:40:06 +0200
From:      Roman Divacky <rdivacky@freebsd.org>
To:        Chagin Dmitry <chagin.dmitry@gmail.com>
Cc:        Alexander Leidinger <Alexander@leidinger.net>, freebsd-emulation@freebsd.org
Subject:   Re: kern/117010: [linux] linux_getdents() get somethinng like buffer overflow
Message-ID:  <20080727184006.GA99255@freebsd.org>
In-Reply-To: <alpine.BSF.1.10.0807271958020.3912@ora.chd.net>
References:  <200807250700.m6P70FSF036132@freefall.freebsd.org> <20080726091045.4c617dc7@deskjail> <alpine.BSF.1.10.0807271958020.3912@ora.chd.net>

next in thread | previous in thread | raw e-mail | index | archive | help
On Sun, Jul 27, 2008 at 09:05:03PM +0400, Chagin Dmitry wrote:
> On Sat, 26 Jul 2008, Alexander Leidinger wrote:
> 
> >Quoting Chagin Dmitry <chagin.dmitry@gmail.com> (Fri, 25 Jul 2008 07:00:15 
> >GMT):
> >
> >>The following reply was made to PR kern/117010; it has been noted by 
> >>GNATS.
> >>
> >>From: Chagin Dmitry <chagin.dmitry@gmail.com>
> >>To: bug-followup@freebsd.org, samflanker@gmail.com
> >>Cc:
> >>Subject: Re: kern/117010: [linux] linux_getdents() get somethinng like 
> >>buffer
> >> overflow
> >>Date: Fri, 25 Jul 2008 10:22:46 +0400 (MSD)
> >>
> >> Please, try a patch below:
> >>
> >> diff --git a/src/sys/compat/linux/linux_file.c 
> >> b/src/sys/compat/linux/linux_file
> >> index 303bc3f..d88f95f 100644
> >> --- a/src/sys/compat/linux/linux_file.c
> >> +++ b/src/sys/compat/linux/linux_file.c
> >> @@ -303,8 +303,8 @@ struct l_dirent64 {
> >>  	char            d_name[LINUX_NAME_MAX + 1];
> >>   };
> >>
> >> -#define LINUX_RECLEN(de,namlen) \
> >> -    ALIGN((((char *)&(de)->d_name - (char *)de) + (namlen) + 1))
> >> +#define LINUX_RECLEN(de,namlen,trail) \
> >> +    ALIGN((((char *)&(de)->d_name - (char *)de) + (namlen) + trail))
> >
> >
> >The start of de->d_name minus the start of de should be the same as the 
> >offset of d_name in de, so I would expect that this is expressed with the 
> >offsetof maro instead of handmade.
> >
> >So the result of this is the offset plus a len + something.
> >
> 
> well... agree.
> 
> >>   #define        LINUX_DIRBLKSIZ         512
> >>
> >> @@ -436,8 +436,8 @@ again:
> >>  		}
> >
> >I try to understand the code before this. There's "if (reclen & 3)" error 
> >out. Does it mean it has to be a multiple of 4? If yes it should be 
> >changed to some modulo calculation to make it obvious (the compiler should 
> >be able to do such micro optimisations, but I doubt the error case needs 
> >to be micro optimized).
> >
> 
> this code looks as a workaround... exists since v1.1, I don't understand 
> what is it.
> 
> 
> >>  		linuxreclen = (is64bit)
> >> -                   ? LINUX_RECLEN(&linux_dirent64, bdp->d_namlen)
> >> -                   : LINUX_RECLEN(&linux_dirent, bdp->d_namlen);
> >> +                   ? LINUX_RECLEN(&linux_dirent64, bdp->d_namlen, 1)
> >> +                   : LINUX_RECLEN(&linux_dirent, bdp->d_namlen, 2);
> >
> >Translated: The length of the linux record is the offset plus the FreeBSD 
> >size plus something. Doesn't make sense to me. sizeof(linux_dirent) sound 
> >more suitable for this variable name. From the code it can not be the 
> >length of the linux record, but the size of a linux dirent struct which 
> >would be required to put all info inside (+ some more space... very 
> >suspicious).
> >
> >>  		if (reclen > len || resid < linuxreclen) {
> >>  			outp++;
> >
> >First part: if the length of the current record is larger than the 
> >remaining free space (if it does not fit) go out of the loop... ok.
> >
> 
> no, here reclen is the length of FreeBSD record and len is the remaining 
> space in FreeBSD records buffer.
> 
> 
> >Second part: if the length (in bytes?) is smaller than the theoretical 
> >size of the linux struct, go out of the loop. Ouch. Please tell me this is 
> >wrong (I didn't had breakfast yet, I really hope I misanalysed this 
> >because of this fact).
> >
> 
> no, resid is the free space in Linux records buffer, linuxreclen is the 
> length of the Linux record.
> 
> >I smell buffer mismanagement because of the strange 1 or 2 being added to 
> >the size, and I smell some convoluted logic there. Instead of trying to 
> >poke the thing until it works, I suggest to step back and have a look at 
> >the big picture if the entire part of the function can be improved.
> 
> This is the Linux, as Roman says - linux is a really strange :)
> See linux source fs/readdir.c implementation of filldir functions

I'll look at the readdir.c implementation, analyze it but I guess
Dmitry's version is ok. if I find it correct I think we should
wait for someone to test it but if noone does I think it can be
commited with just Dmitry's testing and my analysis :)

roman



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