diff options
author | Reshetova, Elena <elena.reshetova@intel.com> | 2017-06-30 06:08:00 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-07-01 10:39:08 -0400 |
commit | 14afee4b6092fde451ee17604e5f5c89da33e71e (patch) | |
tree | 19be7a1d72a1b25c5e5366c1213cdda982aacca2 /net/ipv4/tcp_output.c | |
parent | 2638595afccf6554bfe55268ff9b2d3ac3dff2e6 (diff) |
net: convert sock.sk_wmem_alloc from atomic_t to refcount_t
refcount_t type and corresponding API should be
used instead of atomic_t when the variable is used as
a reference counter. This allows to avoid accidental
refcounter overflows that might lead to use-after-free
situations.
Signed-off-by: Elena Reshetova <elena.reshetova@intel.com>
Signed-off-by: Hans Liljestrand <ishkamiel@gmail.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: David Windsor <dwindsor@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/tcp_output.c')
-rw-r--r-- | net/ipv4/tcp_output.c | 15 |
1 files changed, 7 insertions, 8 deletions
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 9a9c395b6235..1d79137f3795 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c | |||
@@ -861,12 +861,11 @@ void tcp_wfree(struct sk_buff *skb) | |||
861 | struct sock *sk = skb->sk; | 861 | struct sock *sk = skb->sk; |
862 | struct tcp_sock *tp = tcp_sk(sk); | 862 | struct tcp_sock *tp = tcp_sk(sk); |
863 | unsigned long flags, nval, oval; | 863 | unsigned long flags, nval, oval; |
864 | int wmem; | ||
865 | 864 | ||
866 | /* Keep one reference on sk_wmem_alloc. | 865 | /* Keep one reference on sk_wmem_alloc. |
867 | * Will be released by sk_free() from here or tcp_tasklet_func() | 866 | * Will be released by sk_free() from here or tcp_tasklet_func() |
868 | */ | 867 | */ |
869 | wmem = atomic_sub_return(skb->truesize - 1, &sk->sk_wmem_alloc); | 868 | WARN_ON(refcount_sub_and_test(skb->truesize - 1, &sk->sk_wmem_alloc)); |
870 | 869 | ||
871 | /* If this softirq is serviced by ksoftirqd, we are likely under stress. | 870 | /* If this softirq is serviced by ksoftirqd, we are likely under stress. |
872 | * Wait until our queues (qdisc + devices) are drained. | 871 | * Wait until our queues (qdisc + devices) are drained. |
@@ -875,7 +874,7 @@ void tcp_wfree(struct sk_buff *skb) | |||
875 | * - chance for incoming ACK (processed by another cpu maybe) | 874 | * - chance for incoming ACK (processed by another cpu maybe) |
876 | * to migrate this flow (skb->ooo_okay will be eventually set) | 875 | * to migrate this flow (skb->ooo_okay will be eventually set) |
877 | */ | 876 | */ |
878 | if (wmem >= SKB_TRUESIZE(1) && this_cpu_ksoftirqd() == current) | 877 | if (refcount_read(&sk->sk_wmem_alloc) >= SKB_TRUESIZE(1) && this_cpu_ksoftirqd() == current) |
879 | goto out; | 878 | goto out; |
880 | 879 | ||
881 | for (oval = READ_ONCE(sk->sk_tsq_flags);; oval = nval) { | 880 | for (oval = READ_ONCE(sk->sk_tsq_flags);; oval = nval) { |
@@ -925,7 +924,7 @@ enum hrtimer_restart tcp_pace_kick(struct hrtimer *timer) | |||
925 | if (nval != oval) | 924 | if (nval != oval) |
926 | continue; | 925 | continue; |
927 | 926 | ||
928 | if (!atomic_inc_not_zero(&sk->sk_wmem_alloc)) | 927 | if (!refcount_inc_not_zero(&sk->sk_wmem_alloc)) |
929 | break; | 928 | break; |
930 | /* queue this socket to tasklet queue */ | 929 | /* queue this socket to tasklet queue */ |
931 | tsq = this_cpu_ptr(&tsq_tasklet); | 930 | tsq = this_cpu_ptr(&tsq_tasklet); |
@@ -1045,7 +1044,7 @@ static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it, | |||
1045 | skb->sk = sk; | 1044 | skb->sk = sk; |
1046 | skb->destructor = skb_is_tcp_pure_ack(skb) ? __sock_wfree : tcp_wfree; | 1045 | skb->destructor = skb_is_tcp_pure_ack(skb) ? __sock_wfree : tcp_wfree; |
1047 | skb_set_hash_from_sk(skb, sk); | 1046 | skb_set_hash_from_sk(skb, sk); |
1048 | atomic_add(skb->truesize, &sk->sk_wmem_alloc); | 1047 | refcount_add(skb->truesize, &sk->sk_wmem_alloc); |
1049 | 1048 | ||
1050 | skb_set_dst_pending_confirm(skb, sk->sk_dst_pending_confirm); | 1049 | skb_set_dst_pending_confirm(skb, sk->sk_dst_pending_confirm); |
1051 | 1050 | ||
@@ -2176,7 +2175,7 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, | |||
2176 | limit = min_t(u32, limit, sysctl_tcp_limit_output_bytes); | 2175 | limit = min_t(u32, limit, sysctl_tcp_limit_output_bytes); |
2177 | limit <<= factor; | 2176 | limit <<= factor; |
2178 | 2177 | ||
2179 | if (atomic_read(&sk->sk_wmem_alloc) > limit) { | 2178 | if (refcount_read(&sk->sk_wmem_alloc) > limit) { |
2180 | /* Always send the 1st or 2nd skb in write queue. | 2179 | /* Always send the 1st or 2nd skb in write queue. |
2181 | * No need to wait for TX completion to call us back, | 2180 | * No need to wait for TX completion to call us back, |
2182 | * after softirq/tasklet schedule. | 2181 | * after softirq/tasklet schedule. |
@@ -2192,7 +2191,7 @@ static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb, | |||
2192 | * test again the condition. | 2191 | * test again the condition. |
2193 | */ | 2192 | */ |
2194 | smp_mb__after_atomic(); | 2193 | smp_mb__after_atomic(); |
2195 | if (atomic_read(&sk->sk_wmem_alloc) > limit) | 2194 | if (refcount_read(&sk->sk_wmem_alloc) > limit) |
2196 | return true; | 2195 | return true; |
2197 | } | 2196 | } |
2198 | return false; | 2197 | return false; |
@@ -2812,7 +2811,7 @@ int __tcp_retransmit_skb(struct sock *sk, struct sk_buff *skb, int segs) | |||
2812 | /* Do not sent more than we queued. 1/4 is reserved for possible | 2811 | /* Do not sent more than we queued. 1/4 is reserved for possible |
2813 | * copying overhead: fragmentation, tunneling, mangling etc. | 2812 | * copying overhead: fragmentation, tunneling, mangling etc. |
2814 | */ | 2813 | */ |
2815 | if (atomic_read(&sk->sk_wmem_alloc) > | 2814 | if (refcount_read(&sk->sk_wmem_alloc) > |
2816 | min_t(u32, sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), | 2815 | min_t(u32, sk->sk_wmem_queued + (sk->sk_wmem_queued >> 2), |
2817 | sk->sk_sndbuf)) | 2816 | sk->sk_sndbuf)) |
2818 | return -EAGAIN; | 2817 | return -EAGAIN; |