|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [PATCH net-next 02/15] net: skbuff: add skb_checksum_full() that reports failure
skb_checksum() can't fail. When the range runs into unreadable frags
or past the end of the skb it returns 0, which is also a valid
checksum, so its callers can't tell the sum from a failure. For the
receive-side validation helpers that means a packet whose payload was
never checksummed can pass, and commit 20ddd0fd2fcd ("net: skbuff:
don't BUG() on leftover length in skb_checksum() and friends") made the
past-the-end case one of those instead of a BUG().
Add skb_checksum_full(), which adds the range's sum to *@csump and
returns 0, or returns -EFAULT for unreadable frags or a range past the
end of the skb, and -EINVAL for a negative length, leaving *@csump
alone. It's __must_check so a caller can't ignore the failure, and it
recurses into frag_list members through itself so a failure there
isn't lost either.
skb_checksum() becomes a wrapper around it that behaves as before for
the callers that haven't been converted: it returns 0 on failure and
warns once if that was because of unreadable frags. The following
patches convert the core users, and the rest will follow per subsystem.
Assisted-by: LLM
Signed-off-by: Josef Bacik <josef@xxxxxxxxxxxxxx>
---
include/linux/skbuff.h | 2 ++
net/core/skbuff.c | 56 +++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 48 insertions(+), 10 deletions(-)
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 09db32f70b53..969b1c3269ec 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -4279,6 +4279,8 @@ int skb_copy_bits(const struct sk_buff *skb, int offset,
void *to, int len);
int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len);
int __must_check skb_copy_and_csum_bits(const struct sk_buff *skb, int offset,
u8 *to, int len, __wsum *csum);
+int __must_check skb_checksum_full(const struct sk_buff *skb, int offset,
+ int len, __wsum *csump);
int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset,
struct pipe_inode_info *pipe, unsigned int len,
unsigned int flags);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index a2b52a5a61a9..061c61422a38 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3545,12 +3545,31 @@ int skb_store_bits(struct sk_buff *skb, int offset,
const void *from, int len)
EXPORT_SYMBOL(skb_store_bits);
/* Checksum skb data. */
-__wsum skb_checksum(const struct sk_buff *skb, int offset, int len, __wsum
csum)
+/**
+ * skb_checksum_full - checksum a range of an skb
+ * @skb: buffer to checksum
+ * @offset: offset in @skb to start at
+ * @len: number of bytes to checksum
+ * @csump: the sum to add to; on success the range's sum is added to it
+ *
+ * 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 *@csump is not changed.
+ */
+int skb_checksum_full(const struct sk_buff *skb, int offset, int len,
+ __wsum *csump)
{
int start = skb_headlen(skb);
int i, copy = start - offset;
struct sk_buff *frag_iter;
+ __wsum csum = *csump;
int pos = 0;
+ int err;
+
+ if (unlikely(len < 0)) {
+ DEBUG_NET_WARN_ON_ONCE(1);
+ return -EINVAL;
+ }
/* Checksum header. */
if (copy > 0) {
@@ -3558,13 +3577,13 @@ __wsum skb_checksum(const struct sk_buff *skb, int
offset, int len, __wsum csum)
copy = len;
csum = csum_partial(skb->data + offset, copy, csum);
if ((len -= copy) == 0)
- return csum;
+ goto out;
offset += copy;
pos = 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;
@@ -3593,7 +3612,7 @@ __wsum skb_checksum(const struct sk_buff *skb, int
offset, int len, __wsum csum)
}
if (!(len -= copy))
- return csum;
+ goto out;
offset += copy;
}
start = end;
@@ -3606,14 +3625,17 @@ __wsum skb_checksum(const struct sk_buff *skb, int
offset, int len, __wsum csum)
end = start + frag_iter->len;
if ((copy = end - offset) > 0) {
- __wsum csum2;
+ __wsum csum2 = 0;
+
if (copy > len)
copy = len;
- csum2 = skb_checksum(frag_iter, offset - start, copy,
- 0);
+ err = skb_checksum_full(frag_iter, offset - start, copy,
+ &csum2);
+ if (err)
+ return err;
csum = csum_block_add(csum, csum2, pos);
if ((len -= copy) == 0)
- return csum;
+ goto out;
offset += copy;
pos += copy;
}
@@ -3621,9 +3643,23 @@ __wsum skb_checksum(const struct sk_buff *skb, int
offset, int len, __wsum csum)
}
if (unlikely(len)) {
DEBUG_NET_WARN_ON_ONCE(1);
- return 0;
+ return -EFAULT;
}
+out:
+ *csump = csum;
+ return 0;
+}
+EXPORT_SYMBOL(skb_checksum_full);
+/* skb_checksum() has no way to report failure, so it returns 0, which is a
+ * valid checksum. Use skb_checksum_full() in new code.
+ */
+__wsum skb_checksum(const struct sk_buff *skb, int offset, int len, __wsum
csum)
+{
+ if (unlikely(skb_checksum_full(skb, offset, len, &csum))) {
+ WARN_ON_ONCE(!skb_frags_readable(skb));
+ return 0;
+ }
return csum;
}
EXPORT_SYMBOL(skb_checksum);
--
2.55.0
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |