A subsequent patch is going to change struct epoll_event to enable it to support new architectures. On such architectures, the current struct layout still needs to be supported for compat tasks. In preparation for that change, add a struct compat_epoll_event and handle it in epoll syscalls.
Note, it may be cleaner to have a separate compat entry point for epoll_ctl. For now, use only the native entry point to avoid changing the syscall table on all architectures.
Signed-off-by: Kristina Martsenko kristina.martsenko@arm.com --- fs/eventpoll.c | 22 ++++++++++++++++++---- include/linux/eventpoll.h | 17 +++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/fs/eventpoll.c b/fs/eventpoll.c index e5c9a29f0da1..8b9e4fdda28e 100644 --- a/fs/eventpoll.c +++ b/fs/eventpoll.c @@ -2185,9 +2185,21 @@ SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd, { struct epoll_event epds;
- if (ep_op_has_event(op) && - copy_from_user(&epds, event, sizeof(struct epoll_event))) - return -EFAULT; + if (ep_op_has_event(op)) { + if (in_compat_syscall()) { + struct compat_epoll_event compat_epds; + + if (copy_from_user(&compat_epds, event, + sizeof(struct compat_epoll_event))) + return -EFAULT; + + epds.events = compat_epds.events; + epds.data = compat_epds.data; + } else { + if (copy_from_user(&epds, event, sizeof(struct epoll_event))) + return -EFAULT; + } + }
return do_epoll_ctl(epfd, op, fd, &epds, false); } @@ -2202,13 +2214,15 @@ static int do_epoll_wait(int epfd, struct epoll_event __user *events, int error; struct fd f; struct eventpoll *ep; + size_t event_size = in_compat_syscall() ? sizeof(struct compat_epoll_event) + : sizeof(struct epoll_event);
/* The maximum number of event must be greater than zero */ if (maxevents <= 0 || maxevents > EP_MAX_EVENTS) return -EINVAL;
/* Verify that the area passed by the user is writeable */ - if (!access_ok(events, maxevents * sizeof(struct epoll_event))) + if (!access_ok(events, maxevents * event_size)) return -EFAULT;
/* Get the "struct file *" for the eventpoll file */ diff --git a/include/linux/eventpoll.h b/include/linux/eventpoll.h index 3337745d81bd..15acfb6771c3 100644 --- a/include/linux/eventpoll.h +++ b/include/linux/eventpoll.h @@ -8,6 +8,7 @@ #ifndef _LINUX_EVENTPOLL_H #define _LINUX_EVENTPOLL_H
+#include <linux/compat.h> #include <uapi/linux/eventpoll.h> #include <uapi/linux/kcmp.h>
@@ -68,6 +69,11 @@ static inline void eventpoll_release(struct file *file) {}
#endif
+struct compat_epoll_event { + __poll_t events; + __u64 data; +}; + #if defined(CONFIG_ARM) && defined(CONFIG_OABI_COMPAT) /* ARM OABI has an incompatible struct layout and needs a special handler */ extern struct epoll_event __user * @@ -78,6 +84,17 @@ static inline struct epoll_event __user * epoll_put_uevent(__poll_t revents, __u64 data, struct epoll_event __user *uevent) { + if (in_compat_syscall()) { + struct compat_epoll_event __user *compat_uevent = + (struct compat_epoll_event __user *)uevent; + + if (__put_user(revents, &compat_uevent->events) || + __put_user(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)) return NULL;