From owner-svn-src-all@FreeBSD.ORG Sun Nov 13 10:28:02 2011 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id ED7DA106566C; Sun, 13 Nov 2011 10:28:01 +0000 (UTC) (envelope-from kib@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id DCE5A8FC12; Sun, 13 Nov 2011 10:28:01 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id pADAS1cn087282; Sun, 13 Nov 2011 10:28:01 GMT (envelope-from kib@svn.freebsd.org) Received: (from kib@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id pADAS1Ip087280; Sun, 13 Nov 2011 10:28:01 GMT (envelope-from kib@svn.freebsd.org) Message-Id: <201111131028.pADAS1Ip087280@svn.freebsd.org> From: Konstantin Belousov Date: Sun, 13 Nov 2011 10:28:01 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r227485 - head/sys/kern X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 13 Nov 2011 10:28:02 -0000 Author: kib Date: Sun Nov 13 10:28:01 2011 New Revision: 227485 URL: http://svn.freebsd.org/changeset/base/227485 Log: To limit amount of the kernel memory allocated, and to optimize the iteration over the fdsets, kern_select() limits the length of the fdsets copied in by the last valid file descriptor index. If any bit is set in a mask above the limit, current implementation ignores the filedescriptor, instead of returning EBADF. Fix the issue by scanning the tails of fdset before entering the select loop and returning EBADF if any bit above last valid filedescriptor index is set. The performance impact of the additional check is only imposed on the (somewhat) buggy applications that pass bad file descriptors to select(2) or pselect(2). PR: kern/155606, kern/162379 Discussed with: cognet, glebius Tested by: andreast (powerpc, all 64/32bit ABI combinations, big-endian), marius (sparc64, big-endian) MFC after: 2 weeks Modified: head/sys/kern/sys_generic.c Modified: head/sys/kern/sys_generic.c ============================================================================== --- head/sys/kern/sys_generic.c Sun Nov 13 06:39:49 2011 (r227484) +++ head/sys/kern/sys_generic.c Sun Nov 13 10:28:01 2011 (r227485) @@ -831,6 +831,54 @@ sys_select(struct thread *td, struct sel NFDBITS)); } +/* + * In the unlikely case when user specified n greater then the last + * open file descriptor, check that no bits are set after the last + * valid fd. We must return EBADF if any is set. + * + * There are applications that rely on the behaviour. + * + * nd is fd_lastfile + 1. + */ +static int +select_check_badfd(fd_set *fd_in, int nd, int ndu, int abi_nfdbits) +{ + char *addr, *oaddr; + int b, i, res; + uint8_t bits; + + if (nd >= ndu || fd_in == NULL) + return (0); + + oaddr = NULL; + bits = 0; /* silence gcc */ + for (i = nd; i < ndu; i++) { + b = i / NBBY; +#if BYTE_ORDER == LITTLE_ENDIAN + addr = (char *)fd_in + b; +#else + addr = (char *)fd_in; + if (abi_nfdbits == NFDBITS) { + addr += rounddown(b, sizeof(fd_mask)) + + sizeof(fd_mask) - 1 - b % sizeof(fd_mask); + } else { + addr += rounddown(b, sizeof(uint32_t)) + + sizeof(uint32_t) - 1 - b % sizeof(uint32_t); + } +#endif + if (addr != oaddr) { + res = fubyte(addr); + if (res == -1) + return (EFAULT); + oaddr = addr; + bits = res; + } + if ((bits & (1 << (i % NBBY))) != 0) + return (EBADF); + } + return (0); +} + int kern_select(struct thread *td, int nd, fd_set *fd_in, fd_set *fd_ou, fd_set *fd_ex, struct timeval *tvp, int abi_nfdbits) @@ -845,14 +893,26 @@ kern_select(struct thread *td, int nd, f fd_mask s_selbits[howmany(2048, NFDBITS)]; fd_mask *ibits[3], *obits[3], *selbits, *sbp; struct timeval atv, rtv, ttv; - int error, timo; + int error, lf, ndu, timo; u_int nbufbytes, ncpbytes, ncpubytes, nfdbits; if (nd < 0) return (EINVAL); fdp = td->td_proc->p_fd; - if (nd > fdp->fd_lastfile + 1) - nd = fdp->fd_lastfile + 1; + ndu = nd; + lf = fdp->fd_lastfile; + if (nd > lf + 1) + nd = lf + 1; + + error = select_check_badfd(fd_in, nd, ndu, abi_nfdbits); + if (error != 0) + return (error); + error = select_check_badfd(fd_ou, nd, ndu, abi_nfdbits); + if (error != 0) + return (error); + error = select_check_badfd(fd_ex, nd, ndu, abi_nfdbits); + if (error != 0) + return (error); /* * Allocate just enough bits for the non-null fd_sets. Use the