aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorVlad Yasevich <vladislav.yasevich@hp.com>2007-09-16 19:02:12 -0400
committerDavid S. Miller <davem@davemloft.net>2007-09-16 19:02:12 -0400
commit293035479942400a7fe8e4f72465d4e4e466b91a (patch)
treeaf9890403a554b4cf8389a9116080a0d1aa187fb /net
parentddeee3ce7fbf0e800f2a26a76d6018b42b337cc2 (diff)
[SCTP]: Add RCU synchronization around sctp_localaddr_list
sctp_localaddr_list is modified dynamically via NETDEV_UP and NETDEV_DOWN events, but there is not synchronization between writer (even handler) and readers. As a result, the readers can access an entry that has been freed and crash the sytem. Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com> Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Acked-by: Sridhar Samdurala <sri@us.ibm.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/sctp/bind_addr.c2
-rw-r--r--net/sctp/ipv6.c34
-rw-r--r--net/sctp/protocol.c54
-rw-r--r--net/sctp/socket.c38
4 files changed, 90 insertions, 38 deletions
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index fdb287a9e2e2..7fc369f9035d 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -163,8 +163,10 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new,
163 addr->a.v4.sin_port = htons(bp->port); 163 addr->a.v4.sin_port = htons(bp->port);
164 164
165 addr->use_as_src = use_as_src; 165 addr->use_as_src = use_as_src;
166 addr->valid = 1;
166 167
167 INIT_LIST_HEAD(&addr->list); 168 INIT_LIST_HEAD(&addr->list);
169 INIT_RCU_HEAD(&addr->rcu);
168 list_add_tail(&addr->list, &bp->address_list); 170 list_add_tail(&addr->list, &bp->address_list);
169 SCTP_DBG_OBJCNT_INC(addr); 171 SCTP_DBG_OBJCNT_INC(addr);
170 172
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index f8aa23dda1c1..e12fa0a91da4 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -77,13 +77,18 @@
77 77
78#include <asm/uaccess.h> 78#include <asm/uaccess.h>
79 79
80/* Event handler for inet6 address addition/deletion events. */ 80/* Event handler for inet6 address addition/deletion events.
81 * The sctp_local_addr_list needs to be protocted by a spin lock since
82 * multiple notifiers (say IPv4 and IPv6) may be running at the same
83 * time and thus corrupt the list.
84 * The reader side is protected with RCU.
85 */
81static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, 86static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
82 void *ptr) 87 void *ptr)
83{ 88{
84 struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; 89 struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
85 struct sctp_sockaddr_entry *addr; 90 struct sctp_sockaddr_entry *addr = NULL;
86 struct list_head *pos, *temp; 91 struct sctp_sockaddr_entry *temp;
87 92
88 switch (ev) { 93 switch (ev) {
89 case NETDEV_UP: 94 case NETDEV_UP:
@@ -94,19 +99,26 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
94 memcpy(&addr->a.v6.sin6_addr, &ifa->addr, 99 memcpy(&addr->a.v6.sin6_addr, &ifa->addr,
95 sizeof(struct in6_addr)); 100 sizeof(struct in6_addr));
96 addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex; 101 addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex;
97 list_add_tail(&addr->list, &sctp_local_addr_list); 102 addr->valid = 1;
103 spin_lock_bh(&sctp_local_addr_lock);
104 list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
105 spin_unlock_bh(&sctp_local_addr_lock);
98 } 106 }
99 break; 107 break;
100 case NETDEV_DOWN: 108 case NETDEV_DOWN:
101 list_for_each_safe(pos, temp, &sctp_local_addr_list) { 109 spin_lock_bh(&sctp_local_addr_lock);
102 addr = list_entry(pos, struct sctp_sockaddr_entry, list); 110 list_for_each_entry_safe(addr, temp,
103 if (ipv6_addr_equal(&addr->a.v6.sin6_addr, &ifa->addr)) { 111 &sctp_local_addr_list, list) {
104 list_del(pos); 112 if (ipv6_addr_equal(&addr->a.v6.sin6_addr,
105 kfree(addr); 113 &ifa->addr)) {
114 addr->valid = 0;
115 list_del_rcu(&addr->list);
106 break; 116 break;
107 } 117 }
108 } 118 }
109 119 spin_unlock_bh(&sctp_local_addr_lock);
120 if (addr && !addr->valid)
121 call_rcu(&addr->rcu, sctp_local_addr_free);
110 break; 122 break;
111 } 123 }
112 124
@@ -367,7 +379,9 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist,
367 addr->a.v6.sin6_port = 0; 379 addr->a.v6.sin6_port = 0;
368 addr->a.v6.sin6_addr = ifp->addr; 380 addr->a.v6.sin6_addr = ifp->addr;
369 addr->a.v6.sin6_scope_id = dev->ifindex; 381 addr->a.v6.sin6_scope_id = dev->ifindex;
382 addr->valid = 1;
370 INIT_LIST_HEAD(&addr->list); 383 INIT_LIST_HEAD(&addr->list);
384 INIT_RCU_HEAD(&addr->rcu);
371 list_add_tail(&addr->list, addrlist); 385 list_add_tail(&addr->list, addrlist);
372 } 386 }
373 } 387 }
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index e98579b788b8..7ee120e85913 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -153,6 +153,9 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist,
153 addr->a.v4.sin_family = AF_INET; 153 addr->a.v4.sin_family = AF_INET;
154 addr->a.v4.sin_port = 0; 154 addr->a.v4.sin_port = 0;
155 addr->a.v4.sin_addr.s_addr = ifa->ifa_local; 155 addr->a.v4.sin_addr.s_addr = ifa->ifa_local;
156 addr->valid = 1;
157 INIT_LIST_HEAD(&addr->list);
158 INIT_RCU_HEAD(&addr->rcu);
156 list_add_tail(&addr->list, addrlist); 159 list_add_tail(&addr->list, addrlist);
157 } 160 }
158 } 161 }
@@ -192,16 +195,24 @@ static void sctp_free_local_addr_list(void)
192 } 195 }
193} 196}
194 197
198void sctp_local_addr_free(struct rcu_head *head)
199{
200 struct sctp_sockaddr_entry *e = container_of(head,
201 struct sctp_sockaddr_entry, rcu);
202 kfree(e);
203}
204
195/* Copy the local addresses which are valid for 'scope' into 'bp'. */ 205/* Copy the local addresses which are valid for 'scope' into 'bp'. */
196int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, 206int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope,
197 gfp_t gfp, int copy_flags) 207 gfp_t gfp, int copy_flags)
198{ 208{
199 struct sctp_sockaddr_entry *addr; 209 struct sctp_sockaddr_entry *addr;
200 int error = 0; 210 int error = 0;
201 struct list_head *pos, *temp;
202 211
203 list_for_each_safe(pos, temp, &sctp_local_addr_list) { 212 rcu_read_lock();
204 addr = list_entry(pos, struct sctp_sockaddr_entry, list); 213 list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
214 if (!addr->valid)
215 continue;
205 if (sctp_in_scope(&addr->a, scope)) { 216 if (sctp_in_scope(&addr->a, scope)) {
206 /* Now that the address is in scope, check to see if 217 /* Now that the address is in scope, check to see if
207 * the address type is really supported by the local 218 * the address type is really supported by the local
@@ -221,6 +232,7 @@ int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope,
221 } 232 }
222 233
223end_copy: 234end_copy:
235 rcu_read_unlock();
224 return error; 236 return error;
225} 237}
226 238
@@ -600,13 +612,18 @@ static void sctp_v4_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr)
600 seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr)); 612 seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr));
601} 613}
602 614
603/* Event handler for inet address addition/deletion events. */ 615/* Event handler for inet address addition/deletion events.
616 * The sctp_local_addr_list needs to be protocted by a spin lock since
617 * multiple notifiers (say IPv4 and IPv6) may be running at the same
618 * time and thus corrupt the list.
619 * The reader side is protected with RCU.
620 */
604static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, 621static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
605 void *ptr) 622 void *ptr)
606{ 623{
607 struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; 624 struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
608 struct sctp_sockaddr_entry *addr; 625 struct sctp_sockaddr_entry *addr = NULL;
609 struct list_head *pos, *temp; 626 struct sctp_sockaddr_entry *temp;
610 627
611 switch (ev) { 628 switch (ev) {
612 case NETDEV_UP: 629 case NETDEV_UP:
@@ -615,19 +632,25 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
615 addr->a.v4.sin_family = AF_INET; 632 addr->a.v4.sin_family = AF_INET;
616 addr->a.v4.sin_port = 0; 633 addr->a.v4.sin_port = 0;
617 addr->a.v4.sin_addr.s_addr = ifa->ifa_local; 634 addr->a.v4.sin_addr.s_addr = ifa->ifa_local;
618 list_add_tail(&addr->list, &sctp_local_addr_list); 635 addr->valid = 1;
636 spin_lock_bh(&sctp_local_addr_lock);
637 list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
638 spin_unlock_bh(&sctp_local_addr_lock);
619 } 639 }
620 break; 640 break;
621 case NETDEV_DOWN: 641 case NETDEV_DOWN:
622 list_for_each_safe(pos, temp, &sctp_local_addr_list) { 642 spin_lock_bh(&sctp_local_addr_lock);
623 addr = list_entry(pos, struct sctp_sockaddr_entry, list); 643 list_for_each_entry_safe(addr, temp,
644 &sctp_local_addr_list, list) {
624 if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) { 645 if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) {
625 list_del(pos); 646 addr->valid = 0;
626 kfree(addr); 647 list_del_rcu(&addr->list);
627 break; 648 break;
628 } 649 }
629 } 650 }
630 651 spin_unlock_bh(&sctp_local_addr_lock);
652 if (addr && !addr->valid)
653 call_rcu(&addr->rcu, sctp_local_addr_free);
631 break; 654 break;
632 } 655 }
633 656
@@ -1160,6 +1183,7 @@ SCTP_STATIC __init int sctp_init(void)
1160 1183
1161 /* Initialize the local address list. */ 1184 /* Initialize the local address list. */
1162 INIT_LIST_HEAD(&sctp_local_addr_list); 1185 INIT_LIST_HEAD(&sctp_local_addr_list);
1186 spin_lock_init(&sctp_local_addr_lock);
1163 sctp_get_local_addr_list(); 1187 sctp_get_local_addr_list();
1164 1188
1165 /* Register notifier for inet address additions/deletions. */ 1189 /* Register notifier for inet address additions/deletions. */
@@ -1227,6 +1251,9 @@ SCTP_STATIC __exit void sctp_exit(void)
1227 sctp_v6_del_protocol(); 1251 sctp_v6_del_protocol();
1228 inet_del_protocol(&sctp_protocol, IPPROTO_SCTP); 1252 inet_del_protocol(&sctp_protocol, IPPROTO_SCTP);
1229 1253
1254 /* Unregister notifier for inet address additions/deletions. */
1255 unregister_inetaddr_notifier(&sctp_inetaddr_notifier);
1256
1230 /* Free the local address list. */ 1257 /* Free the local address list. */
1231 sctp_free_local_addr_list(); 1258 sctp_free_local_addr_list();
1232 1259
@@ -1240,9 +1267,6 @@ SCTP_STATIC __exit void sctp_exit(void)
1240 inet_unregister_protosw(&sctp_stream_protosw); 1267 inet_unregister_protosw(&sctp_stream_protosw);
1241 inet_unregister_protosw(&sctp_seqpacket_protosw); 1268 inet_unregister_protosw(&sctp_seqpacket_protosw);
1242 1269
1243 /* Unregister notifier for inet address additions/deletions. */
1244 unregister_inetaddr_notifier(&sctp_inetaddr_notifier);
1245
1246 sctp_sysctl_unregister(); 1270 sctp_sysctl_unregister();
1247 list_del(&sctp_ipv4_specific.list); 1271 list_del(&sctp_ipv4_specific.list);
1248 1272
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 33354602ae86..a3acf78d06ba 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -4057,9 +4057,9 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
4057 int __user *optlen) 4057 int __user *optlen)
4058{ 4058{
4059 sctp_assoc_t id; 4059 sctp_assoc_t id;
4060 struct list_head *pos;
4060 struct sctp_bind_addr *bp; 4061 struct sctp_bind_addr *bp;
4061 struct sctp_association *asoc; 4062 struct sctp_association *asoc;
4062 struct list_head *pos, *temp;
4063 struct sctp_sockaddr_entry *addr; 4063 struct sctp_sockaddr_entry *addr;
4064 rwlock_t *addr_lock; 4064 rwlock_t *addr_lock;
4065 int cnt = 0; 4065 int cnt = 0;
@@ -4096,15 +4096,19 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
4096 addr = list_entry(bp->address_list.next, 4096 addr = list_entry(bp->address_list.next,
4097 struct sctp_sockaddr_entry, list); 4097 struct sctp_sockaddr_entry, list);
4098 if (sctp_is_any(&addr->a)) { 4098 if (sctp_is_any(&addr->a)) {
4099 list_for_each_safe(pos, temp, &sctp_local_addr_list) { 4099 rcu_read_lock();
4100 addr = list_entry(pos, 4100 list_for_each_entry_rcu(addr,
4101 struct sctp_sockaddr_entry, 4101 &sctp_local_addr_list, list) {
4102 list); 4102 if (!addr->valid)
4103 continue;
4104
4103 if ((PF_INET == sk->sk_family) && 4105 if ((PF_INET == sk->sk_family) &&
4104 (AF_INET6 == addr->a.sa.sa_family)) 4106 (AF_INET6 == addr->a.sa.sa_family))
4105 continue; 4107 continue;
4108
4106 cnt++; 4109 cnt++;
4107 } 4110 }
4111 rcu_read_unlock();
4108 } else { 4112 } else {
4109 cnt = 1; 4113 cnt = 1;
4110 } 4114 }
@@ -4127,14 +4131,16 @@ static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
4127 int max_addrs, void *to, 4131 int max_addrs, void *to,
4128 int *bytes_copied) 4132 int *bytes_copied)
4129{ 4133{
4130 struct list_head *pos, *next;
4131 struct sctp_sockaddr_entry *addr; 4134 struct sctp_sockaddr_entry *addr;
4132 union sctp_addr temp; 4135 union sctp_addr temp;
4133 int cnt = 0; 4136 int cnt = 0;
4134 int addrlen; 4137 int addrlen;
4135 4138
4136 list_for_each_safe(pos, next, &sctp_local_addr_list) { 4139 rcu_read_lock();
4137 addr = list_entry(pos, struct sctp_sockaddr_entry, list); 4140 list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
4141 if (!addr->valid)
4142 continue;
4143
4138 if ((PF_INET == sk->sk_family) && 4144 if ((PF_INET == sk->sk_family) &&
4139 (AF_INET6 == addr->a.sa.sa_family)) 4145 (AF_INET6 == addr->a.sa.sa_family))
4140 continue; 4146 continue;
@@ -4149,6 +4155,7 @@ static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
4149 cnt ++; 4155 cnt ++;
4150 if (cnt >= max_addrs) break; 4156 if (cnt >= max_addrs) break;
4151 } 4157 }
4158 rcu_read_unlock();
4152 4159
4153 return cnt; 4160 return cnt;
4154} 4161}
@@ -4156,14 +4163,16 @@ static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
4156static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to, 4163static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
4157 size_t space_left, int *bytes_copied) 4164 size_t space_left, int *bytes_copied)
4158{ 4165{
4159 struct list_head *pos, *next;
4160 struct sctp_sockaddr_entry *addr; 4166 struct sctp_sockaddr_entry *addr;
4161 union sctp_addr temp; 4167 union sctp_addr temp;
4162 int cnt = 0; 4168 int cnt = 0;
4163 int addrlen; 4169 int addrlen;
4164 4170
4165 list_for_each_safe(pos, next, &sctp_local_addr_list) { 4171 rcu_read_lock();
4166 addr = list_entry(pos, struct sctp_sockaddr_entry, list); 4172 list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
4173 if (!addr->valid)
4174 continue;
4175
4167 if ((PF_INET == sk->sk_family) && 4176 if ((PF_INET == sk->sk_family) &&
4168 (AF_INET6 == addr->a.sa.sa_family)) 4177 (AF_INET6 == addr->a.sa.sa_family))
4169 continue; 4178 continue;
@@ -4171,8 +4180,10 @@ static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
4171 sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), 4180 sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
4172 &temp); 4181 &temp);
4173 addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; 4182 addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
4174 if (space_left < addrlen) 4183 if (space_left < addrlen) {
4175 return -ENOMEM; 4184 cnt = -ENOMEM;
4185 break;
4186 }
4176 memcpy(to, &temp, addrlen); 4187 memcpy(to, &temp, addrlen);
4177 4188
4178 to += addrlen; 4189 to += addrlen;
@@ -4180,6 +4191,7 @@ static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
4180 space_left -= addrlen; 4191 space_left -= addrlen;
4181 *bytes_copied += addrlen; 4192 *bytes_copied += addrlen;
4182 } 4193 }
4194 rcu_read_unlock();
4183 4195
4184 return cnt; 4196 return cnt;
4185} 4197}