commit ba196952f57f2cb22be75fa5a4f363035a111103
Author: Jim Fehlig <jfehlig@novell.com>
Date:   Fri May 21 07:52:09 2010 -0600

    Fix race in finding available vnc port
    
    The qemu driver contains a subtle race in the logic to find next
    available vnc port.  Currently it iterates through all available ports
    and returns the first for which bind(2) succeeds.  However it is possible
    that a previously issued port has not yet been bound by qemu, resulting
    in the same port used for a subsequent domain.
    
    This patch addresses the race by using a simple bitmap to "reserve" the
    ports allocated by libvirt.
    
    V2:
      - Put port bitmap in struct qemud_driver
      - Initialize bitmap in qemudStartup
    
    V3:
      - Check for failure of virBitmapGetBit
      - Additional check for port != -1 before calling virbitmapClearBit
    
    V4:
      - Check for failure of virBitmap{Set,Clear}Bit

Index: libvirt-0.7.6/src/qemu/qemu_conf.h
===================================================================
--- libvirt-0.7.6.orig/src/qemu/qemu_conf.h
+++ libvirt-0.7.6/src/qemu/qemu_conf.h
@@ -39,6 +39,7 @@
 #include "pci.h"
 #include "cpu_conf.h"
 #include "driver.h"
+#include "bitmap.h"
 
 #define qemudDebug(fmt, ...) do {} while(0)
 
@@ -145,6 +146,8 @@ struct qemud_driver {
     char *saveImageFormat;
 
     pciDeviceList *activePciHostdevs;
+
+    virBitmapPtr reservedVNCPorts;
 };
 
 typedef struct _qemuDomainPCIAddressSet qemuDomainPCIAddressSet;
Index: libvirt-0.7.6/src/qemu/qemu_driver.c
===================================================================
--- libvirt-0.7.6.orig/src/qemu/qemu_driver.c
+++ libvirt-0.7.6/src/qemu/qemu_driver.c
@@ -1016,6 +1016,11 @@ qemudStartup(int privileged) {
          virEventAddTimeout(-1, qemuDomainEventFlush, qemu_driver, NULL)) < 0)
         goto error;
 
+    /* Allocate bitmap for vnc port reservation */
+    if ((qemu_driver->reservedVNCPorts =
+         virBitmapAlloc(QEMU_VNC_PORT_MAX - QEMU_VNC_PORT_MIN)) == NULL)
+        goto out_of_memory;
+
     if (privileged) {
         if (virAsprintf(&qemu_driver->logDir,
                         "%s/log/libvirt/qemu", LOCAL_STATE_DIR) == -1)
@@ -1276,6 +1281,7 @@ qemudShutdown(void) {
     virCapabilitiesFree(qemu_driver->caps);
 
     virDomainObjListDeinit(&qemu_driver->domains);
+    virBitmapFree(qemu_driver->reservedVNCPorts);
 
     VIR_FREE(qemu_driver->securityDriverName);
     VIR_FREE(qemu_driver->logDir);
@@ -2076,13 +2082,22 @@ qemuInitPCIAddresses(struct qemud_driver
     return ret;
 }
 
-static int qemudNextFreeVNCPort(struct qemud_driver *driver ATTRIBUTE_UNUSED) {
+static int qemudNextFreeVNCPort(struct qemud_driver *driver) {
     int i;
 
     for (i = QEMU_VNC_PORT_MIN; i < QEMU_VNC_PORT_MAX; i++) {
         int fd;
         int reuse = 1;
         struct sockaddr_in addr;
+        bool used = false;
+
+        if (virBitmapGetBit(driver->reservedVNCPorts,
+                            i - QEMU_VNC_PORT_MIN, &used) < 0)
+            VIR_DEBUG("virBitmapGetBit failed on bit %d", i - QEMU_VNC_PORT_MIN);
+
+        if (used)
+            continue;
+
         addr.sin_family = AF_INET;
         addr.sin_port = htons(i);
         addr.sin_addr.s_addr = htonl(INADDR_ANY);
@@ -2098,6 +2113,12 @@ static int qemudNextFreeVNCPort(struct q
         if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
             /* Not in use, lets grab it */
             close(fd);
+            /* Add port to bitmap of reserved ports */
+            if (virBitmapSetBit(driver->reservedVNCPorts,
+                                i - QEMU_VNC_PORT_MIN) < 0) {
+                VIR_DEBUG("virBitmapSetBit failed on bit %d",
+                          i - QEMU_VNC_PORT_MIN);
+            }
             return i;
         }
         close(fd);
@@ -2802,8 +2823,16 @@ cleanup:
     qemuRemoveCgroup(conn, driver, vm, 0);
     if ((vm->def->ngraphics == 1) &&
         vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
-        vm->def->graphics[0]->data.vnc.autoport)
+        vm->def->graphics[0]->data.vnc.autoport &&
+        vm->def->graphics[0]->data.vnc.port != -1) {
+        if (virBitmapClearBit(driver->reservedVNCPorts,
+                              vm->def->graphics[0]->data.vnc.port - \
+                              QEMU_VNC_PORT_MIN) < 0) {
+            VIR_DEBUG("virBitmapClearBit failed on bit %d",
+                      vm->def->graphics[0]->data.vnc.port - QEMU_VNC_PORT_MIN);
+        }
         vm->def->graphics[0]->data.vnc.port = -1;
+    }
     if (logfile != -1)
         close(logfile);
     vm->def->id = -1;
@@ -2910,6 +2939,21 @@ retry:
 
     qemudRemoveDomainStatus(conn, driver, vm);
 
+    /* Remove VNC port from port reservation bitmap, but only if it was
+       reserved by the driver (autoport=yes)
+    */
+    if ((vm->def->ngraphics == 1) &&
+        vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC &&
+        vm->def->graphics[0]->data.vnc.autoport &&
+        vm->def->graphics[0]->data.vnc.port != -1) {
+        if (virBitmapClearBit(driver->reservedVNCPorts,
+                              vm->def->graphics[0]->data.vnc.port - \
+                              QEMU_VNC_PORT_MIN) < 0) {
+            VIR_DEBUG("virBitmapClearBit failed on bit %d",
+                      vm->def->graphics[0]->data.vnc.port - QEMU_VNC_PORT_MIN);
+       }
+    }
+
     vm->pid = -1;
     vm->def->id = -1;
     vm->state = VIR_DOMAIN_SHUTOFF;
