Commit 22626216c46f2ec86287e75ea86dd9ac3df54265
Committed by
David S. Miller
1 parent
b2211a361a
Exists in
master
and in
7 other branches
[SCTP]: Fix local_addr deletions during list traversals.
Since the lists are circular, we need to explicitely tag the address to be deleted since we might end up freeing the list head instead. This fixes some interesting SCTP crashes. Signed-off-by: Chidambar 'ilLogict' Zinnoury <illogict@online.fr> Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 3 changed files with 9 additions and 3 deletions Side-by-side Diff
net/sctp/bind_addr.c
... | ... | @@ -209,6 +209,7 @@ |
209 | 209 | int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr) |
210 | 210 | { |
211 | 211 | struct sctp_sockaddr_entry *addr, *temp; |
212 | + int found = 0; | |
212 | 213 | |
213 | 214 | /* We hold the socket lock when calling this function, |
214 | 215 | * and that acts as a writer synchronizing lock. |
215 | 216 | |
... | ... | @@ -216,13 +217,14 @@ |
216 | 217 | list_for_each_entry_safe(addr, temp, &bp->address_list, list) { |
217 | 218 | if (sctp_cmp_addr_exact(&addr->a, del_addr)) { |
218 | 219 | /* Found the exact match. */ |
220 | + found = 1; | |
219 | 221 | addr->valid = 0; |
220 | 222 | list_del_rcu(&addr->list); |
221 | 223 | break; |
222 | 224 | } |
223 | 225 | } |
224 | 226 | |
225 | - if (addr && !addr->valid) { | |
227 | + if (found) { | |
226 | 228 | call_rcu(&addr->rcu, sctp_local_addr_free); |
227 | 229 | SCTP_DBG_OBJCNT_DEC(addr); |
228 | 230 | return 0; |
net/sctp/ipv6.c
... | ... | @@ -89,6 +89,7 @@ |
89 | 89 | struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; |
90 | 90 | struct sctp_sockaddr_entry *addr = NULL; |
91 | 91 | struct sctp_sockaddr_entry *temp; |
92 | + int found = 0; | |
92 | 93 | |
93 | 94 | switch (ev) { |
94 | 95 | case NETDEV_UP: |
95 | 96 | |
... | ... | @@ -111,13 +112,14 @@ |
111 | 112 | &sctp_local_addr_list, list) { |
112 | 113 | if (ipv6_addr_equal(&addr->a.v6.sin6_addr, |
113 | 114 | &ifa->addr)) { |
115 | + found = 1; | |
114 | 116 | addr->valid = 0; |
115 | 117 | list_del_rcu(&addr->list); |
116 | 118 | break; |
117 | 119 | } |
118 | 120 | } |
119 | 121 | spin_unlock_bh(&sctp_local_addr_lock); |
120 | - if (addr && !addr->valid) | |
122 | + if (found) | |
121 | 123 | call_rcu(&addr->rcu, sctp_local_addr_free); |
122 | 124 | break; |
123 | 125 | } |
net/sctp/protocol.c
... | ... | @@ -628,6 +628,7 @@ |
628 | 628 | struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; |
629 | 629 | struct sctp_sockaddr_entry *addr = NULL; |
630 | 630 | struct sctp_sockaddr_entry *temp; |
631 | + int found = 0; | |
631 | 632 | |
632 | 633 | switch (ev) { |
633 | 634 | case NETDEV_UP: |
634 | 635 | |
... | ... | @@ -647,13 +648,14 @@ |
647 | 648 | list_for_each_entry_safe(addr, temp, |
648 | 649 | &sctp_local_addr_list, list) { |
649 | 650 | if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) { |
651 | + found = 1; | |
650 | 652 | addr->valid = 0; |
651 | 653 | list_del_rcu(&addr->list); |
652 | 654 | break; |
653 | 655 | } |
654 | 656 | } |
655 | 657 | spin_unlock_bh(&sctp_local_addr_lock); |
656 | - if (addr && !addr->valid) | |
658 | + if (found) | |
657 | 659 | call_rcu(&addr->rcu, sctp_local_addr_free); |
658 | 660 | break; |
659 | 661 | } |