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