[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH net-next 06/15] net: skbuff: add skb_crc32c_full() that reports failure



skb_crc32c() has the same problem as skb_checksum(): when the range runs
into unreadable frags or past the end of the skb it returns 0, which is
also a valid CRC.  sctp_compute_cksum() complements it, so a failed
walk looks like a checksum of 0xffffffff whatever the packet contains,
and the packet is accepted whenever its checksum field holds that.

Add skb_crc32c_full(), which updates *@crcp and returns 0, or returns
-EFAULT or -EINVAL and leaves *@crcp alone, like skb_checksum_full().
skb_crc32c() becomes a wrapper that behaves as before for the callers
that haven't been converted.

Convert skb_crc32c_csum_help(), which already returns an error when it
can't fill in the CRC, so a transmitted SCTP packet that can't be
checksummed is dropped instead of going out with a CRC of 0xffffffff.
The SCTP and other callers will follow with the skb_checksum() callers.

Fixes: 20ddd0fd2fcd ("net: skbuff: don't BUG() on leftover length in 
skb_checksum() and friends")
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
 include/linux/skbuff.h |  2 ++
 net/core/dev.c         |  7 +++++--
 net/core/skbuff.c      | 51 ++++++++++++++++++++++++++++++++++++++++++--------
 3 files changed, 50 insertions(+), 10 deletions(-)

diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 969b1c3269ec..327a01a5d6d0 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -4329,6 +4329,8 @@ static inline int memcpy_to_msg(struct msghdr *msg, void 
*data, int len)
 
 __wsum skb_checksum(const struct sk_buff *skb, int offset, int len,
                    __wsum csum);
+int __must_check skb_crc32c_full(const struct sk_buff *skb, int offset,
+                                int len, u32 *crcp);
 u32 skb_crc32c(const struct sk_buff *skb, int offset, int len, u32 crc);
 
 static inline void * __must_check
diff --git a/net/core/dev.c b/net/core/dev.c
index 8eb9bf1ff9eb..1d34fa4544a2 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3738,8 +3738,11 @@ int skb_crc32c_csum_help(struct sk_buff *skb)
        if (ret)
                goto out;
 
-       crc = ~skb_crc32c(skb, start, skb->len - start, ~0);
-       *(__le32 *)(skb->data + offset) = cpu_to_le32(crc);
+       crc = ~0;
+       ret = skb_crc32c_full(skb, start, skb->len - start, &crc);
+       if (ret)
+               goto out;
+       *(__le32 *)(skb->data + offset) = cpu_to_le32(~crc);
        skb_reset_csum_not_inet(skb);
 out:
        return ret;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index de433d452939..3b2a3747f009 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3788,23 +3788,41 @@ int skb_copy_and_csum_bits(const struct sk_buff *skb, 
int offset,
 EXPORT_SYMBOL(skb_copy_and_csum_bits);
 
 #ifdef CONFIG_NET_CRC32C
-u32 skb_crc32c(const struct sk_buff *skb, int offset, int len, u32 crc)
+/**
+ *     skb_crc32c_full - CRC32C a range of an skb
+ *     @skb: buffer to read
+ *     @offset: offset in @skb to start at
+ *     @len: number of bytes to include
+ *     @crcp: the CRC to continue from; on success, updated with the range
+ *
+ *     Return: 0 on success, -EINVAL if @len is negative, or -EFAULT if @skb
+ *     has unreadable frags in the range or runs out before @len does.  On
+ *     failure *@crcp is not changed.
+ */
+int skb_crc32c_full(const struct sk_buff *skb, int offset, int len, u32 *crcp)
 {
        int start = skb_headlen(skb);
        int i, copy = start - offset;
        struct sk_buff *frag_iter;
+       u32 crc = *crcp;
+       int err;
+
+       if (unlikely(len < 0)) {
+               DEBUG_NET_WARN_ON_ONCE(1);
+               return -EINVAL;
+       }
 
        if (copy > 0) {
                copy = min(copy, len);
                crc = crc32c(crc, skb->data + offset, copy);
                len -= copy;
                if (len == 0)
-                       return crc;
+                       goto out;
                offset += copy;
        }
 
-       if (WARN_ON_ONCE(!skb_frags_readable(skb)))
-               return 0;
+       if (!skb_frags_readable(skb))
+               return -EFAULT;
 
        for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
                int end;
@@ -3829,7 +3847,7 @@ u32 skb_crc32c(const struct sk_buff *skb, int offset, int 
len, u32 crc)
                        }
                        len -= copy;
                        if (len == 0)
-                               return crc;
+                               goto out;
                        offset += copy;
                }
                start = end;
@@ -3844,19 +3862,36 @@ u32 skb_crc32c(const struct sk_buff *skb, int offset, 
int len, u32 crc)
                copy = end - offset;
                if (copy > 0) {
                        copy = min(copy, len);
-                       crc = skb_crc32c(frag_iter, offset - start, copy, crc);
+                       err = skb_crc32c_full(frag_iter, offset - start, copy,
+                                             &crc);
+                       if (err)
+                               return err;
                        len -= copy;
                        if (len == 0)
-                               return crc;
+                               goto out;
                        offset += copy;
                }
                start = end;
        }
        if (unlikely(len)) {
                DEBUG_NET_WARN_ON_ONCE(1);
-               return 0;
+               return -EFAULT;
        }
+out:
+       *crcp = crc;
+       return 0;
+}
+EXPORT_SYMBOL(skb_crc32c_full);
 
+/* skb_crc32c() has no way to report failure, so it returns 0, which is a
+ * valid CRC.  Use skb_crc32c_full() in new code.
+ */
+u32 skb_crc32c(const struct sk_buff *skb, int offset, int len, u32 crc)
+{
+       if (unlikely(skb_crc32c_full(skb, offset, len, &crc))) {
+               WARN_ON_ONCE(!skb_frags_readable(skb));
+               return 0;
+       }
        return crc;
 }
 EXPORT_SYMBOL(skb_crc32c);

-- 
2.55.0




 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.