Date: Sun, 24 Nov 2019 20:44:15 +0000 (UTC) From: Vladimir Kondratyev <wulf@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r355066 - head/sys/compat/linux Message-ID: <201911242044.xAOKiFvc045717@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: wulf Date: Sun Nov 24 20:44:14 2019 New Revision: 355066 URL: https://svnweb.freebsd.org/changeset/base/355066 Log: Linux epoll: Check both read and write kqueue events existence in EPOLL_CTL_ADD Linux epoll EPOLL_CTL_ADD op handler should always check registration of both EVFILT_READ and EVFILT_WRITE kevents to deceide if supplied file descriptor fd is already registered with epoll instance. Reviewed by: emaste MFC after: 1 week Differential Revision: https://reviews.freebsd.org/D22515 Modified: head/sys/compat/linux/linux_event.c Modified: head/sys/compat/linux/linux_event.c ============================================================================== --- head/sys/compat/linux/linux_event.c Sun Nov 24 20:41:47 2019 (r355065) +++ head/sys/compat/linux/linux_event.c Sun Nov 24 20:44:14 2019 (r355066) @@ -98,14 +98,16 @@ __attribute__((packed)) #define LINUX_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event)) static void epoll_fd_install(struct thread *td, int fd, epoll_udata_t udata); -static int epoll_to_kevent(struct thread *td, struct file *epfp, - int fd, struct epoll_event *l_event, int *kev_flags, - struct kevent *kevent, int *nkevents); +static int epoll_to_kevent(struct thread *td, int fd, + struct epoll_event *l_event, struct kevent *kevent, + int *nkevents); static void kevent_to_epoll(struct kevent *kevent, struct epoll_event *l_event); static int epoll_kev_copyout(void *arg, struct kevent *kevp, int count); static int epoll_kev_copyin(void *arg, struct kevent *kevp, int count); -static int epoll_delete_event(struct thread *td, struct file *epfp, - int fd, int filter); +static int epoll_register_kevent(struct thread *td, struct file *epfp, + int fd, int filter, unsigned int flags); +static int epoll_fd_registered(struct thread *td, struct file *epfp, + int fd); static int epoll_delete_all_events(struct thread *td, struct file *epfp, int fd); @@ -296,31 +298,31 @@ linux_epoll_create1(struct thread *td, struct linux_ep /* Structure converting function from epoll to kevent. */ static int -epoll_to_kevent(struct thread *td, struct file *epfp, - int fd, struct epoll_event *l_event, int *kev_flags, +epoll_to_kevent(struct thread *td, int fd, struct epoll_event *l_event, struct kevent *kevent, int *nkevents) { uint32_t levents = l_event->events; struct linux_pemuldata *pem; struct proc *p; + unsigned short kev_flags = EV_ADD | EV_ENABLE; /* flags related to how event is registered */ if ((levents & LINUX_EPOLLONESHOT) != 0) - *kev_flags |= EV_DISPATCH; + kev_flags |= EV_DISPATCH; if ((levents & LINUX_EPOLLET) != 0) - *kev_flags |= EV_CLEAR; + kev_flags |= EV_CLEAR; if ((levents & LINUX_EPOLLERR) != 0) - *kev_flags |= EV_ERROR; + kev_flags |= EV_ERROR; if ((levents & LINUX_EPOLLRDHUP) != 0) - *kev_flags |= EV_EOF; + kev_flags |= EV_EOF; /* flags related to what event is registered */ if ((levents & LINUX_EPOLL_EVRD) != 0) { - EV_SET(kevent++, fd, EVFILT_READ, *kev_flags, 0, 0, 0); + EV_SET(kevent++, fd, EVFILT_READ, kev_flags, 0, 0, 0); ++(*nkevents); } if ((levents & LINUX_EPOLL_EVWR) != 0) { - EV_SET(kevent++, fd, EVFILT_WRITE, *kev_flags, 0, 0, 0); + EV_SET(kevent++, fd, EVFILT_WRITE, kev_flags, 0, 0, 0); ++(*nkevents); } @@ -451,7 +453,6 @@ linux_epoll_ctl(struct thread *td, struct linux_epoll_ epoll_kev_copyin}; struct epoll_event le; cap_rights_t rights; - int kev_flags; int nchanges = 0; int error; @@ -484,9 +485,7 @@ linux_epoll_ctl(struct thread *td, struct linux_epoll_ ciargs.changelist = kev; if (args->op != LINUX_EPOLL_CTL_DEL) { - kev_flags = EV_ADD | EV_ENABLE; - error = epoll_to_kevent(td, epfp, args->fd, &le, - &kev_flags, kev, &nchanges); + error = epoll_to_kevent(td, args->fd, &le, kev, &nchanges); if (error != 0) goto leave0; } @@ -499,19 +498,10 @@ linux_epoll_ctl(struct thread *td, struct linux_epoll_ break; case LINUX_EPOLL_CTL_ADD: - /* - * kqueue_register() return ENOENT if event does not exists - * and the EV_ADD flag is not set. Reset EV_ENABLE flag to - * avoid accidental activation of fired oneshot events. - */ - kev[0].flags &= ~(EV_ADD | EV_ENABLE); - error = kqfd_register(args->epfd, &kev[0], td, M_WAITOK); - if (error != ENOENT) { + if (epoll_fd_registered(td, epfp, args->fd)) { error = EEXIST; goto leave0; } - error = 0; - kev[0].flags |= (EV_ADD | EV_ENABLE); break; case LINUX_EPOLL_CTL_DEL: @@ -651,7 +641,8 @@ linux_epoll_pwait(struct thread *td, struct linux_epol } static int -epoll_delete_event(struct thread *td, struct file *epfp, int fd, int filter) +epoll_register_kevent(struct thread *td, struct file *epfp, int fd, int filter, + unsigned int flags) { struct epoll_copyin_args ciargs; struct kevent kev; @@ -660,18 +651,36 @@ epoll_delete_event(struct thread *td, struct file *epf epoll_kev_copyin}; ciargs.changelist = &kev; - EV_SET(&kev, fd, filter, EV_DELETE | EV_DISABLE, 0, 0, 0); + EV_SET(&kev, fd, filter, flags, 0, 0, 0); return (kern_kevent_fp(td, epfp, 1, 0, &k_ops, NULL)); } static int +epoll_fd_registered(struct thread *td, struct file *epfp, int fd) +{ + /* + * Set empty filter flags to avoid accidental modification of already + * registered events. In the case of event re-registration: + * 1. If event does not exists kevent() does nothing and returns ENOENT + * 2. If event does exists, it's enabled/disabled state is preserved + * but fflags, data and udata fields are overwritten. So we can not + * set socket lowats and store user's context pointer in udata. + */ + if (epoll_register_kevent(td, epfp, fd, EVFILT_READ, 0) != ENOENT || + epoll_register_kevent(td, epfp, fd, EVFILT_WRITE, 0) != ENOENT) + return (1); + + return (0); +} + +static int epoll_delete_all_events(struct thread *td, struct file *epfp, int fd) { int error1, error2; - error1 = epoll_delete_event(td, epfp, fd, EVFILT_READ); - error2 = epoll_delete_event(td, epfp, fd, EVFILT_WRITE); + error1 = epoll_register_kevent(td, epfp, fd, EVFILT_READ, EV_DELETE); + error2 = epoll_register_kevent(td, epfp, fd, EVFILT_WRITE, EV_DELETE); /* return 0 if at least one result positive */ return (error1 == 0 ? 0 : error2);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201911242044.xAOKiFvc045717>