aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp/socket.c
diff options
context:
space:
mode:
authorVlad Yasevich <vladislav.yasevich@hp.com>2007-09-16 19:02:12 -0400
committerDavid S. Miller <davem@davemloft.net>2007-09-16 19:02:12 -0400
commit293035479942400a7fe8e4f72465d4e4e466b91a (patch)
treeaf9890403a554b4cf8389a9116080a0d1aa187fb /net/sctp/socket.c
parentddeee3ce7fbf0e800f2a26a76d6018b42b337cc2 (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>
Diffstat (limited to 'net/sctp/socket.c')
-rw-r--r--net/sctp/socket.c38
1 files changed, 25 insertions, 13 deletions
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,
4156static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to, 4163static 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}