diff options
author | Vlad Yasevich <vladislav.yasevich@hp.com> | 2007-09-16 19:02:12 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-09-16 19:02:12 -0400 |
commit | 293035479942400a7fe8e4f72465d4e4e466b91a (patch) | |
tree | af9890403a554b4cf8389a9116080a0d1aa187fb /net/sctp/socket.c | |
parent | ddeee3ce7fbf0e800f2a26a76d6018b42b337cc2 (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.c | 38 |
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, | |||
4156 | static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to, | 4163 | static 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 | } |