[lxc-devel] [lxd/master] Avoid race condition in network fill function

stgraber on Github lxc-bot at linuxcontainers.org
Sun Nov 27 02:51:13 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 354 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20161127/74a98fd0/attachment.bin>
-------------- next part --------------
From 345c9909a2a0c6535b6a477a5a78a9b73fb6b2ca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sat, 26 Nov 2016 21:50:53 -0500
Subject: [PATCH] Avoid race condition in network fill function
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/container_lxc.go | 72 ++++++++++++++++++++++++++++++----------------------
 lxd/db_containers.go |  9 +++++++
 2 files changed, 51 insertions(+), 30 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index fe416be..82a91d3 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -5254,6 +5254,26 @@ func (c *containerLXC) fillNetworkDevice(name string, m shared.Device) (shared.D
 		}
 	}
 
+	updateKey := func(key string, value string) error {
+		tx, err := dbBegin(c.daemon.db)
+		if err != nil {
+			return err
+		}
+
+		err = dbContainerConfigInsert(tx, c.id, map[string]string{key: value})
+		if err != nil {
+			tx.Rollback()
+			return err
+		}
+
+		err = txCommit(tx)
+		if err != nil {
+			return err
+		}
+
+		return nil
+	}
+
 	// Fill in the MAC address
 	if m["nictype"] != "physical" && m["hwaddr"] == "" {
 		configKey := fmt.Sprintf("volatile.%s.hwaddr", name)
@@ -5265,24 +5285,20 @@ func (c *containerLXC) fillNetworkDevice(name string, m shared.Device) (shared.D
 				return nil, err
 			}
 
-			c.localConfig[configKey] = volatileHwaddr
-			c.expandedConfig[configKey] = volatileHwaddr
-
 			// Update the database
-			tx, err := dbBegin(c.daemon.db)
+			err = updateKey(configKey, volatileHwaddr)
 			if err != nil {
-				return nil, err
-			}
-
-			err = dbContainerConfigInsert(tx, c.id, map[string]string{configKey: volatileHwaddr})
-			if err != nil {
-				tx.Rollback()
-				return nil, err
-			}
+				// Check if something else filled it in behind our back
+				value, err1 := dbContainerConfigGet(c.daemon.db, c.id, configKey)
+				if err1 != nil || value == "" {
+					return nil, err
+				}
 
-			err = txCommit(tx)
-			if err != nil {
-				return nil, err
+				c.localConfig[configKey] = value
+				c.expandedConfig[configKey] = value
+			} else {
+				c.localConfig[configKey] = volatileHwaddr
+				c.expandedConfig[configKey] = volatileHwaddr
 			}
 		}
 		newDevice["hwaddr"] = volatileHwaddr
@@ -5299,24 +5315,20 @@ func (c *containerLXC) fillNetworkDevice(name string, m shared.Device) (shared.D
 				return nil, err
 			}
 
-			c.localConfig[configKey] = volatileName
-			c.expandedConfig[configKey] = volatileName
-
 			// Update the database
-			tx, err := dbBegin(c.daemon.db)
-			if err != nil {
-				return nil, err
-			}
-
-			err = dbContainerConfigInsert(tx, c.id, map[string]string{configKey: volatileName})
+			err = updateKey(configKey, volatileName)
 			if err != nil {
-				tx.Rollback()
-				return nil, err
-			}
+				// Check if something else filled it in behind our back
+				value, err1 := dbContainerConfigGet(c.daemon.db, c.id, configKey)
+				if err1 != nil || value == "" {
+					return nil, err
+				}
 
-			err = txCommit(tx)
-			if err != nil {
-				return nil, err
+				c.localConfig[configKey] = value
+				c.expandedConfig[configKey] = value
+			} else {
+				c.localConfig[configKey] = volatileName
+				c.expandedConfig[configKey] = volatileName
 			}
 		}
 		newDevice["name"] = volatileName
diff --git a/lxd/db_containers.go b/lxd/db_containers.go
index 7cc9a3e..eec6e9c 100644
--- a/lxd/db_containers.go
+++ b/lxd/db_containers.go
@@ -224,6 +224,15 @@ func dbContainerConfigInsert(tx *sql.Tx, id int, config map[string]string) error
 	return nil
 }
 
+func dbContainerConfigGet(db *sql.DB, id int, key string) (string, error) {
+	q := "SELECT value FROM containers_config WHERE container_id=? AND key=?"
+	value := ""
+	arg1 := []interface{}{id, key}
+	arg2 := []interface{}{&value}
+	err := dbQueryRowScan(db, q, arg1, arg2)
+	return value, err
+}
+
 func dbContainerConfigRemove(db *sql.DB, id int, name string) error {
 	_, err := dbExec(db, "DELETE FROM containers_config WHERE key=? AND container_id=?", name, id)
 	return err


More information about the lxc-devel mailing list