Skip to content

Commit 7a369c4

Browse files
fix: [WIN-NPM] concurrent map iteration and write for ipset selector reference (#1578)
fix concurrent map iteration and write for ipset selector reference
1 parent bb67582 commit 7a369c4

File tree

2 files changed

+32
-3
lines changed

2 files changed

+32
-3
lines changed

npm/pkg/dataplane/dataplane_windows.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,28 @@ func (dp *DataPlane) updatePod(pod *updateNPMPod) error {
142142

143143
// for every ipset we're removing from the endpoint, remove from the endpoint any policy that requires the set
144144
for _, setName := range pod.IPSetsToRemove {
145+
/*
146+
Scenarios:
147+
1. There's a chance a policy is/was just removed, but the ipset's selector hasn't been updated yet.
148+
We may try to remove the policy again here, which is ok.
149+
150+
2. If a policy is added to the ipset's selector after getting the selector (meaning dp.AddPolicy() was called),
151+
we won't try to remove the policy, which is fine since the policy must've never existed on the endpoint.
152+
153+
3. If a policy is added to the ipset's selector in a dp.AddPolicy() thread AFTER getting the selector here,
154+
then the ensuing policyMgr.AddPolicy() call will wait for this function to release the endpointCache lock.
155+
156+
4. If a policy is added to the ipset's selector in a dp.AddPolicy() thread BEFORE getting the selector here,
157+
there could be a race between policyMgr.RemovePolicy() here and policyMgr.AddPolicy() there.
158+
*/
145159
selectorReference, err := dp.ipsetMgr.GetSelectorReferencesBySet(setName)
146160
if err != nil {
147161
return err
148162
}
149163

150164
for policyKey := range selectorReference {
151165
// Now check if any of these network policies are applied on this endpoint.
152-
// If yes then proceed to delete the network policy
153-
// Remove policy should be deleting this netpol reference
166+
// If yes then proceed to delete the network policy.
154167
if _, ok := endpoint.netPolReference[policyKey]; ok {
155168
// Delete the network policy
156169
endpointList := map[string]string{
@@ -168,6 +181,18 @@ func (dp *DataPlane) updatePod(pod *updateNPMPod) error {
168181
// for every ipset we're adding to the endpoint, consider adding to the endpoint every policy that the set touches
169182
toAddPolicies := make(map[string]struct{})
170183
for _, setName := range pod.IPSetsToAdd {
184+
/*
185+
Scenarios:
186+
1. If a policy is added to the ipset's selector after getting the selector (meaning dp.AddPolicy() was called),
187+
we will miss adding the policy here, but will add the policy to all endpoints in that other thread, which has
188+
to wait on the endpointCache lock when calling getEndpointsToApplyPolicy().
189+
190+
2. We may add the policy here and in the dp.AddPolicy() thread if the policy is added to the ipset's selector before
191+
that other thread calls policyMgr.AddPolicy(), which is ok.
192+
193+
3. FIXME: If a policy is/was just removed, but the ipset's selector hasn't been updated yet,
194+
we may try to add the policy again here...
195+
*/
171196
selectorReference, err := dp.ipsetMgr.GetSelectorReferencesBySet(setName)
172197
if err != nil {
173198
return err

npm/pkg/dataplane/ipsets/ipsetmanager_windows.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,11 @@ func (iMgr *IPSetManager) GetSelectorReferencesBySet(setName string) (map[string
136136
fmt.Sprintf("[ipset manager] selector ipset %s does not exist", setName))
137137
}
138138
set := iMgr.setMap[setName]
139-
return set.SelectorReference, nil
139+
m := make(map[string]struct{}, len(set.SelectorReference))
140+
for r := range set.SelectorReference {
141+
m[r] = struct{}{}
142+
}
143+
return m, nil
140144
}
141145

142146
func (iMgr *IPSetManager) validateSelectorIPSets(setList map[string]struct{}) error {

0 commit comments

Comments
 (0)