Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 293ad604 authored by Octavian Purdila's avatar Octavian Purdila Committed by David S. Miller
Browse files

tcp: Fix for race due to temporary drop of the socket lock in skb_splice_bits.



skb_splice_bits temporary drops the socket lock while iterating over
the socket queue in order to break a reverse locking condition which
happens with sendfile. This, however, opens a window of opportunity
for tcp_collapse() to aggregate skbs and thus potentially free the
current skb used in skb_splice_bits and tcp_read_sock.

This patch fixes the problem by (re-)getting the same "logical skb"
after the lock has been temporary dropped.

Based on idea and initial patch from Evgeniy Polyakov.

Signed-off-by: default avatarOctavian Purdila <opurdila@ixiacom.com>
Acked-by: default avatarEvgeniy Polyakov <johnpol@2ka.mipt.ru>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 26af65cb
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -1445,6 +1445,7 @@ done:

	if (spd.nr_pages) {
		int ret;
		struct sock *sk = __skb->sk;

		/*
		 * Drop the socket lock, otherwise we have reverse
@@ -1455,9 +1456,9 @@ done:
		 * we call into ->sendpage() with the i_mutex lock held
		 * and networking will grab the socket lock.
		 */
		release_sock(__skb->sk);
		release_sock(sk);
		ret = splice_to_pipe(pipe, &spd);
		lock_sock(__skb->sk);
		lock_sock(sk);
		return ret;
	}

+8 −1
Original line number Diff line number Diff line
@@ -1227,7 +1227,14 @@ int tcp_read_sock(struct sock *sk, read_descriptor_t *desc,
				copied += used;
				offset += used;
			}
			if (offset != skb->len)
			/*
			 * If recv_actor drops the lock (e.g. TCP splice
			 * receive) the skb pointer might be invalid when
			 * getting here: tcp_collapse might have deleted it
			 * while aggregating skbs from the socket queue.
			 */
			skb = tcp_recv_skb(sk, seq-1, &offset);
			if (!skb || (offset+1 != skb->len))
				break;
		}
		if (tcp_hdr(skb)->fin) {