summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorStefan Metzmacher <metze@samba.org>2026-04-07 19:03:15 +0300
committerChristian Brauner <brauner@kernel.org>2026-05-11 13:25:31 +0300
commit2eef8b32e4c84caa927495f1d9bc9529d2bc5ac6 (patch)
tree2701c98fd943be603cbc3e0d5f089b039da84980 /include
parentdb0493512931fe1e5a71612e6a358df1aa22d80c (diff)
downloadlinux-2eef8b32e4c84caa927495f1d9bc9529d2bc5ac6.tar.xz
uaccess: add copy_struct_{from,to}_bounce_buffer() helpers
These are similar to copy_struct_{from,to}_user() but operate on kernel buffers instead of user buffers. They can be used when there is a temporary bounce buffer used, e.g. in msg_control or similar places. It allows us to have the same logic to handle old vs. current and current vs. new structures in the same compatible way. copy_struct_from_sockptr() will also be able to use copy_struct_from_bounce_buffer() for the kernel case as follow us patch. I'll use this in my IPPROTO_SMBDIRECT work, but maybe it will also be useful for others... IPPROTO_QUIC will likely also use it. Cc: Dmitry Safonov <0x7f454c46@gmail.com> Cc: Dmitry Safonov <dima@arista.com> Cc: Francesco Ruggeri <fruggeri@arista.com> Cc: Salam Noureddine <noureddine@arista.com> Cc: David Ahern <dsahern@kernel.org> Cc: David S. Miller <davem@davemloft.net> Cc: Michal Luczaj <mhal@rbox.co> Cc: David Wei <dw@davidwei.uk> Cc: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Cc: Luiz Augusto von Dentz <luiz.dentz@gmail.com> Cc: Marcel Holtmann <marcel@holtmann.org> Cc: Xin Long <lucien.xin@gmail.com> Cc: Eric Dumazet <edumazet@google.com> Cc: Kuniyuki Iwashima <kuniyu@google.com> Cc: Paolo Abeni <pabeni@redhat.com> Cc: Willem de Bruijn <willemb@google.com> Cc: Neal Cardwell <ncardwell@google.com> Cc: Jakub Kicinski <kuba@kernel.org> Cc: Simon Horman <horms@kernel.org> Cc: Aleksa Sarai <cyphar@cyphar.com> Cc: Christian Brauner <brauner@kernel.org> CC: Kees Cook <keescook@chromium.org> Cc: netdev@vger.kernel.org Cc: linux-bluetooth@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Stefan Metzmacher <metze@samba.org> Link: https://patch.msgid.link/f29570914590c50b9b6f451eb3a38d0fe1d954df.1775576651.git.metze@samba.org Signed-off-by: Christian Brauner <brauner@kernel.org>
Diffstat (limited to 'include')
-rw-r--r--include/linux/uaccess.h63
1 files changed, 63 insertions, 0 deletions
diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h
index 09a09cc4aac2..e4a64976f1c5 100644
--- a/include/linux/uaccess.h
+++ b/include/linux/uaccess.h
@@ -518,6 +518,69 @@ copy_struct_to_user(void __user *dst, size_t usize, const void *src,
return 0;
}
+static __always_inline void
+__copy_struct_generic_bounce_buffer(void *dst, size_t dstsize,
+ const void *src, size_t srcsize,
+ bool *ignored_trailing)
+{
+ size_t size = min(dstsize, srcsize);
+ size_t rest = max(dstsize, srcsize) - size;
+
+ /* Deal with trailing bytes. */
+ if (dstsize > srcsize)
+ memset(dst + size, 0, rest);
+ if (ignored_trailing)
+ *ignored_trailing = dstsize < srcsize &&
+ memchr_inv(src + size, 0, rest) != NULL;
+ /* Copy the interoperable parts of the struct. */
+ memcpy(dst, src, size);
+}
+
+/**
+ * This is like copy_struct_from_user(), but the
+ * src buffer was already copied into a kernel
+ * bounce buffer, so it will never return -EFAULT.
+ */
+static __always_inline __must_check int
+copy_struct_from_bounce_buffer(void *dst, size_t dstsize,
+ const void *src, size_t srcsize)
+{
+ bool ignored_trailing;
+
+ /* Double check if ksize is larger than a known object size. */
+ if (WARN_ON_ONCE(dstsize > __builtin_object_size(dst, 1)))
+ return -E2BIG;
+
+ __copy_struct_generic_bounce_buffer(dst, dstsize,
+ src, srcsize,
+ &ignored_trailing);
+ if (unlikely(ignored_trailing))
+ return -E2BIG;
+
+ return 0;
+}
+
+/**
+ * This is like copy_struct_to_user(), but the
+ * dst buffer is a kernel bounce buffer instead
+ * of a direct userspace buffer, so it will never return -EFAULT.
+ */
+static __always_inline __must_check int
+copy_struct_to_bounce_buffer(void *dst, size_t dstsize,
+ const void *src,
+ size_t srcsize,
+ bool *ignored_trailing)
+{
+ /* Double check if srcsize is larger than a known object size. */
+ if (WARN_ON_ONCE(srcsize > __builtin_object_size(src, 1)))
+ return -E2BIG;
+
+ __copy_struct_generic_bounce_buffer(dst, dstsize,
+ src, srcsize,
+ ignored_trailing);
+ return 0;
+}
+
bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size);
long copy_from_kernel_nofault(void *dst, const void *src, size_t size);