Date: Mon, 12 Aug 2002 19:38:51 +1000 (EST) From: Bruce Evans <bde@zeta.org.au> To: Niels Provos <provos@citi.umich.edu> Cc: Kris Kennaway <kris@obsecurity.org>, <security@FreeBSD.ORG> Subject: Re: [provos@citi.umich.edu: OpenBSD Security Advisory: Select Boundary Condition] Message-ID: <20020812183610.I23649-100000@gamplex.bde.org> In-Reply-To: <20020811223145.GQ22399@citi.citi.umich.edu>
next in thread | previous in thread | raw e-mail | index | archive | help
On Sun, 11 Aug 2002, Niels Provos wrote:
> On Sun, Aug 11, 2002 at 02:47:23PM -0700, Kris Kennaway wrote:
> > In case anyone is wondering, it looks like FreeBSD fixed this security
> > hole 6 years ago, in the following commit:
> >
> > ---
> > Revision 1.19 / (download) - annotate - [select for diffs], Tue Aug 20 07:17:48 1996 UTC (5 years, 11 months ago) by smpatel
> > Branch: MAIN
> > Changes since 1.18: +43 -15 lines
> > Diff to previous 1.18 (colored)
> >
> > Remove the kernel FD_SETSIZE limit for select().
> > Make select()'s first argument 'int' not 'u_int'.
> >
> > Reviewed by: bde
> > ---
> Read that commit message carefully. That problem was introduced into
> FreeBSD six years ago. It was fixed last year.
>
> revision 1.74
> date: 2001/02/27 00:50:20; author: jlemon; state: Exp; lines: +3 -2
> Cast nfds to u_int before range checking it in order to catch negative
> values.
>
> PR: 25393
>
> NetBSD fixed it somewhat later.
It is necessary to read all of the patches and the originally code carefully
to understand the history of this bug :-). FreeBSD doesn't seem to have
ever had it for select(), but it had it for poll():
(1) There was no actual problem in rev.1.18, since uap->nd was (bogusly)
u_int and is compared with FD_SETSIZE. If userland passes a small
negative value, then it gets converted to a large unsigned one via
a type pun. The type pun even works the same as a cast would since
all supported machines are 2's complement. E.g., -1 gets converted
to UINT_MAX which is > FD_SETSIZE so select() fails. The possible
values for the bogus conversion of non- small negative values are
also clear on all supported machines, since they all have 32-bit
ints -- the values are (u_int)INT_MAX + 1 through UINT_MAX. Anyway,
the value of uip->nd is guaranteed to be between 0 and FD_SETSIZE.
(2) Rev.1.19 did not introduce a problem. The relevant part of it just
removed the type puns by changing uap->nd to int and adding an
explicit check that uap->nd >= 0. This also improves the error
handling -- we return EINVAL immediately if uap-nd < 0 instead of
treating it as a too-large value and using the "forgiving; slightly
wrong" code.
The hack of optimizing the check for negative values together with
checking for small positive values by casting to u_int should not
be used in code written in the last 15 years or so, since compilers
have been doing it automatically if possible, and its correctness
is time-consuming to check especially if the types are typedefed
types instead of just plain int/u_int.
(3) poll() was obtained from NetBSD in rev.1.29. This version was
correct since the corresponding variable uap->nfds actually has
type u_int (at least in -current where it has type nfds_t = u_int)
it is compared early with p->p_fd->fd_nfiles. Omitting the explicit
comparison with 0 is non-bogus here since u_int's are inherently
nonnegative. However the proving the correctness of the comparision
of a u_int with the signed (int) p->p_fd_fd_nfiles involves the same
considerations.
(4) poll() was broken in rev.1.71 by clobbering uap->nfds by converting
it to a a variable of the wrong type (int nfds) without checking
that it fits first.
(5) poll() was "fixed" in rev.1.74 (20 days after it was broken) by
casting nfds back to u_int. The casts back and forth don't change
the value on 2's complement machines but are obfuscations.
(6) I complained about the bogus types and casts in rev.1.74 and they
were fixed a few hours later in rev.1.75.
> I did not contact anyone at FreeBSD or NetBSD because it was not a
> problem there in case you were wondering.
No problem.
014_scarg.patch seems to be correct and non-bogus except it uses the
cast-to-u_int hack for select().
Bruce
To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-security" in the body of the message
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20020812183610.I23649-100000>
