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>
int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr)
{
struct sctp_sockaddr_entry *addr, *temp;
int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr)
{
struct sctp_sockaddr_entry *addr, *temp;
/* We hold the socket lock when calling this function,
* and that acts as a writer synchronizing lock.
/* We hold the socket lock when calling this function,
* and that acts as a writer synchronizing lock.
list_for_each_entry_safe(addr, temp, &bp->address_list, list) {
if (sctp_cmp_addr_exact(&addr->a, del_addr)) {
/* Found the exact match. */
list_for_each_entry_safe(addr, temp, &bp->address_list, list) {
if (sctp_cmp_addr_exact(&addr->a, del_addr)) {
/* Found the exact match. */
addr->valid = 0;
list_del_rcu(&addr->list);
break;
}
}
addr->valid = 0;
list_del_rcu(&addr->list);
break;
}
}
- if (addr && !addr->valid) {
call_rcu(&addr->rcu, sctp_local_addr_free);
SCTP_DBG_OBJCNT_DEC(addr);
return 0;
call_rcu(&addr->rcu, sctp_local_addr_free);
SCTP_DBG_OBJCNT_DEC(addr);
return 0;
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
struct sctp_sockaddr_entry *addr = NULL;
struct sctp_sockaddr_entry *temp;
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
struct sctp_sockaddr_entry *addr = NULL;
struct sctp_sockaddr_entry *temp;
switch (ev) {
case NETDEV_UP:
switch (ev) {
case NETDEV_UP:
&sctp_local_addr_list, list) {
if (ipv6_addr_equal(&addr->a.v6.sin6_addr,
&ifa->addr)) {
&sctp_local_addr_list, list) {
if (ipv6_addr_equal(&addr->a.v6.sin6_addr,
&ifa->addr)) {
addr->valid = 0;
list_del_rcu(&addr->list);
break;
}
}
spin_unlock_bh(&sctp_local_addr_lock);
addr->valid = 0;
list_del_rcu(&addr->list);
break;
}
}
spin_unlock_bh(&sctp_local_addr_lock);
- if (addr && !addr->valid)
call_rcu(&addr->rcu, sctp_local_addr_free);
break;
}
call_rcu(&addr->rcu, sctp_local_addr_free);
break;
}
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
struct sctp_sockaddr_entry *addr = NULL;
struct sctp_sockaddr_entry *temp;
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
struct sctp_sockaddr_entry *addr = NULL;
struct sctp_sockaddr_entry *temp;
switch (ev) {
case NETDEV_UP:
switch (ev) {
case NETDEV_UP:
list_for_each_entry_safe(addr, temp,
&sctp_local_addr_list, list) {
if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) {
list_for_each_entry_safe(addr, temp,
&sctp_local_addr_list, list) {
if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) {
addr->valid = 0;
list_del_rcu(&addr->list);
break;
}
}
spin_unlock_bh(&sctp_local_addr_lock);
addr->valid = 0;
list_del_rcu(&addr->list);
break;
}
}
spin_unlock_bh(&sctp_local_addr_lock);
- if (addr && !addr->valid)
call_rcu(&addr->rcu, sctp_local_addr_free);
break;
}
call_rcu(&addr->rcu, sctp_local_addr_free);
break;
}