[PATCH net-next 06/15] net: skbuff: add skb_crc32c_full() that reports failure
flat view
HOTtoday
From: Josef Bacik <josef@toxicpanda.com>
Date: 2026-10-19 13:51:19
Also in:
xen-devel
Subsystem:
networking [general], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
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@toxicpanda.com>
---
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