aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv4/tcp_output.c
diff options
context:
space:
mode:
authorReshetova, Elena <elena.reshetova@intel.com>2017-06-30 06:08:00 -0400
committerDavid S. Miller <davem@davemloft.net>2017-07-01 10:39:08 -0400
commit14afee4b6092fde451ee17604e5f5c89da33e71e (patch)
tree19be7a1d72a1b25c5e5366c1213cdda982aacca2 /net/ipv4/tcp_output.c
parent2638595afccf6554bfe55268ff9b2d3ac3dff2e6 (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.c15
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;