[libvirt] [PATCH 2/6] libxl: Fix races in libxl event code
Daniel Veillard
veillard at redhat.com
Fri Jan 25 16:36:36 UTC 2013
On Mon, Jan 21, 2013 at 12:22:20PM -0700, Jim Fehlig wrote:
> The libxl driver is racy in it's interactions with libxl and libvirt's
> event loop. The event loop can invoke callbacks after libxl has
> deregistered the event, and possibly access freed data associated with
> the event.
>
> This patch fixes the race by converting libxlDomainObjPrivate to a
> virObjectLockable, and locking it while executing libxl upcalls and
> libvirt event loop callbacks.
>
> Note that using the virDomainObj lock is not satisfactory since it may
> be desirable to hold the virDomainObj lock even when libxl events such
> as reading and writing to xenstore need processed.
> ---
> src/libxl/libxl_conf.h | 3 +
> src/libxl/libxl_driver.c | 140 ++++++++++++++++++++++++++++-------------------
> 2 files changed, 87 insertions(+), 56 deletions(-)
>
> diff --git a/src/libxl/libxl_conf.h b/src/libxl/libxl_conf.h
> index a3cce08..3d5a461 100644
> --- a/src/libxl/libxl_conf.h
> +++ b/src/libxl/libxl_conf.h
> @@ -35,6 +35,7 @@
> # include "capabilities.h"
> # include "configmake.h"
> # include "virportallocator.h"
> +# include "virobject.h"
>
>
> # define LIBXL_VNC_PORT_MIN 5900
> @@ -81,6 +82,8 @@ struct _libxlDriverPrivate {
> typedef struct _libxlDomainObjPrivate libxlDomainObjPrivate;
> typedef libxlDomainObjPrivate *libxlDomainObjPrivatePtr;
> struct _libxlDomainObjPrivate {
> + virObjectLockable parent;
> +
> /* per domain libxl ctx */
> libxl_ctx *ctx;
> libxl_evgen_domain_death *deathW;
> diff --git a/src/libxl/libxl_driver.c b/src/libxl/libxl_driver.c
> index d4f38e3..81c04ed 100644
> --- a/src/libxl/libxl_driver.c
> +++ b/src/libxl/libxl_driver.c
> @@ -59,18 +59,16 @@
> /* Number of Xen scheduler parameters */
> #define XEN_SCHED_CREDIT_NPARAM 2
>
> -struct libxlOSEventHookFDInfo {
> - libxlDomainObjPrivatePtr priv;
> - void *xl_priv;
> - int watch;
> -};
> -
> -struct libxlOSEventHookTimerInfo {
> +/* Object used to store info related to libxl event registrations */
> +typedef struct _libxlEventHookInfo libxlEventHookInfo;
> +typedef libxlEventHookInfo *libxlEventHookInfoPtr;
> +struct _libxlEventHookInfo {
> libxlDomainObjPrivatePtr priv;
> void *xl_priv;
> int id;
> };
>
> +static virClassPtr libxlDomainObjPrivateClass;
> static libxlDriverPrivatePtr libxl_driver = NULL;
>
> /* Function declarations */
> @@ -84,6 +82,20 @@ libxlVmStart(libxlDriverPrivatePtr driver, virDomainObjPtr vm,
> bool start_paused, int restore_fd);
>
> /* Function definitions */
> +static int
> +libxlDomainObjPrivateOnceInit(void)
> +{
> + if (!(libxlDomainObjPrivateClass = virClassNew(virClassForObjectLockable(),
> + "libxlDomainObjPrivate",
> + sizeof(libxlDomainObjPrivate),
> + NULL)))
> + return -1;
> +
> + return 0;
> +}
> +
> +VIR_ONCE_GLOBAL_INIT(libxlDomainObjPrivate)
> +
> static void
> libxlDriverLock(libxlDriverPrivatePtr driver)
> {
> @@ -97,14 +109,21 @@ libxlDriverUnlock(libxlDriverPrivatePtr driver)
> }
>
> static void
> +libxlEventHookInfoFree(void *obj)
> +{
> + VIR_FREE(obj);
> +}
> +
> +static void
> libxlFDEventCallback(int watch ATTRIBUTE_UNUSED,
> int fd,
> int vir_events,
> void *fd_info)
> {
> - struct libxlOSEventHookFDInfo *info = fd_info;
> + libxlEventHookInfoPtr info = fd_info;
> int events = 0;
>
> + virObjectLock(info->priv);
> if (vir_events & VIR_EVENT_HANDLE_READABLE)
> events |= POLLIN;
> if (vir_events & VIR_EVENT_HANDLE_WRITABLE)
> @@ -114,42 +133,37 @@ libxlFDEventCallback(int watch ATTRIBUTE_UNUSED,
> if (vir_events & VIR_EVENT_HANDLE_HANGUP)
> events |= POLLHUP;
>
> + virObjectUnlock(info->priv);
> libxl_osevent_occurred_fd(info->priv->ctx, info->xl_priv, fd, 0, events);
> }
>
> -static void
> -libxlFreeFDInfo(void *obj)
> -{
> - VIR_FREE(obj);
> -}
> -
> static int
> libxlFDRegisterEventHook(void *priv, int fd, void **hndp,
> short events, void *xl_priv)
> {
> int vir_events = VIR_EVENT_HANDLE_ERROR;
> - struct libxlOSEventHookFDInfo *fdinfo;
> + libxlEventHookInfoPtr info;
>
> - if (VIR_ALLOC(fdinfo) < 0) {
> + if (VIR_ALLOC(info) < 0) {
> virReportOOMError();
> return -1;
> }
>
> - fdinfo->priv = priv;
> - fdinfo->xl_priv = xl_priv;
> - *hndp = fdinfo;
> -
> if (events & POLLIN)
> vir_events |= VIR_EVENT_HANDLE_READABLE;
> if (events & POLLOUT)
> vir_events |= VIR_EVENT_HANDLE_WRITABLE;
> - fdinfo->watch = virEventAddHandle(fd, vir_events, libxlFDEventCallback,
> - fdinfo, libxlFreeFDInfo);
> - if (fdinfo->watch < 0) {
> - VIR_FREE(fdinfo);
> - return fdinfo->watch;
> + info->id = virEventAddHandle(fd, vir_events, libxlFDEventCallback,
> + info, libxlEventHookInfoFree);
> + if (info->id < 0) {
> + VIR_FREE(info);
> + return -1;
> }
>
> + info->priv = priv;
> + info->xl_priv = xl_priv;
> + *hndp = info;
> +
> return 0;
> }
>
> @@ -159,15 +173,18 @@ libxlFDModifyEventHook(void *priv ATTRIBUTE_UNUSED,
> void **hndp,
> short events)
> {
> - struct libxlOSEventHookFDInfo *fdinfo = *hndp;
> + libxlEventHookInfoPtr info = *hndp;
> int vir_events = VIR_EVENT_HANDLE_ERROR;
>
> + virObjectLock(info->priv);
> if (events & POLLIN)
> vir_events |= VIR_EVENT_HANDLE_READABLE;
> if (events & POLLOUT)
> vir_events |= VIR_EVENT_HANDLE_WRITABLE;
>
> - virEventUpdateHandle(fdinfo->watch, vir_events);
> + virEventUpdateHandle(info->id, vir_events);
> + virObjectUnlock(info->priv);
> +
> return 0;
> }
>
> @@ -176,29 +193,31 @@ libxlFDDeregisterEventHook(void *priv ATTRIBUTE_UNUSED,
> int fd ATTRIBUTE_UNUSED,
> void *hnd)
> {
> - struct libxlOSEventHookFDInfo *fdinfo = hnd;
> + libxlEventHookInfoPtr info = hnd;
> + libxlDomainObjPrivatePtr p = info->priv;
>
> - virEventRemoveHandle(fdinfo->watch);
> + virObjectLock(p);
> + virEventRemoveHandle(info->id);
> + virObjectUnlock(p);
> }
>
> static void
> libxlTimerCallback(int timer ATTRIBUTE_UNUSED, void *timer_info)
> {
> - struct libxlOSEventHookTimerInfo *info = timer_info;
> + libxlEventHookInfoPtr info = timer_info;
> + libxlDomainObjPrivatePtr p = info->priv;
>
> + virObjectLock(p);
> /* libxl expects the event to be deregistered when calling
> libxl_osevent_occurred_timeout, but we dont want the event info
> destroyed. Disable the timeout and only remove it after returning
> from libxl. */
> virEventUpdateTimeout(info->id, -1);
> - libxl_osevent_occurred_timeout(info->priv->ctx, info->xl_priv);
> + virObjectUnlock(p);
> + libxl_osevent_occurred_timeout(p->ctx, info->xl_priv);
> + virObjectLock(p);
> virEventRemoveTimeout(info->id);
> -}
> -
> -static void
> -libxlTimerInfoFree(void* obj)
> -{
> - VIR_FREE(obj);
> + virObjectUnlock(p);
> }
>
> static int
> @@ -207,13 +226,13 @@ libxlTimeoutRegisterEventHook(void *priv,
> struct timeval abs_t,
> void *xl_priv)
> {
> + libxlEventHookInfoPtr info;
> struct timeval now;
> struct timeval res;
> static struct timeval zero;
> - struct libxlOSEventHookTimerInfo *timer_info;
> - int timeout, timer_id;
> + int timeout;
>
> - if (VIR_ALLOC(timer_info) < 0) {
> + if (VIR_ALLOC(info) < 0) {
> virReportOOMError();
> return -1;
> }
> @@ -228,17 +247,17 @@ libxlTimeoutRegisterEventHook(void *priv,
> } else {
> timeout = res.tv_sec * 1000 + (res.tv_usec + 999) / 1000;
> }
> - timer_id = virEventAddTimeout(timeout, libxlTimerCallback,
> - timer_info, libxlTimerInfoFree);
> - if (timer_id < 0) {
> - VIR_FREE(timer_info);
> - return timer_id;
> + info->id = virEventAddTimeout(timeout, libxlTimerCallback,
> + info, libxlEventHookInfoFree);
> + if (info->id < 0) {
> + VIR_FREE(info);
> + return -1;
> }
>
> - timer_info->priv = priv;
> - timer_info->xl_priv = xl_priv;
> - timer_info->id = timer_id;
> - *hndp = timer_info;
> + info->priv = priv;
> + info->xl_priv = xl_priv;
> + *hndp = info;
> +
> return 0;
> }
>
> @@ -257,10 +276,13 @@ libxlTimeoutModifyEventHook(void *priv ATTRIBUTE_UNUSED,
> void **hndp,
> struct timeval abs_t ATTRIBUTE_UNUSED)
> {
> - struct libxlOSEventHookTimerInfo *timer_info = *hndp;
> + libxlEventHookInfoPtr info = *hndp;
>
> + virObjectLock(info->priv);
> /* Make the timeout fire */
> - virEventUpdateTimeout(timer_info->id, 0);
> + virEventUpdateTimeout(info->id, 0);
> + virObjectUnlock(info->priv);
> +
> return 0;
> }
>
> @@ -268,9 +290,12 @@ static void
> libxlTimeoutDeregisterEventHook(void *priv ATTRIBUTE_UNUSED,
> void *hnd)
> {
> - struct libxlOSEventHookTimerInfo *timer_info = hnd;
> + libxlEventHookInfoPtr info = hnd;
> + libxlDomainObjPrivatePtr p = info->priv;
>
> - virEventRemoveTimeout(timer_info->id);
> + virObjectLock(p);
> + virEventRemoveTimeout(info->id);
> + virObjectUnlock(p);
> }
>
> static const libxl_osevent_hooks libxl_event_callbacks = {
> @@ -287,12 +312,15 @@ libxlDomainObjPrivateAlloc(void)
> {
> libxlDomainObjPrivatePtr priv;
>
> - if (VIR_ALLOC(priv) < 0)
> + if (libxlDomainObjPrivateInitialize() < 0)
> + return NULL;
> +
> + if (!(priv = virObjectLockableNew(libxlDomainObjPrivateClass)))
> return NULL;
>
> if (libxl_ctx_alloc(&priv->ctx, LIBXL_VERSION, 0, libxl_driver->logger)) {
> VIR_ERROR(_("Failed libxl context initialization"));
> - VIR_FREE(priv);
> + virObjectUnref(priv);
> return NULL;
> }
>
> @@ -310,7 +338,7 @@ libxlDomainObjPrivateFree(void *data)
> libxl_evdisable_domain_death(priv->ctx, priv->deathW);
>
> libxl_ctx_free(priv->ctx);
> - VIR_FREE(priv);
> + virObjectUnref(priv);
> }
>
> /* driver must be locked before calling */
ACK, adding a new lock allows to synchronize access, fine, i just hope
there is no risk of deadlock, we will see :-)
Daniel
--
Daniel Veillard | Open Source and Standards, Red Hat
veillard at redhat.com | libxml Gnome XML XSLT toolkit http://xmlsoft.org/
http://veillard.com/ | virtualization library http://libvirt.org/
More information about the libvir-list
mailing list