git: f5a365e51fee - main - inet6: protect address manipulation with a lock

From: Mateusz Guzik <mjg_at_FreeBSD.org>
Date: Thu, 30 Mar 2023 08:47:01 UTC
The branch main has been updated by mjg:

URL: https://cgit.FreeBSD.org/src/commit/?id=f5a365e51feea75d1e5ebc86c53808d8cae7b6d7

commit f5a365e51feea75d1e5ebc86c53808d8cae7b6d7
Author:     Mateusz Guzik <mjg@FreeBSD.org>
AuthorDate: 2023-03-29 12:46:41 +0000
Commit:     Mateusz Guzik <mjg@FreeBSD.org>
CommitDate: 2023-03-30 08:46:38 +0000

    inet6: protect address manipulation with a lock
    
    This is a total hack/bare minimum which follows inet4.
    
    Otherwise 2 threads removing the same address can easily crash.
    
    Reviewed by:    kp
    Sponsored by:   Rubicon Communications, LLC ("Netgate")
    Differential Revision:  https://reviews.freebsd.org/D39317
---
 sys/netinet6/in6.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/sys/netinet6/in6.c b/sys/netinet6/in6.c
index 27dc3550177c..3d967e9a40c7 100644
--- a/sys/netinet6/in6.c
+++ b/sys/netinet6/in6.c
@@ -168,6 +168,9 @@ static void in6_leave_proxy_ndp_mc(struct ifnet *, const struct in6_addr *);
 #define ifa2ia6(ifa)	((struct in6_ifaddr *)(ifa))
 #define ia62ifa(ia6)	(&((ia6)->ia_ifa))
 
+static struct sx in6_control_sx;
+SX_SYSINIT(in6_control_sx, &in6_control_sx, "in6_control");
+
 void
 in6_newaddrmsg(struct in6_ifaddr *ia, int cmd)
 {
@@ -254,6 +257,7 @@ in6_control(struct socket *so, u_long cmd, void *data,
 	struct	in6_aliasreq *ifra = (struct in6_aliasreq *)data;
 	struct sockaddr_in6 *sa6;
 	int error;
+	bool control_locked = false;
 
 	/*
 	 * Compat to make pre-10.x ifconfig(8) operable.
@@ -411,6 +415,8 @@ in6_control(struct socket *so, u_long cmd, void *data,
 		if (td != NULL && (error = prison_check_ip6(td->td_ucred,
 		    &sa6->sin6_addr)) != 0)
 			return (error);
+		sx_xlock(&in6_control_sx);
+		control_locked = true;
 		ia = in6ifa_ifpwithaddr(ifp, &sa6->sin6_addr);
 	} else
 		ia = NULL;
@@ -582,6 +588,9 @@ in6_control(struct socket *so, u_long cmd, void *data,
 
 	error = 0;
 out:
+	if (control_locked)
+		sx_xunlock(&in6_control_sx);
+
 	if (ia != NULL)
 		ifa_free(&ia->ia_ifa);
 	return (error);