Commit 22626216c46f2ec86287e75ea86dd9ac3df54265

Authored by Chidambar 'ilLogict' Zinnoury
Committed by David S. Miller
1 parent b2211a361a

[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;
... ... @@ -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 }
... ... @@ -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 }