Skip to contentSkip to conversation

Fix copy_from_user_nofault()

PATCH In review Florian Lehner 3 participants 5 messages Apr 10, 2023 – Apr 13, 2023

Subsystems (6)
lore
Share & tools
lore
#0 lore raw

v2 cover Fix copy_from_user_nofault()

The original patch got submitted by Alexei Starovoitov with [0] and fixes issues that got also reported in [1].

This resubmission adds !pagefault_disabled() to the check in check_heap_object().

Changelog:
v1->v2: Addressed comments from Alexei Starovoitov
- move pagefault_disabled() check into first patch
- keep __copy_from_user_inatomic() in copy_from_user_nofault()

[0] https://lore.kernel.org/all/[email protected]/
[1] https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1033398

Alexei Starovoitov (2):
mm: Fix copy_from_user_nofault().
perf: Fix arch_perf_out_copy_user().

 arch/x86/include/asm/perf_event.h |  2 --
 arch/x86/lib/Makefile             |  2 +-
 arch/x86/lib/usercopy.c           | 55 -------------------------------
 kernel/events/internal.h          | 16 +--------
 mm/maccess.c                      | 52 ++++++++++++++++++++++++-----
 mm/usercopy.c                     |  2 +-
 6 files changed, 47 insertions(+), 82 deletions(-)
 delete mode 100644 arch/x86/lib/usercopy.c
Signature · 1 line
2.39.2
#1 lore raw

PATCH v2 1/2 mm: Fix copy_from_user_nofault().

From: Alexei Starovoitov <[email protected]>

There are several issues with copy_from_user_nofault():

- access_ok() is designed for user context only and for that reason it has WARN_ON_IN_IRQ() which triggers when bpf, kprobe, eprobe and perf on ppc are calling it from irq.

- it's missing nmi_uaccess_okay() which is a nop on all architectures except x86 where it's required.
The comment in arch/x86/mm/tlb.c explains the details why it's necessary. Calling copy_from_user_nofault() from bpf, [ke]probe without this check is not safe.

- __copy_from_user_inatomic() under CONFIG_HARDENED_USERCOPY is calling check_object_size()->__check_object_size()->check_heap_object()->find_vmap_area()->spin_lock() which is not safe to do from bpf, [ke]probe and perf due to potential deadlock.

Fix all three issues. At the end the copy_from_user_nofault() becomes equivalent to copy_from_user_nmi() from safety point of view with a difference in the return value.

---
 mm/maccess.c  | 16 +++++++++++-----
 mm/usercopy.c |  2 +-
 2 files changed, 12 insertions(+), 6 deletions(-)
mm/maccess.c +11 -5
index 074f6b086671..518a25667323 100644
@@ -5,6 +5,7 @@
 #include <linux/export.h>
 #include <linux/mm.h>
 #include <linux/uaccess.h>
+#include <asm/tlb.h>
 
 bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src,
 		size_t size)
@@ -113,11 +114,16 @@ long strncpy_from_kernel_nofault(char *dst, const void *unsafe_addr, long count)
 long copy_from_user_nofault(void *dst, const void __user *src, size_t size)
 {
 	long ret = -EFAULT;
-	if (access_ok(src, size)) {
-		pagefault_disable();
-		ret = __copy_from_user_inatomic(dst, src, size);
-		pagefault_enable();
-	}
+
+	if (!__access_ok(src, size))
+		return ret;
+
+	if (!nmi_uaccess_okay())
+		return ret;
+
+	pagefault_disable();
+	ret = __copy_from_user_inatomic(dst, src, size);
+	pagefault_enable();
 
 	if (ret)
 		return -EFAULT;
mm/usercopy.c +1 -1
index 4c3164beacec..83c164aba6e0 100644
@@ -173,7 +173,7 @@ static inline void check_heap_object(const void *ptr, unsigned long n,
 		return;
 	}
 
-	if (is_vmalloc_addr(ptr)) {
+	if (is_vmalloc_addr(ptr) && !pagefault_disabled()) {
 		struct vmap_area *area = find_vmap_area(addr);
 
 		if (!area)
Signature · 1 line
2.39.2
#2 lore raw

PATCH v2 2/2 perf: Fix arch_perf_out_copy_user().

In reply to Florian Lehner

From: Alexei Starovoitov <[email protected]>

There are several issues with arch_perf_out_copy_user(). On x86 it's the same as copy_from_user_nmi() and all is good, but on other archs:

- __access_ok() is missing.
Only on m68k, s390, parisc, sparc64 archs this function returns 'true'. Other archs must call it before user memory access.
- nmi_uaccess_okay() is missing.
- __copy_from_user_inatomic() issues under CONFIG_HARDENED_USERCOPY.

The latter two issues existed in copy_from_user_nofault() as well and were fixed in the previous patch.

This patch copies comments from copy_from_user_nmi() into mm/maccess.c and splits copy_from_user_nofault() into copy_from_user_nmi() that returns number of not copied bytes and copy_from_user_nofault() that returns -EFAULT or zero.
With that copy_from_user_nmi() becomes generic and is used by perf on all architectures.

---
 arch/x86/include/asm/perf_event.h |  2 --
 arch/x86/lib/Makefile             |  2 +-
 arch/x86/lib/usercopy.c           | 55 -------------------------------
 kernel/events/internal.h          | 16 +--------
 mm/maccess.c                      | 48 ++++++++++++++++++++++-----
 5 files changed, 41 insertions(+), 82 deletions(-)
 delete mode 100644 arch/x86/lib/usercopy.c
arch/x86/include/asm/perf_event.h +0 -2
index 8fc15ed5e60b..b1e27ca28563 100644
@@ -598,6 +598,4 @@ static __always_inline void perf_lopwr_cb(bool lopwr_in)
  static inline void amd_pmu_disable_virt(void) { }
 #endif
 
-#define arch_perf_out_copy_user copy_from_user_nmi
-
 #endif /* _ASM_X86_PERF_EVENT_H */
arch/x86/lib/Makefile +1 -1
index 4f1a40a86534..e85937696afd 100644
@@ -42,7 +42,7 @@ clean-files := inat-tables.c
 obj-$(CONFIG_SMP) += msr-smp.o cache-smp.o
 
 lib-y := delay.o misc.o cmdline.o cpu.o
-lib-y += usercopy_$(BITS).o usercopy.o getuser.o putuser.o
+lib-y += usercopy_$(BITS).o getuser.o putuser.o
 lib-y += memcpy_$(BITS).o
 lib-y += pc-conf-reg.o
 lib-$(CONFIG_ARCH_HAS_COPY_MC) += copy_mc.o copy_mc_64.o
arch/x86/lib/usercopy.c deleted +0 -55

55 lines not on this page · raw message

kernel/events/internal.h +1 -15
index 5150d5f84c03..62fe2089a1f9 100644
@@ -190,21 +190,7 @@ memcpy_skip(void *dst, const void *src, unsigned long n)
 
 DEFINE_OUTPUT_COPY(__output_skip, memcpy_skip)
 
-#ifndef arch_perf_out_copy_user
-#define arch_perf_out_copy_user arch_perf_out_copy_user
-
-static inline unsigned long
-arch_perf_out_copy_user(void *dst, const void *src, unsigned long n)
-{
-	unsigned long ret;
-
-	pagefault_disable();
-	ret = __copy_from_user_inatomic(dst, src, n);
-	pagefault_enable();
-
-	return ret;
-}
-#endif
+#define arch_perf_out_copy_user copy_from_user_nmi
 
 DEFINE_OUTPUT_COPY(__output_copy_user, arch_perf_out_copy_user)
 
mm/maccess.c +39 -9

67 lines not on this page · raw message

Signature · 1 line
2.39.2
#3 lore raw

Re: [v2 bpf-next 2/2] perf: Fix arch_perf_out_copy_user().

Quoted 8 lines · Florian Lehner
kernel/events/internal.h +0 -6
-#ifndef arch_perf_out_copy_user
-#define arch_perf_out_copy_user arch_perf_out_copy_user
-
-static inline unsigned long
-arch_perf_out_copy_user(void *dst, const void *src, unsigned long n)
-{

kernel/events/internal.h memcpy_skip(void *dst, const void *src, unsigned long n) · line 193

Quoted 10 lines
kernel/events/internal.h +1 -9
-       unsigned long ret;
-
-       pagefault_disable();
-       ret = __copy_from_user_inatomic(dst, src, n);
-       pagefault_enable();
-
-       return ret;
-}
-#endif
+#define arch_perf_out_copy_user copy_from_user_nmi

This fails to build on aarch64. See BPF CI:
https://github.com/kernel-patches/bpf/actions/runs/4683969153/jobs/8299612449

Easy to fix, but I'd like to get an Ack from x86 folks for the 2nd patch.

I've applied the first patch to bpf-next, since it fixes the real issue for kernels with hardened_usercopy=y

#4 lore raw
In reply to Florian Lehner
This series was applied to bpf/bpf-next.git (master) by Alexei Starovoitov <[email protected]>:

Hello:

This series was applied to bpf/bpf-next.git (master) by Alexei Starovoitov <[email protected]>:

Quoted 6 lines · On Mon, 10 Apr 2023 19:43:43 +0200 you wrote:

The original patch got submitted by Alexei Starovoitov with [0] and fixes issues that got also reported in [1].

This resubmission adds !pagefault_disabled() to the check in check_heap_object().

Quoted 6 lines

Changelog:
v1->v2: Addressed comments from Alexei Starovoitov
- move pagefault_disabled() check into first patch
- keep __copy_from_user_inatomic() in copy_from_user_nofault()

[...]

Here is the summary with links:
- [v2,bpf-next,1/2] mm: Fix copy_from_user_nofault().

    https://git.kernel.org/bpf/bpf-next/c/d319f344561d

- [v2,bpf-next,2/2] perf: Fix arch_perf_out_copy_user(). (no matching commit)

You are awesome, thank you!

Signature · 2 lines
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html