Date: Tue, 26 Nov 2019 18:12:13 +0000 (UTC) From: Vincenzo Maffione <vmaffione@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org Subject: svn commit: r355117 - stable/12/usr.sbin/bhyve Message-ID: <201911261812.xAQICD3l077943@repo.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: vmaffione Date: Tue Nov 26 18:12:13 2019 New Revision: 355117 URL: https://svnweb.freebsd.org/changeset/base/355117 Log: MFC r354659 bhyve: rework mevent processing to fix a race condition At the end of both mevent_add() and mevent_update(), mevent_notify() is called to wakeup the I/O thread, that will call kevent(changelist) to update the kernel. A race condition is possible where the client calls mevent_add() and mevent_update(EV_ENABLE) before the I/O thread has the chance to wake up and call mevent_build()+kevent(changelist) in response to mevent_add(). The mevent_add() is therefore ignored by the I/O thread, and kevent(fd, EV_ENABLE) is called before kevent(fd, EV_ADD), resuliting in a failure of the kevent(fd, EV_ENABLE) call. PR: 241808 Reviewed by: jhb, markj Differential Revision: https://reviews.freebsd.org/D22286 Modified: stable/12/usr.sbin/bhyve/mevent.c Directory Properties: stable/12/ (props changed) Modified: stable/12/usr.sbin/bhyve/mevent.c ============================================================================== --- stable/12/usr.sbin/bhyve/mevent.c Tue Nov 26 18:10:45 2019 (r355116) +++ stable/12/usr.sbin/bhyve/mevent.c Tue Nov 26 18:12:13 2019 (r355117) @@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$"); #endif #include <err.h> #include <errno.h> +#include <stdbool.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -62,12 +63,6 @@ __FBSDID("$FreeBSD$"); #define MEVENT_MAX 64 -#define MEV_ADD 1 -#define MEV_ENABLE 2 -#define MEV_DISABLE 3 -#define MEV_DEL_PENDING 4 -#define MEV_ADD_DISABLED 5 - extern char *vmname; static pthread_t mevent_tid; @@ -83,7 +78,7 @@ struct mevent { enum ev_type me_type; void *me_param; int me_cq; - int me_state; + int me_state; /* Desired kevent flags. */ int me_closefd; LIST_ENTRY(mevent) me_list; }; @@ -156,30 +151,7 @@ mevent_kq_filter(struct mevent *mevp) static int mevent_kq_flags(struct mevent *mevp) { - int ret; - - switch (mevp->me_state) { - case MEV_ADD: - ret = EV_ADD; /* implicitly enabled */ - break; - case MEV_ADD_DISABLED: - ret = EV_ADD | EV_DISABLE; - break; - case MEV_ENABLE: - ret = EV_ENABLE; - break; - case MEV_DISABLE: - ret = EV_DISABLE; - break; - case MEV_DEL_PENDING: - ret = EV_DELETE; - break; - default: - assert(0); - break; - } - - return (ret); + return (mevp->me_state); } static int @@ -224,9 +196,15 @@ mevent_build(int mfd, struct kevent *kev) mevp->me_cq = 0; LIST_REMOVE(mevp, me_list); - if (mevp->me_state == MEV_DEL_PENDING) { + if (mevp->me_state & EV_DELETE) { free(mevp); } else { + /* + * We need to add the event only once, so we can + * reset the EV_ADD bit after it has been propagated + * to the kevent() arguments the first time. + */ + mevp->me_state &= ~EV_ADD; LIST_INSERT_HEAD(&global_head, mevp, me_list); } @@ -318,7 +296,7 @@ mevent_add(int tfd, enum ev_type type, void (*func)(int, enum ev_type, void *), void *param) { - return mevent_add_state(tfd, type, func, param, MEV_ADD); + return (mevent_add_state(tfd, type, func, param, EV_ADD)); } struct mevent * @@ -326,36 +304,46 @@ mevent_add_disabled(int tfd, enum ev_type type, void (*func)(int, enum ev_type, void *), void *param) { - return mevent_add_state(tfd, type, func, param, MEV_ADD_DISABLED); + return (mevent_add_state(tfd, type, func, param, EV_ADD | EV_DISABLE)); } static int -mevent_update(struct mevent *evp, int newstate) +mevent_update(struct mevent *evp, bool enable) { + int newstate; + + mevent_qlock(); + /* * It's not possible to enable/disable a deleted event */ - if (evp->me_state == MEV_DEL_PENDING) - return (EINVAL); + assert((evp->me_state & EV_DELETE) == 0); + newstate = evp->me_state; + if (enable) { + newstate |= EV_ENABLE; + newstate &= ~EV_DISABLE; + } else { + newstate |= EV_DISABLE; + newstate &= ~EV_ENABLE; + } + /* * No update needed if state isn't changing */ - if (evp->me_state == newstate) - return (0); - - mevent_qlock(); + if (evp->me_state != newstate) { + evp->me_state = newstate; - evp->me_state = newstate; - - /* - * Place the entry onto the changed list if not already there. - */ - if (evp->me_cq == 0) { - evp->me_cq = 1; - LIST_REMOVE(evp, me_list); - LIST_INSERT_HEAD(&change_head, evp, me_list); - mevent_notify(); + /* + * Place the entry onto the changed list if not + * already there. + */ + if (evp->me_cq == 0) { + evp->me_cq = 1; + LIST_REMOVE(evp, me_list); + LIST_INSERT_HEAD(&change_head, evp, me_list); + mevent_notify(); + } } mevent_qunlock(); @@ -367,14 +355,14 @@ int mevent_enable(struct mevent *evp) { - return (mevent_update(evp, MEV_ENABLE)); + return (mevent_update(evp, true)); } int mevent_disable(struct mevent *evp) { - return (mevent_update(evp, MEV_DISABLE)); + return (mevent_update(evp, false)); } static int @@ -392,7 +380,7 @@ mevent_delete_event(struct mevent *evp, int closefd) LIST_INSERT_HEAD(&change_head, evp, me_list); mevent_notify(); } - evp->me_state = MEV_DEL_PENDING; + evp->me_state = EV_DELETE; if (closefd) evp->me_closefd = 1;
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201911261812.xAQICD3l077943>