arch/x86/include/asm/uaccess.h | 17 +++++++++++------ include/linux/uaccess.h | 16 ++++++++++++++++ kernel/sys.c | 3 ++- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h index 3a0dd3c2b233..3b2c57c91418 100644 --- a/arch/x86/include/asm/uaccess.h +++ b/arch/x86/include/asm/uaccess.h @@ -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 diff --git a/include/linux/uaccess.h b/include/linux/uaccess.h index 56328601218c..1a70ef70784c 100644 --- a/include/linux/uaccess.h +++ b/include/linux/uaccess.h @@ -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__ */ diff --git a/kernel/sys.c b/kernel/sys.c index 62e842055cc9..65b2d0103a73 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -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))) + asm volatile("# HERE!"); + if (_small_copy_to_user(tbuf, &tmp, sizeof(struct tms))) return -EFAULT; } force_successful_syscall_return();