diff options
author | Vlad Yasevich <vladislav.yasevich@hp.com> | 2007-09-16 19:02:12 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-09-16 19:02:12 -0400 |
commit | 293035479942400a7fe8e4f72465d4e4e466b91a (patch) | |
tree | af9890403a554b4cf8389a9116080a0d1aa187fb /net/sctp/protocol.c | |
parent | ddeee3ce7fbf0e800f2a26a76d6018b42b337cc2 (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/sctp/protocol.c')
-rw-r--r-- | net/sctp/protocol.c | 54 |
1 files changed, 39 insertions, 15 deletions
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 | ||
198 | void 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'. */ |
196 | int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, | 206 | int 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 | ||
223 | end_copy: | 234 | end_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 | */ | ||
604 | static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, | 621 | static 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 | ||