Introduce wrapper lttng_copy_struct_from_user
authorMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Thu, 24 Mar 2022 15:10:06 +0000 (11:10 -0400)
committerMathieu Desnoyers <mathieu.desnoyers@efficios.com>
Mon, 15 Jul 2024 21:01:43 +0000 (17:01 -0400)
Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Change-Id: I67e3d0249bbfdb5bc6e9d469119220a1fe3f03fd

include/wrapper/uaccess.h

index 4ccb26a006397350b604cfc7a33cbb072c7dcfe2..eb1e4b20f0771d1d653b812e5de15a33660cb213 100644 (file)
 
 #endif /* LTTNG_LINUX_VERSION_CODE >= LTTNG_KERNEL_VERSION(5,0,0) */
 
+#if LTTNG_LINUX_VERSION_CODE >= LTTNG_KERNEL_VERSION(5,4,0)
+static __always_inline __must_check int
+lttng_copy_struct_from_user(void *dst, size_t ksize, const void __user *src,
+                     size_t usize)
+{
+       return copy_struct_from_user(dst, ksize, src, usize);
+}
+#else /* LTTNG_LINUX_VERSION_CODE >= LTTNG_KERNEL_VERSION(5,4,0) */
+/**
+ * lttng_check_zeroed_user: check if a userspace buffer only contains zero bytes
+ * @from: Source address, in userspace.
+ * @size: Size of buffer.
+ *
+ * This is effectively shorthand for "memchr_inv(from, 0, size) == NULL" for
+ * userspace addresses (and is more efficient because we don't care where the
+ * first non-zero byte is).
+ *
+ * Returns:
+ *  * 0: There were non-zero bytes present in the buffer.
+ *  * 1: The buffer was full of zero bytes.
+ *  * -EFAULT: access to userspace failed.
+ */
+int lttng_check_zeroed_user(const void __user *from, size_t size)
+{
+       unsigned long val;
+       uintptr_t align = (uintptr_t) from % sizeof(unsigned long);
+       int ret;
+
+       if (unlikely(size == 0))
+               return 1;
+
+       from -= align;
+       size += align;
+
+       if (!lttng_access_ok(VERIFY_READ, from, size)
+               return -EFAULT;
+
+       ret = get_user(val, (unsigned long __user *) from);
+       if (ret)
+               return ret;
+       if (align)
+               val &= ~aligned_byte_mask(align);
+
+       while (size > sizeof(unsigned long)) {
+               if (unlikely(val))
+                       goto done;
+
+               from += sizeof(unsigned long);
+               size -= sizeof(unsigned long);
+
+               ret = get_user(val, (unsigned long __user *) from);
+               if (ret)
+                       return ret;
+       }
+
+       if (size < sizeof(unsigned long))
+               val &= aligned_byte_mask(size);
+
+done:
+       return (val == 0);
+}
+
+static __always_inline __must_check int
+lttng_copy_struct_from_user(void *dst, size_t ksize, const void __user *src,
+                     size_t usize)
+{
+       size_t size = min(ksize, usize);
+       size_t rest = max(ksize, usize) - size;
+
+       /* Deal with trailing bytes. */
+       if (usize < ksize) {
+               memset(dst + size, 0, rest);
+       } else if (usize > ksize) {
+               int ret = lttng_check_zeroed_user(src + size, rest);
+               if (ret <= 0)
+                       return ret ?: -E2BIG;
+       }
+       /* Copy the interoperable parts of the struct. */
+       if (copy_from_user(dst, src, size))
+               return -EFAULT;
+       return 0;
+}
+#endif /* LTTNG_LINUX_VERSION_CODE >= LTTNG_KERNEL_VERSION(5,4,0) */
+
 #endif /* _LTTNG_WRAPPER_UACCESS_H */
This page took 0.02715 seconds and 4 git commands to generate.