aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChidambar 'ilLogict' Zinnoury <illogict@online.fr>2008-03-11 21:05:02 -0400
committerDavid S. Miller <davem@davemloft.net>2008-03-11 21:05:02 -0400
commit22626216c46f2ec86287e75ea86dd9ac3df54265 (patch)
treeee7fb381a17fb8105b7f2f8cd40cc4b587d064d0
parentb2211a361a4289c83971f89da53fe2eb9e72769d (diff)
[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>
-rw-r--r--net/sctp/bind_addr.c4
-rw-r--r--net/sctp/ipv6.c4
-rw-r--r--net/sctp/protocol.c4
3 files changed, 9 insertions, 3 deletions
diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c
index a27511ebc4cb..ceefda025e2d 100644
--- a/net/sctp/bind_addr.c
+++ b/net/sctp/bind_addr.c
@@ -209,6 +209,7 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new,
209int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr) 209int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr)
210{ 210{
211 struct sctp_sockaddr_entry *addr, *temp; 211 struct sctp_sockaddr_entry *addr, *temp;
212 int found = 0;
212 213
213 /* We hold the socket lock when calling this function, 214 /* We hold the socket lock when calling this function,
214 * and that acts as a writer synchronizing lock. 215 * and that acts as a writer synchronizing lock.
@@ -216,13 +217,14 @@ int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr)
216 list_for_each_entry_safe(addr, temp, &bp->address_list, list) { 217 list_for_each_entry_safe(addr, temp, &bp->address_list, list) {
217 if (sctp_cmp_addr_exact(&addr->a, del_addr)) { 218 if (sctp_cmp_addr_exact(&addr->a, del_addr)) {
218 /* Found the exact match. */ 219 /* Found the exact match. */
220 found = 1;
219 addr->valid = 0; 221 addr->valid = 0;
220 list_del_rcu(&addr->list); 222 list_del_rcu(&addr->list);
221 break; 223 break;
222 } 224 }
223 } 225 }
224 226
225 if (addr && !addr->valid) { 227 if (found) {
226 call_rcu(&addr->rcu, sctp_local_addr_free); 228 call_rcu(&addr->rcu, sctp_local_addr_free);
227 SCTP_DBG_OBJCNT_DEC(addr); 229 SCTP_DBG_OBJCNT_DEC(addr);
228 return 0; 230 return 0;
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c
index 87f940587d5f..9aa0733aee87 100644
--- a/net/sctp/ipv6.c
+++ b/net/sctp/ipv6.c
@@ -89,6 +89,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
89 struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; 89 struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
90 struct sctp_sockaddr_entry *addr = NULL; 90 struct sctp_sockaddr_entry *addr = NULL;
91 struct sctp_sockaddr_entry *temp; 91 struct sctp_sockaddr_entry *temp;
92 int found = 0;
92 93
93 switch (ev) { 94 switch (ev) {
94 case NETDEV_UP: 95 case NETDEV_UP:
@@ -111,13 +112,14 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
111 &sctp_local_addr_list, list) { 112 &sctp_local_addr_list, list) {
112 if (ipv6_addr_equal(&addr->a.v6.sin6_addr, 113 if (ipv6_addr_equal(&addr->a.v6.sin6_addr,
113 &ifa->addr)) { 114 &ifa->addr)) {
115 found = 1;
114 addr->valid = 0; 116 addr->valid = 0;
115 list_del_rcu(&addr->list); 117 list_del_rcu(&addr->list);
116 break; 118 break;
117 } 119 }
118 } 120 }
119 spin_unlock_bh(&sctp_local_addr_lock); 121 spin_unlock_bh(&sctp_local_addr_lock);
120 if (addr && !addr->valid) 122 if (found)
121 call_rcu(&addr->rcu, sctp_local_addr_free); 123 call_rcu(&addr->rcu, sctp_local_addr_free);
122 break; 124 break;
123 } 125 }
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c
index 688546dccd82..ad0a4069b95b 100644
--- a/net/sctp/protocol.c
+++ b/net/sctp/protocol.c
@@ -628,6 +628,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
628 struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; 628 struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
629 struct sctp_sockaddr_entry *addr = NULL; 629 struct sctp_sockaddr_entry *addr = NULL;
630 struct sctp_sockaddr_entry *temp; 630 struct sctp_sockaddr_entry *temp;
631 int found = 0;
631 632
632 switch (ev) { 633 switch (ev) {
633 case NETDEV_UP: 634 case NETDEV_UP:
@@ -647,13 +648,14 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
647 list_for_each_entry_safe(addr, temp, 648 list_for_each_entry_safe(addr, temp,
648 &sctp_local_addr_list, list) { 649 &sctp_local_addr_list, list) {
649 if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) { 650 if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) {
651 found = 1;
650 addr->valid = 0; 652 addr->valid = 0;
651 list_del_rcu(&addr->list); 653 list_del_rcu(&addr->list);
652 break; 654 break;
653 } 655 }
654 } 656 }
655 spin_unlock_bh(&sctp_local_addr_lock); 657 spin_unlock_bh(&sctp_local_addr_lock);
656 if (addr && !addr->valid) 658 if (found)
657 call_rcu(&addr->rcu, sctp_local_addr_free); 659 call_rcu(&addr->rcu, sctp_local_addr_free);
658 break; 660 break;
659 } 661 }