svn commit: r355066 - head/sys/compat/linux
Vladimir Kondratyev
wulf at FreeBSD.org
Sun Nov 24 20:44:15 UTC 2019
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);
More information about the svn-src-all
mailing list