uaccess: Convert small fixed size copy_{to/from}_user() to scoped user access
Subsystems (76)
Christophe Leroy (CS GROUP)
reviewercover uaccess: Convert small fixed size copy_{to/from}_user() to scoped user access
A lot of copy_from_user() and copy_to_user() perform copies of small fixed size pieces of data between kernel and userspace, and don't care about partial copies.
copy_from_user() and copy_to_user() are big functions optimised for copying large amount of data, with cache management, etc ... This is often overkill for small copies that could just be inlined instead.
What makes things a bit more tricky is that those copy functions are designed to handle partial copies in case of page fault. But among the 6000 callers of those functions, only 2% really care about the quantity of no-copied data that those functions return. All other ones fails as soon as the returned value is not 0, returning -EACCESS.
So first step in this series is to introduce variants called copy_from_user_partial() and copy_to_user_partial() which will be called by the 2% users that care about the partial copy, then the original copy_from_user() and copy_to_user() are changed to return -EFAULT when the copy fails.
Then the second step is to implement copy of small fixed-size data with scoped user access instead of calling the arch specific heavy user copy functions.
Patch 5, can be split in different patches for each archicture or subsystem, but let's get a first feedback and agree on the principle.
Christophe Leroy (CS GROUP) (9):
uaccess: Split check_zeroed_user() out of usercopy.c
uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce
ifdefery
x86/umip: Be stricter in fixup_umip_exception()
uaccess: Introduce copy_{to/from}_user_partial()
uaccess: Switch to copy_{to/from}_user_partial() when relevant
uaccess: Change copy_{to/from}_user to return -EFAULT
x86: Add unsafe_copy_from_user()
arm64: Add unsafe_copy_from_user()
uaccess: Convert small fixed size copy_{to/from}_user() to scoped user
access
arch/alpha/Kconfig | 1 +
arch/alpha/kernel/osf_sys.c | 4 +-
arch/alpha/kernel/termios.c | 2 +-
arch/arc/include/asm/uaccess.h | 3 -
arch/arc/kernel/disasm.c | 2 +-
arch/arm/include/asm/uaccess.h | 2 -
arch/arm64/include/asm/gcs.h | 2 +-
arch/arm64/include/asm/uaccess.h | 30 +++--
arch/arm64/kernel/signal32.c | 2 +-
arch/csky/Kconfig | 1 +
arch/hexagon/include/asm/uaccess.h | 3 -
arch/loongarch/include/asm/uaccess.h | 3 -
arch/m68k/include/asm/uaccess.h | 3 -
arch/microblaze/include/asm/uaccess.h | 2 -
arch/mips/include/asm/uaccess.h | 3 -
arch/mips/kernel/rtlx.c | 8 +-
arch/mips/kernel/vpe.c | 2 +-
arch/nios2/include/asm/uaccess.h | 2 -
arch/openrisc/include/asm/uaccess.h | 2 -
arch/parisc/include/asm/uaccess.h | 3 -
arch/powerpc/Kconfig | 1 +
arch/powerpc/kvm/book3s_64_mmu_hv.c | 4 +-
arch/powerpc/kvm/book3s_64_mmu_radix.c | 4 +-
arch/powerpc/kvm/book3s_hv.c | 2 +-
arch/riscv/Kconfig | 1 +
arch/riscv/kernel/signal.c | 2 +-
arch/s390/include/asm/idals.h | 8 +-
arch/s390/include/asm/uaccess.h | 3 -
arch/sh/include/asm/uaccess.h | 2 -
arch/sparc/include/asm/uaccess_32.h | 3 -
arch/sparc/include/asm/uaccess_64.h | 2 -
arch/sparc/kernel/termios.c | 2 +-
arch/um/include/asm/uaccess.h | 3 -
arch/um/kernel/process.c | 2 +-
arch/x86/Kconfig | 1 +
arch/x86/include/asm/uaccess.h | 29 ++++-
arch/x86/kernel/umip.c | 2 +-
arch/x86/lib/insn-eval.c | 2 +-
arch/x86/um/signal.c | 2 +-
arch/xtensa/include/asm/uaccess.h | 2 -
drivers/android/binder_alloc.c | 2 +-
drivers/comedi/comedi_fops.c | 4 +-
drivers/dma/idxd/cdev.c | 2 +-
drivers/firmware/efi/test/efi_test.c | 2 +-
drivers/fsi/fsi-scom.c | 2 +-
.../amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 2 +-
drivers/gpu/drm/i915/gt/intel_sseu.c | 4 +-
drivers/gpu/drm/i915/i915_gem.c | 4 +-
drivers/hwtracing/intel_th/msu.c | 2 +-
drivers/misc/ibmvmc.c | 2 +-
drivers/misc/vmw_vmci/vmci_host.c | 2 +-
drivers/most/most_cdev.c | 2 +-
drivers/net/ieee802154/ca8210.c | 4 +-
drivers/net/wireless/ath/wil6210/debugfs.c | 2 +-
.../intel/iwlwifi/pcie/gen1_2/trans.c | 2 +-
drivers/net/wireless/ti/wlcore/debugfs.c | 2 +-
drivers/ps3/ps3-lpm.c | 2 +-
drivers/s390/crypto/zcrypt_api.h | 4 +-
drivers/spi/spidev.c | 2 +-
.../staging/media/atomisp/pci/atomisp_cmd.c | 8 +-
drivers/tty/tty_ioctl.c | 14 +--
drivers/tty/vt/vc_screen.c | 4 +-
drivers/usb/gadget/function/f_hid.c | 4 +-
drivers/usb/gadget/function/f_printer.c | 2 +-
drivers/vfio/vfio_iommu_type1.c | 4 +-
drivers/xen/xenbus/xenbus_dev_frontend.c | 2 +-
fs/namespace.c | 2 +-
fs/ocfs2/dlmfs/dlmfs.c | 2 +-
fs/proc/base.c | 4 +-
include/asm-generic/uaccess.h | 2 -
include/linux/bpfptr.h | 2 +-
include/linux/sockptr.h | 4 +-
include/linux/uaccess.h | 107 ++++++++++++++----
ipc/msg.c | 8 +-
ipc/sem.c | 8 +-
ipc/shm.c | 18 +--
kernel/regset.c | 2 +-
kernel/sys.c | 4 +-
lib/Kconfig | 3 +
lib/Makefile | 4 +-
lib/kfifo.c | 8 +-
lib/{usercopy.c => usercheck.c} | 22 ----
lib/usercopy.c | 66 -----------
mm/kasan/kasan_test_c.c | 4 +-
mm/memory.c | 2 +-
net/x25/af_x25.c | 2 +-
rust/helpers/uaccess.c | 6 +-
sound/pci/emu10k1/emufx.c | 4 +-
sound/pci/rme9652/hdsp.c | 6 +-
sound/soc/intel/avs/probes.c | 6 +-
sound/soc/sof/compress.c | 12 +-
sound/soc/sof/sof-client-probes.c | 6 +-
92 files changed, 269 insertions(+), 288 deletions(-)
copy lib/{usercopy.c => usercheck.c} (73%)Signature · 1 line
2.49.0
Christophe Leroy (CS GROUP)
reviewerRFC PATCH 1/9 uaccess: Split check_zeroed_user() out of usercopy.c
Until commit f5a1a536fa14 ("lib: introduce copy_struct_from_user() helper"), lib/usercopy.c was containing only the out-line version of user copy fonctions.
That commit added function check_zeroed_user() into the same file. Move that function into a new file named usercheck.c, so that next patch can change usercopy.c build to a conditional build.
---
lib/Makefile | 1 +
lib/{usercopy.c => usercheck.c} | 22 ------------
lib/usercopy.c | 62 ---------------------------------
3 files changed, 1 insertion(+), 84 deletions(-)
copy lib/{usercopy.c => usercheck.c} (73%)lib/Makefile +1 -0
@@ -59,6 +59,7 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \ percpu-refcount.o rhashtable.o base64.o \ once.o refcount.o rcuref.o usercopy.o errseq.o bucket_locks.o \ generic-radix-tree.o bitmap-str.o +obj-y += usercheck.o obj-y += string_helpers.o obj-y += hexdump.o obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o
lib/usercheck.c renamed from lib/usercopy.c +0 -22
@@ -2,32 +2,10 @@#include <linux/compiler.h> #include <linux/errno.h> #include <linux/export.h> -#include <linux/fault-inject-usercopy.h> -#include <linux/instrumented.h> #include <linux/kernel.h> -#include <linux/nospec.h> -#include <linux/string.h> #include <linux/uaccess.h> #include <linux/wordpart.h> -/* out-of-line parts */ - -#if !defined(INLINE_COPY_FROM_USER ) -unsigned long _copy_from_user (void *to,const void __user *from,unsigned long n) -{ -return _inline_copy_from_user (to, from, n); -} -EXPORT_SYMBOL (_copy_from_user); -#endif - -#if !defined(INLINE_COPY_TO_USER ) -unsigned long _copy_to_user (void __user *to,const void *from,unsigned long n) -{ -return _inline_copy_to_user (to, from, n); -} -EXPORT_SYMBOL (_copy_to_user); -#endif -/** * check_zeroed_user: check if a userspace buffer only contains zero bytes * @from: Source address, in userspace.
lib/usercopy.c +0 -62
71 lines not on this page · raw message
Signature · 1 line
2.49.0
Christophe Leroy (CS GROUP)
reviewerRFC PATCH 2/9 uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce ifdefery
In reply to Christophe Leroy (CS GROUP)Among the 21 architectures supported by the kernel, 16 define both INLINE_COPY_TO_USER and INLINE_COPY_FROM_USER while the 5 other ones don't define any of the two.
To simplify and reduce risk of mistakes, convert them to a single kconfig item named CONFIG_ARCH_WANTS_NOINLINE_COPY which will be selected by the 5 architectures that don't want inlined copy.
To minimise complication in a later patch, also remove ifdefery and replace it with IS_ENABLED().
--- arch/alpha/Kconfig | 1 + arch/arc/include/asm/uaccess.h | 3 --- arch/arm/include/asm/uaccess.h | 2 -- arch/arm64/include/asm/uaccess.h | 3 --- arch/csky/Kconfig | 1 + arch/hexagon/include/asm/uaccess.h | 3 --- arch/loongarch/include/asm/uaccess.h | 3 --- arch/m68k/include/asm/uaccess.h | 3 --- arch/microblaze/include/asm/uaccess.h | 2 -- arch/mips/include/asm/uaccess.h | 3 --- arch/nios2/include/asm/uaccess.h | 2 -- arch/openrisc/include/asm/uaccess.h | 2 -- arch/parisc/include/asm/uaccess.h | 3 --- arch/powerpc/Kconfig | 1 + arch/riscv/Kconfig | 1 + arch/s390/include/asm/uaccess.h | 3 --- arch/sh/include/asm/uaccess.h | 2 -- arch/sparc/include/asm/uaccess_32.h | 3 --- arch/sparc/include/asm/uaccess_64.h | 2 -- arch/um/include/asm/uaccess.h | 3 --- arch/x86/Kconfig | 1 + arch/xtensa/include/asm/uaccess.h | 2 -- include/asm-generic/uaccess.h | 2 -- include/linux/uaccess.h | 32 ++++++++++++--------------- lib/Kconfig | 3 +++ lib/Makefile | 3 ++- lib/usercopy.c | 4 ---- rust/helpers/uaccess.c | 2 +- 28 files changed, 25 insertions(+), 70 deletions(-)
arch/alpha/Kconfig +1 -0
@@ -11,6 +11,7 @@ config ALPHA select ARCH_NO_PREEMPT select ARCH_NO_SG_CHAIN select ARCH_USE_CMPXCHG_LOCKREF + select ARCH_WANTS_NOINLINE_COPY_USER select FORCE_PCI select PCI_DOMAINS if PCI select PCI_SYSCALL if PCI
arch/arc/include/asm/uaccess.h +0 -3
@@ -628,9 +628,6 @@ static inline unsigned long __clear_user(void __user *to, unsigned long n)return res; } -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER -#define __clear_user __clear_user#include <asm-generic/uaccess.h>
arch/arm/include/asm/uaccess.h +0 -2
@@ -616,8 +616,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) }#define __clear_user (addr, n) (memset((void __force *)addr, 0, n), 0)#endif -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER static inline unsigned long __must_check clear_user (void __user *to,unsigned long n) {
arch/arm64/include/asm/uaccess.h +0 -3
@@ -456,9 +456,6 @@ do { \unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ }while (0 ) -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER - extern unsigned long __must_check __arch_clear_user(void __user *to, unsigned long n);static inline unsigned long __must_check __clear_user (void __user *to,unsigned long n) {
arch/csky/Kconfig +1 -0
@@ -40,6 +40,7 @@ config CSKY select ARCH_NEED_CMPXCHG_1_EMU select ARCH_WANT_FRAME_POINTERS if !CPU_CK610 && $(cc-option,-mbacktrace) select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT + select ARCH_WANTS_NOINLINE_COPY_USER select COMMON_CLK select CLKSRC_MMIO select CSKY_MPINTC if CPU_CK860
arch/hexagon/include/asm/uaccess.h +0 -3
@@ -26,9 +26,6 @@ unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n);unsigned long raw_copy_to_user (void __user *to,const void *from,unsigned long n); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER -__kernel_size_t __clear_user_hexagon (void __user *dest,unsigned long count);#define __clear_user (a, s) __clear_user_hexagon((a), (s))
arch/loongarch/include/asm/uaccess.h +0 -3
@@ -292,9 +292,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)return __copy_user ((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER -/* * __clear_user: - Zero a block of memory in user space, with less checking. * @addr: Destination address, in user space.
arch/m68k/include/asm/uaccess.h +0 -3
@@ -377,9 +377,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)return __constant_copy_to_user (to, from, n);return __generic_copy_to_user (to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER -#define __get_kernel_nofault (dst, src, type, err_label) \ do { \ type *__gk_dst = (type *)(dst); \
arch/microblaze/include/asm/uaccess.h +0 -2
@@ -250,8 +250,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) {return __copy_tofrom_user (to, (__force const void __user *)from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER /* * Copy a null terminated string from userspace.
arch/mips/include/asm/uaccess.h +0 -3
@@ -433,9 +433,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)return __cu_len_r; } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER -extern __kernel_size_t __bzero (void __user *addr,__kernel_size_t size);/*
arch/nios2/include/asm/uaccess.h +0 -2
@@ -57,8 +57,6 @@ extern unsigned long raw_copy_from_user(void *to, const void __user *from, unsigned long n);extern unsigned long raw_copy_to_user (void __user *to,const void *from,unsigned long n); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER extern long strncpy_from_user (char *__to,const char __user *__from,long __len);
arch/openrisc/include/asm/uaccess.h +0 -2
@@ -218,8 +218,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long size) {return __copy_tofrom_user ((__force void *)to, from, size); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER extern unsigned long __clear_user (void __user *addr,unsigned long size);
arch/parisc/include/asm/uaccess.h +0 -3
@@ -197,7 +197,4 @@ unsigned long __must_check raw_copy_to_user(void __user *dst, const void *src, unsigned long len);unsigned long __must_check raw_copy_from_user (void *dst,const void __user *src,unsigned long len); -#define INLINE_COPY_TO_USER -#define INLINE_COPY_FROM_USER -#endif /* __PARISC_UACCESS_H */
arch/powerpc/Kconfig +1 -0
@@ -187,6 +187,7 @@ config PPC select ARCH_WANT_LD_ORPHAN_WARN select ARCH_WANT_OPTIMIZE_DAX_VMEMMAP if PPC_RADIX_MMU select ARCH_WANTS_MODULES_DATA_IN_VMALLOC if PPC_BOOK3S_32 || PPC_8xx + select ARCH_WANTS_NOINLINE_COPY_USER select ARCH_WEAK_RELEASE_ACQUIRE select AUDIT_ARCH_COMPAT_GENERIC select BINFMT_ELF
arch/riscv/Kconfig +1 -0
@@ -88,6 +88,7 @@ config RISCV select ARCH_WANT_OPTIMIZE_DAX_VMEMMAP select ARCH_WANT_OPTIMIZE_HUGETLB_VMEMMAP select ARCH_WANTS_NO_INSTR + select ARCH_WANTS_NOINLINE_COPY_USER if MMU select ARCH_WANTS_THP_SWAP if HAVE_ARCH_TRANSPARENT_HUGEPAGE select ARCH_WEAK_RELEASE_ACQUIRE if ARCH_USE_QUEUED_SPINLOCKS select BINFMT_FLAT_NO_DATA_START_OFFSET if !MMU
arch/s390/include/asm/uaccess.h +0 -3
@@ -30,9 +30,6 @@ void debug_user_asce(int exit);#define uaccess_kmsan_or_inline __always_inline#endif -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER -static uaccess_kmsan_or_inline __must_check unsigned long raw_copy_from_user (void *to,const void __user *from,unsigned long size) {
arch/sh/include/asm/uaccess.h +0 -2
@@ -95,8 +95,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n) {return __copy_user ((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER /* * Clear the area and return remaining number of bytes
arch/sparc/include/asm/uaccess_32.h +0 -3
@@ -190,9 +190,6 @@ static inline unsigned long raw_copy_from_user(void *to, const void __user *fromreturn __copy_user ((__force void __user *) to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER -static inline unsigned long __clear_user (void __user *addr,unsigned long size) {unsigned long ret;
arch/sparc/include/asm/uaccess_64.h +0 -2
@@ -231,8 +231,6 @@ unsigned long __must_check raw_copy_from_user(void *to,unsigned long __must_check raw_copy_to_user (void __user *to,const void *from,unsigned long size); -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER unsigned long __must_check raw_copy_in_user(void __user *to, const void __user *from,
arch/um/include/asm/uaccess.h +0 -3
@@ -27,9 +27,6 @@ static inline int __access_ok(const void __user *ptr, unsigned long size);#define __access_ok __access_ok#define __clear_user __clear_user -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER -#include <asm-generic/uaccess.h> static inline int __access_ok (const void __user *ptr,unsigned long size)
arch/x86/Kconfig +1 -0
@@ -143,6 +143,7 @@ config X86 select ARCH_WANTS_CLOCKSOURCE_READ_INLINE if X86_64 select ARCH_WANTS_DYNAMIC_TASK_STRUCT select ARCH_WANTS_NO_INSTR + select ARCH_WANTS_NOINLINE_COPY_USER select ARCH_WANT_GENERAL_HUGETLB select ARCH_WANT_HUGE_PMD_SHARE if X86_64 select ARCH_WANT_LD_ORPHAN_WARN
arch/xtensa/include/asm/uaccess.h +0 -2
@@ -237,8 +237,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)prefetch (from);return __xtensa_copy_user ((__force void *)to, from, n); } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER /* * We need to return the number of bytes not cleared. Our memset()
include/asm-generic/uaccess.h +0 -2
@@ -91,8 +91,6 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long n)memcpy ((void __force *)to, from, n); return 0; } -#define INLINE_COPY_FROM_USER -#define INLINE_COPY_TO_USER #endif /* CONFIG_UACCESS_MEMCPY */ /*
include/linux/uaccess.h +14 -18
@@ -83,8 +83,8 @@* with that. They should not be used directly; they are used to implement * the 6 functions (copy_{to,from}_user(), __copy_{to,from}_user_inatomic()) * that are used instead. Out of those, __... ones are inlined. Plain -* copy_{to,from}_user() might or might not be inlined. If you want them -* inlined, have asm/uaccess.h define INLINE_COPY_{TO,FROM}_USER. +* copy_{to,from}_user() might or might not be inlined. If you don't want them +* inlined, select CONFIG_ARCH_WANTS_NOINLINE_COPY_USER. * * NOTE: only copy_from_user() zero-pads the destination in case of short copy. * Neither __copy_from_user() nor __copy_from_user_inatomic() zero anything
@@ -157,8 +157,8 @@ __copy_to_user(void __user *to, const void *from, unsigned long n) }/* -* Architectures that #define INLINE_COPY_TO_USER use this function -* directly in the normal copy_to/from_user(), the other ones go +* Architectures that don't select CONFIG_ARCH_WANTS_NOINLINE_COPY_USER use +* this function directly in the normal copy_to/from_user(), the other ones go * through an extern _copy_to/from_user(), which expands the same code * here. */
@@ -190,10 +190,9 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n)memset (to + (n - res),0 , res);return res; } -#ifndef INLINE_COPY_FROM_USER +extern __must_check unsigned long _copy_from_user (void *,const void __user *,unsigned long ); -#endif static inline __must_check unsigned long _inline_copy_to_user (void __user *to,const void *from,unsigned long n)
@@ -207,21 +206,19 @@ _inline_copy_to_user(void __user *to, const void *from, unsigned long n) }return n; } -#ifndef INLINE_COPY_TO_USER +extern __must_check unsigned long _copy_to_user (void __user *,const void *,unsigned long ); -#endif static __always_inline unsigned long __must_check copy_from_user (void *to,const void __user *from,unsigned long n) {if (!check_copy_size (to, n, false))return n; -#ifdef INLINE_COPY_FROM_USER -return _inline_copy_from_user (to, from, n); -#else -return _copy_from_user (to, from, n); -#endif +if (IS_ENABLED (ARCH_WANTS_NOINLINE_COPY_USER )) +return _copy_from_user (to, from, n); +else +return _inline_copy_from_user (to, from, n); } static __always_inline unsigned long __must_check
@@ -230,11 +227,10 @@ copy_to_user(void __user *to, const void *from, unsigned long n)if (!check_copy_size (from, n, true))return n; -#ifdef INLINE_COPY_TO_USER -return _inline_copy_to_user (to, from, n); -#else -return _copy_to_user (to, from, n); -#endif +if (IS_ENABLED (ARCH_WANTS_NOINLINE_COPY_USER )) +return _copy_to_user (to, from, n); +else +return _inline_copy_to_user (to, from, n); }#ifndef copy_mc_to_kernel
lib/Kconfig +3 -0
@@ -68,6 +68,9 @@ config ARCH_HAS_STRNCPY_FROM_USER config ARCH_HAS_STRNLEN_USER bool +config ARCH_WANTS_NOINLINE_COPY_USER + bool + config GENERIC_STRNCPY_FROM_USER def_bool !ARCH_HAS_STRNCPY_FROM_USER
lib/Makefile +2 -1
@@ -57,9 +57,10 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \ list_sort.o uuid.o iov_iter.o clz_ctz.o \ bsearch.o find_bit.o llist.o lwq.o memweight.o kfifo.o \ percpu-refcount.o rhashtable.o base64.o \ - once.o refcount.o rcuref.o usercopy.o errseq.o bucket_locks.o \ + once.o refcount.o rcuref.o errseq.o bucket_locks.o \ generic-radix-tree.o bitmap-str.o obj-y += usercheck.o +obj-$(CONFIG_ARCH_WANTS_NOINLINE_COPY_USER) += usercopy.o obj-y += string_helpers.o obj-y += hexdump.o obj-$(CONFIG_TEST_HEXDUMP) += test_hexdump.o
lib/usercopy.c +0 -4
@@ -4,18 +4,14 @@/* out-of-line parts */ -#if !defined(INLINE_COPY_FROM_USER )unsigned long _copy_from_user (void *to,const void __user *from,unsigned long n) {return _inline_copy_from_user (to, from, n); }EXPORT_SYMBOL (_copy_from_user); -#endif -#if !defined(INLINE_COPY_TO_USER )unsigned long _copy_to_user (void __user *to,const void *from,unsigned long n) {return _inline_copy_to_user (to, from, n); }EXPORT_SYMBOL (_copy_to_user); -#endif
rust/helpers/uaccess.c +1 -1
@@ -14,7 +14,7 @@ rust_helper_copy_to_user(void __user *to, const void *from, unsigned long n)return copy_to_user (to, from, n); } -#ifdef INLINE_COPY_FROM_USER +#ifndef CONFIG_ARCH_WANTS_NOINLINE_COPY_USER __rust_helper unsigned long rust_helper__copy_from_user (void *to,const void __user *from,unsigned long n) {
Signature · 1 line
2.49.0
Re: [RFC PATCH v1 2/9] uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce ifdefery
Quoted 6 lines · Christophe Leroy (CS GROUP)
Among the 21 architectures supported by the kernel, 16 define both
INLINE_COPY_TO_USERandINLINE_COPY_FROM_USERwhile the 5 other ones don't define any of the two.To simplify and reduce risk of mistakes, convert them to a single kconfig item named
CONFIG_ARCH_WANTS_NOINLINE_COPYwhich will be
We've got a special word for it: outline. Can you name it CONFIG_OUTLINE_USERCOPY, or similar?
Quoted 6 lines
selected by the 5 architectures that don't want inlined copy.
To minimise complication in a later patch, also remove ifdefery and replace it with
IS_ENABLED().Signed-off-by Christophe Leroy (CS GROUP) <[email protected]>
Andrew has taken my consolidation patch for INLINE_COPY_USER:
https://lore.kernel.org/all/[email protected]/
Please base your series on top of it.
I'm not sure this patch is relevant to the goal of your series. Maybe send it separately?
Thanks,
Yury
Re: [RFC PATCH v1 2/9] uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce ifdefery
Quoted 9 lines · Yury Norov
Quoted 6 lines · Christophe Leroy (CS GROUP)
Among the 21 architectures supported by the kernel, 16 define both
INLINE_COPY_TO_USERandINLINE_COPY_FROM_USERwhile the 5 other ones don't define any of the two.To simplify and reduce risk of mistakes, convert them to a single kconfig item named
CONFIG_ARCH_WANTS_NOINLINE_COPYwhich will beWe've got a special word for it: outline. Can you name it
CONFIG_OUTLINE_USERCOPY, or similar?
You can't swap the "in" for "out" like this. "out of line" is the opposite of "inline" in this context, while "outline" means something different and unrelated.
~Andrew
Re: [RFC PATCH v1 2/9] uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce ifdefery
Quoted 6 lines · Andrew Cooper
Quoted 5 lines · Yury Norov
Quoted 4 lines · Christophe Leroy (CS GROUP)
Among the 21 architectures supported by the kernel, 16 define both
INLINE_COPY_TO_USERandINLINE_COPY_FROM_USERwhile the 5 other ones don't define any of the two.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
To simplify and reduce risk of mistakes, convert them to a single kconfig item named
CONFIG_ARCH_WANTS_NOINLINE_COPYwhich will beWe've got a special word for it: outline. Can you name it
CONFIG_OUTLINE_USERCOPY, or similar?You can't swap the "in" for "out" like this. "out of line" is the opposite of "inline" in this context, while "outline" means something different and unrelated.
Check KASAN_OUTLINE vs KASAN_INLINE for example
Re: [RFC PATCH v1 2/9] uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce ifdefery
Quoted 7 lines · Yury Norov
Quoted 6 lines · Andrew Cooper
Quoted 5 lines · Yury Norov
Quoted 4 lines · Christophe Leroy (CS GROUP)
Among the 21 architectures supported by the kernel, 16 define both
INLINE_COPY_TO_USERandINLINE_COPY_FROM_USERwhile the 5 other ones don't define any of the two.
Quoted 8 lines
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
To simplify and reduce risk of mistakes, convert them to a single kconfig item named
CONFIG_ARCH_WANTS_NOINLINE_COPYwhich will beWe've got a special word for it: outline. Can you name it
CONFIG_OUTLINE_USERCOPY, or similar?You can't swap the "in" for "out" like this. "out of line" is the opposite of "inline" in this context, while "outline" means something different and unrelated.
Check
KASAN_OUTLINEvsKASAN_INLINEfor example
Then I suggest it gets corrected before more examples try to copy this non-english.
~Andrew
Re: [RFC PATCH v1 2/9] uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce ifdefery
In reply to Andrew CooperQuoted 6 lines · Andrew Cooper
Quoted 5 lines · Yury Norov
Quoted 4 lines · Christophe Leroy (CS GROUP)
Among the 21 architectures supported by the kernel, 16 define both
INLINE_COPY_TO_USERandINLINE_COPY_FROM_USERwhile the 5 other ones don't define any of the two.
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
To simplify and reduce risk of mistakes, convert them to a single kconfig item named
CONFIG_ARCH_WANTS_NOINLINE_COPYwhich will beWe've got a special word for it: outline. Can you name it
CONFIG_OUTLINE_USERCOPY, or similar?You can't swap the "in" for "out" like this. "out of line" is the opposite of "inline" in this context, while "outline" means something different and unrelated.
Yeah. Technically much more correct for it is inline vs. functional. Not that that term won't be misunderstood as well :-)
Segher
Christophe Leroy (CS GROUP)
reviewerRFC PATCH 3/9 x86/umip: Be stricter in fixup_umip_exception()
In reply to Christophe Leroy (CS GROUP)fixup_umip_exception() calls copy_to_user() and checks whether the returned value is strictly positive.
A subsequent patch will change the return of copy_to_user() to return -EFAULT in case of error.
Change the test to checking that the result is not 0.
At the time being copy_to_user() return an unsigned value so 'strictly positive' is the same as 'not 0'.
--- arch/x86/kernel/umip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kernel/umip.c +1 -1
@@ -409,7 +409,7 @@ bool fixup_umip_exception(struct pt_regs *regs)return false; nr_copied =copy_to_user (uaddr, dummy_data, dummy_data_size); -if (nr_copied >0 ) { +if (nr_copied) {/* * If copy fails, send a signal and tell caller that * fault was fixed up.
Signature · 1 line
2.49.0
Christophe Leroy (CS GROUP)
reviewerRFC PATCH 4/9 uaccess: Introduce copy_{to/from}_user_partial()
In reply to Christophe Leroy (CS GROUP)Today there are approximately 3000 calls for copy_to_user() and 3000 calls to copy_from_user().
The majority of callers of copy_{to/from}_user() don't care about the return value, they only check whether it is 0 or not, and when it is not 0 they handle it as a -EACCES.
In order to allow better optimisation of copy_{to/from}_user() when the size of the copy is known at build time, create new fonctions named copy_{to/from}_user_partial() to be used by the few callers that are interested in partial copies and need to now how many bytes remain at the end of the copy.
For the time being it is just the same as copy_{to/from}_user().
--- include/linux/uaccess.h | 4 ++++ 1 file changed, 4 insertions(+)
include/linux/uaccess.h +4 -0
@@ -221,6 +221,8 @@ copy_from_user(void *to, const void __user *from, unsigned long n)return _inline_copy_from_user (to, from, n); } +#define copy_from_user_partial copy_from_user +static __always_inline unsigned long __must_check copy_to_user (void __user *to,const void *from,unsigned long n) {
@@ -233,6 +235,8 @@ copy_to_user(void __user *to, const void *from, unsigned long n)return _inline_copy_to_user (to, from, n); } +#define copy_to_user_partial copy_to_user +#ifndef copy_mc_to_kernel/* * Without arch opt-in this generic copy_mc_to_kernel() will not handle
Signature · 1 line
2.49.0
Geert Uytterhoeven
maintainerRe: [RFC PATCH v1 4/9] uaccess: Introduce copy_{to/from}_user_partial()
Hi Christophe,
Thanks for your patch!
Quoted 6 lines · Christophe Leroy (CS GROUP)
Today there are approximately 3000 calls for
copy_to_user()and 3000 calls tocopy_from_user().The majority of callers of copy_{to/from}_user() don't care about the return value, they only check whether it is 0 or not, and when it is not 0 they handle it as a
-EACCES.
I think the "a" can be dropped.
Quoted 2 lines
In order to allow better optimisation of copy_{to/from}_user() when the size of the copy is known at build time, create new fonctions
functions
Quoted 2 lines
named copy_{to/from}_user_partial() to be used by the few callers that are interested in partial copies and need to now how many
know
Quoted 5 lines
bytes remain at the end of the copy.
For the time being it is just the same as copy_{to/from}_user().
Signed-off-by Christophe Leroy (CS GROUP) <[email protected]>
Gr{oetje,eeting}s,
Geert
Signature · 5 lines
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected] In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds
Christophe Leroy (CS GROUP)
reviewerRFC PATCH 5/9 uaccess: Switch to copy_{to/from}_user_partial() when relevant
In reply to Christophe Leroy (CS GROUP)In a subsequent patch, copy_{to/from}_user() will be modified to return -EFAULT when copy fails.
Among the 6000 calls to copy_{to/from}_user(), around 2% rely on copy_{to/from}_user() doing partial copies and returning amount of not copied bytes. Change those users to copy_{to/from}_user_partial().
This change was done based on whether callers assign the returned value to a variable or just check whether the return value is 0 or not.
Several of them only use it for debug to print the amount of bytes not copied. Those could maybe be changed to stop reporting that amount and not be converted to partial copy.
Some not trivial handling might have been unecessarily converted. This is not a problem and they can be converted back later for better performance.
The callers where located with following commands then reviewed one by one:
sed -i s/"return copy_to_user("/"return copy_to_user_partial("/g `git grep -l "return copy_to_user("`
sed -i s/" = copy_to_user("/" = copy_to_user_partial("/g `git grep -l " = copy_to_user("`
sed -i s/" += copy_to_user("/" += copy_to_user_partial("/g `git grep -l " += copy_to_user("`
sed -i s/" -= copy_to_user("/" -= copy_to_user_partial("/g `git grep -l " -= copy_to_user("`Then the same was done with copy_from_user().
During the review, patterns like the following were rejected and kept as is:
- return copy_to_user(osf_stat, &tmp, sizeof(tmp)) ? -EFAULT : 0;
+ return copy_to_user_partial(osf_stat, &tmp, sizeof(tmp)) ? -EFAULT : 0;
--- arch/alpha/kernel/osf_sys.c | 4 ++-- arch/alpha/kernel/termios.c | 2 +- arch/arc/kernel/disasm.c | 2 +- arch/arm64/include/asm/gcs.h | 2 +- arch/arm64/kernel/signal32.c | 2 +- arch/mips/kernel/rtlx.c | 8 ++++---- arch/mips/kernel/vpe.c | 2 +- arch/powerpc/kvm/book3s_64_mmu_hv.c | 4 ++-- arch/powerpc/kvm/book3s_64_mmu_radix.c | 4 ++-- arch/powerpc/kvm/book3s_hv.c | 2 +- arch/riscv/kernel/signal.c | 2 +- arch/s390/include/asm/idals.h | 8 ++++---- arch/sparc/kernel/termios.c | 2 +- arch/um/kernel/process.c | 2 +- arch/x86/lib/insn-eval.c | 2 +- arch/x86/um/signal.c | 2 +- drivers/android/binder_alloc.c | 2 +- drivers/comedi/comedi_fops.c | 4 ++-- drivers/dma/idxd/cdev.c | 2 +- drivers/firmware/efi/test/efi_test.c | 2 +- drivers/fsi/fsi-scom.c | 2 +- .../amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 2 +- drivers/gpu/drm/i915/gt/intel_sseu.c | 4 ++-- drivers/gpu/drm/i915/i915_gem.c | 4 ++-- drivers/hwtracing/intel_th/msu.c | 2 +- drivers/misc/ibmvmc.c | 2 +- drivers/misc/vmw_vmci/vmci_host.c | 2 +- drivers/most/most_cdev.c | 2 +- drivers/net/ieee802154/ca8210.c | 4 ++-- drivers/net/wireless/ath/wil6210/debugfs.c | 2 +- .../wireless/intel/iwlwifi/pcie/gen1_2/trans.c | 2 +- drivers/net/wireless/ti/wlcore/debugfs.c | 2 +- drivers/ps3/ps3-lpm.c | 2 +- drivers/s390/crypto/zcrypt_api.h | 4 ++-- drivers/spi/spidev.c | 2 +- .../staging/media/atomisp/pci/atomisp_cmd.c | 8 ++++---- drivers/tty/tty_ioctl.c | 14 +++++++------- drivers/tty/vt/vc_screen.c | 4 ++-- drivers/usb/gadget/function/f_hid.c | 4 ++-- drivers/usb/gadget/function/f_printer.c | 2 +- drivers/vfio/vfio_iommu_type1.c | 4 ++-- drivers/xen/xenbus/xenbus_dev_frontend.c | 2 +- fs/namespace.c | 2 +- fs/ocfs2/dlmfs/dlmfs.c | 2 +- fs/proc/base.c | 4 ++-- include/linux/bpfptr.h | 2 +- include/linux/sockptr.h | 4 ++-- ipc/msg.c | 8 ++++---- ipc/sem.c | 8 ++++---- ipc/shm.c | 18 +++++++++--------- kernel/regset.c | 2 +- kernel/sys.c | 4 ++-- lib/kfifo.c | 8 ++++---- mm/kasan/kasan_test_c.c | 4 ++-- mm/memory.c | 2 +- net/x25/af_x25.c | 2 +- rust/helpers/uaccess.c | 4 ++-- sound/pci/emu10k1/emufx.c | 4 ++-- sound/pci/rme9652/hdsp.c | 6 +++--- sound/soc/intel/avs/probes.c | 6 +++--- sound/soc/sof/compress.c | 12 ++++++------ sound/soc/sof/sof-client-probes.c | 6 +++--- 62 files changed, 122 insertions(+), 122 deletions(-)
arch/alpha/kernel/osf_sys.c +2 -2
@@ -944,7 +944,7 @@ get_tv32(struct timespec64 *o, struct timeval32 __user *i)static inline long put_tv32 (struct timeval32 __user *o,struct timespec64 *i) { -return copy_to_user (o, &(struct timeval32 ){ +return copy_to_user_partial (o, &(struct timeval32 ){ .tv_sec = i->tv_sec, .tv_usec = i->tv_nsec /NSEC_PER_USEC },sizeof (struct timeval32 ));
@@ -953,7 +953,7 @@ put_tv32(struct timeval32 __user *o, struct timespec64 *i)static inline long put_tv_to_tv32 (struct timeval32 __user *o,struct __kernel_old_timeval *i) { -return copy_to_user (o, &(struct timeval32 ){ +return copy_to_user_partial (o, &(struct timeval32 ){ .tv_sec = i->tv_sec, .tv_usec = i->tv_usec},sizeof (struct timeval32 ));
arch/alpha/kernel/termios.c +1 -1
@@ -52,5 +52,5 @@ int kernel_termios_to_user_termio(struct termio __user *termio, v.c_cc[_VEOL2] = termios->c_cc[VEOL2 ]; v.c_cc[_VSWTC] = termios->c_cc[VSWTC ]; -return copy_to_user (termio, &v,sizeof (struct termio )); +return copy_to_user_partial (termio, &v,sizeof (struct termio )); }
arch/arc/kernel/disasm.c +1 -1
@@ -34,7 +34,7 @@ void __kprobes disasm_instr(unsigned long addr, struct disasm_state *state,/* This fetches the upper part of the 32 bit instruction * in both the cases of Little Endian or Big Endian configurations. */ if (userspace) { - bytes_not_copied =copy_from_user (ins_buf, + bytes_not_copied =copy_from_user_partial (ins_buf, (const void __user *) addr,8 );if (bytes_not_copied >6 ) goto fault;
arch/arm64/include/asm/gcs.h +1 -1
@@ -139,7 +139,7 @@ static inline u64 get_user_gcs(unsigned long __user *addr, int *err)/* Ensure previous GCS operation are visible before we read the page */ gcsb_dsync (); - ret =copy_from_user (&load, addr,sizeof (load)); + ret =copy_from_user_partial (&load, addr,sizeof (load));if (ret !=0 ) *err = ret;return load;
arch/arm64/kernel/signal32.c +1 -1
@@ -53,7 +53,7 @@ static inline int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set) cset.sig[0 ] = set->sig[0 ] &0xffffffffull ; cset.sig[1 ] = set->sig[0 ] >>32 ; -return copy_to_user (uset, &cset,sizeof (*uset)); +return copy_to_user_partial (uset, &cset,sizeof (*uset)); } static inline int get_sigset_t(sigset_t *set,
arch/mips/kernel/rtlx.c +4 -4
@@ -262,13 +262,13 @@ ssize_t rtlx_read(int index, void __user *buff, size_t count)/* then how much from the read pointer onwards */ fl =min (count, (size_t )lx->buffer_size - lx->lx_read); - failed =copy_to_user (buff, lx->lx_buffer + lx->lx_read, fl); + failed =copy_to_user_partial (buff, lx->lx_buffer + lx->lx_read, fl);if (failed) goto out;/* and if there is anything left at the beginning of the buffer */ if (count - fl) - failed =copy_to_user (buff + fl, lx->lx_buffer, count - fl); + failed =copy_to_user_partial (buff + fl, lx->lx_buffer, count - fl); out: count -= failed;
@@ -304,13 +304,13 @@ ssize_t rtlx_write(int index, const void __user *buffer, size_t count)/* first bit from write pointer to the end of the buffer, or count */ fl =min (count, (size_t ) rt->buffer_size - rt->rt_write); - failed =copy_from_user (rt->rt_buffer + rt->rt_write, buffer, fl); + failed =copy_from_user_partial (rt->rt_buffer + rt->rt_write, buffer, fl);if (failed) goto out;/* if there's any left copy to the beginning of the buffer */ if (count - fl) - failed =copy_from_user (rt->rt_buffer, buffer + fl, count - fl); + failed =copy_from_user_partial (rt->rt_buffer, buffer + fl, count - fl); out: count -= failed;
arch/mips/kernel/vpe.c +1 -1
@@ -854,7 +854,7 @@ static ssize_t vpe_write(struct file *file, const char __user *buffer,return -ENOMEM ; } - count -=copy_from_user (v->pbuffer + v->len, buffer, count); + count -=copy_from_user_partial (v->pbuffer + v->len, buffer, count);if (!count)return -EFAULT ;
arch/powerpc/kvm/book3s_64_mmu_hv.c +2 -2
@@ -2028,7 +2028,7 @@ static ssize_t debugfs_htab_read(struct file *file, char __user *buf, n = p->chars_left;if (n > len) n = len; - r =copy_to_user (buf, p->buf + p->buf_index, n); + r =copy_to_user_partial (buf, p->buf + p->buf_index, n); n -= r; p->chars_left -= n; p->buf_index += n;
@@ -2068,7 +2068,7 @@ static ssize_t debugfs_htab_read(struct file *file, char __user *buf, p->chars_left = n;if (n > len) n = len; - r =copy_to_user (buf, p->buf, n); + r =copy_to_user_partial (buf, p->buf, n); n -= r; p->chars_left -= n; p->buf_index = n;
arch/powerpc/kvm/book3s_64_mmu_radix.c +2 -2
@@ -1307,7 +1307,7 @@ static ssize_t debugfs_radix_read(struct file *file, char __user *buf, n = p->chars_left;if (n > len) n = len; - r =copy_to_user (buf, p->buf + p->buf_index, n); + r =copy_to_user_partial (buf, p->buf + p->buf_index, n); n -= r; p->chars_left -= n; p->buf_index += n;
@@ -1407,7 +1407,7 @@ static ssize_t debugfs_radix_read(struct file *file, char __user *buf, p->chars_left = n;if (n > len) n = len; - r =copy_to_user (buf, p->buf, n); + r =copy_to_user_partial (buf, p->buf, n); n -= r; p->chars_left -= n; p->buf_index = n;
arch/powerpc/kvm/book3s_hv.c +1 -1
@@ -2916,7 +2916,7 @@ static ssize_t debugfs_timings_read(struct file *file, char __user *buf,return 0 ;if (len > p->buflen - pos) len = p->buflen - pos; - n =copy_to_user (buf, p->buf + pos, len); + n =copy_to_user_partial (buf, p->buf + pos, len);if (n) {if (n == len)return -EFAULT ;
arch/riscv/kernel/signal.c +1 -1
@@ -139,7 +139,7 @@ static long __restore_v_state(struct pt_regs *regs, void __user *sc_vec)* Copy the whole vector content from user space datap. Use * copy_from_user to prevent information leak. */ -return copy_from_user (current->thread.vstate.datap, datap, riscv_v_vsize); +return copy_from_user_partial (current->thread.vstate.datap, datap, riscv_v_vsize); }static long save_cfiss_state (struct pt_regs *regs,void __user *sc_cfi)
arch/s390/include/asm/idals.h +4 -4
@@ -301,14 +301,14 @@ static inline size_t idal_buffer_to_user(struct idal_buffer *ib, void __user *toBUG_ON (count > ib->size);for (i =0 ; count >IDA_BLOCK_SIZE ; i++) { vaddr =dma64_to_virt (ib->data[i]); - left =copy_to_user (to, vaddr,IDA_BLOCK_SIZE ); + left =copy_to_user_partial (to, vaddr,IDA_BLOCK_SIZE );if (left)return left + count -IDA_BLOCK_SIZE ; to = (void __user *)to +IDA_BLOCK_SIZE ; count -=IDA_BLOCK_SIZE ; } vaddr =dma64_to_virt (ib->data[i]); -return copy_to_user (to, vaddr, count); +return copy_to_user_partial (to, vaddr, count); }/*
@@ -323,14 +323,14 @@ static inline size_t idal_buffer_from_user(struct idal_buffer *ib, const void __BUG_ON (count > ib->size);for (i =0 ; count >IDA_BLOCK_SIZE ; i++) { vaddr =dma64_to_virt (ib->data[i]); - left =copy_from_user (vaddr, from,IDA_BLOCK_SIZE ); + left =copy_from_user_partial (vaddr, from,IDA_BLOCK_SIZE );if (left)return left + count -IDA_BLOCK_SIZE ; from = (void __user *)from +IDA_BLOCK_SIZE ; count -=IDA_BLOCK_SIZE ; } vaddr =dma64_to_virt (ib->data[i]); - return copy_from_user(vaddr, from, count); + return copy_from_user_partial(vaddr, from, count); }#endif
arch/sparc/kernel/termios.c +1 -1
@@ -27,7 +27,7 @@ int kernel_termios_to_user_termio(struct termio __user *termio, v.c_cc[_VMIN] = termios->c_cc[VMIN ]; v.c_cc[_VTIME] = termios->c_cc[VTIME ]; } -return copy_to_user (termio, &v,sizeof (struct termio )); +return copy_to_user_partial (termio, &v,sizeof (struct termio )); } int user_termios_to_kernel_termios(struct ktermios *k,
arch/um/kernel/process.c +1 -1
@@ -252,7 +252,7 @@ EXPORT_SYMBOL(uml_strdup);int copy_from_user_proc (void *to,void __user *from,int size) { -return copy_from_user (to, from, size); +return copy_from_user_partial (to, from, size); }int singlestepping (void )
arch/x86/lib/insn-eval.c +1 -1
@@ -1512,7 +1512,7 @@ int insn_fetch_from_user(struct pt_regs *regs, unsigned char buf[MAX_INSN_SIZE])if (insn_get_effective_ip (regs, &ip))return -EINVAL ; - not_copied =copy_from_user (buf, (void __user *)ip,MAX_INSN_SIZE ); + not_copied =copy_from_user_partial (buf, (void __user *)ip,MAX_INSN_SIZE );return MAX_INSN_SIZE - not_copied; }
arch/x86/um/signal.c +1 -1
@@ -40,7 +40,7 @@ static int copy_sc_from_user(struct pt_regs *regs,/* Always make any pending restarted system calls return -EINTR */ current->restart_block.fn = do_no_restart_syscall; - err =copy_from_user (&sc, from,sizeof (sc)); + err =copy_from_user_partial (&sc, from,sizeof (sc));if (err)return err;
drivers/android/binder_alloc.c +1 -1
@@ -1346,7 +1346,7 @@ binder_alloc_copy_user_to_buffer(struct binder_alloc *alloc, buffer_offset, &pgoff); size =min_t (size_t, bytes,PAGE_SIZE - pgoff); kptr =kmap_local_page (page) + pgoff; - ret =copy_from_user (kptr, from, size); + ret =copy_from_user_partial (kptr, from, size);kunmap_local (kptr);if (ret)return bytes - size + ret;
drivers/comedi/comedi_fops.c +2 -2
@@ -2659,7 +2659,7 @@ static unsigned int comedi_buf_copy_to_user(struct comedi_subdevice *s,unsigned int copy_amount =min (n,PAGE_SIZE - offset);unsigned int uncopied; - uncopied =copy_to_user (dest, buf_page_list[page].virt_addr + + uncopied =copy_to_user_partial (dest, buf_page_list[page].virt_addr + offset, copy_amount); copy_amount -= uncopied; n -= copy_amount;
@@ -2687,7 +2687,7 @@ static unsigned int comedi_buf_copy_from_user(struct comedi_subdevice *s,unsigned int copy_amount =min (n,PAGE_SIZE - offset);unsigned int uncopied; - uncopied =copy_from_user (buf_page_list[page].virt_addr + + uncopied =copy_from_user_partial (buf_page_list[page].virt_addr + offset, src, copy_amount); copy_amount -= uncopied; n -= copy_amount;
drivers/dma/idxd/cdev.c +1 -1
@@ -751,7 +751,7 @@ int idxd_copy_cr(struct idxd_wq *wq, ioasid_t pasid, unsigned long addr,* to addr in the mm. */ kthread_use_mm (mm); - left =copy_to_user ((void __user *)addr + status_size, cr + status_size, + left =copy_to_user_partial ((void __user *)addr + status_size, cr + status_size, len - status_size);/* * Copy status only after the rest of completion record is copied
drivers/firmware/efi/test/efi_test.c +1 -1
@@ -133,7 +133,7 @@ copy_ucs2_to_user_len(efi_char16_t __user *dst, efi_char16_t *src, size_t len)if (!src)return 0 ; -return copy_to_user (dst, src, len); +return copy_to_user_partial (dst, src, len); }static long efi_runtime_get_variable (unsigned long arg)
drivers/fsi/fsi-scom.c +1 -1
@@ -332,7 +332,7 @@ static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,return rc; } - rc =copy_to_user (buf, &val, len); + rc =copy_to_user_partial (buf, &val, len);if (rc)dev_dbg (dev,"copy to user failed:%d\n" , rc);
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +1 -1
@@ -1346,7 +1346,7 @@ static ssize_t dp_sdp_message_debugfs_write(struct file *f, const char __user *b acrtc_state =to_dm_crtc_state (connector->base.state->crtc->state); - r =copy_from_user (data, buf, write_size); + r =copy_from_user_partial (data, buf, write_size); write_size -= r;
drivers/gpu/drm/i915/gt/intel_sseu.c +2 -2
@@ -114,7 +114,7 @@ int intel_sseu_copy_eumask_to_user(void __user *to, } } -return copy_to_user (to, eu_mask, len); +return copy_to_user_partial (to, eu_mask, len); }/**
@@ -146,7 +146,7 @@ int intel_sseu_copy_ssmask_to_user(void __user *to, } } -return copy_to_user (to, ss_mask, len); +return copy_to_user_partial (to, ss_mask, len); } static void gen11_compute_sseu_info(struct sseu_dev_info *sseu,
drivers/gpu/drm/i915/i915_gem.c +2 -2
@@ -291,7 +291,7 @@ gtt_user_read(struct io_mapping *mapping,io_mapping_unmap_atomic (vaddr);if (unwritten) { vaddr =io_mapping_map_wc (mapping, base,PAGE_SIZE ); - unwritten =copy_to_user (user_data, + unwritten =copy_to_user_partial (user_data, (void __force *)vaddr + offset, length);io_mapping_unmap (vaddr);
@@ -525,7 +525,7 @@ ggtt_write(struct io_mapping *mapping,io_mapping_unmap_atomic (vaddr);if (unwritten) { vaddr =io_mapping_map_wc (mapping, base,PAGE_SIZE ); - unwritten =copy_from_user ((void __force *)vaddr + offset, + unwritten =copy_from_user_partial ((void __force *)vaddr + offset, user_data, length);io_mapping_unmap (vaddr); }
drivers/hwtracing/intel_th/msu.c +1 -1
@@ -1457,7 +1457,7 @@ static unsigned long msc_win_to_user(void *data, void *src, size_t len)struct msc_win_to_user_struct *u = data;unsigned long ret; - ret =copy_to_user (u->buf + u->offset, src, len); + ret =copy_to_user_partial (u->buf + u->offset, src, len); u->offset += len - ret;return ret;
drivers/misc/ibmvmc.c +1 -1
@@ -1112,7 +1112,7 @@ static ssize_t ibmvmc_write(struct file *file, const char *buffer,while (c >0 ) { bytes =min_t (size_t, c, vmc_buffer->size); - bytes -=copy_from_user (buf, p, bytes); + bytes -=copy_from_user_partial (buf, p, bytes);if (!bytes) { ret = -EFAULT ; goto out;
drivers/misc/vmw_vmci/vmci_host.c +1 -1
@@ -213,7 +213,7 @@ static int drv_cp_harray_to_user(void __user *user_buf_uva, *user_buf_size = array_size *sizeof (*handles);if (*user_buf_size) - *retval =copy_to_user (user_buf_uva, + *retval =copy_to_user_partial (user_buf_uva,vmci_handle_arr_get_handles (handle_array), *user_buf_size);
drivers/most/most_cdev.c +1 -1
@@ -265,7 +265,7 @@ comp_read(struct file *filp, char __user *buf, size_t count, loff_t *offset) count, mbo->processed_length - c->mbo_offs); - not_copied =copy_to_user (buf, + not_copied =copy_to_user_partial (buf, mbo->virt_address + c->mbo_offs, to_copy);
drivers/net/ieee802154/ca8210.c +2 -2
@@ -2460,7 +2460,7 @@ static ssize_t ca8210_test_int_user_write(return -EBADE ; } - ret =copy_from_user (command, in_buf, len); + ret =copy_from_user_partial (command, in_buf, len);if (ret) { dev_err( &priv->spi->dev,
@@ -2548,7 +2548,7 @@ static ssize_t ca8210_test_int_user_read( cmdlen = fifo_buffer[1 ]; bytes_not_copied = cmdlen +2 ; - bytes_not_copied =copy_to_user (buf, fifo_buffer, bytes_not_copied); + bytes_not_copied =copy_to_user_partial (buf, fifo_buffer, bytes_not_copied);if (bytes_not_copied >0 ) { dev_err( &priv->spi->dev,
drivers/net/wireless/ath/wil6210/debugfs.c +1 -1
@@ -659,7 +659,7 @@ static ssize_t wil_read_file_ioblob(struct file *file, char __user *user_buf,wil_memcpy_fromio_32 (buf, (const void __iomem *) wil_blob->blob.data + aligned_pos, aligned_count); - ret =copy_to_user (user_buf, buf + unaligned_bytes, count); + ret =copy_to_user_partial (user_buf, buf + unaligned_bytes, count);wil_mem_access_unlock (wil);wil_pm_runtime_put (wil);
drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +1 -1
@@ -3060,7 +3060,7 @@ static bool iwl_write_to_user_buf(char __user *user_buf, ssize_t count,if (*size > buf_size_left) *size = buf_size_left; - *size -=copy_to_user (user_buf, buf, *size); + *size -=copy_to_user_partial (user_buf, buf, *size); *bytes_copied += *size;if (buf_size_left == *size)
drivers/net/wireless/ti/wlcore/debugfs.c +1 -1
@@ -1088,7 +1088,7 @@ static ssize_t dev_mem_read(struct file *file,mutex_unlock (&wl->mutex);if (ret ==0 ) { - ret =copy_to_user (user_buf, buf, bytes); + ret =copy_to_user_partial (user_buf, buf, bytes);if (ret < bytes) { bytes -= ret; *ppos += bytes;
drivers/ps3/ps3-lpm.c +1 -1
@@ -999,7 +999,7 @@ int ps3_lpm_copy_tb_to_user(unsigned long offset, void __user *buf,return result ==LV1_WRONG_STATE ? -EBUSY : -EINVAL ; } - result =copy_to_user (buf, lpm_priv->tb_cache, tmp); + result =copy_to_user_partial (buf, lpm_priv->tb_cache, tmp);if (result) { dev_dbg(sbd_core(), "%s:%u: 0x%llx bytes at 0x%p\n",
drivers/s390/crypto/zcrypt_api.h +2 -2
@@ -185,7 +185,7 @@ static inline unsigned long z_copy_from_user(bool userspace, unsigned long n) {if (likely (userspace)) -return copy_from_user (to, from, n); +return copy_from_user_partial (to, from, n);memcpy (to, (void __force *)from, n);return 0 ; }
@@ -196,7 +196,7 @@ static inline unsigned long z_copy_to_user(bool userspace, unsigned long n) {if (likely (userspace)) -return copy_to_user (to, from, n); +return copy_to_user_partial (to, from, n);memcpy ((void __force *)to, from, n);return 0 ; }
drivers/spi/spidev.c +1 -1
@@ -157,7 +157,7 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)if (status >0 ) {unsigned long missing; - missing =copy_to_user (buf, spidev->rx_buffer, status); + missing =copy_to_user_partial (buf, spidev->rx_buffer, status);if (missing == status) status = -EFAULT ;else
drivers/staging/media/atomisp/pci/atomisp_cmd.c +4 -4
@@ -1491,7 +1491,7 @@ int atomisp_gdc_cac_table(struct atomisp_sub_device *asd, int flag, }for (i =0 ; i <IA_CSS_MORPH_TABLE_NUM_PLANES ; i++) { - ret =copy_from_user (tab->coordinates_x[i], + ret =copy_from_user_partial (tab->coordinates_x[i], config->coordinates_x[i], config->height * config->width *sizeof (*config->coordinates_x[i]));
@@ -1502,7 +1502,7 @@ int atomisp_gdc_cac_table(struct atomisp_sub_device *asd, int flag,atomisp_css_morph_table_free (tab);return -EFAULT ; } - ret =copy_from_user (tab->coordinates_y[i], + ret =copy_from_user_partial (tab->coordinates_y[i], config->coordinates_y[i], config->height * config->width *sizeof (*config->coordinates_y[i]));
@@ -1709,7 +1709,7 @@ int atomisp_3a_stat(struct atomisp_sub_device *asd, int flag, config->exp_id = s3a_buf->s3a_data->exp_id; config->isp_config_id = s3a_buf->s3a_data->isp_config_id; - ret =copy_to_user (config->data, asd->params.s3a_user_stat->data, + ret =copy_to_user_partial (config->data, asd->params.s3a_user_stat->data, asd->params.s3a_output_bytes);if (ret) { dev_err(isp->dev, "copy to user failed: copied %lu bytes\n",
@@ -2031,7 +2031,7 @@ static unsigned int long copy_from_compatible(void *to, const void *from, unsigned long n, bool from_user) { if (from_user) - return copy_from_user(to, (void __user *)from, n); + return copy_from_user_partial(to, (void __user *)from, n); elsememcpy (to ,from ,n );return 0 ;
drivers/tty/tty_ioctl.c +7 -7
@@ -388,29 +388,29 @@ __weak int kernel_termios_to_user_termio(struct termio __user *termio, v.c_lflag = termios->c_lflag; v.c_line = termios->c_line;memcpy (v.c_cc, termios->c_cc,NCC ); -return copy_to_user (termio, &v,sizeof (struct termio )); +return copy_to_user_partial (termio, &v,sizeof (struct termio )); }#ifdef TCGETS2 __weak int user_termios_to_kernel_termios (struct ktermios *k,struct termios2 __user *u) { -return copy_from_user (k, u,sizeof (struct termios2 )); +return copy_from_user_partial (k, u,sizeof (struct termios2 )); }__weak int kernel_termios_to_user_termios (struct termios2 __user *u,struct ktermios *k) { -return copy_to_user (u, k,sizeof (struct termios2 )); +return copy_to_user_partial (u, k,sizeof (struct termios2 )); }__weak int user_termios_to_kernel_termios_1 (struct ktermios *k,struct termios __user *u) { -return copy_from_user (k, u,sizeof (struct termios )); +return copy_from_user_partial (k, u,sizeof (struct termios )); }__weak int kernel_termios_to_user_termios_1 (struct termios __user *u,struct ktermios *k) { -return copy_to_user (u, k,sizeof (struct termios )); +return copy_to_user_partial (u, k,sizeof (struct termios )); }#else
@@ -418,12 +418,12 @@ __weak int kernel_termios_to_user_termios_1(struct termios __user *u,__weak int user_termios_to_kernel_termios (struct ktermios *k,struct termios __user *u) { -return copy_from_user (k, u,sizeof (struct termios )); +return copy_from_user_partial (k, u,sizeof (struct termios )); }__weak int kernel_termios_to_user_termios (struct termios __user *u,struct ktermios *k) { -return copy_to_user (u, k,sizeof (struct termios )); +return copy_to_user_partial (u, k,sizeof (struct termios )); }#endif /* TCGETS2 */
drivers/tty/vt/vc_screen.c +2 -2
@@ -450,7 +450,7 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)*/ console_unlock (); - ret =copy_to_user (buf, con_buf + skip, this_round); + ret =copy_to_user_partial (buf, con_buf + skip, this_round);console_lock ();if (ret) {
@@ -630,7 +630,7 @@ vcs_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)* in the write data from userspace safely. */ console_unlock (); - ret =copy_from_user (con_buf, buf, this_round); + ret =copy_from_user_partial (con_buf, buf, this_round);console_lock ();if (ret) {
drivers/usb/gadget/function/f_hid.c +2 -2
@@ -349,7 +349,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,spin_unlock_irqrestore (&hidg->read_spinlock, flags);/* copy to user outside spinlock */ - count -=copy_to_user (buffer, req->buf + list->pos, count); + count -=copy_to_user_partial (buffer, req->buf + list->pos, count); list->pos += count;/*
@@ -410,7 +410,7 @@ static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer,spin_unlock_irqrestore (&hidg->read_spinlock, flags);if (tmp_buf !=NULL ) { - count -=copy_to_user (buffer, tmp_buf, count); + count -=copy_to_user_partial (buffer, tmp_buf, count);kfree (tmp_buf); }else { count = -ENOMEM ;
drivers/usb/gadget/function/f_printer.c +1 -1
@@ -525,7 +525,7 @@ printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)else size = len; - size -=copy_to_user (buf, current_rx_buf, size); + size -=copy_to_user_partial (buf, current_rx_buf, size); bytes_copied += size; len -= size; buf += size;
drivers/vfio/vfio_iommu_type1.c +2 -2
@@ -3173,7 +3173,7 @@ static int vfio_iommu_type1_dma_rw_chunk(struct vfio_iommu *iommu, vaddr = dma->vaddr + offset;if (write) { - *copied =copy_to_user ((void __user *)vaddr, data, + *copied =copy_to_user_partial ((void __user *)vaddr, data, count) ?0 : count;if (*copied && iommu->dirty_page_tracking) {unsigned long pgshift =__ffs (iommu->pgsize_bitmap);
@@ -3186,7 +3186,7 @@ static int vfio_iommu_type1_dma_rw_chunk(struct vfio_iommu *iommu, (offset >> pgshift) + 1); } }else - *copied =copy_from_user (data, (void __user *)vaddr, + *copied =copy_from_user_partial (data, (void __user *)vaddr, count) ?0 : count;if (kthread)kthread_unuse_mm (mm);
drivers/xen/xenbus/xenbus_dev_frontend.c +1 -1
@@ -150,7 +150,7 @@ static ssize_t xenbus_file_read(struct file *filp,while (i < len) {size_t sz =min_t (size_t, len - i, rb->len - rb->cons); - ret =copy_to_user (ubuf + i, &rb->msg[rb->cons], sz); + ret =copy_to_user_partial (ubuf + i, &rb->msg[rb->cons], sz); i += sz - ret; rb->cons += sz - ret;
fs/namespace.c +1 -1
@@ -4033,7 +4033,7 @@ static void *copy_mount_options(const void __user * data)if (!copy)return ERR_PTR (-ENOMEM ); - left =copy_from_user (copy, data,PAGE_SIZE ); + left =copy_from_user_partial (copy, data,PAGE_SIZE );/* * Not all architectures have an exact copy_from_user(). Resort to
fs/ocfs2/dlmfs/dlmfs.c +1 -1
@@ -255,7 +255,7 @@ static ssize_t dlmfs_file_write(struct file *filp,if (!count)return 0 ; - bytes_left =copy_from_user (lvb_buf, buf, count); + bytes_left =copy_from_user_partial (lvb_buf, buf, count); count -= bytes_left;if (count)user_dlm_write_lvb (inode, lvb_buf, count);
fs/proc/base.c +2 -2
@@ -278,7 +278,7 @@ static ssize_t get_mm_proctitle(struct mm_struct *mm, char __user *buf, len -= pos;if (len > count) len = count; - len -=copy_to_user (buf, page+pos, len); + len -=copy_to_user_partial (buf, page+pos, len);if (!len) len = -EFAULT ; ret = len;
@@ -359,7 +359,7 @@ static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf, got =access_remote_vm (mm, pos, page, size,FOLL_ANON );if (got <=0 )break ; - got -=copy_to_user (buf, page, got); + got -=copy_to_user_partial (buf, page, got);if (unlikely (!got)) {if (!len) len = -EFAULT ;
include/linux/bpfptr.h +1 -1
@@ -50,7 +50,7 @@ static inline int copy_from_bpfptr_offset(void *dst, bpfptr_t src, size_t offset, size_t size) {if (!bpfptr_is_kernel (src)) -return copy_from_user (dst, src.user + offset, size); +return copy_from_user_partial (dst, src.user + offset, size);return copy_from_kernel_nofault (dst, src.kernel + offset, size); }
include/linux/sockptr.h +2 -2
@@ -45,7 +45,7 @@ static inline int copy_from_sockptr_offset(void *dst, sockptr_t src, size_t offset, size_t size) {if (!sockptr_is_kernel (src)) -return copy_from_user (dst, src.user + offset, size); +return copy_from_user_partial (dst, src.user + offset, size);memcpy (dst, src.kernel + offset, size);return 0 ; }
@@ -111,7 +111,7 @@ static inline int copy_to_sockptr_offset(sockptr_t dst, size_t offset, const void *src, size_t size) { if (!sockptr_is_kernel(dst)) - return copy_to_user(dst.user + offset, src, size); + return copy_to_user_partial(dst.user + offset, src, size);memcpy (dst.kernel + offset, src, size);return 0 ; }
ipc/msg.c +4 -4
@@ -322,7 +322,7 @@ copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version) {switch (version) {case IPC_64 : -return copy_to_user (buf, in,sizeof (*in)); +return copy_to_user_partial (buf, in,sizeof (*in));case IPC_OLD : {struct msqid_ds out;
@@ -355,7 +355,7 @@ copy_msqid_to_user(void __user *buf, struct msqid64_ds *in, int version) out.msg_lspid = in->msg_lspid; out.msg_lrpid = in->msg_lrpid; -return copy_to_user (buf, &out,sizeof (out)); +return copy_to_user_partial (buf, &out,sizeof (out)); }default :return -EINVAL ;
@@ -712,7 +712,7 @@ static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in, v.msg_qbytes = in->msg_qbytes; v.msg_lspid = in->msg_lspid; v.msg_lrpid = in->msg_lrpid; -return copy_to_user (buf, &v,sizeof (v)); +return copy_to_user_partial (buf, &v,sizeof (v)); }else {struct compat_msqid_ds v;memset (&v,0 ,sizeof (v));
@@ -725,7 +725,7 @@ static int copy_compat_msqid_to_user(void __user *buf, struct msqid64_ds *in, v.msg_qbytes = in->msg_qbytes; v.msg_lspid = in->msg_lspid; v.msg_lrpid = in->msg_lrpid; -return copy_to_user (buf, &v,sizeof (v)); +return copy_to_user_partial (buf, &v,sizeof (v)); } }
ipc/sem.c +4 -4
@@ -1196,7 +1196,7 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, {switch (version) {case IPC_64 : -return copy_to_user (buf, in,sizeof (*in)); +return copy_to_user_partial (buf, in,sizeof (*in));case IPC_OLD : {struct semid_ds out;
@@ -1209,7 +1209,7 @@ static unsigned long copy_semid_to_user(void __user *buf, struct semid64_ds *in, out.sem_ctime = in->sem_ctime; out.sem_nsems = in->sem_nsems; -return copy_to_user (buf, &out,sizeof (out)); +return copy_to_user_partial (buf, &out,sizeof (out)); }default :return -EINVAL ;
@@ -1759,7 +1759,7 @@ static int copy_compat_semid_to_user(void __user *buf, struct semid64_ds *in, v.sem_ctime =lower_32_bits (in->sem_ctime); v.sem_ctime_high =upper_32_bits (in->sem_ctime); v.sem_nsems = in->sem_nsems; -return copy_to_user (buf, &v,sizeof (v)); +return copy_to_user_partial (buf, &v,sizeof (v)); }else {struct compat_semid_ds v;memset (&v,0 ,sizeof (v));
@@ -1767,7 +1767,7 @@ static int copy_compat_semid_to_user(void __user *buf, struct semid64_ds *in, v.sem_otime = in->sem_otime; v.sem_ctime = in->sem_ctime; v.sem_nsems = in->sem_nsems; -return copy_to_user (buf, &v,sizeof (v)); +return copy_to_user_partial (buf, &v,sizeof (v)); } }
ipc/shm.c +9 -9
@@ -853,7 +853,7 @@ static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ {switch (version) {case IPC_64 : -return copy_to_user (buf, in,sizeof (*in)); +return copy_to_user_partial (buf, in,sizeof (*in));case IPC_OLD : {struct shmid_ds out;
@@ -868,7 +868,7 @@ static inline unsigned long copy_shmid_to_user(void __user *buf, struct shmid64_ out.shm_lpid = in->shm_lpid; out.shm_nattch = in->shm_nattch; -return copy_to_user (buf, &out,sizeof (out)); +return copy_to_user_partial (buf, &out,sizeof (out)); }default :return -EINVAL ;
@@ -905,7 +905,7 @@ static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminf {switch (version) {case IPC_64 : -return copy_to_user (buf, in,sizeof (*in)); +return copy_to_user_partial (buf, in,sizeof (*in));case IPC_OLD : {struct shminfo out;
@@ -920,7 +920,7 @@ static inline unsigned long copy_shminfo_to_user(void __user *buf, struct shminf out.shmseg = in->shmseg; out.shmall = in->shmall; -return copy_to_user (buf, &out,sizeof (out)); +return copy_to_user_partial (buf, &out,sizeof (out)); }default :return -EINVAL ;
@@ -1359,7 +1359,7 @@ static int copy_compat_shminfo_to_user(void __user *buf, struct shminfo64 *in, info.shmmni = in->shmmni; info.shmseg = in->shmseg; info.shmall = in->shmall; -return copy_to_user (buf, &info,sizeof (info)); +return copy_to_user_partial (buf, &info,sizeof (info)); }else {struct shminfo info;memset (&info,0 ,sizeof (info));
@@ -1368,7 +1368,7 @@ static int copy_compat_shminfo_to_user(void __user *buf, struct shminfo64 *in, info.shmmni = in->shmmni; info.shmseg = in->shmseg; info.shmall = in->shmall; -return copy_to_user (buf, &info,sizeof (info)); +return copy_to_user_partial (buf, &info,sizeof (info)); } }
@@ -1384,7 +1384,7 @@ static int put_compat_shm_info(struct shm_info *ip, info.shm_swp = ip->shm_swp; info.swap_attempts = ip->swap_attempts; info.swap_successes = ip->swap_successes; -return copy_to_user (uip, &info,sizeof (info)); +return copy_to_user_partial (uip, &info,sizeof (info)); } static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in,
@@ -1404,7 +1404,7 @@ static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in, v.shm_nattch = in->shm_nattch; v.shm_cpid = in->shm_cpid; v.shm_lpid = in->shm_lpid; -return copy_to_user (buf, &v,sizeof (v)); +return copy_to_user_partial (buf, &v,sizeof (v)); }else {struct compat_shmid_ds v;memset (&v,0 ,sizeof (v));
@@ -1417,7 +1417,7 @@ static int copy_compat_shmid_to_user(void __user *buf, struct shmid64_ds *in, v.shm_nattch = in->shm_nattch; v.shm_cpid = in->shm_cpid; v.shm_lpid = in->shm_lpid; -return copy_to_user (buf, &v,sizeof (v)); +return copy_to_user_partial (buf, &v,sizeof (v)); } }
kernel/regset.c +1 -1
@@ -70,7 +70,7 @@ int copy_regset_to_user(struct task_struct *target, ret =regset_get_alloc (target, regset, size, &buf);if (ret >0 ) - ret =copy_to_user (data, buf, ret) ? -EFAULT :0 ; + ret =copy_to_user_partial (data, buf, ret) ? -EFAULT :0 ;kvfree (buf);return ret; }
kernel/sys.c +2 -2
@@ -1343,7 +1343,7 @@ static int override_release(char __user *release, size_t len) v =LINUX_VERSION_PATCHLEVEL +60 ; copy =clamp_t (size_t, len,1 ,sizeof (buf)); copy =scnprintf (buf, copy,"2.6.%u%s" , v, rest); - ret =copy_to_user (release, buf, copy +1 ); + ret =copy_to_user_partial (release, buf, copy +1 ); }return ret; }
@@ -1567,7 +1567,7 @@ SYSCALL_DEFINE2(getrlimit, unsigned int, resource, struct rlimit __user *, rlim) ret =do_prlimit (current, resource,NULL , &value);if (!ret) - ret =copy_to_user (rlim, &value,sizeof (*rlim)) ? -EFAULT :0 ; + ret =copy_to_user_partial (rlim, &value,sizeof (*rlim)) ? -EFAULT :0 ;return ret; }
lib/kfifo.c +4 -4
@@ -203,11 +203,11 @@ static unsigned long kfifo_copy_from_user(struct __kfifo *fifo, } l =min (len, size - off); - ret =copy_from_user (fifo->data + off, from, l); + ret =copy_from_user_partial (fifo->data + off, from, l);if (unlikely (ret)) ret =DIV_ROUND_UP (ret + len - l, esize);else { - ret =copy_from_user (fifo->data, from + l, len - l); + ret =copy_from_user_partial (fifo->data, from + l, len - l);if (unlikely (ret)) ret =DIV_ROUND_UP (ret, esize); }
@@ -263,11 +263,11 @@ static unsigned long kfifo_copy_to_user(struct __kfifo *fifo, void __user *to, } l =min (len, size - off); - ret =copy_to_user (to, fifo->data + off, l); + ret =copy_to_user_partial (to, fifo->data + off, l);if (unlikely (ret)) ret =DIV_ROUND_UP (ret + len - l, esize);else { - ret =copy_to_user (to + l, fifo->data, len - l); + ret =copy_to_user_partial (to + l, fifo->data, len - l);if (unlikely (ret)) ret =DIV_ROUND_UP (ret, esize); }
mm/kasan/kasan_test_c.c +2 -2
@@ -2169,9 +2169,9 @@ static void copy_user_test_oob(struct kunit *test) usermem = (char __user *)useraddr;KUNIT_EXPECT_KASAN_FAIL (test, - unused =copy_from_user (kmem, usermem, size +1 )); + unused =copy_from_user_partial (kmem, usermem, size +1 ));KUNIT_EXPECT_KASAN_FAIL_READ (test, - unused =copy_to_user (usermem, kmem, size +1 )); + unused =copy_to_user_partial (usermem, kmem, size +1 ));KUNIT_EXPECT_KASAN_FAIL (test, unused =__copy_from_user (kmem, usermem, size +1 )); KUNIT_EXPECT_KASAN_FAIL_READ(test,
mm/memory.c +1 -1
@@ -7529,7 +7529,7 @@ long copy_folio_from_user(struct folio *dst_folio, kaddr =kmap_local_page (subpage);if (!allow_pagefault)pagefault_disable (); - rc =copy_from_user (kaddr, usr_src + i *PAGE_SIZE ,PAGE_SIZE ); + rc =copy_from_user_partial (kaddr, usr_src + i *PAGE_SIZE ,PAGE_SIZE );if (!allow_pagefault)pagefault_enable ();kunmap_local (kaddr);
net/x25/af_x25.c +1 -1
@@ -471,7 +471,7 @@ static int x25_getsockopt(struct socket *sock, int level, int optname, goto out; val =test_bit (X25_Q_BIT_FLAG , &x25_sk (sk)->flags); - rc =copy_to_user (optval, &val, len) ? -EFAULT :0 ; + rc =copy_to_user_partial (optval, &val, len) ? -EFAULT :0 ; out:return rc; }
rust/helpers/uaccess.c +2 -2
@@ -5,13 +5,13 @@__rust_helper unsigned long rust_helper_copy_from_user (void *to,const void __user *from,unsigned long n) { -return copy_from_user (to, from, n); +return copy_from_user_partial (to, from, n); }__rust_helper unsigned long rust_helper_copy_to_user (void __user *to,const void *from,unsigned long n) { -return copy_to_user (to, from, n); +return copy_to_user_partial (to, from, n); }#ifndef CONFIG_ARCH_WANTS_NOINLINE_COPY_USER
sound/pci/emu10k1/emufx.c +2 -2
@@ -739,10 +739,10 @@ static int copy_gctl_to_user(struct snd_emu10k1 *emu, _dst = (struct snd_emu10k1_fx8010_control_gpr __user *)dst;if (emu->support_tlv) -return copy_to_user (&_dst[idx], src,sizeof (*src)); +return copy_to_user_partial (&_dst[idx], src,sizeof (*src)); octl = (struct snd_emu10k1_fx8010_control_old_gpr __user *)dst; -return copy_to_user (&octl[idx], src,sizeof (*octl)); +return copy_to_user_partial (&octl[idx], src,sizeof (*octl)); } static int copy_ctl_elem_id(const struct emu10k1_ctl_elem_id *list, int i,
sound/pci/rme9652/hdsp.c +3 -3
@@ -4541,7 +4541,7 @@ static int snd_hdsp_capture_release(struct snd_pcm_substream *substream)static inline int copy_u32_le (void __user *dest,void __iomem *src) {u32 val =readl (src); -return copy_to_user (dest, &val,4 ); +return copy_to_user_partial (dest, &val,4 ); }static inline int copy_u64_le (void __user *dest,void __iomem *src_low,void __iomem *src_high)
@@ -4551,7 +4551,7 @@ static inline int copy_u64_le(void __user *dest, void __iomem *src_low, void __i rms_low =readl (src_low); rms_high =readl (src_high); rms = ((u64 )rms_high <<32 ) | rms_low; -return copy_to_user (dest, &rms,8 ); +return copy_to_user_partial (dest, &rms,8 ); }static inline int copy_u48_le (void __user *dest,void __iomem *src_low,void __iomem *src_high)
@@ -4561,7 +4561,7 @@ static inline int copy_u48_le(void __user *dest, void __iomem *src_low, void __i rms_low =readl (src_low) &0xffffff00 ; rms_high =readl (src_high) &0xffffff00 ; rms = ((u64 )rms_high <<32 ) | rms_low; -return copy_to_user (dest, &rms,8 ); +return copy_to_user_partial (dest, &rms,8 ); }static int hdsp_9652_get_peak (struct hdsp *hdsp,struct hdsp_peak_rms __user *peak_rms)
sound/soc/intel/avs/probes.c +3 -3
@@ -244,10 +244,10 @@ static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr n = rtd->buffer_size - offset;if (count < n) { - ret =copy_to_user (buf, ptr, count); + ret =copy_to_user_partial (buf, ptr, count); }else { - ret =copy_to_user (buf, ptr, n); - ret +=copy_to_user (buf + n, rtd->dma_area, count - n); + ret =copy_to_user_partial (buf, ptr, n); + ret +=copy_to_user_partial (buf + n, rtd->dma_area, count - n); }if (ret)
sound/soc/sof/compress.c +6 -6
@@ -324,10 +324,10 @@ static int sof_compr_copy_playback(struct snd_compr_runtime *rtd, n = rtd->buffer_size - offset;if (count < n) { - ret =copy_from_user (ptr, buf, count); + ret =copy_from_user_partial (ptr, buf, count); }else { - ret =copy_from_user (ptr, buf, n); - ret +=copy_from_user (rtd->dma_area, buf + n, count - n); + ret =copy_from_user_partial (ptr, buf, n); + ret +=copy_from_user_partial (rtd->dma_area, buf + n, count - n); }return count - ret;
@@ -345,10 +345,10 @@ static int sof_compr_copy_capture(struct snd_compr_runtime *rtd, n = rtd->buffer_size - offset;if (count < n) { - ret =copy_to_user (buf, ptr, count); + ret =copy_to_user_partial (buf, ptr, count); }else { - ret =copy_to_user (buf, ptr, n); - ret +=copy_to_user (buf + n, rtd->dma_area, count - n); + ret =copy_to_user_partial (buf, ptr, n); + ret +=copy_to_user_partial (buf + n, rtd->dma_area, count - n); }return count - ret;
sound/soc/sof/sof-client-probes.c +3 -3
@@ -184,10 +184,10 @@ static int sof_probes_compr_copy(struct snd_soc_component *component, n = rtd->buffer_size - offset;if (count < n) { - ret =copy_to_user (buf, ptr, count); + ret =copy_to_user_partial (buf, ptr, count); }else { - ret =copy_to_user (buf, ptr, n); - ret +=copy_to_user (buf + n, rtd->dma_area, count - n); + ret =copy_to_user_partial (buf, ptr, n); + ret +=copy_to_user_partial (buf + n, rtd->dma_area, count - n); }if (ret)
Signature · 1 line
2.49.0
Alice Ryhl
maintainerRe: [RFC PATCH v1 5/9] uaccess: Switch to copy_{to/from}_user_partial() when relevant
Quoted 8 lines · Christophe Leroy (CS GROUP)
rust/helpers/uaccess.c +0
-0@@ -5,13 +5,13 @@__rust_helper unsigned long rust_helper_copy_from_user (void *to,const void __user *from,unsigned long n) {
rust/helpers/uaccess.c line 15
Quoted 10 lines
rust/helpers/uaccess.c +2
-2-return copy_from_user (to, from, n); +return copy_from_user_partial (to, from, n); }__rust_helper unsigned long rust_helper_copy_to_user (void __user *to,const void *from,unsigned long n) { -return copy_to_user (to, from, n); +return copy_to_user_partial (to, from, n); }
No Rust code uses the return value for anything other than comparing it with zero, so you can keep these as copy_[from|to]_user() without issues.
Thanks, Alice
Re: [RFC PATCH v1 5/9] uaccess: Switch to copy_{to/from}_user_partial() when relevant
In reply to Christophe Leroy (CS GROUP)Quoted 3 lines · Christophe Leroy (CS GROUP)
In a subsequent patch, copy_{to/from}_user() will be modified to return
-EFAULTwhen copy fails.
Please don't do this.
This is a maintenance nightmare, and changes pretty much three decades of semantics, and will cause *very* subtle backporting issues if somebody happens to rely on the old / new behavior.
I understand the reasoning for the change, but I really don't think the pain of creating yet another user copy interface is worth it.
We already have a lot of different versions of user copies for different reasons, and while they all tend to have a good reason (and some not-so-good, but historical reasons) for existing, this one doesn't seem worth it.
The main - perhaps only - reason for this "partial" version is that you want to do that "automatically inlined and optimized fixed-sized case".
But here's the thing: I think you can already do that. Yes, it requires some improvements to unsafe_copy_from_user(), but *that* interface doesn't have three decades of history associated with it, _and_ you're extending on that one anyway in this series.
"unsafe_copy_from_user()" is very odd, is meant only for small simple copies that can be inlined and it's special-cased for 'objtool' anyway (because objtool would have complained about an out-of-line call, although it could have been special-cased other ways).
In other words: unsafe_copy_from_user() is *very* close to what you want for that "Oh, I noticed that it's a small fixed-size copy, so I want to special-case copy-from-user for that".
The _only_ issue with unsafe_copy_from_user() is that you can't see that there were partial successes. But if *that* was fixed, then this whole "create a new copy_from_user interface" issue would just go away.
So please - let's just change unsafe_copy_from_user() to be usable for the partial case.
And the thing is, all the existing unsafe_copy_from_user() implementations already effectively *have* the "how much did I not copy" internally, and they actually do extra work to hide it, ie they have things like that
int _i;
that is "how many bytes have I copied" in the powerpc implementation, or the x86 code does
size_t __ucu_len = (_len);
where that "ucu_len" is updated as you go along and is literally the "how many bytes are left to copy" return value that is missing from this interface.
So what I would suggest is
- introduce a new user accessor helper that is used for *both*
unsafe_copy_to/from_user() *and* the "inline small constant-sized normal copy_to/from_user()" calls
- it's the same thing as the existing unsafe_copy_to/from_user()
implementation, except it exposes how many bytes are left to be copied to the exception label.
IOW, it would look something like
#define unsafe_copy_to_user_outlen(_dst,_src,_len,label)...
which is exactly the same as the current unsafe_copy_to_user(), *except* it changes "_len" as it does along.
And then you use that for both the "real" unsafe_copy_user and for the "small constant values" case.
Just as an example, attached is a completely stupid rough draft of a patch that does this for x86 and only for unsafe_copy_to_user().
And I made a very very hacky change to kernel/sys.c to see what the code generation looks like.
This is what it results in on x86 with clang (with all the magic .section data edited out):
... edited out the code to generate the times ... this is the actual user copy:
# HERE!
movabsq $81985529216486895, %rcx # imm = 0x123456789ABCDEF
cmpq %rcx, %rbx
cmovaq %rcx, %rbx
stac
movq %r13, (%rbx) # exception to .LBB45_8
movq %r14, 8(%rbx) # exception to .LBB45_8
movq %r15, 16(%rbx) # exception to .LBB45_8
movq %rax, 24(%rbx) # exception to .LBB45_8
clac
.LBB45_6:
movq jiffies(%rip), %rdi
callq jiffies_64_to_clock_t
.LBB45_7:
addq $16, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
retq
.LBB45_8:
clac
movq $-14, %rax
jmp .LBB45_7and notice how the compiler noticed that the 'outlen' isn't actually used, and turned the exception label into just a "return -EFAULT" and never actually generated any code for updating remaining lengths?
That actually looks pretty much optimal for a 32-byte user copy.
And it didn't involve changing the semantics at all.
Just to check, I changed that "times()" system call to return the number of bytes uncopied instead (to emulate the "I actually want to know what's left" case), and it generated this:
# HERE!
movabsq $81985529216486895, %rcx # imm = 0x123456789ABCDEF
cmpq %rcx, %rbx
cmovaq %rcx, %rbx
stac
movl $32, %ecx
movq %r13, (%rbx) # exception to .LBB45_7
movl $24, %ecx
movq %r15, 8(%rbx) # exception to .LBB45_7
movl $16, %ecx
movq %r14, 16(%rbx) # exception to .LBB45_7
movl $8, %ecx
movq %rax, 24(%rbx) # exception to .LBB45_7
clac
xorl %ecx, %ecx
.LBB45_8:
movq %rcx, %rax
addq $16, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15 retq
.LBB45_6:
movq jiffies(%rip), %rdi
jmp jiffies_64_to_clock_t # TAILCALL
.LBB45_7:
clac
jmp .LBB45_8so it all seems to work - although obviously the above is *not* the normal case.
NOTE NOTE NOTE! The attached patch is entirely untested. I obviously did some "test code generation" with it, but I only *looked* at the result, and maybe it has some fundamental problem that I just didn't notice. So treat this as a "how about this approach" patch, not as anything more serious than that.
And the kerrnel/sys.c hack is very obviously just that: a complate hack for testing.
A real patch would do that "for small constant-sized copies, turn copy_to_user() automatically into "_small_copy_to_user()".
The attached is *not* a real patch. Treat it with the contempt it deserves.
Linus
arch/x86/include/asm/uaccess.h | 17 +++++++++++------ include/linux/uaccess.h | 16 ++++++++++++++++ kernel/sys.c | 3 ++- 3 files changed, 29 insertions(+), 7 deletions(-)
arch/x86/include/asm/uaccess.h +11 -6
@@ -606,15 +606,20 @@ _label: \ len -=sizeof (type); \ } -#define unsafe_copy_to_user (_dst,_src,_len,label) \ +#define unsafe_copy_to_user_outlen (_dst,_src,_len,label) \ do { \ char __user *__ucu_dst = (_dst); \ const char *__ucu_src = (_src); \ - size_t __ucu_len = (_len); \ - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \ - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \ - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \ - unsafe_copy_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ + unsafe_copy_loop(__ucu_dst, __ucu_src, _len, u64, label); \ + unsafe_copy_loop(__ucu_dst, __ucu_src, _len, u32, label); \ + unsafe_copy_loop(__ucu_dst, __ucu_src, _len, u16, label); \ + unsafe_copy_loop(__ucu_dst, __ucu_src, _len, u8, label); \ +} while (0) + +#define unsafe_copy_to_user (_dst,_src,_len,label) \ +do { \ + size_t __ucu_len = _len; \ + unsafe_copy_to_user_outlen(_dst,_src,__ucu_len,label); \ } while (0)#ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
include/linux/uaccess.h +16 -0
@@ -874,4 +874,20 @@ void __noreturn usercopy_abort(const char *name, const char *detail, unsigned long len);#endif +static __always_inline __must_check unsigned long +_small_copy_to_user (void __user *to,const void *from,unsigned long n) +{ +size_t uncopied = n; + +might_fault (); +if (should_fail_usercopy ()) +return n; +instrument_copy_to_user (to, from, n); + scoped_user_write_access_size(to, n, failed) +unsafe_copy_to_user_outlen (to, from, uncopied, failed); +return 0 ; +failed: +return uncopied; +} +#endif /* __LINUX_UACCESS_H__ */
kernel/sys.c +2 -1
@@ -1067,7 +1067,8 @@ SYSCALL_DEFINE1(times, struct tms __user *, tbuf)struct tms tmp;do_sys_times (&tmp); -if (copy_to_user (tbuf, &tmp,sizeof (struct tms ))) + asmvolatile ("# HERE!" ); +if (_small_copy_to_user (tbuf, &tmp,sizeof (struct tms )))return -EFAULT ; }force_successful_syscall_return ();
Re: [RFC PATCH v1 5/9] uaccess: Switch to copy_{to/from}_user_partial() when relevant
Quoted 64 lines · On Mon, 27 Apr 2026 12:01:23 -0700 Linus Torvalds…
Quoted 3 lines · Christophe Leroy (CS GROUP)
In a subsequent patch, copy_{to/from}_user() will be modified to return
-EFAULTwhen copy fails.Please don't do this.
This is a maintenance nightmare, and changes pretty much three decades of semantics, and will cause *very* subtle backporting issues if somebody happens to rely on the old / new behavior.
I understand the reasoning for the change, but I really don't think the pain of creating yet another user copy interface is worth it.
We already have a lot of different versions of user copies for different reasons, and while they all tend to have a good reason (and some not-so-good, but historical reasons) for existing, this one doesn't seem worth it.
The main - perhaps only - reason for this "partial" version is that you want to do that "automatically inlined and optimized fixed-sized case".
But here's the thing: I think you can already do that. Yes, it requires some improvements to
unsafe_copy_from_user(), but *that* interface doesn't have three decades of history associated with it, _and_ you're extending on that one anyway in this series."
unsafe_copy_from_user()" is very odd, is meant only for small simple copies that can be inlined and it's special-cased for 'objtool' anyway (because objtool would have complained about an out-of-line call, although it could have been special-cased other ways).In other words:
unsafe_copy_from_user()is *very* close to what you want for that "Oh, I noticed that it's a small fixed-size copy, so I want to special-case copy-from-user for that".The _only_ issue with
unsafe_copy_from_user()is that you can't see that there were partial successes. But if *that* was fixed, then this whole "create a newcopy_from_userinterface" issue would just go away.So please - let's just change
unsafe_copy_from_user()to be usable for the partial case.And the thing is, all the existing
unsafe_copy_from_user()implementations already effectively *have* the "how much did I not copy" internally, and they actually do extra work to hide it, ie they have things like thatint _i;that is "how many bytes have I copied" in the powerpc implementation, or the x86 code does
size_t __ucu_len = (_len);where that "
ucu_len" is updated as you go along and is literally the "how many bytes are left to copy" return value that is missing from this interface.So what I would suggest is
Quoted 7 lines
- introduce a new user accessor helper that is used for *both*
unsafe_copy_to/from_user() *and* the "inline small constant-sized normal copy_to/from_user()" calls- it's the same thing as the existing unsafe_copy_to/from_user()
implementation, except it exposes how many bytes are left to be copied to the exception label.
I think there is a slight difference in that the normal copy_to_user() will determine the exact offset of the error by retrying with byte copies.
There is also the issue of misaligned copies.
Then there is the 'bugbear' of hardened user copies.
Chasing down the stack to find whether the kernel buffer crosses a stack frame is probably more expensive than the copy for the typically small copies that will use on-stack buffers.
David
Quoted 108 lines
IOW, it would look something like
#define unsafe_copy_to_user_outlen(_dst,_src,_len,label)...which is exactly the same as the current
unsafe_copy_to_user(), *except* it changes "_len" as it does along.And then you use that for both the "real"
unsafe_copy_userand for the "small constant values" case.Just as an example, attached is a completely stupid rough draft of a patch that does this for x86 and only for
unsafe_copy_to_user().And I made a very very hacky change to
kernel/sys.cto see what the code generation looks like.This is what it results in on x86 with clang (with all the magic .section data edited out):
... edited out the code to generate the times ... this is the actual user copy:
# HERE!
movabsq $81985529216486895, %rcx # imm =0x123456789ABCDEF
cmpq %rcx, %rbx
cmovaq %rcx, %rbx
stac
movq %r13, (%rbx) # exception to .LBB45_8
movq %r14, 8(%rbx) # exception to .LBB45_8
movq %r15, 16(%rbx) # exception to .LBB45_8
movq %rax, 24(%rbx) # exception to .LBB45_8
clac
.LBB45_6:
movq jiffies(%rip), %rdi
callqjiffies_64_to_clock_t
.LBB45_7:addq $16, %rsp popq %rbx popq %r12 popq %r13 popq %r14 popq %r15 retq .LBB45_8: clac movq $-14, %rax jmp .LBB45_7and notice how the compiler noticed that the 'outlen' isn't actually used, and turned the exception label into just a "return
-EFAULT" and never actually generated any code for updating remaining lengths?That actually looks pretty much optimal for a 32-byte user copy.
And it didn't involve changing the semantics at all.
Just to check, I changed that "
times()" system call to return the number of bytes uncopied instead (to emulate the "I actually want to know what's left" case), and it generated this:# HERE!
movabsq $81985529216486895, %rcx # imm =0x123456789ABCDEF
cmpq %rcx, %rbx
cmovaq %rcx, %rbx
stac
movl $32, %ecx
movq %r13, (%rbx) # exception to .LBB45_7
movl $24, %ecx
movq %r15, 8(%rbx) # exception to .LBB45_7
movl $16, %ecx
movq %r14, 16(%rbx) # exception to .LBB45_7
movl $8, %ecx
movq %rax, 24(%rbx) # exception to .LBB45_7
clac
xorl %ecx, %ecx
.LBB45_8:movq %rcx, %rax addq $16, %rsp popq %rbx popq %r12 popq %r13 popq %r14 popq %r15retq
.LBB45_6:
movq jiffies(%rip), %rdi
jmpjiffies_64_to_clock_t# TAILCALL
.LBB45_7:clac jmp .LBB45_8so it all seems to work - although obviously the above is *not* the normal case.
NOTE NOTE NOTE! The attached patch is entirely untested. I obviously did some "test code generation" with it, but I only *looked* at the result, and maybe it has some fundamental problem that I just didn't notice. So treat this as a "how about this approach" patch, not as anything more serious than that.
And the
kerrnel/sys.chack is very obviously just that: a complate hack for testing.A real patch would do that "for small constant-sized copies, turn
copy_to_user()automatically into "_small_copy_to_user()".The attached is *not* a real patch. Treat it with the contempt it deserves.
Linus
Re: [RFC PATCH v1 5/9] uaccess: Switch to copy_{to/from}_user_partial() when relevant
Quoted 3 lines · David Laight
I think there is a slight difference in that the normal
copy_to_user()will determine the exact offset of the error by retrying with byte copies.
I have this dim memory that we decided that you can't reply on byte exactness anyway, because not all architectures gave that guarantee for the user copies.
But that thing came up many years ago, I might mis-remember.
Linus
Christophe Leroy (CS GROUP)
reviewerRFC PATCH 6/9 uaccess: Change copy_{to/from}_user to return -EFAULT
In reply to Christophe Leroy (CS GROUP)Now that copy_{to/from}_user_partial() are used by callers which expect partial copy with number of not copied bytes as return value, change copy_{to/from}_user() to return an int, and return -EFAULT when the copy is not complete.
--- include/linux/uaccess.h | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-)
include/linux/uaccess.h +24 -4
@@ -211,7 +211,7 @@ extern __must_check unsigned long _copy_to_user(void __user *, const void *, unsigned long);static __always_inline unsigned long __must_check -copy_from_user (void *to,const void __user *from,unsigned long n) +copy_from_user_common (void *to,const void __user *from,unsigned long n,bool partial) {if (!check_copy_size (to, n, false))return n;
@@ -221,10 +221,20 @@ copy_from_user(void *to, const void __user *from, unsigned long n)return _inline_copy_from_user (to, from, n); } -#define copy_from_user_partial copy_from_user +static __always_inline unsigned long __must_check +copy_from_user_partial (void *to,const void __user *from,unsigned long n) +{ +return copy_from_user_common (to, from, n, true); +} + +static __always_inline int __must_check +copy_from_user (void *to,const void __user *from,unsigned long n) +{ +return copy_from_user_common (to, from, n, false) ? -EFAULT :0 ; +}static __always_inline unsigned long __must_check -copy_to_user (void __user *to,const void *from,unsigned long n) +copy_to_user_common (void __user *to,const void *from,unsigned long n,bool partial) {if (!check_copy_size (from, n, true))return n;
@@ -235,7 +245,17 @@ copy_to_user(void __user *to, const void *from, unsigned long n)return _inline_copy_to_user (to, from, n); } -#define copy_to_user_partial copy_to_user +static __always_inline unsigned long __must_check +copy_to_user_partial (void __user *to,const void *from,unsigned long n) +{ +return copy_to_user_common (to, from, n, true); +} + +static __always_inline int __must_check +copy_to_user (void __user *to,const void *from,unsigned long n) +{ +return copy_to_user_common (to, from, n, false) ? -EFAULT :0 ; +}#ifndef copy_mc_to_kernel/*
Signature · 1 line
2.49.0
Christophe Leroy (CS GROUP)
reviewerRFC PATCH 7/9 x86: Add unsafe_copy_from_user()
In reply to Christophe Leroy (CS GROUP)At the time being, x86 and arm64 are missing unsafe_copy_from_user().
Add it.
--- arch/x86/include/asm/uaccess.h | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-)
arch/x86/include/asm/uaccess.h +24 -5
@@ -598,7 +598,7 @@ _label: \* We want the unsafe accessors to always be inlined and use * the error labels - thus the macro games. */ -#define unsafe_copy_loop (dst, src, len, type, label) \ +#define unsafe_put_loop (dst, src, len, type, label) \ while (len >= sizeof(type)) { \ unsafe_put_user(*(type *)(src),(type __user *)(dst),label); \ dst += sizeof(type); \
@@ -611,10 +611,29 @@ do { \ char __user *__ucu_dst = (_dst); \const char *__ucu_src = (_src); \size_t __ucu_len = (_len); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u64, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u32, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u16, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u64, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u32, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u16, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +} while (0) + +#define unsafe_get_loop (dst, src, len, type, label) \ + while (len >= sizeof(type)) { \ + unsafe_get_user(*(type __user *)(src),(type *)(dst),label); \ + dst += sizeof(type); \ + src += sizeof(type); \ + len -= sizeof(type); \ + } + +#define unsafe_copy_from_user (_dst,_src,_len,label) \ +do { \ + char *__ucu_dst = (_dst); \ + const char __user *__ucu_src = (_src); \ + size_t __ucu_len = (_len); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ } while (0)#ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT
Signature · 1 line
2.49.0
Re: [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
Quoted 1 line · Christophe Leroy (CS GROUP)
At the time being, x86 and arm64 are missing
unsafe_copy_from_user().
No, they don't. They (should) rely on a generic implementation from linux/uaccess.h, like every other arch, except for PPC and RISCV.
But they #define arch_unsafe_get_user, and the unsafe_copy_from_user() becomes undefined conditionally on that.
So please, fix that bug instead of introducing another arch flavor. We'd always choose generic version, unless there's strong evidence that arch one is better.
Thanks,
Yury
Quoted 57 lines
Add it.
Signed-off-by Christophe Leroy (CS GROUP) <[email protected]>--- arch/x86/include/asm/uaccess.h | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-)arch/x86/include/asm/uaccess.h +24
-5@@ -598,7 +598,7 @@ _label: \* We want the unsafe accessors to always be inlined and use * the error labels - thus the macro games. */ -#define unsafe_copy_loop (dst, src, len, type, label) \ +#define unsafe_put_loop (dst, src, len, type, label) \ while (len >= sizeof(type)) { \ unsafe_put_user(*(type *)(src),(type __user *)(dst),label); \ dst += sizeof(type); \@@ -611,10 +611,29 @@ do { \ char __user *__ucu_dst = (_dst); \const char *__ucu_src = (_src); \size_t __ucu_len = (_len); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u64, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u32, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u16, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u64, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u32, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u16, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +} while (0) + +#define unsafe_get_loop (dst, src, len, type, label) \ + while (len >= sizeof(type)) { \ + unsafe_get_user(*(type __user *)(src),(type *)(dst),label); \ + dst += sizeof(type); \ + src += sizeof(type); \ + len -= sizeof(type); \ + } + +#define unsafe_copy_from_user (_dst,_src,_len,label) \ +do { \ + char *__ucu_dst = (_dst); \ + const char __user *__ucu_src = (_src); \ + size_t __ucu_len = (_len); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ } while (0)#ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT Signature · 1 line
2.49.0
Christophe Leroy (CS GROUP)
reviewerRe: [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
Le 27/04/2026 à 19:58, Yury Norov a écrit :
Quoted 6 lines · Christophe Leroy (CS GROUP)
Quoted 1 line
At the time being, x86 and arm64 are missing
unsafe_copy_from_user().No, they don't. They (should) rely on a generic implementation from
linux/uaccess.h, like every other arch, except for PPC and RISCV.
Quoted 6 lines
But they #define
arch_unsafe_get_user, and theunsafe_copy_from_user()becomes undefined conditionally on that.So please, fix that bug instead of introducing another arch flavor. We'd always choose generic version, unless there's strong evidence that arch one is better.
But they both implement the exact same unsafe_copy_to_user(). What is the difference here ?
Should that function become generic too ?
Christophe
Quoted 62 lines
Thanks,
YuryQuoted 57 lines
Add it.
Signed-off-by Christophe Leroy (CS GROUP) <[email protected]>--- arch/x86/include/asm/uaccess.h | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-)arch/x86/include/asm/uaccess.h +24
-5@@ -598,7 +598,7 @@ _label: \* We want the unsafe accessors to always be inlined and use * the error labels - thus the macro games. */ -#define unsafe_copy_loop (dst, src, len, type, label) \ +#define unsafe_put_loop (dst, src, len, type, label) \ while (len >= sizeof(type)) { \ unsafe_put_user(*(type *)(src),(type __user *)(dst),label); \ dst += sizeof(type); \@@ -611,10 +611,29 @@ do { \ char __user *__ucu_dst = (_dst); \const char *__ucu_src = (_src); \size_t __ucu_len = (_len); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u64, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u32, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u16, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u64, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u32, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u16, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +} while (0) + +#define unsafe_get_loop (dst, src, len, type, label) \ + while (len >= sizeof(type)) { \ + unsafe_get_user(*(type __user *)(src),(type *)(dst),label); \ + dst += sizeof(type); \ + src += sizeof(type); \ + len -= sizeof(type); \ + } + +#define unsafe_copy_from_user (_dst,_src,_len,label) \ +do { \ + char *__ucu_dst = (_dst); \ + const char __user *__ucu_src = (_src); \ + size_t __ucu_len = (_len); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ } while (0)#ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT Signature · 1 line
2.49.0
Re: [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
Quoted 12 lines · Christophe Leroy (CS GROUP)
Le 27/04/2026 à 19:58, Yury Norov a écrit :
Quoted 9 lines · Christophe Leroy (CS GROUP)
Quoted 1 line
At the time being, x86 and arm64 are missing
unsafe_copy_from_user().No, they don't. They (should) rely on a generic implementation from
linux/uaccess.h, like every other arch, except for PPC and RISCV.But they #define
arch_unsafe_get_user, and theunsafe_copy_from_user()becomes undefined conditionally on that.
Quoted 8 lines
Quoted 3 lines
So please, fix that bug instead of introducing another arch flavor. We'd always choose generic version, unless there's strong evidence that arch one is better.
But they both implement the exact same
unsafe_copy_to_user(). What is the difference here ?Should that function become generic too ?
This is what Linus said when added x86 implementation for copy_from_user() in c512c69187197:
Note that it only does this [arch version] for the copying _to_ user space, and we still don't have a unsafe version of copy_from_user().
That's partly because we have no current users of it, but also partly because the copy_from_user() case is slightly different and cannot efficiently be implemented in terms of a unsafe_get_user() loop (because gcc can't do asm goto with outputs).
In the unsafe_copy_to_user case, arch versions were justified. Just as said, I'm not against arch version for unsafe_copy_from_user(), but it should be explained very well.
Re: [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
Quoted 3 lines · Yury Norov
This is what Linus said when added x86 implementation for
copy_from_user()inc512c69187197:
Note that some things have happily changed in the six+ years since...
Quoted 4 lines
That's partly because we have no current users of it, but also partly because the
copy_from_user()case is slightly different and cannot efficiently be implemented in terms of aunsafe_get_user()loop (because gcc can't do asm goto with outputs).
now everybody can do asm goto with outputs.
Yes, it's disabled on older versions, so it's not *always* available, but all modern versions do it. And if you care about performance, you won't be using an old compiler.
Linus
Re: [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
Quoted 12 lines · Linus Torvalds
Quoted 3 lines · Yury Norov
This is what Linus said when added x86 implementation for
copy_from_user()inc512c69187197:Note that some things have happily changed in the six+ years since...
Quoted 4 lines
That's partly because we have no current users of it, but also partly because the
copy_from_user()case is slightly different and cannot efficiently be implemented in terms of aunsafe_get_user()loop (because gcc can't do asm goto with outputs).
Quoted 5 lines
now everybody can do asm goto with outputs.
Yes, it's disabled on older versions, so it's not *always* available, but all modern versions do it. And if you care about performance, you won't be using an old compiler.
The minimal GCC version is 8.1, and asm goto with outputs is supported since GCC-11. That would brake the build, if we just switch to using it without "CC_IS_GCC && (GCC_VERSION >= 110100)" guard.
Is it worth to maintain 2 version of the function? I don't know...
Thanks,
Yury
Re: [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
Hi!
Quoted 19 lines · Yury Norov
Quoted 12 lines · Linus Torvalds
Quoted 3 lines · Yury Norov
This is what Linus said when added x86 implementation for
copy_from_user()inc512c69187197:Note that some things have happily changed in the six+ years since...
Quoted 4 lines
That's partly because we have no current users of it, but also partly because the
copy_from_user()case is slightly different and cannot efficiently be implemented in terms of aunsafe_get_user()loop (because gcc can't do asm goto with outputs).Quoted 5 lines
now everybody can do asm goto with outputs.
Yes, it's disabled on older versions, so it's not *always* available, but all modern versions do it. And if you care about performance, you won't be using an old compiler.
Quoted 5 lines
The minimal GCC version is 8.1, and asm goto with outputs is supported since GCC-11. That would brake the build, if we just switch to using it without "
CC_IS_GCC&& (GCC_VERSION>= 110100)" guard.Is it worth to maintain 2 version of the function? I don't know...
GCC 11 was released five years and a day ago. The last GCC 11 release (that of 11.5) is not even two years ago though (but there will be no more!)
So it would be not conservative at all to require GCC 11 as minimum now, some people might even call it a bit aggressive. But in a year (or maybe two) the lay of the land will be rather different.
Segher
Re: [RFC PATCH v1 7/9] x86: Add unsafe_copy_from_user()
In reply to Christophe Leroy (CS GROUP)Quoted 29 lines · On Mon, 27 Apr 2026 19:13:48 +0200 "Christophe Leroy (CS GROUP)"…
At the time being, x86 and arm64 are missing
unsafe_copy_from_user().Add it.
Signed-off-by Christophe Leroy (CS GROUP) <[email protected]>--- arch/x86/include/asm/uaccess.h | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-)arch/x86/include/asm/uaccess.h +1
-4@@ -598,7 +598,7 @@ _label: \* We want the unsafe accessors to always be inlined and use * the error labels - thus the macro games. */ -#define unsafe_copy_loop (dst, src, len, type, label) \ +#define unsafe_put_loop (dst, src, len, type, label) \ while (len >= sizeof(type)) { \ unsafe_put_user(*(type *)(src),(type __user *)(dst),label); \ dst += sizeof(type); \@@ -611,10 +611,29 @@ do { \ char __user *__ucu_dst = (_dst); \const char *__ucu_src = (_src); \size_t __ucu_len = (_len); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u64, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u32, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u16, label); \
arch/x86/include/asm/uaccess.h do { \ · line 622
Quoted 10 lines
arch/x86/include/asm/uaccess.h +9
-1-unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u64, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u32, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u16, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +} while (0) + +#define unsafe_get_loop (dst, src, len, type, label) \ + while (len >= sizeof(type)) { \ + unsafe_get_user(*(type __user *)(src),(type *)(dst),label); \
Hi,
Just wanted to check if src and dst need to be swapped? Same for arm64 patch.
Quoted 21 lines
arch/x86/include/asm/uaccess.h +14
-0+ dst +=sizeof (type); \ + src +=sizeof (type); \ + len -=sizeof (type); \ + } + +#define unsafe_copy_from_user (_dst,_src,_len,label) \ +do { \ + char *__ucu_dst = (_dst); \ + const char __user *__ucu_src = (_src); \ + size_t __ucu_len = (_len); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ } while (0)#ifdef CONFIG_CC_HAS_ASM_GOTO_OUTPUT Signature · 1 line
2.49.0
Christophe Leroy (CS GROUP)
reviewerRFC PATCH 8/9 arm64: Add unsafe_copy_from_user()
In reply to Christophe Leroy (CS GROUP)At the time being, x86 and arm64 are missing unsafe_copy_from_user().
Add it.
--- arch/arm64/include/asm/uaccess.h | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-)
arch/arm64/include/asm/uaccess.h +24 -5
@@ -437,7 +437,7 @@ static inline void user_access_restore(unsigned long enabled) { }* We want the unsafe accessors to always be inlined and use * the error labels - thus the macro games. */ -#define unsafe_copy_loop (dst, src, len, type, label) \ +#define unsafe_put_loop (dst, src, len, type, label) \ while (len >= sizeof(type)) { \ unsafe_put_user(*(type *)(src),(type __user *)(dst),label); \ dst += sizeof(type); \
@@ -450,10 +450,29 @@ do { \ char __user *__ucu_dst = (_dst); \const char *__ucu_src = (_src); \size_t __ucu_len = (_len); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u64, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u32, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u16, label); \ -unsafe_copy_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u64, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u32, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u16, label); \ +unsafe_put_loop (__ucu_dst, __ucu_src, __ucu_len, u8, label); \ +} while (0) + +#define unsafe_get_loop (dst, src, len, type, label) \ + while (len >= sizeof(type)) { \ + unsafe_get_user(*(type __user *)(src),(type *)(dst),label); \ + dst += sizeof(type); \ + src += sizeof(type); \ + len -= sizeof(type); \ + } + +#define unsafe_copy_from_user (_dst,_src,_len,label) \ +do { \ + char *__ucu_dst = (_dst); \ + const char __user *__ucu_src = (_src); \ + size_t __ucu_len = (_len); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u64, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u32, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u16, label); \ + unsafe_get_loop(__ucu_dst, __ucu_src, __ucu_len, u8, label); \ } while (0)extern unsigned long __must_check __arch_clear_user (void __user *to,unsigned long n);
Signature · 1 line
2.49.0
Will Deacon
maintainerRe: [RFC PATCH v1 8/9] arm64: Add unsafe_copy_from_user()
[+Catalin]
Quoted 8 lines · Christophe Leroy (CS GROUP)
At the time being, x86 and arm64 are missing
unsafe_copy_from_user().Add it.
Signed-off-by Christophe Leroy (CS GROUP) <[email protected]>--- arch/arm64/include/asm/uaccess.h | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-)
Why?
And please cc the arm64 maintainers on arm64 patches next time. You've managed to cc most of the world apart from us.
Will
Christophe Leroy (CS GROUP)
reviewerRFC PATCH 9/9 uaccess: Convert small fixed size copy_{to/from}_user() to scoped user access
In reply to Christophe Leroy (CS GROUP)copy_{to/from}_user() is a heavy function optimised for copy of large blocs of memory between user and kernel space.
When the number of bytes to be copied is known at build time and small, using scoped user access removes the burden of that optimisation.
--- include/linux/uaccess.h | 47 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+)
include/linux/uaccess.h +47 -0
@@ -50,6 +50,8 @@#define mask_user_address (src) (src)#endif +#define SMALL_COPY_USER 64 +/* * Architectures should provide two primitives (raw_copy_{to,from}_user()) * and get rid of their private instances of copy_{to,from}_user() and
@@ -191,6 +193,9 @@ _inline_copy_from_user(void *to, const void __user *from, unsigned long n)return res; } +static __always_inline __must_check unsigned long +_small_copy_from_user (void *to,const void __user *from,unsigned long n); +extern __must_check unsigned long _copy_from_user (void *,const void __user *,unsigned long );
@@ -207,6 +212,9 @@ _inline_copy_to_user(void __user *to, const void *from, unsigned long n)return n; } +static __always_inline __must_check unsigned long +_small_copy_to_user (void __user *to,const void *from,unsigned long n); +extern __must_check unsigned long _copy_to_user (void __user *,const void *,unsigned long );
@@ -215,6 +223,8 @@ copy_from_user_common(void *to, const void __user *from, unsigned long n, bool p {if (!check_copy_size (to, n, false))return n; +if (!partial &&__builtin_constant_p (n) && n <=SMALL_COPY_USER ) +return _small_copy_from_user (to, from, n);if (IS_ENABLED (ARCH_WANTS_NOINLINE_COPY_USER ))return _copy_from_user (to, from, n);else
@@ -239,6 +249,8 @@ copy_to_user_common(void __user *to, const void *from, unsigned long n, bool parif (!check_copy_size (from, n, true))return n; +if (!partial &&__builtin_constant_p (n) && n <=SMALL_COPY_USER ) +return _small_copy_to_user (to, from, n);if (IS_ENABLED (ARCH_WANTS_NOINLINE_COPY_USER ))return _copy_to_user (to, from, n);else
@@ -838,6 +850,41 @@ for (bool done = false; !done; done = true) \#define scoped_user_rw_access (uptr, elbl) \ scoped_user_rw_access_size(uptr, sizeof(*(uptr)), elbl) +static __always_inline __must_check unsigned long +_small_copy_from_user (void *to,const void __user *from,unsigned long n) +{ +might_fault (); +instrument_copy_from_user_before (to, from, n); + scoped_user_read_access_size(from, n, failed) { +/* +* Ensure that bad access_ok() speculation will not lead +* to nasty side effects *after* the copy is finished: +*/ +if (!can_do_masked_user_access ()) +barrier_nospec (); +unsafe_copy_from_user (to, from, n, failed); + } +instrument_copy_from_user_after (to, from, n,0 ); +return 0 ; +failed: +instrument_copy_from_user_after (to, from, n, n); +return n; +} + +static __always_inline __must_check unsigned long +_small_copy_to_user (void __user *to,const void *from,unsigned long n) +{ +might_fault (); +if (should_fail_usercopy ()) +return n; +instrument_copy_to_user (to, from, n); + scoped_user_write_access_size(to, n, failed) +unsafe_copy_to_user (to, from, n, failed); +return 0 ; +failed: +return n; +} +/** * get_user_inline - Read user data inlined * @val: The variable to store the value read from user memory
Signature · 1 line
2.49.0
Quoted 16 lines · Christophe Leroy (CS GROUP)
copy_{to/from}_user() is a heavy function optimised for copy of large blocs of memory between user and kernel space.
When the number of bytes to be copied is known at build time and small, using scoped user access removes the burden of that optimisation.
Signed-off-by Christophe Leroy (CS GROUP) <[email protected]>--- include/linux/uaccess.h | 47 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+)include/linux/uaccess.h +0
-0
include/linux/uaccess.h line 53
Quoted 4 lines
include/linux/uaccess.h +1
-0@@ -50,6 +50,8 @@#define mask_user_address (src) (src)#endif +#define SMALL_COPY_USER 64
Let's make it L1_CACHE_BYTES, and maybe configurable?
Helge Deller
maintainerHello Christophe,
Quoted 6 lines · Christophe Leroy (CS GROUP)
A lot of
copy_from_user()andcopy_to_user()perform copies of small fixed size pieces of data between kernel and userspace, and don't care about partial copies.
copy_from_user()andcopy_to_user()are big functions optimised for copying large amount of data, with cache management, etc ...
They take care of much more: alignments, exception handling (e.g. if userpage is read-only and kernel writes to it), various rules when to return faults (e.g. sometime reading from page0 is allowed for other arches not), and much more. I've seen so many strange things during the last few years, and you would need to get it right if you want to "make small" versions of those functions.
Quoted 2 lines
This is often overkill for small copies that could just be inlined instead.
Isn't put_user() and get_user() for that ?
And if you inline you need to take care of faults as well, so indirectly you will add more fault handlers (or fault pointers) to the generated code, effectively making the kernel bigger.
Quoted 12 lines
What makes things a bit more tricky is that those copy functions are designed to handle partial copies in case of page fault. But among the 6000 callers of those functions, only 2% really care about the quantity of no-copied data that those functions return. All other ones fails as soon as the returned value is not 0, returning
-EACCESS.So first step in this series is to introduce variants called
copy_from_user_partial()andcopy_to_user_partial()which will be called by the 2% users that care about the partial copy, then the originalcopy_from_user()andcopy_to_user()are changed to return-EFAULTwhen the copy fails.
Quoted 3 lines
Then the second step is to implement copy of small fixed-size data with scoped user access instead of calling the arch specific heavy user copy functions.
I'm not against your idea or your patch, but I wonder if you really gain much from it.
Have you done some size or speed comparisons ?
Helge
Quoted 111 lines
Patch 5, can be split in different patches for each archicture or subsystem, but let's get a first feedback and agree on the principle.
Christophe Leroy (CS GROUP) (9):
uaccess: Splitcheck_zeroed_user()out ofusercopy.c
uaccess: Convert INLINE_COPY_{TO/FROM}_USER to kconfig and reduce
ifdefery
x86/umip: Be stricter infixup_umip_exception()
uaccess: Introduce copy_{to/from}_user_partial()
uaccess: Switch to copy_{to/from}_user_partial() when relevant
uaccess: Change copy_{to/from}_user to return-EFAULT
x86: Addunsafe_copy_from_user()
arm64: Addunsafe_copy_from_user()
uaccess: Convert small fixed size copy_{to/from}_user() to scoped user
accessarch/alpha/Kconfig | 1 + arch/alpha/kernel/osf_sys.c | 4 +- arch/alpha/kernel/termios.c | 2 +- arch/arc/include/asm/uaccess.h | 3 - arch/arc/kernel/disasm.c | 2 +- arch/arm/include/asm/uaccess.h | 2 - arch/arm64/include/asm/gcs.h | 2 +- arch/arm64/include/asm/uaccess.h | 30 +++-- arch/arm64/kernel/signal32.c | 2 +- arch/csky/Kconfig | 1 + arch/hexagon/include/asm/uaccess.h | 3 - arch/loongarch/include/asm/uaccess.h | 3 - arch/m68k/include/asm/uaccess.h | 3 - arch/microblaze/include/asm/uaccess.h | 2 - arch/mips/include/asm/uaccess.h | 3 - arch/mips/kernel/rtlx.c | 8 +- arch/mips/kernel/vpe.c | 2 +- arch/nios2/include/asm/uaccess.h | 2 - arch/openrisc/include/asm/uaccess.h | 2 - arch/parisc/include/asm/uaccess.h | 3 - arch/powerpc/Kconfig | 1 + arch/powerpc/kvm/book3s_64_mmu_hv.c | 4 +- arch/powerpc/kvm/book3s_64_mmu_radix.c | 4 +- arch/powerpc/kvm/book3s_hv.c | 2 +- arch/riscv/Kconfig | 1 + arch/riscv/kernel/signal.c | 2 +- arch/s390/include/asm/idals.h | 8 +- arch/s390/include/asm/uaccess.h | 3 - arch/sh/include/asm/uaccess.h | 2 - arch/sparc/include/asm/uaccess_32.h | 3 - arch/sparc/include/asm/uaccess_64.h | 2 - arch/sparc/kernel/termios.c | 2 +- arch/um/include/asm/uaccess.h | 3 - arch/um/kernel/process.c | 2 +- arch/x86/Kconfig | 1 + arch/x86/include/asm/uaccess.h | 29 ++++- arch/x86/kernel/umip.c | 2 +- arch/x86/lib/insn-eval.c | 2 +- arch/x86/um/signal.c | 2 +- arch/xtensa/include/asm/uaccess.h | 2 - drivers/android/binder_alloc.c | 2 +- drivers/comedi/comedi_fops.c | 4 +- drivers/dma/idxd/cdev.c | 2 +- drivers/firmware/efi/test/efi_test.c | 2 +- drivers/fsi/fsi-scom.c | 2 +- .../amd/display/amdgpu_dm/amdgpu_dm_debugfs.c | 2 +- drivers/gpu/drm/i915/gt/intel_sseu.c | 4 +- drivers/gpu/drm/i915/i915_gem.c | 4 +- drivers/hwtracing/intel_th/msu.c | 2 +- drivers/misc/ibmvmc.c | 2 +- drivers/misc/vmw_vmci/vmci_host.c | 2 +- drivers/most/most_cdev.c | 2 +- drivers/net/ieee802154/ca8210.c | 4 +- drivers/net/wireless/ath/wil6210/debugfs.c | 2 +- .../intel/iwlwifi/pcie/gen1_2/trans.c | 2 +- drivers/net/wireless/ti/wlcore/debugfs.c | 2 +- drivers/ps3/ps3-lpm.c | 2 +- drivers/s390/crypto/zcrypt_api.h | 4 +- drivers/spi/spidev.c | 2 +- .../staging/media/atomisp/pci/atomisp_cmd.c | 8 +- drivers/tty/tty_ioctl.c | 14 +-- drivers/tty/vt/vc_screen.c | 4 +- drivers/usb/gadget/function/f_hid.c | 4 +- drivers/usb/gadget/function/f_printer.c | 2 +- drivers/vfio/vfio_iommu_type1.c | 4 +- drivers/xen/xenbus/xenbus_dev_frontend.c | 2 +- fs/namespace.c | 2 +- fs/ocfs2/dlmfs/dlmfs.c | 2 +- fs/proc/base.c | 4 +- include/asm-generic/uaccess.h | 2 - include/linux/bpfptr.h | 2 +- include/linux/sockptr.h | 4 +- include/linux/uaccess.h | 107 ++++++++++++++---- ipc/msg.c | 8 +- ipc/sem.c | 8 +- ipc/shm.c | 18 +-- kernel/regset.c | 2 +- kernel/sys.c | 4 +- lib/Kconfig | 3 + lib/Makefile | 4 +- lib/kfifo.c | 8 +- lib/{usercopy.c => usercheck.c} | 22 ---- lib/usercopy.c | 66 ----------- mm/kasan/kasan_test_c.c | 4 +- mm/memory.c | 2 +- net/x25/af_x25.c | 2 +- rust/helpers/uaccess.c | 6 +- sound/pci/emu10k1/emufx.c | 4 +- sound/pci/rme9652/hdsp.c | 6 +- sound/soc/intel/avs/probes.c | 6 +- sound/soc/sof/compress.c | 12 +- sound/soc/sof/sof-client-probes.c | 6 +- 92 files changed, 269 insertions(+), 288 deletions(-)copy
lib/{usercopy.c=>usercheck.c} (73%)