diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c index 2ccd3eedbd6733..abb8cfb99d6cf9 100644 --- a/drivers/input/input-compat.c +++ b/drivers/input/input-compat.c @@ -14,7 +14,7 @@ int input_event_from_user(const char __user *buffer, struct input_event *event) { - if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) { + if (current->compat_input || (in_compat_syscall() && !COMPAT_USE_64BIT_TIME)) { struct input_event_compat compat_event; if (copy_from_user(&compat_event, buffer, @@ -38,7 +38,7 @@ int input_event_from_user(const char __user *buffer, int input_event_to_user(char __user *buffer, const struct input_event *event) { - if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) { + if (current->compat_input || (in_compat_syscall() && !COMPAT_USE_64BIT_TIME)) { struct input_event_compat compat_event; compat_event.sec = event->input_event_sec; @@ -62,7 +62,7 @@ int input_event_to_user(char __user *buffer, int input_ff_effect_from_user(const char __user *buffer, size_t size, struct ff_effect *effect) { - if (in_compat_syscall()) { + if (current->compat_input || (in_compat_syscall() && !COMPAT_USE_64BIT_TIME)) { struct ff_effect_compat *compat_effect; if (size != sizeof(struct ff_effect_compat)) diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h index 3b7bb12b023bc6..e78c0492ce0d36 100644 --- a/drivers/input/input-compat.h +++ b/drivers/input/input-compat.h @@ -53,7 +53,7 @@ struct ff_effect_compat { static inline size_t input_event_size(void) { - return (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) ? + return (current->compat_input || (in_compat_syscall() && !COMPAT_USE_64BIT_TIME)) ? sizeof(struct input_event_compat) : sizeof(struct input_event); } diff --git a/include/linux/sched.h b/include/linux/sched.h index 77f01ac385f7a5..01125573065ecd 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1535,6 +1535,11 @@ struct task_struct { #ifdef CONFIG_USER_EVENTS struct user_event_mm *user_event_mm; #endif + /* + * Whether the task wants to use compat input syscalls even if it's + * a 64-bit process. + */ + bool compat_input; /* * New fields for task_struct should be added above here, so that diff --git a/include/uapi/linux/prctl.h b/include/uapi/linux/prctl.h index 961216093f11ab..86fca7d168cc66 100644 --- a/include/uapi/linux/prctl.h +++ b/include/uapi/linux/prctl.h @@ -311,4 +311,9 @@ struct prctl_mm_map { # define PR_SET_MEM_MODEL_DEFAULT 0 # define PR_SET_MEM_MODEL_TSO 1 +#define PR_GET_COMPAT_INPUT 0x63494e50 +#define PR_SET_COMPAT_INPUT 0x43494e50 +# define PR_SET_COMPAT_INPUT_DISABLE 0 +# define PR_SET_COMPAT_INPUT_ENABLE 1 + #endif /* _LINUX_PRCTL_H */ diff --git a/kernel/sys.c b/kernel/sys.c index 2db751ce25a260..1be74620b0b659 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -2768,6 +2768,21 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, return -EINVAL; error = arch_prctl_mem_model_set(me, arg2); break; + case PR_GET_COMPAT_INPUT: + if (arg2 || arg3 || arg4 || arg5) + return -EINVAL; + error = current->compat_input; + break; + case PR_SET_COMPAT_INPUT: + if (arg3 || arg4 || arg5) + return -EINVAL; + if (arg2 == PR_SET_COMPAT_INPUT_DISABLE) + current->compat_input = false; + else if (arg2 == PR_SET_COMPAT_INPUT_ENABLE) + current->compat_input = true; + else + return -EINVAL; + break; default: error = -EINVAL; break;