In PCuABI, when copying a block of memory to/from userspace containing capabilities/pointers, the copy_{to,from}_user_with_ptr variants need to be used to ensure capabilities are preserved in full.
Introduce bpf specific helper methods/macros to support this.
Since compat64 syscalls do not contain capabilities, the _with_ptr variants are not required in the in_compat64_syscall() case.
Signed-off-by: Zachary Leaf zachary.leaf@arm.com --- include/linux/bpf.h | 8 ++++++++ include/linux/bpfptr.h | 16 ++++++++++++++++ 2 files changed, 24 insertions(+)
diff --git a/include/linux/bpf.h b/include/linux/bpf.h index e056737bc823..4f04dad82b90 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -67,6 +67,14 @@ extern bool bpf_global_ma_set; __bpf_put_uattr(x, (union compat_bpf_attr __user *)uattr, to_field) : \ __bpf_put_uattr(x, (union bpf_attr __user *)uattr, to_field))
+#define bpf_copy_to_user_with_ptr(dest, src, size) \ + (in_compat64_syscall() ? copy_to_user(dest, src, size) \ + : copy_to_user_with_ptr(dest, src, size)) + +#define bpf_copy_from_user_with_ptr(dest, src, size) \ + (in_compat64_syscall() ? copy_from_user(dest, src, size) \ + : copy_from_user_with_ptr(dest, src, size)) + #define bpf_field_exists(uattr_size, field) \ (in_compat_syscall() ? \ (uattr_size >= offsetofend(union compat_bpf_attr, field)) : \ diff --git a/include/linux/bpfptr.h b/include/linux/bpfptr.h index 7fdf9692d76e..c85b3a121dc5 100644 --- a/include/linux/bpfptr.h +++ b/include/linux/bpfptr.h @@ -3,6 +3,7 @@ #ifndef _LINUX_BPFPTR_H #define _LINUX_BPFPTR_H
+#include <linux/compat.h> #include <linux/mm.h> #include <linux/sockptr.h>
@@ -62,11 +63,26 @@ static inline int copy_from_bpfptr_offset(void *dst, bpfptr_t src, return copy_from_kernel_nofault(dst, src.kernel + offset, size); }
+static inline int copy_from_bpfptr_offset_with_ptr(void *dst, bpfptr_t src, + size_t offset, size_t size) +{ + if (!bpfptr_is_kernel(src)) + return copy_from_user_with_ptr(dst, src.user + offset, size); + return copy_from_kernel_nofault(dst, src.kernel + offset, size); +} + static inline int copy_from_bpfptr(void *dst, bpfptr_t src, size_t size) { return copy_from_bpfptr_offset(dst, src, 0, size); }
+static inline int copy_from_bpfptr_with_ptr(void *dst, bpfptr_t src, size_t size) +{ + return (in_compat64_syscall() + ? copy_from_bpfptr_offset(dst, src, 0, size) + : copy_from_bpfptr_offset_with_ptr(dst, src, 0, size)); +} + static inline int copy_to_bpfptr_offset(bpfptr_t dst, size_t offset, const void *src, size_t size) {