aboutsummaryrefslogtreecommitdiff
path: root/website/static/security/patches/SA-25:09/netinet-14.patch
diff options
context:
space:
mode:
Diffstat (limited to 'website/static/security/patches/SA-25:09/netinet-14.patch')
-rw-r--r--website/static/security/patches/SA-25:09/netinet-14.patch198
1 files changed, 198 insertions, 0 deletions
diff --git a/website/static/security/patches/SA-25:09/netinet-14.patch b/website/static/security/patches/SA-25:09/netinet-14.patch
new file mode 100644
index 0000000000..0c022135e8
--- /dev/null
+++ b/website/static/security/patches/SA-25:09/netinet-14.patch
@@ -0,0 +1,198 @@
+--- sys/netinet/in_pcb.c.orig
++++ sys/netinet/in_pcb.c
+@@ -2702,10 +2702,13 @@
+ INP_PCBPORTHASH(inp->inp_lport, pcbinfo->ipi_porthashmask)];
+
+ /*
+- * Add entry to load balance group.
+- * Only do this if SO_REUSEPORT_LB is set.
++ * Ignore SO_REUSEPORT_LB if the socket is connected. Really this case
++ * should be an error, but for UDP sockets it is not, and some
++ * applications erroneously set it on connected UDP sockets, so we can't
++ * change this without breaking compatibility.
+ */
+- if ((inp->inp_socket->so_options & SO_REUSEPORT_LB) != 0) {
++ if (!connected &&
++ (inp->inp_socket->so_options & SO_REUSEPORT_LB) != 0) {
+ int error = in_pcbinslbgrouphash(inp, M_NODOM);
+ if (error != 0)
+ return (error);
+@@ -2836,6 +2839,10 @@
+ connected = !in_nullhost(inp->inp_faddr);
+ }
+
++ /* See the comment in in_pcbinshash(). */
++ if (connected && (inp->inp_flags & INP_INLBGROUP) != 0)
++ in_pcbremlbgrouphash(inp);
++
+ /*
+ * When rehashing, the caller must ensure that either the new or the old
+ * foreign address was unspecified.
+--- tests/sys/netinet/so_reuseport_lb_test.c.orig
++++ tests/sys/netinet/so_reuseport_lb_test.c
+@@ -29,6 +29,8 @@
+
+ #include <sys/cdefs.h>
+ #include <sys/param.h>
++#include <sys/filio.h>
++#include <sys/ioccom.h>
+ #include <sys/socket.h>
+
+ #include <netinet/in.h>
+@@ -236,10 +238,156 @@
+ }
+ }
+
++/*
++ * The kernel erroneously permits calling connect() on a UDP socket with
++ * SO_REUSEPORT_LB set. Verify that packets sent to the bound address are
++ * dropped unless they come from the connected address.
++ */
++ATF_TC_WITHOUT_HEAD(connect_udp);
++ATF_TC_BODY(connect_udp, tc)
++{
++ struct sockaddr_in sin = {
++ .sin_family = AF_INET,
++ .sin_len = sizeof(sin),
++ .sin_addr = { htonl(INADDR_LOOPBACK) },
++ };
++ ssize_t n;
++ int error, len, s1, s2, s3;
++ char ch;
++
++ s1 = socket(PF_INET, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s1 >= 0);
++ s2 = socket(PF_INET, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s2 >= 0);
++ s3 = socket(PF_INET, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s3 >= 0);
++
++ error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
++ sizeof(int));
++ ATF_REQUIRE_MSG(error == 0,
++ "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
++ error = bind(s1, (struct sockaddr *)&sin, sizeof(sin));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ error = bind(s2, (struct sockaddr *)&sin, sizeof(sin));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ error = bind(s3, (struct sockaddr *)&sin, sizeof(sin));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ /* Connect to an address not owned by s2. */
++ error = getsockname(s3, (struct sockaddr *)&sin,
++ (socklen_t[]){sizeof(sin)});
++ ATF_REQUIRE(error == 0);
++ error = connect(s1, (struct sockaddr *)&sin, sizeof(sin));
++ ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));
++
++ /* Try to send a packet to s1 from s2. */
++ error = getsockname(s1, (struct sockaddr *)&sin,
++ (socklen_t[]){sizeof(sin)});
++ ATF_REQUIRE(error == 0);
++
++ ch = 42;
++ n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,
++ sizeof(sin));
++ ATF_REQUIRE(n == 1);
++
++ /* Give the packet some time to arrive. */
++ usleep(100000);
++
++ /* s1 is connected to s3 and shouldn't receive from s2. */
++ error = ioctl(s1, FIONREAD, &len);
++ ATF_REQUIRE(error == 0);
++ ATF_REQUIRE_MSG(len == 0, "unexpected data available");
++
++ /* ... but s3 can of course send to s1. */
++ n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin,
++ sizeof(sin));
++ ATF_REQUIRE(n == 1);
++ usleep(100000);
++ error = ioctl(s1, FIONREAD, &len);
++ ATF_REQUIRE(error == 0);
++ ATF_REQUIRE_MSG(len == 1, "unexpected data available");
++}
++
++/*
++ * The kernel erroneously permits calling connect() on a UDP socket with
++ * SO_REUSEPORT_LB set. Verify that packets sent to the bound address are
++ * dropped unless they come from the connected address.
++ */
++ATF_TC_WITHOUT_HEAD(connect_udp6);
++ATF_TC_BODY(connect_udp6, tc)
++{
++ struct sockaddr_in6 sin6 = {
++ .sin6_family = AF_INET6,
++ .sin6_len = sizeof(sin6),
++ .sin6_addr = IN6ADDR_LOOPBACK_INIT,
++ };
++ ssize_t n;
++ int error, len, s1, s2, s3;
++ char ch;
++
++ s1 = socket(PF_INET6, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s1 >= 0);
++ s2 = socket(PF_INET6, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s2 >= 0);
++ s3 = socket(PF_INET6, SOCK_DGRAM, 0);
++ ATF_REQUIRE(s3 >= 0);
++
++ error = setsockopt(s1, SOL_SOCKET, SO_REUSEPORT_LB, (int[]){1},
++ sizeof(int));
++ ATF_REQUIRE_MSG(error == 0,
++ "setsockopt(SO_REUSEPORT_LB) failed: %s", strerror(errno));
++ error = bind(s1, (struct sockaddr *)&sin6, sizeof(sin6));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ error = bind(s2, (struct sockaddr *)&sin6, sizeof(sin6));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ error = bind(s3, (struct sockaddr *)&sin6, sizeof(sin6));
++ ATF_REQUIRE_MSG(error == 0, "bind() failed: %s", strerror(errno));
++
++ /* Connect to an address not owned by s2. */
++ error = getsockname(s3, (struct sockaddr *)&sin6,
++ (socklen_t[]){sizeof(sin6)});
++ ATF_REQUIRE(error == 0);
++ error = connect(s1, (struct sockaddr *)&sin6, sizeof(sin6));
++ ATF_REQUIRE_MSG(error == 0, "connect() failed: %s", strerror(errno));
++
++ /* Try to send a packet to s1 from s2. */
++ error = getsockname(s1, (struct sockaddr *)&sin6,
++ (socklen_t[]){sizeof(sin6)});
++ ATF_REQUIRE(error == 0);
++
++ ch = 42;
++ n = sendto(s2, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,
++ sizeof(sin6));
++ ATF_REQUIRE(n == 1);
++
++ /* Give the packet some time to arrive. */
++ usleep(100000);
++
++ /* s1 is connected to s3 and shouldn't receive from s2. */
++ error = ioctl(s1, FIONREAD, &len);
++ ATF_REQUIRE(error == 0);
++ ATF_REQUIRE_MSG(len == 0, "unexpected data available");
++
++ /* ... but s3 can of course send to s1. */
++ n = sendto(s3, &ch, sizeof(ch), 0, (struct sockaddr *)&sin6,
++ sizeof(sin6));
++ ATF_REQUIRE(n == 1);
++ usleep(100000);
++ error = ioctl(s1, FIONREAD, &len);
++ ATF_REQUIRE(error == 0);
++ ATF_REQUIRE_MSG(len == 1, "unexpected data available");
++}
++
+ ATF_TP_ADD_TCS(tp)
+ {
+ ATF_TP_ADD_TC(tp, basic_ipv4);
+ ATF_TP_ADD_TC(tp, basic_ipv6);
++ ATF_TP_ADD_TC(tp, connect_udp);
++ ATF_TP_ADD_TC(tp, connect_udp6);
+
+ return (atf_no_error());
+ }