The 'data' member of struct epoll_event may contain a user pointer. On some architectures (for example CHERI) a user pointer is a 129-bit capability, so the __u64 type is not big enough to hold it. Use the __kernel_uintptr_t type instead, which is big enough on the affected architectures while remaining 64-bit on others.
In addition, special copy routines need to be used when copying user pointers to and from userspace. Use these for epoll_event.data.
Signed-off-by: Kristina Martsenko kristina.martsenko@arm.com --- fs/eventpoll.c | 5 +++-- include/linux/eventpoll.h | 6 +++--- include/uapi/linux/eventpoll.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/fs/eventpoll.c b/fs/eventpoll.c index 8b9e4fdda28e..8a1d3b3fe9c9 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -2194,9 +2194,10 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, return -EFAULT;
epds.events = compat_epds.events; - epds.data = compat_epds.data; + epds.data = (__kernel_uintptr_t)as_user_ptr(compat_epds.data); } else { - if (copy_from_user(&epds, event, sizeof(struct epoll_event))) + if (copy_from_user_with_ptr(&epds, event, + sizeof(struct epoll_event))) return -EFAULT; } } diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index 15acfb6771c3..457811d82ff2 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -81,7 +81,7 @@ epoll_put_uevent(__poll_t revents, __u64 data, struct epoll_event __user *uevent); #else static inline struct epoll_event __user * -epoll_put_uevent(__poll_t revents, __u64 data, +epoll_put_uevent(__poll_t revents, __kernel_uintptr_t data, struct epoll_event __user *uevent) { if (in_compat_syscall()) { @@ -89,14 +89,14 @@ epoll_put_uevent(__poll_t revents, __u64 data, (struct compat_epoll_event __user *)uevent;
if (__put_user(revents, &compat_uevent->events) || - __put_user(data, &compat_uevent->data)) + __put_user((__u64)data, &compat_uevent->data)) return NULL;
return (struct epoll_event __user *)(compat_uevent+1); }
if (__put_user(revents, &uevent->events) || - __put_user(data, &uevent->data)) + __put_user_ptr(data, &uevent->data)) return NULL;
return uevent+1; diff --git a/include/uapi/linux/eventpoll.h b/include/uapi/linux/eventpoll.h index 8a3432d0f0dc..ee17d8995caa 100644 --- a/include/uapi/linux/eventpoll.h +++ b/include/uapi/linux/eventpoll.h @@ -76,7 +76,7 @@
struct epoll_event { __poll_t events; - __u64 data; + __kernel_uintptr_t data; } EPOLL_PACKED;
#ifdef CONFIG_PM_SLEEP