Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 1 May 2026 09:11:17 +0100
From:      Oliver Pinter <oliver.pntr@gmail.com>
To:        Mark Johnston <markj@freebsd.org>
Cc:        "src-committers@freebsd.org" <src-committers@freebsd.org>,  "dev-commits-src-all@freebsd.org" <dev-commits-src-all@freebsd.org>,  "dev-commits-src-main@freebsd.org" <dev-commits-src-main@freebsd.org>, Mariusz Zaborski <oshogbo@freebsd.org>
Subject:   Re: git: f5ea3dce2cbe - main - libnv: switch fd_wait() from select(2) to poll(2)
Message-ID:  <CAPjTQNGzzY4sgCdKy0wrO2ipyYA1yefLhbbDE0RV2qFDCy06Hg@mail.gmail.com>
In-Reply-To: <69f219fc.3d583.73783562@gitrepo.freebsd.org>

index | next in thread | previous in thread | raw e-mail

[-- Attachment #1 --]
On Wednesday, April 29, 2026, Mark Johnston <markj@freebsd.org> wrote:

> The branch main has been updated by markj:
>
> URL: https://cgit.FreeBSD.org/src/commit/?id=
> f5ea3dce2cbe1ee2068c5e5c11bb066f5789685b
>
> commit f5ea3dce2cbe1ee2068c5e5c11bb066f5789685b
> Author:     Mariusz Zaborski <oshogbo@FreeBSD.org>
> AuthorDate: 2026-04-28 14:35:10 +0000
> Commit:     Mark Johnston <markj@FreeBSD.org>
> CommitDate: 2026-04-29 14:39:28 +0000
>
>     libnv: switch fd_wait() from select(2) to poll(2)
>
>     The previous implementation used FD_SET() on a stack-allocated fd_set,
>     which is an out-of-bounds write whenever the socket fd is >= FD_SETSIZE
>     (1024).


This problem seems like a more generic problem, after looking into
sys/select.h

What about adding a check for the FD_SET about the variable is on the stack
or not? And enforce the FD_SETSIZE limit almost the same way as it is
already done with fortify source.

Maybe the https://lists.llvm.org/pipermail/llvm-dev/2013-October/066294.html
can
be used (or abused) to implement this check.

Feel free to correct me, if I'm wrong.


>     Approved by:    so
>     Security:       FreeBSD-SA-26:16.libnv
>     Security:       CVE-2026-39457
>     Reported by:    Joshua Rogers of AISLE Research Team (
> https://aisle.com/)
>     Reviewed by:    markj
>     Differential Revision:  https://reviews.freebsd.org/D56689
> ---
>  lib/libnv/msgio.c                       | 12 +++----
>  lib/libnv/tests/nvlist_send_recv_test.c | 56
> +++++++++++++++++++++++++++++++++
>  2 files changed, 62 insertions(+), 6 deletions(-)
>
> diff --git a/lib/libnv/msgio.c b/lib/libnv/msgio.c
> index f6f75241ff06..de2994e47fef 100644
> --- a/lib/libnv/msgio.c
> +++ b/lib/libnv/msgio.c
> @@ -32,10 +32,10 @@
>
>  #include <sys/param.h>
>  #include <sys/socket.h>
> -#include <sys/select.h>
>
>  #include <errno.h>
>  #include <fcntl.h>
> +#include <poll.h>
>  #include <stdbool.h>
>  #include <stdint.h>
>  #include <stdlib.h>
> @@ -86,14 +86,14 @@ msghdr_add_fd(struct cmsghdr *cmsg, int fd)
>  static void
>  fd_wait(int fd, bool doread)
>  {
> -       fd_set fds;
> +       struct pollfd pfd;
>
>         PJDLOG_ASSERT(fd >= 0);
>
> -       FD_ZERO(&fds);
> -       FD_SET(fd, &fds);
> -       (void)select(fd + 1, doread ? &fds : NULL, doread ? NULL : &fds,
> -           NULL, NULL);
> +       pfd.fd = fd;
> +       pfd.events = doread ? POLLIN : POLLOUT;
> +       pfd.revents = 0;
> +       (void)poll(&pfd, 1, -1);
>  }
>
>  static int
> diff --git a/lib/libnv/tests/nvlist_send_recv_test.c
> b/lib/libnv/tests/nvlist_send_recv_test.c
> index 4a5c10df656d..d655a26a7362 100644
> --- a/lib/libnv/tests/nvlist_send_recv_test.c
> +++ b/lib/libnv/tests/nvlist_send_recv_test.c
> @@ -27,6 +27,8 @@
>   */
>
>  #include <sys/param.h>
> +#include <sys/resource.h>
> +#include <sys/select.h>
>  #include <sys/socket.h>
>  #include <sys/sysctl.h>
>  #include <sys/wait.h>
> @@ -531,6 +533,59 @@ ATF_TC_BODY(nvlist_send_recv__send_nvlist__stream,
> tc)
>         nvlist_send_recv__send_nvlist(SOCK_STREAM);
>  }
>
> +/*
> + * Regression test for fd_wait(): the previous select(2)-based
> implementation
> + * called FD_SET() unconditionally, which is an out-of-bounds stack write
> when
> + * the socket fd is >= FD_SETSIZE.  Force the socketpair fds above
> FD_SETSIZE
> + * and verify a full nvlist round-trip still works.
> + */
> +ATF_TC_WITHOUT_HEAD(nvlist_send_recv__highfd);
> +ATF_TC_BODY(nvlist_send_recv__highfd, tc)
> +{
> +       struct rlimit rl;
> +       nvlist_t *nvl;
> +       int socks[2], hi_send, hi_recv, status;
> +       pid_t pid;
> +
> +       hi_send = FD_SETSIZE + 5;
> +       hi_recv = FD_SETSIZE + 6;
> +
> +       rl.rlim_cur = rl.rlim_max = hi_recv + 1;
> +       if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
> +               atf_tc_skip("cannot raise RLIMIT_NOFILE: %s",
> strerror(errno));
> +
> +       ATF_REQUIRE(socketpair(PF_UNIX, SOCK_STREAM, 0, socks) == 0);
> +       ATF_REQUIRE(dup2(socks[0], hi_recv) == hi_recv);
> +       ATF_REQUIRE(dup2(socks[1], hi_send) == hi_send);
> +       (void)close(socks[0]);
> +       (void)close(socks[1]);
> +
> +       pid = fork();
> +       ATF_REQUIRE(pid >= 0);
> +       if (pid == 0) {
> +               /* Child: send. */
> +               (void)close(hi_recv);
> +               nvl = nvlist_create(0);
> +               nvlist_add_string(nvl, "key", "value");
> +               if (nvlist_send(hi_send, nvl) != 0)
> +                       err(EXIT_FAILURE, "nvlist_send");
> +               nvlist_destroy(nvl);
> +               _exit(0);
> +       }
> +
> +       (void)close(hi_send);
> +       nvl = nvlist_recv(hi_recv, 0);
> +       ATF_REQUIRE(nvl != NULL);
> +       ATF_REQUIRE(nvlist_error(nvl) == 0);
> +       ATF_REQUIRE(nvlist_exists_string(nvl, "key"));
> +       ATF_REQUIRE(strcmp(nvlist_get_string(nvl, "key"), "value") == 0);
> +       nvlist_destroy(nvl);
> +
> +       ATF_REQUIRE(waitpid(pid, &status, 0) == pid);
> +       ATF_REQUIRE(status == 0);
> +       (void)close(hi_recv);
> +}
> +
>  ATF_TC_WITHOUT_HEAD(nvlist_send_recv__send_closed_fd__dgram);
>  ATF_TC_BODY(nvlist_send_recv__send_closed_fd__dgram, tc)
>  {
> @@ -734,6 +789,7 @@ ATF_TP_ADD_TCS(tp)
>
>         ATF_TP_ADD_TC(tp, nvlist_send_recv__send_nvlist__dgram);
>         ATF_TP_ADD_TC(tp, nvlist_send_recv__send_nvlist__stream);
> +       ATF_TP_ADD_TC(tp, nvlist_send_recv__highfd);
>         ATF_TP_ADD_TC(tp, nvlist_send_recv__send_closed_fd__dgram);
>         ATF_TP_ADD_TC(tp, nvlist_send_recv__send_closed_fd__stream);
>         ATF_TP_ADD_TC(tp, nvlist_send_recv__send_many_fds__dgram);
>
>

[-- Attachment #2 --]
<br><br>On Wednesday, April 29, 2026, Mark Johnston &lt;<a href="mailto:markj@freebsd.org">markj@freebsd.org</a>&gt; wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">The branch main has been updated by markj:<br>
<br>
URL: <a href="https://cgit.FreeBSD.org/src/commit/?id=f5ea3dce2cbe1ee2068c5e5c11bb066f5789685b" target="_blank">https://cgit.FreeBSD.org/src/<wbr>commit/?id=<wbr>f5ea3dce2cbe1ee2068c5e5c11bb06<wbr>6f5789685b</a><br>;
<br>
commit f5ea3dce2cbe1ee2068c5e5c11bb06<wbr>6f5789685b<br>
Author:     Mariusz Zaborski &lt;oshogbo@FreeBSD.org&gt;<br>
AuthorDate: 2026-04-28 14:35:10 +0000<br>
Commit:     Mark Johnston &lt;markj@FreeBSD.org&gt;<br>
CommitDate: 2026-04-29 14:39:28 +0000<br>
<br>
    libnv: switch fd_wait() from select(2) to poll(2)<br>
<br>
    The previous implementation used FD_SET() on a stack-allocated fd_set,<br>
    which is an out-of-bounds write whenever the socket fd is &gt;= FD_SETSIZE<br>
    (1024).</blockquote><div><br></div><div>This problem seems like a more generic problem, after looking into sys/select.h</div><div><br></div><div>What about adding a check for the FD_SET about the variable is on the stack or not? And enforce the FD_SETSIZE limit almost the same way as it is already done with fortify source.</div><div><br></div><div>Maybe the <a href="https://lists.llvm.org/pipermail/llvm-dev/2013-October/066294.html">https://lists.llvm.org/pipermail/llvm-dev/2013-October/066294.html</a> can be used (or abused) to implement this check.</div><div> </div><div>Feel free to correct me, if I&#39;m wrong.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
    Approved by:    so<br>
    Security:       FreeBSD-SA-26:16.libnv<br>
    Security:       CVE-2026-39457<br>
    Reported by:    Joshua Rogers of AISLE Research Team (<a href="https://aisle.com/" target="_blank">https://aisle.com/</a>)<br>;
    Reviewed by:    markj<br>
    Differential Revision:  <a href="https://reviews.freebsd.org/D56689" target="_blank">https://reviews.freebsd.org/<wbr>D56689</a><br>;
---<br>
 lib/libnv/msgio.c                       | 12 +++----<br>
 lib/libnv/tests/nvlist_send_<wbr>recv_test.c | 56 ++++++++++++++++++++++++++++++<wbr>+++<br>
 2 files changed, 62 insertions(+), 6 deletions(-)<br>
<br>
diff --git a/lib/libnv/msgio.c b/lib/libnv/msgio.c<br>
index f6f75241ff06..de2994e47fef 100644<br>
--- a/lib/libnv/msgio.c<br>
+++ b/lib/libnv/msgio.c<br>
@@ -32,10 +32,10 @@<br>
<br>
 #include &lt;sys/param.h&gt;<br>
 #include &lt;sys/socket.h&gt;<br>
-#include &lt;sys/select.h&gt;<br>
<br>
 #include &lt;errno.h&gt;<br>
 #include &lt;fcntl.h&gt;<br>
+#include &lt;poll.h&gt;<br>
 #include &lt;stdbool.h&gt;<br>
 #include &lt;stdint.h&gt;<br>
 #include &lt;stdlib.h&gt;<br>
@@ -86,14 +86,14 @@ msghdr_add_fd(struct cmsghdr *cmsg, int fd)<br>
 static void<br>
 fd_wait(int fd, bool doread)<br>
 {<br>
-       fd_set fds;<br>
+       struct pollfd pfd;<br>
<br>
        PJDLOG_ASSERT(fd &gt;= 0);<br>
<br>
-       FD_ZERO(&amp;fds);<br>
-       FD_SET(fd, &amp;fds);<br>
-       (void)select(fd + 1, doread ? &amp;fds : NULL, doread ? NULL : &amp;fds,<br>
-           NULL, NULL);<br>
+       pfd.fd = fd;<br>
+       pfd.events = doread ? POLLIN : POLLOUT;<br>
+       pfd.revents = 0;<br>
+       (void)poll(&amp;pfd, 1, -1);<br>
 }<br>
<br>
 static int<br>
diff --git a/lib/libnv/tests/nvlist_send_<wbr>recv_test.c b/lib/libnv/tests/nvlist_send_<wbr>recv_test.c<br>
index 4a5c10df656d..d655a26a7362 100644<br>
--- a/lib/libnv/tests/nvlist_send_<wbr>recv_test.c<br>
+++ b/lib/libnv/tests/nvlist_send_<wbr>recv_test.c<br>
@@ -27,6 +27,8 @@<br>
  */<br>
<br>
 #include &lt;sys/param.h&gt;<br>
+#include &lt;sys/resource.h&gt;<br>
+#include &lt;sys/select.h&gt;<br>
 #include &lt;sys/socket.h&gt;<br>
 #include &lt;sys/sysctl.h&gt;<br>
 #include &lt;sys/wait.h&gt;<br>
@@ -531,6 +533,59 @@ ATF_TC_BODY(nvlist_send_recv__<wbr>send_nvlist__stream, tc)<br>
        nvlist_send_recv__send_nvlist(<wbr>SOCK_STREAM);<br>
 }<br>
<br>
+/*<br>
+ * Regression test for fd_wait(): the previous select(2)-based implementation<br>
+ * called FD_SET() unconditionally, which is an out-of-bounds stack write when<br>
+ * the socket fd is &gt;= FD_SETSIZE.  Force the socketpair fds above FD_SETSIZE<br>
+ * and verify a full nvlist round-trip still works.<br>
+ */<br>
+ATF_TC_WITHOUT_HEAD(nvlist_<wbr>send_recv__highfd);<br>
+ATF_TC_BODY(nvlist_send_recv_<wbr>_highfd, tc)<br>
+{<br>
+       struct rlimit rl;<br>
+       nvlist_t *nvl;<br>
+       int socks[2], hi_send, hi_recv, status;<br>
+       pid_t pid;<br>
+<br>
+       hi_send = FD_SETSIZE + 5;<br>
+       hi_recv = FD_SETSIZE + 6;<br>
+<br>
+       rl.rlim_cur = rl.rlim_max = hi_recv + 1;<br>
+       if (setrlimit(RLIMIT_NOFILE, &amp;rl) != 0)<br>
+               atf_tc_skip(&quot;cannot raise RLIMIT_NOFILE: %s&quot;, strerror(errno));<br>
+<br>
+       ATF_REQUIRE(socketpair(PF_<wbr>UNIX, SOCK_STREAM, 0, socks) == 0);<br>
+       ATF_REQUIRE(dup2(socks[0], hi_recv) == hi_recv);<br>
+       ATF_REQUIRE(dup2(socks[1], hi_send) == hi_send);<br>
+       (void)close(socks[0]);<br>
+       (void)close(socks[1]);<br>
+<br>
+       pid = fork();<br>
+       ATF_REQUIRE(pid &gt;= 0);<br>
+       if (pid == 0) {<br>
+               /* Child: send. */<br>
+               (void)close(hi_recv);<br>
+               nvl = nvlist_create(0);<br>
+               nvlist_add_string(nvl, &quot;key&quot;, &quot;value&quot;);<br>
+               if (nvlist_send(hi_send, nvl) != 0)<br>
+                       err(EXIT_FAILURE, &quot;nvlist_send&quot;);<br>
+               nvlist_destroy(nvl);<br>
+               _exit(0);<br>
+       }<br>
+<br>
+       (void)close(hi_send);<br>
+       nvl = nvlist_recv(hi_recv, 0);<br>
+       ATF_REQUIRE(nvl != NULL);<br>
+       ATF_REQUIRE(nvlist_error(nvl) == 0);<br>
+       ATF_REQUIRE(nvlist_exists_<wbr>string(nvl, &quot;key&quot;));<br>
+       ATF_REQUIRE(strcmp(nvlist_get_<wbr>string(nvl, &quot;key&quot;), &quot;value&quot;) == 0);<br>
+       nvlist_destroy(nvl);<br>
+<br>
+       ATF_REQUIRE(waitpid(pid, &amp;status, 0) == pid);<br>
+       ATF_REQUIRE(status == 0);<br>
+       (void)close(hi_recv);<br>
+}<br>
+<br>
 ATF_TC_WITHOUT_HEAD(nvlist_<wbr>send_recv__send_closed_fd__<wbr>dgram);<br>
 ATF_TC_BODY(nvlist_send_recv__<wbr>send_closed_fd__dgram, tc)<br>
 {<br>
@@ -734,6 +789,7 @@ ATF_TP_ADD_TCS(tp)<br>
<br>
        ATF_TP_ADD_TC(tp, nvlist_send_recv__send_nvlist_<wbr>_dgram);<br>
        ATF_TP_ADD_TC(tp, nvlist_send_recv__send_nvlist_<wbr>_stream);<br>
+       ATF_TP_ADD_TC(tp, nvlist_send_recv__highfd);<br>
        ATF_TP_ADD_TC(tp, nvlist_send_recv__send_closed_<wbr>fd__dgram);<br>
        ATF_TP_ADD_TC(tp, nvlist_send_recv__send_closed_<wbr>fd__stream);<br>
        ATF_TP_ADD_TC(tp, nvlist_send_recv__send_many_<wbr>fds__dgram);<br>
<br>
</blockquote>
home | help

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