From owner-svn-src-head@freebsd.org Sun Nov 24 20:44:15 2019 Return-Path: Delivered-To: svn-src-head@mailman.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.nyi.freebsd.org (Postfix) with ESMTP id A2CD21BA52C; Sun, 24 Nov 2019 20:44:15 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) server-signature RSA-PSS (4096 bits) client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 47Lhwb3qdHz4Rtx; Sun, 24 Nov 2019 20:44:15 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 6680C1CBF; Sun, 24 Nov 2019 20:44:15 +0000 (UTC) (envelope-from wulf@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id xAOKiFT9045718; Sun, 24 Nov 2019 20:44:15 GMT (envelope-from wulf@FreeBSD.org) Received: (from wulf@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id xAOKiFvc045717; Sun, 24 Nov 2019 20:44:15 GMT (envelope-from wulf@FreeBSD.org) Message-Id: <201911242044.xAOKiFvc045717@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: wulf set sender to wulf@FreeBSD.org using -f From: Vladimir Kondratyev Date: Sun, 24 Nov 2019 20:44:15 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r355066 - head/sys/compat/linux X-SVN-Group: head X-SVN-Commit-Author: wulf X-SVN-Commit-Paths: head/sys/compat/linux X-SVN-Commit-Revision: 355066 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 24 Nov 2019 20:44:15 -0000 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);