NetBSD Problem Report #57504

From www@netbsd.org  Thu Jul  6 02:03:42 2023
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits))
	(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id E1A001A923D
	for <gnats-bugs@gnats.NetBSD.org>; Thu,  6 Jul 2023 02:03:41 +0000 (UTC)
Message-Id: <20230706020340.BF73D1A923E@mollari.NetBSD.org>
Date: Thu,  6 Jul 2023 02:03:40 +0000 (UTC)
From: campbell+netbsd@mumble.net
Reply-To: campbell+netbsd@mumble.net
To: gnats-bugs@NetBSD.org
Subject: select with large enough bogus fd number set hangs instead of failing with EBADF
X-Send-Pr-Version: www-1.0

>Number:         57504
>Category:       kern
>Synopsis:       select with large enough bogus fd number set hangs instead of failing with EBADF
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kre
>State:          closed
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Thu Jul 06 02:05:00 +0000 2023
>Closed-Date:    Wed Nov 20 14:24:57 +0000 2024
>Last-Modified:  Wed Nov 20 14:24:57 +0000 2024
>Originator:     Taylor R Campbell
>Release:        current
>Organization:
The SelectBSD Foundation
>Environment:
>Description:
$ cat selwhat.c
#include <sys/poll.h>
#include <sys/select.h>

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(int argc, char **argv)
{
	fd_set readfds;
	int fd, ret;

	setprogname(argv[0]);
	if (argc != 2)
		errx(1, "Usage: %s <fd>", getprogname());
	fd = atoi(argv[1]);

	if (fcntl(fd, F_GETFL) != -1 || errno != EBADF)
		errx(1, "fd %d is already open", fd);

	FD_ZERO(&readfds);
	FD_SET(fd, &readfds);
	errno = 0;
	ret = select(fd + 1, &readfds, NULL, NULL, NULL);
	printf("ret=%d\nerrno=%d %s\n", ret, errno, strerror(errno));
	fflush(stdout);
	return ferror(stdout);
}
$ make selwhat
cc -O2   -o selwhat selwhat.c
$ ./selwhat 19
ret=-1
errno=9 Bad file descriptor
$ ./selwhat 20

and it just hangs.

The issue is this logic in revision 1.60 of sys_select.c:

   363          nf = atomic_load_consume(&curlwp->l_fd->fd_dt)->dt_nfiles;
   364          if (nd > nf) {
   365                  /* forgiving; slightly wrong */
   366                  nd = nf;
   367          }

The initial value of dt_nfiles (NDFILE from sys/filedesc.h) is 20, so this code treats select(124, ...) _as if_ it had been select(20, ...).  Finding no fds in {0, 1, 2, ..., 19} set in the fd sets, select chooses to sleep until timeout -- which is to say, forever, in this case.
>How-To-Repeat:

>Fix:
Yes, please!  At the very least, this should be documented if it's intentional.  But it's not clear why this 'forgiving' logic is there at all; is the purpose to avoid large scans when someone calls select(INT_MAX, ...)?

>Release-Note:

>Audit-Trail:

Responsible-Changed-From-To: kern-bug-people->kre
Responsible-Changed-By: kre@NetBSD.org
Responsible-Changed-When: Mon, 16 Sep 2024 18:29:35 +0000
Responsible-Changed-Why:
I have code to fix this issue (validating all fds passed)
currently being tested.


State-Changed-From-To: open->analyzed
State-Changed-By: kre@NetBSD.org
State-Changed-When: Mon, 23 Sep 2024 21:34:56 +0000
State-Changed-Why:
I have code running now which seems to fix this issue.

For now I am just using the test program (selwhat) provided in the PR, but
before committing I need to make some better tests (ones which actually do
a select() intended to work!).   I made one mod to the source, I added
	#define FD_SETSIZE 40000
before any of the #includes (so I can test operation with much larger
arg values).

On my system I have:

jacaranda$ ulimit -H -n
6668

And with that:

/tmp/selwhat 19
ret=-1
errno=9 Bad file descriptor
/tmp/selwhat 20
ret=-1
errno=9 Bad file descriptor
/tmp/selwhat 500
ret=-1
errno=9 Bad file descriptor
/tmp/selwhat 6668
ret=-1
errno=9 Bad file descriptor
/tmp/selwhat 6923
ret=-1
errno=9 Bad file descriptor
/tmp/selwhat 6924
ret=-1
errno=22 Invalid argument
/tmp/selwhat 20000
ret=-1
errno=22 Invalid argument


The code returns EINVAL if the fd arg for select() is >= $(ulimit -H -n) + FD_SETSIZE
(the kernel version of FD_SETSIZE, hence 256).  6668+256 == 2924.   Allowing the
extra is intended to allow applications to calculate the maximum FD_SETSIZE that
will work based upon the hard limit of file descriptors, and round up, and also to
allow some slop should that limit have been reduced after a very high numbered fd
was opened.

That the limit exists is just to prevent applications causing the kernel to do
excess work by supplying INT_MAX as the fd number.   If EINVAL is returned, no
checking of the fd_sets supplied is performed.   Otherwise, EBADF is returned
only if there is a set bit that doesn't represent an open fd (so passing fd == 6900
with only the bit representing fd==0 set, assuming stdin is open, won't generate
an error).

Are any comments on how this should operate, or changes that could be
made (I could use the kernel's "maxfiles" as the limit below which fds are
checked for example, rather than basing it on the process's resource limit)?


From: "Robert Elz" <kre@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/57504 CVS commit: src/sys/kern
Date: Fri, 18 Oct 2024 13:12:34 +0000

 Module Name:	src
 Committed By:	kre
 Date:		Fri Oct 18 13:12:34 UTC 2024

 Modified Files:
 	src/sys/kern: sys_select.c

 Log Message:
 PR kern/57504 : Check all fds passed in to select

 If an application passes in a huge fd_set (select(BIG, ...))
 then check every bit in the fd_sets provided, to make sure
 they are valid.

 If BIG is too big (cannot possibly represent an open fd for
 this process, under any circumstances: ie: not just because
 that many are not currently open) return EINVAL.

 Otherwise, check every set bit to make sure it is valid.  Any
 fd bits set above the applications current highest open fd
 automatically generate EBADF and quick(ish) exit.

 fd's that are within the plausible range are then checked as
 they always were (it is possible for there to be a few there
 above the max open fd - as everything in select is done in
 multiples of __FDBITS (fd_mask) but the max open fd is not so
 constrained.  Those always were checked, continue using the
 same mechanism.

 This should have zero impact on any sane application which
 uses the highest fd for which it set a bit, +1, as the first
 arg to select.   However, if there are any broken applications
 that were relying upon the previous behaviour of simply ignoring
 any fd_masks that started beyond the max number of open files,
 then they might (if they happen to have any bits set) now fail.

 XXX pullup -10 -- but not for a long time.  Someone remind me
     sometime next year.  Leave a long settling time in HEAD just to
     be sure no issues arise, as in practice, almost nothing should
     cause any of the new code to be executed.

     pullup -9 -- probably not, what this fixes isn't significant
     enough to bother going that far back for (IMO).


 To generate a diff of this commit:
 cvs rdiff -u -r1.66 -r1.67 src/sys/kern/sys_select.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: analyzed->needs-pullups
State-Changed-By: kre@NetBSD.org
State-Changed-When: Fri, 18 Oct 2024 13:18:19 +0000
State-Changed-Why:
Needs pullup to -10 (sometime in 2025).


From: Taylor R Campbell <riastradh@NetBSD.org>
To: gnats-bugs@netbsd.org
Cc: kre@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org
Subject: Re: PR/57504 CVS commit: src/sys/kern
Date: Fri, 18 Oct 2024 13:42:07 +0000

 > Date: Fri, 18 Oct 2024 13:12:34 +0000
 > From: "Robert Elz" <kre@netbsd.org>
 > 
 > This should have zero impact on any sane application which
 > uses the highest fd for which it set a bit, +1, as the first
 > arg to select.   However, if there are any broken applications
 > that were relying upon the previous behaviour of simply ignoring
 > any fd_masks that started beyond the max number of open files,
 > then they might (if they happen to have any bits set) now fail.

 I think this came up in a real application, but it's been long enough
 that I forget which one, and unfortunately I neglected to record it in
 the original PR (if it was a real application and not just code
 inspection on my part).

 Some real applications might not keep track of the highest fd they set
 in the fd_set, and just keep track of the highest fd they have ever
 allocated seen, or just pass FD_SETSIZE.

 > XXX pullup -10 -- but not for a long time.  Someone remind me
 >     sometime next year.  Leave a long settling time in HEAD just to
 >     be sure no issues arise, as in practice, almost nothing should
 >     cause any of the new code to be executed.
 > 
 >     pullup -9 -- probably not, what this fixes isn't significant
 >     enough to bother going that far back for (IMO).

 Surely we should have an automatic test in atf for this so can verify
 it has been fixed in pullup too?

 I think a fix for this bug is also worth pulling up to 9 -- basic
 central functionality like select should be reliable, not spuriously
 hang in weird circumstances.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/57504 CVS commit: src/tests/lib/libc/sys
Date: Fri, 18 Oct 2024 16:39:41 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Fri Oct 18 16:39:41 UTC 2024

 Modified Files:
 	src/tests/lib/libc/sys: t_select.c

 Log Message:
 tests/lib/libc/sys/t_select: Test select on bad file descriptors.

 This should immediately fail, not hang, even if the bad fd is
 high-numbered.

 PR kern/57504: select with large enough bogus fd number set hangs
 instead of failing with EBADF


 To generate a diff of this commit:
 cvs rdiff -u -r1.4 -r1.5 src/tests/lib/libc/sys/t_select.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: Robert Elz <kre@munnari.OZ.AU>
To: Taylor R Campbell <riastradh@NetBSD.org>
Cc: gnats-bugs@netbsd.org, netbsd-bugs@netbsd.org
Subject: Re: PR/57504 CVS commit: src/sys/kern
Date: Sat, 19 Oct 2024 00:06:59 +0700

     Date:        Fri, 18 Oct 2024 13:42:07 +0000
     From:        Taylor R Campbell <riastradh@NetBSD.org>
     Message-ID:  <20241018134212.9624084D09@mail.netbsd.org>

   | Some real applications might not keep track of the highest fd they set
   | in the fd_set, and just keep track of the highest fd they have ever
   | allocated seen, or just pass FD_SETSIZE.

 They might, but most I have seen (which still use select, and not
 poll) seem to use max_fd+1 -- probably just copying code from place
 to place as is normal.

   | Surely we should have an automatic test in atf for this so can verify
   | it has been fixed in pullup too?

 Yes, I was looking at doing one of those - waiting for that to happen
 was partly what held up the commit for so long, I've had the code for
 quite a while, & been running it for ages now.   I eventually decided
 that I simply wasn't getting that written any time soon, and decided to
 commit anyway.

   | I think a fix for this bug is also worth pulling up to 9 -- basic
   | central functionality like select should be reliable, not spuriously
   | hang in weird circumstances.

 Perhaps - it isn't as if we have lots of bug reports about things like
 that happening though do we?   It is also possible that -9 might be, or
 approaching, EOL before I get around do doing any pullups - I think this
 needs a long gestation in HEAD, just because this code is likely to be
 exercised very rarely - if there is something not quite right it might
 take a long time to reveal itself.

 kre


From: Robert Elz <kre@munnari.OZ.AU>
To: Taylor R Campbell <riastradh@NetBSD.org>
Cc: gnats-bugs@netbsd.org, netbsd-bugs@netbsd.org
Subject: Re: PR/57504 CVS commit: src/sys/kern
Date: Sat, 19 Oct 2024 00:08:20 +0700

 And now I see you have committed an ATF test already.   Great, thanks.

 kre

State-Changed-From-To: needs-pullups->pending-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Sun, 17 Nov 2024 17:51:46 +0000
State-Changed-Why:
pullup-9 #1926 https://releng.netbsd.org/cgi-bin/req-9.cgi?show=1926
pullup-10 #1011 https://releng.netbsd.org/cgi-bin/req-10.cgi?show=1011


From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/57504 CVS commit: [netbsd-10] src
Date: Mon, 18 Nov 2024 18:16:52 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Mon Nov 18 18:16:52 UTC 2024

 Modified Files:
 	src/sys/kern [netbsd-10]: sys_select.c
 	src/tests/lib/libc/sys [netbsd-10]: t_select.c

 Log Message:
 Pull up following revision(s) (requested by riastradh in ticket #1011):

 	sys/kern/sys_select.c: revision 1.67
 	tests/lib/libc/sys/t_select.c: revision 1.5

 PR kern/57504 : Check all fds passed in to select

 If an application passes in a huge fd_set (select(BIG, ...))
 then check every bit in the fd_sets provided, to make sure
 they are valid.

 If BIG is too big (cannot possibly represent an open fd for
 this process, under any circumstances: ie: not just because
 that many are not currently open) return EINVAL.
 Otherwise, check every set bit to make sure it is valid.  Any
 fd bits set above the applications current highest open fd
 automatically generate EBADF and quick(ish) exit.
 fd's that are within the plausible range are then checked as
 they always were (it is possible for there to be a few there
 above the max open fd - as everything in select is done in
 multiples of __FDBITS (fd_mask) but the max open fd is not so
 constrained.  Those always were checked, continue using the
 same mechanism.

 This should have zero impact on any sane application which
 uses the highest fd for which it set a bit, +1, as the first
 arg to select.   However, if there are any broken applications
 that were relying upon the previous behaviour of simply ignoring
 any fd_masks that started beyond the max number of open files,
 then they might (if they happen to have any bits set) now fail.

 tests/lib/libc/sys/t_select: Test select on bad file descriptors.
 This should immediately fail, not hang, even if the bad fd is
 high-numbered.

 PR kern/57504: select with large enough bogus fd number set hangs
 instead of failing with EBADF


 To generate a diff of this commit:
 cvs rdiff -u -r1.60 -r1.60.4.1 src/sys/kern/sys_select.c
 cvs rdiff -u -r1.4 -r1.4.28.1 src/tests/lib/libc/sys/t_select.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/57504 CVS commit: [netbsd-9] src
Date: Wed, 20 Nov 2024 14:09:10 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Wed Nov 20 14:09:10 UTC 2024

 Modified Files:
 	src/sys/kern [netbsd-9]: sys_select.c
 	src/tests/lib/libc/sys [netbsd-9]: t_select.c

 Log Message:
 Pull up following revision(s) (requested by riastradh in ticket #1926):

 	sys/kern/sys_select.c: revision 1.67 (patch)
 	tests/lib/libc/sys/t_select.c: revision 1.5 (patch)

 PR kern/57504 : Check all fds passed in to select

 If an application passes in a huge fd_set (select(BIG, ...))
 then check every bit in the fd_sets provided, to make sure
 they are valid.

 If BIG is too big (cannot possibly represent an open fd for
 this process, under any circumstances: ie: not just because
 that many are not currently open) return EINVAL.

 Otherwise, check every set bit to make sure it is valid.  Any
 fd bits set above the applications current highest open fd
 automatically generate EBADF and quick(ish) exit.
 fd's that are within the plausible range are then checked as
 they always were (it is possible for there to be a few there
 above the max open fd - as everything in select is done in
 multiples of __FDBITS (fd_mask) but the max open fd is not so
 constrained.  Those always were checked, continue using the
 same mechanism.

 This should have zero impact on any sane application which
 uses the highest fd for which it set a bit, +1, as the first
 arg to select.   However, if there are any broken applications
 that were relying upon the previous behaviour of simply ignoring
 any fd_masks that started beyond the max number of open files,
 then they might (if they happen to have any bits set) now fail.

 tests/lib/libc/sys/t_select: Test select on bad file descriptors.

 This should immediately fail, not hang, even if the bad fd is
 high-numbered.

 PR kern/57504: select with large enough bogus fd number set hangs
 instead of failing with EBADF


 To generate a diff of this commit:
 cvs rdiff -u -r1.46.2.1 -r1.46.2.2 src/sys/kern/sys_select.c
 cvs rdiff -u -r1.4 -r1.4.16.1 src/tests/lib/libc/sys/t_select.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: pending-pullups->closed
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Wed, 20 Nov 2024 14:24:57 +0000
State-Changed-Why:
fixed in HEAD, pulled up to 9 and 10


>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.