When security_bprm_check runs, the arg and env strings for the exec have been copied into bprm->mm. The new address space has not been associated yet with a task_struct until exec_mmap(), so existing BPF user memory helpers can only read from the calling task's old address space. Add bpf_copy_from_user_mm() and bpf_copy_from_user_mm_str() kfuncs. Both take a struct mm_struct pointer directly, allowing callers to access trusted address spaces that are not associated with a task_struct. bpf_copy_from_user_mm() has similar semantics to bpf_copy_from_user_task(). bpf_copy_from_user_mm_str() copies one NUL-terminated string and returns its size including the NUL terminator. It accepts BPF_F_PAD_ZEROS to clear unused destination bytes on success. Register both kfuncs with the common BPF_EVENTS kfunc set and mark them KF_SLEEPABLE because accessing the remote address space can fault. The next patch marks linux_binprm->mm as trusted-or-null, allowing BPF LSM programs attached to security_bprm_check to read arguments beginning at bprm->p after a NULL check and reject an exec based on its command-line arguments. Signed-off-by: Anastasios Papagiannis --- kernel/bpf/helpers.c | 2 + kernel/trace/bpf_trace.c | 83 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c index 6388b6b23e49..9ba3362832cd 100644 --- a/kernel/bpf/helpers.c +++ b/kernel/bpf/helpers.c @@ -4931,6 +4931,8 @@ BTF_ID_FLAGS(func, bpf_iter_kmem_cache_destroy, KF_ITER_DESTROY | KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_local_irq_save) BTF_ID_FLAGS(func, bpf_local_irq_restore) #ifdef CONFIG_BPF_EVENTS +BTF_ID_FLAGS(func, bpf_copy_from_user_mm, KF_SLEEPABLE) +BTF_ID_FLAGS(func, bpf_copy_from_user_mm_str, KF_SLEEPABLE) BTF_ID_FLAGS(func, bpf_probe_read_user_dynptr) BTF_ID_FLAGS(func, bpf_probe_read_kernel_dynptr) BTF_ID_FLAGS(func, bpf_probe_read_user_str_dynptr) diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c index 891897f8a1b3..8931b3b2fc9d 100644 --- a/kernel/trace/bpf_trace.c +++ b/kernel/trace/bpf_trace.c @@ -24,6 +24,7 @@ #include #include #include +#include #include @@ -3619,6 +3620,88 @@ __bpf_kfunc int bpf_send_signal_task(struct task_struct *task, int sig, enum pid return bpf_send_signal_common(sig, type, task, value); } +/** + * bpf_copy_from_user_mm() - Copy data from an address space + * @dst: Destination address, in kernel space + * @dst__sz: Number of bytes to copy + * @unsafe_ptr__ign: Source address in the address space + * @mm: Address space to copy from + * @flags: Reserved for future use; must be zero + * + * Copies data from the user address space associated with @mm. The destination + * is zeroed if the requested number of bytes cannot be copied in full. + * + * Return: 0 on success, -EINVAL if @flags is non-zero, or -EFAULT if the copy + * fails or is partial. + */ +__bpf_kfunc int bpf_copy_from_user_mm(void *dst, u32 dst__sz, + const void __user *unsafe_ptr__ign, + struct mm_struct *mm, u64 flags) +{ + int ret; + + if (unlikely(flags)) + return -EINVAL; + + if (unlikely(!dst__sz)) + return 0; + + ret = access_remote_vm(mm, (unsigned long)unsafe_ptr__ign, + dst, dst__sz, 0); + if (ret == dst__sz) + return 0; + + memset(dst, 0, dst__sz); + return ret < 0 ? ret : -EFAULT; +} + +/** + * bpf_copy_from_user_mm_str() - Copy a string from an address space + * @dst: Destination address, in kernel space. This buffer must be + * at least @dst__sz bytes long + * @dst__sz: Maximum number of bytes to copy, including the trailing NUL + * @unsafe_ptr__ign: Source address in the address space + * @mm: Address space to copy from + * @flags: The only supported flag is BPF_F_PAD_ZEROS + * + * Copies a NUL-terminated string from the user address space associated with + * @mm. If the string is too long, @dst is still NUL-terminated unless @dst__sz + * is zero. + * + * If BPF_F_PAD_ZEROS is set, the unused portion of @dst is cleared on success + * and all of @dst is cleared on failure. + * + * Return: The number of copied bytes including the NUL terminator on success, + * or a negative error code on failure. + */ +__bpf_kfunc int bpf_copy_from_user_mm_str(void *dst, u32 dst__sz, + const void __user *unsafe_ptr__ign, + struct mm_struct *mm, u64 flags) +{ + int ret; + + if (unlikely(flags & ~BPF_F_PAD_ZEROS)) + return -EINVAL; + + if (unlikely(!dst__sz)) + return 0; + + ret = copy_remote_mm_str(mm, (unsigned long)unsafe_ptr__ign, + dst, dst__sz, 0); + if (ret < 0) + goto err; + + if (flags & BPF_F_PAD_ZEROS) + memset(dst + ret, 0, dst__sz - ret); + + return ret + 1; + +err: + if (flags & BPF_F_PAD_ZEROS) + memset(dst, 0, dst__sz); + return ret; +} + __bpf_kfunc int bpf_probe_read_user_dynptr(const struct bpf_dynptr *dptr, u64 off, u64 size, const void __user *unsafe_ptr__ign) { -- 2.55.0