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 | |
| 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>
| -rw-r--r-- | include/net/sctp/sctp.h | 1 | ||||
| -rw-r--r-- | include/net/sctp/structs.h | 6 | ||||
| -rw-r--r-- | net/sctp/bind_addr.c | 2 | ||||
| -rw-r--r-- | net/sctp/ipv6.c | 34 | ||||
| -rw-r--r-- | net/sctp/protocol.c | 54 | ||||
| -rw-r--r-- | net/sctp/socket.c | 38 |
6 files changed, 97 insertions, 38 deletions
diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index d529045c1679..c9cc00c85782 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h | |||
| @@ -123,6 +123,7 @@ | |||
| 123 | * sctp/protocol.c | 123 | * sctp/protocol.c |
| 124 | */ | 124 | */ |
| 125 | extern struct sock *sctp_get_ctl_sock(void); | 125 | extern struct sock *sctp_get_ctl_sock(void); |
| 126 | extern void sctp_local_addr_free(struct rcu_head *head); | ||
| 126 | extern int sctp_copy_local_addr_list(struct sctp_bind_addr *, | 127 | extern int sctp_copy_local_addr_list(struct sctp_bind_addr *, |
| 127 | sctp_scope_t, gfp_t gfp, | 128 | sctp_scope_t, gfp_t gfp, |
| 128 | int flags); | 129 | int flags); |
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index c0d5848c33dc..a89e36197afb 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
| @@ -207,6 +207,9 @@ extern struct sctp_globals { | |||
| 207 | * It is a list of sctp_sockaddr_entry. | 207 | * It is a list of sctp_sockaddr_entry. |
| 208 | */ | 208 | */ |
| 209 | struct list_head local_addr_list; | 209 | struct list_head local_addr_list; |
| 210 | |||
| 211 | /* Lock that protects the local_addr_list writers */ | ||
| 212 | spinlock_t addr_list_lock; | ||
| 210 | 213 | ||
| 211 | /* Flag to indicate if addip is enabled. */ | 214 | /* Flag to indicate if addip is enabled. */ |
| 212 | int addip_enable; | 215 | int addip_enable; |
| @@ -242,6 +245,7 @@ extern struct sctp_globals { | |||
| 242 | #define sctp_port_alloc_lock (sctp_globals.port_alloc_lock) | 245 | #define sctp_port_alloc_lock (sctp_globals.port_alloc_lock) |
| 243 | #define sctp_port_hashtable (sctp_globals.port_hashtable) | 246 | #define sctp_port_hashtable (sctp_globals.port_hashtable) |
| 244 | #define sctp_local_addr_list (sctp_globals.local_addr_list) | 247 | #define sctp_local_addr_list (sctp_globals.local_addr_list) |
| 248 | #define sctp_local_addr_lock (sctp_globals.addr_list_lock) | ||
| 245 | #define sctp_addip_enable (sctp_globals.addip_enable) | 249 | #define sctp_addip_enable (sctp_globals.addip_enable) |
| 246 | #define sctp_prsctp_enable (sctp_globals.prsctp_enable) | 250 | #define sctp_prsctp_enable (sctp_globals.prsctp_enable) |
| 247 | 251 | ||
| @@ -737,8 +741,10 @@ const union sctp_addr *sctp_source(const struct sctp_chunk *chunk); | |||
| 737 | /* This is a structure for holding either an IPv6 or an IPv4 address. */ | 741 | /* This is a structure for holding either an IPv6 or an IPv4 address. */ |
| 738 | struct sctp_sockaddr_entry { | 742 | struct sctp_sockaddr_entry { |
| 739 | struct list_head list; | 743 | struct list_head list; |
| 744 | struct rcu_head rcu; | ||
| 740 | union sctp_addr a; | 745 | union sctp_addr a; |
| 741 | __u8 use_as_src; | 746 | __u8 use_as_src; |
| 747 | __u8 valid; | ||
| 742 | }; | 748 | }; |
| 743 | 749 | ||
| 744 | typedef struct sctp_chunk *(sctp_packet_phandler_t)(struct sctp_association *); | 750 | typedef struct sctp_chunk *(sctp_packet_phandler_t)(struct sctp_association *); |
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 | */ | ||
| 81 | static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, | 86 | static 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 | ||
| 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 | ||
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, | |||
| 4156 | static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to, | 4163 | static 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 | } |
