|
[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
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |