libnetwork/datastore: prevent data races in Key()

The rootChain variable that the Key function references is a
package-global slice. As the append() built-in may append to the slice's
backing array in place, it is theoretically possible for the temporary
slices in concurrent Key() calls to share the same backing array, which
would be a data race. Thankfully in my tests (on Go 1.20.6)

    cap(rootChain) == len(rootChain)

held true, so in practice a new slice is always allocated and there is
no race. But that is a very brittle assumption to depend upon, which
could blow up in our faces at any time without warning. Rewrite the
implementation in a way which cannot lead to data races.

Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
Cory Snider 2023-07-24 12:18:04 -04:00
parent 96b473a0bd
commit 5ef9e2632f

View file

@ -149,9 +149,14 @@ func (cfg *ScopeCfg) IsValid() bool {
// Key provides convenient method to create a Key
func Key(key ...string) string {
keychain := append(rootChain, key...)
str := strings.Join(keychain, "/")
return str + "/"
var b strings.Builder
for _, parts := range [][]string{rootChain, key} {
for _, part := range parts {
b.WriteString(part)
b.WriteString("/")
}
}
return b.String()
}
// ParseKey provides convenient method to unpack the key to complement the Key function