[libvirt] Potential segfault in udev driver

Daniel P. Berrange berrange at redhat.com
Tue Jan 26 09:53:02 UTC 2010


On Tue, Jan 26, 2010 at 03:17:42AM +0100, Matthias Bolte wrote:
> 2010/1/25 Dave Allan <dallan at redhat.com>:
> > On 01/25/2010 02:33 PM, Matthias Bolte wrote:
> >>
> >> 2010/1/25 Dave Allan<dallan at redhat.com>:
> >>>
> >>> On 01/25/2010 06:37 AM, Daniel P. Berrange wrote:
> >>>>
> >>>> On Sun, Jan 24, 2010 at 11:07:59PM +0100, Matthias Bolte wrote:
> >>>>>
> >>>>> udevDeviceMonitorStartup registers udevEventHandleCallback as event
> >>>>> handle, but doesn't store the returned watch id to remove it later on.
> >>>>> Also it's not clear to me whether the event handle should be register
> >>>>> for the whole lifetime of the udev driver instance or just for the
> >>>>> udevEnumerateDevices call.
> >>>>
> >>>> The handler should be active for the lifetime of libvirtd, since the
> >>>> udev driver has to detect hotplug/unplug events over time.
> >>>>
> >>>>>
> >>>>> If for example the call to udevSetupSystemDev [1] fails
> >>>>> udevDeviceMonitorShutdown is called to cleanup, but
> >>>>> udevEventHandleCallback is still registered and may be called when
> >>>>> driverState is NULL again, resulting in a segfault in
> >>>>> udevEventHandleCallback.
> >>>>>
> >>>>> So to solve this the udevEventHandleCallback event handle must be
> >>>>> removed at the appropriate place.
> >>>>
> >>>> Yes, sounds like its needs to be removed in the failure path there
> >>>
> >>> Matthias,
> >>>
> >>> Indeed, that's correct--can you submit a patch?
> >>>
> >>> Dave
> >>>
> >>
> >> Yes, im going to do that.
> >>
> >> One last question. The udev driver stores the udev monitor handle in
> >> the private data pointer of the gloval driver state variable.
> >>
> >>     driverState->privateData = udev_monitor;
> >>
> >> To solve the event handle problem we need to store the watch id
> >> returned by virEventAddHandle somewhere. We could either add a new
> >> private data struct to hold the udev_monitor pointer and the watch id,
> >> or store the watch id variable globally side by side with the driver
> >> state variable. I prefer the first option, because it's cleaner and
> >> the DRV_STATE_UDEV_MONITOR define allows for changing the storage
> >> location of the udev_monitor pointer easily.
> >
> > Yes, I agree--a struct is the right approach.  Thanks for doing this!
> >
> > Dave
> >
> 
> Here's the patch. Maximilian Wilhelm tested it.
> 
> Matthias

> From 07678696504179c70b987947061de2ff658e665c Mon Sep 17 00:00:00 2001
> From: Matthias Bolte <matthias.bolte at googlemail.com>
> Date: Tue, 26 Jan 2010 02:58:37 +0100
> Subject: [PATCH] udev: Remove event handle on shutdown
> 
> This fixes a segfault when the event handler is called after shutdown
> when the global driver state is NULL again.
> 
> Also fix a locking issue in an error path.
> ---
>  src/node_device/node_device_udev.c |   49 +++++++++++++++++++++++++++---------
>  src/node_device/node_device_udev.h |    4 ++-
>  2 files changed, 40 insertions(+), 13 deletions(-)
> 
> diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c
> index 2e459d1..a625d76 100644
> --- a/src/node_device/node_device_udev.c
> +++ b/src/node_device/node_device_udev.c
> @@ -41,6 +41,11 @@
>  
>  #define VIR_FROM_THIS VIR_FROM_NODEDEV
>  
> +struct _udevPrivate {
> +    struct udev_monitor *udev_monitor;
> +    int watch;
> +};
> +
>  static virDeviceMonitorStatePtr driverState = NULL;
>  
>  static int udevStrToLong_ull(char const *s,
> @@ -1354,12 +1359,18 @@ static int udevDeviceMonitorShutdown(void)
>  {
>      int ret = 0;
>  
> +    udevPrivate *priv = NULL;
>      struct udev_monitor *udev_monitor = NULL;
>      struct udev *udev = NULL;
>  
>      if (driverState) {
> -
>          nodeDeviceLock(driverState);
> +
> +        priv = driverState->privateData;
> +
> +        if (priv->watch != -1)
> +            virEventRemoveHandle(priv->watch);
> +
>          udev_monitor = DRV_STATE_UDEV_MONITOR(driverState);
>  
>          if (udev_monitor != NULL) {
> @@ -1375,7 +1386,7 @@ static int udevDeviceMonitorShutdown(void)
>          nodeDeviceUnlock(driverState);
>          virMutexDestroy(&driverState->lock);
>          VIR_FREE(driverState);
> -
> +        VIR_FREE(priv);
>      } else {
>          ret = -1;
>      }
> @@ -1534,18 +1545,28 @@ out:
>  
>  static int udevDeviceMonitorStartup(int privileged ATTRIBUTE_UNUSED)
>  {
> +    udevPrivate *priv = NULL;
>      struct udev *udev = NULL;
> -    struct udev_monitor *udev_monitor = NULL;
>      int ret = 0;
>  
> +    if (VIR_ALLOC(priv) < 0) {
> +        virReportOOMError(NULL);
> +        ret = -1;
> +        goto out;
> +    }
> +
> +    priv->watch = -1;
> +
>      if (VIR_ALLOC(driverState) < 0) {
>          virReportOOMError(NULL);
> +        VIR_FREE(priv);
>          ret = -1;
>          goto out;
>      }
>  
>      if (virMutexInit(&driverState->lock) < 0) {
>          VIR_ERROR0("Failed to initialize mutex for driverState");
> +        VIR_FREE(priv);
>          VIR_FREE(driverState);
>          ret = -1;
>          goto out;
> @@ -1562,18 +1583,19 @@ static int udevDeviceMonitorStartup(int privileged ATTRIBUTE_UNUSED)
>      udev = udev_new();
>      udev_set_log_fn(udev, udevLogFunction);
>  
> -    udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
> -    if (udev_monitor == NULL) {
> +    priv->udev_monitor = udev_monitor_new_from_netlink(udev, "udev");
> +    if (priv->udev_monitor == NULL) {
> +        VIR_FREE(priv);
> +        nodeDeviceUnlock(driverState);
>          VIR_ERROR0("udev_monitor_new_from_netlink returned NULL");
>          ret = -1;
>          goto out;
>      }
>  
> -    udev_monitor_enable_receiving(udev_monitor);
> +    udev_monitor_enable_receiving(priv->udev_monitor);
>  
>      /* udev can be retrieved from udev_monitor */
> -    driverState->privateData = udev_monitor;
> -    nodeDeviceUnlock(driverState);
> +    driverState->privateData = priv;
>  
>      /* We register the monitor with the event callback so we are
>       * notified by udev of device changes before we enumerate existing
> @@ -1583,14 +1605,17 @@ static int udevDeviceMonitorStartup(int privileged ATTRIBUTE_UNUSED)
>       * enumeration.  The alternative is to register the callback after
>       * we enumerate, in which case we will fail to create any devices
>       * that appear while the enumeration is taking place.  */
> -    if (virEventAddHandle(udev_monitor_get_fd(udev_monitor),
> -                          VIR_EVENT_HANDLE_READABLE,
> -                          udevEventHandleCallback,
> -                          NULL, NULL) == -1) {
> +    priv->watch = virEventAddHandle(udev_monitor_get_fd(priv->udev_monitor),
> +                                    VIR_EVENT_HANDLE_READABLE,
> +                                    udevEventHandleCallback, NULL, NULL);
> +    if (priv->watch == -1) {
> +        nodeDeviceUnlock(driverState);
>          ret = -1;
>          goto out;
>      }
>  
> +    nodeDeviceUnlock(driverState);
> +
>      /* Create a fictional 'computer' device to root the device tree. */
>      if (udevSetupSystemDev() != 0) {
>          ret = -1;
> diff --git a/src/node_device/node_device_udev.h b/src/node_device/node_device_udev.h
> index 6c83412..8367494 100644
> --- a/src/node_device/node_device_udev.h
> +++ b/src/node_device/node_device_udev.h
> @@ -23,8 +23,10 @@
>  #include <libudev.h>
>  #include <stdint.h>
>  
> +typedef struct _udevPrivate udevPrivate;
> +
>  #define SYSFS_DATA_SIZE 4096
> -#define DRV_STATE_UDEV_MONITOR(ds) ((struct udev_monitor *)((ds)->privateData))
> +#define DRV_STATE_UDEV_MONITOR(ds) (((udevPrivate *)((ds)->privateData))->udev_monitor)
>  #define DMI_DEVPATH "/sys/devices/virtual/dmi/id"
>  #define DMI_DEVPATH_FALLBACK "/sys/class/dmi/id"
>  #define PROPERTY_FOUND 0


ACK

Daniel
-- 
|: Red Hat, Engineering, London   -o-   http://people.redhat.com/berrange/ :|
|: http://libvirt.org  -o-  http://virt-manager.org  -o-  http://ovirt.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: GnuPG: 7D3B9505  -o-  F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|




More information about the libvir-list mailing list