aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2018-07-23 17:47:59 +0200
committerJason A. Donenfeld <Jason@zx2c4.com>2018-07-23 18:14:15 +0200
commit89a2aab8669f4fa9469ee982c04e50fb4b527aaf (patch)
treec6b43cd7c3e2ff002a8cd5d158c83d8f204cc3d3
parentMerge tag 'drm-fixes-2018-07-20' of git://anongit.freedesktop.org/drm/drm (diff)
downloadlinux-dev-89a2aab8669f4fa9469ee982c04e50fb4b527aaf.tar.xz
linux-dev-89a2aab8669f4fa9469ee982c04e50fb4b527aaf.zip
sk_buff: introduce skb_memzero_explicitjd/skb_memzero_explicit
This walks through every fragment and page of a skb, similar to skb_to_sgvec, and calls memzero_explicit on every bit of associated memory, to ensure that there are no secrets. This is meant to be called before freeing, for special skbs that might contain secrets, such as those used by the cryptographic API or by certain cryptographic functions over Netlink. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
-rw-r--r--include/linux/skbuff.h1
-rw-r--r--net/core/skbuff.c44
2 files changed, 45 insertions, 0 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 610a201126ee..0f8b0433b943 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -1051,6 +1051,7 @@ int __must_check skb_to_sgvec_nomark(struct sk_buff *skb, struct scatterlist *sg
int offset, int len);
int __must_check skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg,
int offset, int len);
+void skb_memzero_explicit(struct sk_buff *skb);
int skb_cow_data(struct sk_buff *skb, int tailbits, struct sk_buff **trailer);
int __skb_pad(struct sk_buff *skb, int pad, bool free_on_error);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 8e51f8555e11..2e573935a7b9 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4016,6 +4016,50 @@ __skb_to_sgvec(struct sk_buff *skb, struct scatterlist *sg, int offset, int len,
return elt;
}
+static void __skb_memzero_explicit(struct sk_buff *skb, unsigned int recursion_level)
+{
+ struct sk_buff *sub_skb;
+ int i;
+
+ if (unlikely(recursion_level >= 24)) {
+ pr_warn("skb not entirely zeroed because of overly complex geometry\n");
+ return;
+ }
+
+ memzero_explicit(skb->data, skb_headlen(skb));
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; ++i) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+ void *addr = skb_frag_address_safe(frag);
+ unsigned int len = skb_frag_size(frag);
+
+ if (addr) {
+ memzero_explicit(addr, len);
+ } else {
+ addr = kmap_atomic(skb_frag_page(frag));
+ if (!addr) {
+ pr_warn("skb not entirely zeroed because of impossible page mapping\n");
+ continue;
+ }
+ memzero_explicit(addr + frag->page_offset, len);
+ kunmap_atomic(addr);
+ }
+ }
+
+ skb_walk_frags(skb, sub_skb)
+ __skb_memzero_explicit(sub_skb, recursion_level + 1);
+}
+
+/**
+ * skb_memzero_explicit - Zero out all allocated bytes of skb to hide secrets
+ * @skb: Socket buffer containing the memory to be zerored
+ */
+void skb_memzero_explicit(struct sk_buff *skb)
+{
+ __skb_memzero_explicit(skb, 0);
+}
+EXPORT_SYMBOL_GPL(skb_memzero_explicit);
+
/**
* skb_to_sgvec - Fill a scatter-gather list from a socket buffer
* @skb: Socket buffer containing the buffers to be mapped