Skip to content

Commit b160c28

Browse files
edumazetkuba-moo
authored andcommitted
tcp: do not mess with cloned skbs in tcp_add_backlog()
Heiner Kallweit reported that some skbs were sent with the following invalid GSO properties : - gso_size > 0 - gso_type == 0 This was triggerring a WARN_ON_ONCE() in rtl8169_tso_csum_v2. Juerg Haefliger was able to reproduce a similar issue using a lan78xx NIC and a workload mixing TCP incoming traffic and forwarded packets. The problem is that tcp_add_backlog() is writing over gso_segs and gso_size even if the incoming packet will not be coalesced to the backlog tail packet. While skb_try_coalesce() would bail out if tail packet is cloned, this overwriting would lead to corruptions of other packets cooked by lan78xx, sharing a common super-packet. The strategy used by lan78xx is to use a big skb, and split it into all received packets using skb_clone() to avoid copies. The drawback of this strategy is that all the small skb share a common struct skb_shared_info. This patch rewrites TCP gso_size/gso_segs handling to only happen on the tail skb, since skb_try_coalesce() made sure it was not cloned. Fixes: 4f693b5 ("tcp: implement coalescing on backlog queue") Signed-off-by: Eric Dumazet <[email protected]> Bisected-by: Juerg Haefliger <[email protected]> Tested-by: Juerg Haefliger <[email protected]> Reported-by: Heiner Kallweit <[email protected]> Link: https://bugzilla.kernel.org/show_bug.cgi?id=209423 Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jakub Kicinski <[email protected]>
1 parent fd23d2d commit b160c28

File tree

1 file changed

+13
-12
lines changed

1 file changed

+13
-12
lines changed

net/ipv4/tcp_ipv4.c

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,13 +1760,15 @@ int tcp_v4_early_demux(struct sk_buff *skb)
17601760
bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
17611761
{
17621762
u32 limit = READ_ONCE(sk->sk_rcvbuf) + READ_ONCE(sk->sk_sndbuf);
1763+
u32 tail_gso_size, tail_gso_segs;
17631764
struct skb_shared_info *shinfo;
17641765
const struct tcphdr *th;
17651766
struct tcphdr *thtail;
17661767
struct sk_buff *tail;
17671768
unsigned int hdrlen;
17681769
bool fragstolen;
17691770
u32 gso_segs;
1771+
u32 gso_size;
17701772
int delta;
17711773

17721774
/* In case all data was pulled from skb frags (in __pskb_pull_tail()),
@@ -1792,13 +1794,6 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
17921794
*/
17931795
th = (const struct tcphdr *)skb->data;
17941796
hdrlen = th->doff * 4;
1795-
shinfo = skb_shinfo(skb);
1796-
1797-
if (!shinfo->gso_size)
1798-
shinfo->gso_size = skb->len - hdrlen;
1799-
1800-
if (!shinfo->gso_segs)
1801-
shinfo->gso_segs = 1;
18021797

18031798
tail = sk->sk_backlog.tail;
18041799
if (!tail)
@@ -1821,6 +1816,15 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
18211816
goto no_coalesce;
18221817

18231818
__skb_pull(skb, hdrlen);
1819+
1820+
shinfo = skb_shinfo(skb);
1821+
gso_size = shinfo->gso_size ?: skb->len;
1822+
gso_segs = shinfo->gso_segs ?: 1;
1823+
1824+
shinfo = skb_shinfo(tail);
1825+
tail_gso_size = shinfo->gso_size ?: (tail->len - hdrlen);
1826+
tail_gso_segs = shinfo->gso_segs ?: 1;
1827+
18241828
if (skb_try_coalesce(tail, skb, &fragstolen, &delta)) {
18251829
TCP_SKB_CB(tail)->end_seq = TCP_SKB_CB(skb)->end_seq;
18261830

@@ -1847,11 +1851,8 @@ bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb)
18471851
}
18481852

18491853
/* Not as strict as GRO. We only need to carry mss max value */
1850-
skb_shinfo(tail)->gso_size = max(shinfo->gso_size,
1851-
skb_shinfo(tail)->gso_size);
1852-
1853-
gso_segs = skb_shinfo(tail)->gso_segs + shinfo->gso_segs;
1854-
skb_shinfo(tail)->gso_segs = min_t(u32, gso_segs, 0xFFFF);
1854+
shinfo->gso_size = max(gso_size, tail_gso_size);
1855+
shinfo->gso_segs = min_t(u32, gso_segs + tail_gso_segs, 0xFFFF);
18551856

18561857
sk->sk_backlog.len += delta;
18571858
__NET_INC_STATS(sock_net(sk),

0 commit comments

Comments
 (0)