Skip to content

Commit baf606d

Browse files
marceloleitnerdavem330
authored andcommitted
ipv4,ipv6: grab rtnl before locking the socket
There are some setsockopt operations in ipv4 and ipv6 that are grabbing rtnl after having grabbed the socket lock. Yet this makes it impossible to do operations that have to lock the socket when already within a rtnl protected scope, like ndo dev_open and dev_stop. We normally take coarse grained locks first but setsockopt inverted that. So this patch invert the lock logic for these operations and makes setsockopt grab rtnl if it will be needed prior to grabbing socket lock. Signed-off-by: Marcelo Ricardo Leitner <[email protected]> Acked-by: Hannes Frederic Sowa <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent fdf9ef8 commit baf606d

File tree

2 files changed

+52
-14
lines changed

2 files changed

+52
-14
lines changed

net/ipv4/ip_sockglue.c

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -536,12 +536,25 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
536536
* Socket option code for IP. This is the end of the line after any
537537
* TCP,UDP etc options on an IP socket.
538538
*/
539+
static bool setsockopt_needs_rtnl(int optname)
540+
{
541+
switch (optname) {
542+
case IP_ADD_MEMBERSHIP:
543+
case IP_ADD_SOURCE_MEMBERSHIP:
544+
case IP_DROP_MEMBERSHIP:
545+
case MCAST_JOIN_GROUP:
546+
case MCAST_LEAVE_GROUP:
547+
return true;
548+
}
549+
return false;
550+
}
539551

540552
static int do_ip_setsockopt(struct sock *sk, int level,
541553
int optname, char __user *optval, unsigned int optlen)
542554
{
543555
struct inet_sock *inet = inet_sk(sk);
544556
int val = 0, err;
557+
bool needs_rtnl = setsockopt_needs_rtnl(optname);
545558

546559
switch (optname) {
547560
case IP_PKTINFO:
@@ -584,6 +597,8 @@ static int do_ip_setsockopt(struct sock *sk, int level,
584597
return ip_mroute_setsockopt(sk, optname, optval, optlen);
585598

586599
err = 0;
600+
if (needs_rtnl)
601+
rtnl_lock();
587602
lock_sock(sk);
588603

589604
switch (optname) {
@@ -846,9 +861,9 @@ static int do_ip_setsockopt(struct sock *sk, int level,
846861
}
847862

848863
if (optname == IP_ADD_MEMBERSHIP)
849-
err = ip_mc_join_group(sk, &mreq);
864+
err = __ip_mc_join_group(sk, &mreq);
850865
else
851-
err = ip_mc_leave_group(sk, &mreq);
866+
err = __ip_mc_leave_group(sk, &mreq);
852867
break;
853868
}
854869
case IP_MSFILTER:
@@ -913,7 +928,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
913928
mreq.imr_multiaddr.s_addr = mreqs.imr_multiaddr;
914929
mreq.imr_address.s_addr = mreqs.imr_interface;
915930
mreq.imr_ifindex = 0;
916-
err = ip_mc_join_group(sk, &mreq);
931+
err = __ip_mc_join_group(sk, &mreq);
917932
if (err && err != -EADDRINUSE)
918933
break;
919934
omode = MCAST_INCLUDE;
@@ -945,9 +960,9 @@ static int do_ip_setsockopt(struct sock *sk, int level,
945960
mreq.imr_ifindex = greq.gr_interface;
946961

947962
if (optname == MCAST_JOIN_GROUP)
948-
err = ip_mc_join_group(sk, &mreq);
963+
err = __ip_mc_join_group(sk, &mreq);
949964
else
950-
err = ip_mc_leave_group(sk, &mreq);
965+
err = __ip_mc_leave_group(sk, &mreq);
951966
break;
952967
}
953968
case MCAST_JOIN_SOURCE_GROUP:
@@ -990,7 +1005,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
9901005
mreq.imr_multiaddr = psin->sin_addr;
9911006
mreq.imr_address.s_addr = 0;
9921007
mreq.imr_ifindex = greqs.gsr_interface;
993-
err = ip_mc_join_group(sk, &mreq);
1008+
err = __ip_mc_join_group(sk, &mreq);
9941009
if (err && err != -EADDRINUSE)
9951010
break;
9961011
greqs.gsr_interface = mreq.imr_ifindex;
@@ -1118,10 +1133,14 @@ static int do_ip_setsockopt(struct sock *sk, int level,
11181133
break;
11191134
}
11201135
release_sock(sk);
1136+
if (needs_rtnl)
1137+
rtnl_unlock();
11211138
return err;
11221139

11231140
e_inval:
11241141
release_sock(sk);
1142+
if (needs_rtnl)
1143+
rtnl_unlock();
11251144
return -EINVAL;
11261145
}
11271146

net/ipv6/ipv6_sockglue.c

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,26 @@ struct ipv6_txoptions *ipv6_update_options(struct sock *sk,
117117
return opt;
118118
}
119119

120+
static bool setsockopt_needs_rtnl(int optname)
121+
{
122+
switch (optname) {
123+
case IPV6_ADD_MEMBERSHIP:
124+
case IPV6_DROP_MEMBERSHIP:
125+
case MCAST_JOIN_GROUP:
126+
case MCAST_LEAVE_GROUP:
127+
return true;
128+
}
129+
return false;
130+
}
131+
120132
static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
121133
char __user *optval, unsigned int optlen)
122134
{
123135
struct ipv6_pinfo *np = inet6_sk(sk);
124136
struct net *net = sock_net(sk);
125137
int val, valbool;
126138
int retv = -ENOPROTOOPT;
139+
bool needs_rtnl = setsockopt_needs_rtnl(optname);
127140

128141
if (optval == NULL)
129142
val = 0;
@@ -140,6 +153,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
140153
if (ip6_mroute_opt(optname))
141154
return ip6_mroute_setsockopt(sk, optname, optval, optlen);
142155

156+
if (needs_rtnl)
157+
rtnl_lock();
143158
lock_sock(sk);
144159

145160
switch (optname) {
@@ -582,9 +597,9 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
582597
break;
583598

584599
if (optname == IPV6_ADD_MEMBERSHIP)
585-
retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
600+
retv = __ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
586601
else
587-
retv = ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
602+
retv = __ipv6_sock_mc_drop(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);
588603
break;
589604
}
590605
case IPV6_JOIN_ANYCAST:
@@ -623,11 +638,11 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
623638
}
624639
psin6 = (struct sockaddr_in6 *)&greq.gr_group;
625640
if (optname == MCAST_JOIN_GROUP)
626-
retv = ipv6_sock_mc_join(sk, greq.gr_interface,
627-
&psin6->sin6_addr);
641+
retv = __ipv6_sock_mc_join(sk, greq.gr_interface,
642+
&psin6->sin6_addr);
628643
else
629-
retv = ipv6_sock_mc_drop(sk, greq.gr_interface,
630-
&psin6->sin6_addr);
644+
retv = __ipv6_sock_mc_drop(sk, greq.gr_interface,
645+
&psin6->sin6_addr);
631646
break;
632647
}
633648
case MCAST_JOIN_SOURCE_GROUP:
@@ -659,8 +674,8 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
659674
struct sockaddr_in6 *psin6;
660675

661676
psin6 = (struct sockaddr_in6 *)&greqs.gsr_group;
662-
retv = ipv6_sock_mc_join(sk, greqs.gsr_interface,
663-
&psin6->sin6_addr);
677+
retv = __ipv6_sock_mc_join(sk, greqs.gsr_interface,
678+
&psin6->sin6_addr);
664679
/* prior join w/ different source is ok */
665680
if (retv && retv != -EADDRINUSE)
666681
break;
@@ -837,11 +852,15 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
837852
}
838853

839854
release_sock(sk);
855+
if (needs_rtnl)
856+
rtnl_unlock();
840857

841858
return retv;
842859

843860
e_inval:
844861
release_sock(sk);
862+
if (needs_rtnl)
863+
rtnl_unlock();
845864
return -EINVAL;
846865
}
847866

0 commit comments

Comments
 (0)