From: Wang Yufen wangyufen@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I48H9Z?from=project-issue CVE: NA
-------------------------------------------------
This patch fixes possible ZSTD_decompressStream failures. When decompressing skb->data, should skip the previous rxm->offset data.
Signed-off-by: Wang Yufen wangyufen@huawei.com Reviewed-by: Wei Yongjun weiyongjun1@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- net/ipv4/tcp_comp.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-)
diff --git a/net/ipv4/tcp_comp.c b/net/ipv4/tcp_comp.c index bd5274091225b..de5f735c5ce61 100644 --- a/net/ipv4/tcp_comp.c +++ b/net/ipv4/tcp_comp.c @@ -593,8 +593,8 @@ static void *tcp_comp_get_rx_stream(struct sock *sk) static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb) { struct tcp_comp_context *ctx = comp_get_ctx(sk); + struct strp_msg *rxm = strp_msg(skb); const int plen = skb->len; - struct strp_msg *rxm; ZSTD_outBuffer outbuf; ZSTD_inBuffer inbuf; int len; @@ -615,11 +615,11 @@ static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb) ctx->rx.data_offset);
memcpy((char *)ctx->rx.compressed_data + ctx->rx.data_offset, - skb->data, plen); + (char *)skb->data + rxm->offset, plen - rxm->offset);
inbuf.src = ctx->rx.compressed_data; inbuf.pos = 0; - inbuf.size = plen + ctx->rx.data_offset; + inbuf.size = plen - rxm->offset + ctx->rx.data_offset; ctx->rx.data_offset = 0;
outbuf.dst = ctx->rx.plaintext_data; @@ -630,7 +630,6 @@ static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb) size_t ret;
to = outbuf.dst; - ret = ZSTD_decompressStream(ctx->rx.dstream, &outbuf, &inbuf); if (ZSTD_isError(ret)) return -EIO; @@ -640,8 +639,8 @@ static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb) len = skb_tailroom(skb);
__skb_put(skb, len); - rxm = strp_msg(skb); - rxm->full_len += len; + rxm->full_len += (len + rxm->offset); + rxm->offset = 0;
len += plen; skb_copy_to_linear_data(skb, to, len);
From: Wang Yufen wangyufen@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I48H9Z?from=project-issue CVE: NA
-------------------------------------------------
In order to separate the compressed data and decompressed data, this patch adds dpkt to tcp_comp_context_rx, dpkt is used to save decompressed skb.
Signed-off-by: Wang Yufen wangyufen@huawei.com Reviewed-by: Wei Yongjun weiyongjun1@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- net/ipv4/tcp_comp.c | 94 ++++++++++++++++++++++++++++++--------------- 1 file changed, 64 insertions(+), 30 deletions(-)
diff --git a/net/ipv4/tcp_comp.c b/net/ipv4/tcp_comp.c index de5f735c5ce61..c25600bcb4434 100644 --- a/net/ipv4/tcp_comp.c +++ b/net/ipv4/tcp_comp.c @@ -51,7 +51,7 @@ struct tcp_comp_context_rx { struct strparser strp; void (*saved_data_ready)(struct sock *sk); struct sk_buff *pkt; - bool decompressed; + struct sk_buff *dpkt; };
struct tcp_comp_context { @@ -534,6 +534,24 @@ static bool comp_advance_skb(struct sock *sk, struct sk_buff *skb, return true; }
+static bool comp_advance_dskb(struct sock *sk, struct sk_buff *skb, + unsigned int len) +{ + struct tcp_comp_context *ctx = comp_get_ctx(sk); + struct strp_msg *rxm = strp_msg(skb); + + if (len < rxm->full_len) { + rxm->offset += len; + rxm->full_len -= len; + return false; + } + + /* Finished with message */ + ctx->rx.dpkt = NULL; + kfree_skb(skb); + return true; +} + static int tcp_comp_rx_context_init(struct tcp_comp_context *ctx) { int dsize; @@ -590,13 +608,14 @@ static void *tcp_comp_get_rx_stream(struct sock *sk) return ctx->rx.plaintext_data; }
-static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb) +static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb, int flags) { struct tcp_comp_context *ctx = comp_get_ctx(sk); struct strp_msg *rxm = strp_msg(skb); const int plen = skb->len; ZSTD_outBuffer outbuf; ZSTD_inBuffer inbuf; + struct sk_buff *nskb; int len; void *to;
@@ -610,6 +629,10 @@ static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb) if (plen + ctx->rx.data_offset > TCP_COMP_MAX_CSIZE) return -ENOMEM;
+ nskb = skb_copy(skb, GFP_KERNEL); + if (!nskb) + return -ENOMEM; + if (ctx->rx.data_offset) memcpy(ctx->rx.compressed_data, ctx->rx.remaining_data, ctx->rx.data_offset); @@ -631,34 +654,38 @@ static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb)
to = outbuf.dst; ret = ZSTD_decompressStream(ctx->rx.dstream, &outbuf, &inbuf); - if (ZSTD_isError(ret)) + if (ZSTD_isError(ret)) { + kfree_skb(nskb); return -EIO; + }
len = outbuf.pos - plen; - if (len > skb_tailroom(skb)) - len = skb_tailroom(skb); + if (len > skb_tailroom(nskb)) + len = skb_tailroom(nskb);
- __skb_put(skb, len); - rxm->full_len += (len + rxm->offset); - rxm->offset = 0; + __skb_put(nskb, len);
len += plen; - skb_copy_to_linear_data(skb, to, len); + skb_copy_to_linear_data(nskb, to, len);
while ((to += len, outbuf.pos -= len) > 0) { struct page *pages; skb_frag_t *frag;
- if (WARN_ON(skb_shinfo(skb)->nr_frags >= MAX_SKB_FRAGS)) + if (WARN_ON(skb_shinfo(nskb)->nr_frags >= MAX_SKB_FRAGS)) { + kfree_skb(nskb); return -EMSGSIZE; + }
- frag = skb_shinfo(skb)->frags + - skb_shinfo(skb)->nr_frags; + frag = skb_shinfo(nskb)->frags + + skb_shinfo(nskb)->nr_frags; pages = alloc_pages(__GFP_NOWARN | GFP_KERNEL | __GFP_COMP, TCP_COMP_ALLOC_ORDER);
- if (!pages) + if (!pages) { + kfree_skb(nskb); return -ENOMEM; + }
__skb_frag_set_page(frag, pages); len = PAGE_SIZE << TCP_COMP_ALLOC_ORDER; @@ -669,11 +696,10 @@ static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb) skb_frag_size_set(frag, len); memcpy(skb_frag_address(frag), to, len);
- skb->truesize += len; - skb->data_len += len; - skb->len += len; - rxm->full_len += len; - skb_shinfo(skb)->nr_frags++; + nskb->truesize += len; + nskb->data_len += len; + nskb->len += len; + skb_shinfo(nskb)->nr_frags++; }
if (ret == 0) @@ -689,6 +715,13 @@ static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb) break; } } + + ctx->rx.dpkt = nskb; + rxm = strp_msg(nskb); + rxm->full_len = nskb->len; + rxm->offset = 0; + comp_advance_skb(sk, skb, plen - rxm->offset); + return 0; }
@@ -715,21 +748,19 @@ static int tcp_comp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, do { int chunk = 0;
- skb = comp_wait_data(sk, flags, timeo, &err); - if (!skb) - goto recv_end; + if (!ctx->rx.dpkt) { + skb = comp_wait_data(sk, flags, timeo, &err); + if (!skb) + goto recv_end;
- if (!ctx->rx.decompressed) { - err = tcp_comp_decompress(sk, skb); + err = tcp_comp_decompress(sk, skb, flags); if (err < 0) { goto recv_end; } - ctx->rx.decompressed = true; } + skb = ctx->rx.dpkt; rxm = strp_msg(skb); - chunk = min_t(unsigned int, rxm->full_len, len); - err = skb_copy_datagram_msg(skb, rxm->offset, msg, chunk); if (err < 0) @@ -738,11 +769,11 @@ static int tcp_comp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, copied += chunk; len -= chunk; if (likely(!(flags & MSG_PEEK))) - comp_advance_skb(sk, skb, chunk); + comp_advance_dskb(sk, skb, chunk); else break;
- if (copied >= target && !ctx->rx.pkt) + if (copied >= target && !ctx->rx.dpkt) break; } while (len > 0);
@@ -758,7 +789,7 @@ bool comp_stream_read(const struct sock *sk) if (!ctx) return false;
- if (ctx->rx.pkt) + if (ctx->rx.pkt || ctx->rx.dpkt) return true;
return false; @@ -775,7 +806,6 @@ static void comp_queue(struct strparser *strp, struct sk_buff *skb) { struct tcp_comp_context *ctx = comp_get_ctx(strp->sk);
- ctx->rx.decompressed = false; ctx->rx.pkt = skb; strp_pause(strp); ctx->rx.saved_data_ready(strp->sk); @@ -923,6 +953,10 @@ void tcp_cleanup_compression(struct sock *sk) kfree_skb(ctx->rx.pkt); ctx->rx.pkt = NULL; } + if (ctx->rx.dpkt) { + kfree_skb(ctx->rx.dpkt); + ctx->rx.dpkt = NULL; + } strp_stop(&ctx->rx.strp);
rcu_assign_pointer(icsk->icsk_ulp_data, NULL);
From: Wang Yufen wangyufen@huawei.com
hulk inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I48H9Z?from=project-issue CVE: NA
-------------------------------------------------
The compressed data and decompressed data is separated. There is no need to save the uncompressed data to remaining_data buffer, can directly read data from the uncompressed skb.
Signed-off-by: Wang Yufen wangyufen@huawei.com Reviewed-by: Wei Yongjun weiyongjun1@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- net/ipv4/tcp_comp.c | 98 ++++++++++++++------------------------------- 1 file changed, 30 insertions(+), 68 deletions(-)
diff --git a/net/ipv4/tcp_comp.c b/net/ipv4/tcp_comp.c index c25600bcb4434..4468c590d4fd7 100644 --- a/net/ipv4/tcp_comp.c +++ b/net/ipv4/tcp_comp.c @@ -44,10 +44,7 @@ struct tcp_comp_context_rx { ZSTD_DStream *dstream; void *dworkspace; void *plaintext_data; - void *compressed_data; - void *remaining_data;
- size_t data_offset; struct strparser strp; void (*saved_data_ready)(struct sock *sk); struct sk_buff *pkt; @@ -573,24 +570,8 @@ static int tcp_comp_rx_context_init(struct tcp_comp_context *ctx) if (!ctx->rx.plaintext_data) goto err_dstream;
- ctx->rx.compressed_data = kvmalloc(TCP_COMP_MAX_CSIZE, GFP_KERNEL); - if (!ctx->rx.compressed_data) - goto err_compressed; - - ctx->rx.remaining_data = kvmalloc(TCP_COMP_MAX_CSIZE, GFP_KERNEL); - if (!ctx->rx.remaining_data) - goto err_remaining; - - ctx->rx.data_offset = 0; - return 0;
-err_remaining: - kvfree(ctx->rx.compressed_data); - ctx->rx.compressed_data = NULL; -err_compressed: - kvfree(ctx->rx.plaintext_data); - ctx->rx.plaintext_data = NULL; err_dstream: kfree(ctx->rx.dworkspace); ctx->rx.dworkspace = NULL; @@ -612,11 +593,12 @@ static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb, int flags) { struct tcp_comp_context *ctx = comp_get_ctx(sk); struct strp_msg *rxm = strp_msg(skb); - const int plen = skb->len; + size_t ret, compressed_len = 0; + int nr_frags_over = 0; ZSTD_outBuffer outbuf; ZSTD_inBuffer inbuf; struct sk_buff *nskb; - int len; + int len, plen; void *to;
to = tcp_comp_get_rx_stream(sk); @@ -626,62 +608,54 @@ static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb, int flags) if (skb_linearize_cow(skb)) return -ENOMEM;
- if (plen + ctx->rx.data_offset > TCP_COMP_MAX_CSIZE) - return -ENOMEM; - nskb = skb_copy(skb, GFP_KERNEL); if (!nskb) return -ENOMEM;
- if (ctx->rx.data_offset) - memcpy(ctx->rx.compressed_data, ctx->rx.remaining_data, - ctx->rx.data_offset); + while (compressed_len < (skb->len - rxm->offset)) { + len = 0; + plen = skb->len - rxm->offset - compressed_len; + if (plen > TCP_COMP_MAX_CSIZE) + plen = TCP_COMP_MAX_CSIZE;
- memcpy((char *)ctx->rx.compressed_data + ctx->rx.data_offset, - (char *)skb->data + rxm->offset, plen - rxm->offset); + inbuf.src = (char *)skb->data + rxm->offset + compressed_len; + inbuf.pos = 0; + inbuf.size = plen;
- inbuf.src = ctx->rx.compressed_data; - inbuf.pos = 0; - inbuf.size = plen - rxm->offset + ctx->rx.data_offset; - ctx->rx.data_offset = 0; - - outbuf.dst = ctx->rx.plaintext_data; - outbuf.pos = 0; - outbuf.size = TCP_COMP_MAX_CSIZE * 32; + outbuf.dst = ctx->rx.plaintext_data; + outbuf.pos = 0; + outbuf.size = TCP_COMP_MAX_CSIZE * 32;
- while (1) { - size_t ret; - - to = outbuf.dst; ret = ZSTD_decompressStream(ctx->rx.dstream, &outbuf, &inbuf); if (ZSTD_isError(ret)) { kfree_skb(nskb); return -EIO; }
- len = outbuf.pos - plen; - if (len > skb_tailroom(nskb)) - len = skb_tailroom(nskb); + if (!compressed_len) { + len = outbuf.pos - skb->len; + if (len > skb_tailroom(nskb)) + len = skb_tailroom(nskb);
- __skb_put(nskb, len); + __skb_put(nskb, len);
- len += plen; - skb_copy_to_linear_data(nskb, to, len); + len += skb->len; + skb_copy_to_linear_data(nskb, to, len); + }
while ((to += len, outbuf.pos -= len) > 0) { struct page *pages; skb_frag_t *frag;
- if (WARN_ON(skb_shinfo(nskb)->nr_frags >= MAX_SKB_FRAGS)) { - kfree_skb(nskb); - return -EMSGSIZE; + if (skb_shinfo(nskb)->nr_frags >= MAX_SKB_FRAGS) { + nr_frags_over = 1; + break; }
frag = skb_shinfo(nskb)->frags + skb_shinfo(nskb)->nr_frags; pages = alloc_pages(__GFP_NOWARN | GFP_KERNEL | __GFP_COMP, TCP_COMP_ALLOC_ORDER); - if (!pages) { kfree_skb(nskb); return -ENOMEM; @@ -702,25 +676,17 @@ static int tcp_comp_decompress(struct sock *sk, struct sk_buff *skb, int flags) skb_shinfo(nskb)->nr_frags++; }
- if (ret == 0) + if (nr_frags_over) break;
- if (inbuf.pos >= plen || !inbuf.pos) { - if (inbuf.pos < inbuf.size) { - memcpy((char *)ctx->rx.remaining_data, - (char *)inbuf.src + inbuf.pos, - inbuf.size - inbuf.pos); - ctx->rx.data_offset = inbuf.size - inbuf.pos; - } - break; - } + compressed_len += inbuf.pos; }
ctx->rx.dpkt = nskb; rxm = strp_msg(nskb); rxm->full_len = nskb->len; rxm->offset = 0; - comp_advance_skb(sk, skb, plen - rxm->offset); + comp_advance_skb(sk, skb, compressed_len);
return 0; } @@ -758,6 +724,7 @@ static int tcp_comp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, goto recv_end; } } + skb = ctx->rx.dpkt; rxm = strp_msg(skb); chunk = min_t(unsigned int, rxm->full_len, len); @@ -916,12 +883,6 @@ static void tcp_comp_context_rx_free(struct tcp_comp_context *ctx)
kvfree(ctx->rx.plaintext_data); ctx->rx.plaintext_data = NULL; - - kvfree(ctx->rx.compressed_data); - ctx->rx.compressed_data = NULL; - - kvfree(ctx->rx.remaining_data); - ctx->rx.remaining_data = NULL; }
static void tcp_comp_context_free(struct rcu_head *head) @@ -953,6 +914,7 @@ void tcp_cleanup_compression(struct sock *sk) kfree_skb(ctx->rx.pkt); ctx->rx.pkt = NULL; } + if (ctx->rx.dpkt) { kfree_skb(ctx->rx.dpkt); ctx->rx.dpkt = NULL;