Version: v1.4.1 (aws-ebpf-sdk-go v1.0.15) · kernel 5.10 (AL2)
Behavior
A pod occasionally fails network policy enforcement with:
ERROR Ingress Pod State Map update failed: unable to update map: invalid argument
ERROR bpf_client.go:1272 unable to create/update map entry and ret -1 and err invalid argument
Just before it, the same pod's program load was logged as a success but with a short map set:
Prog Load Succeeded for ingress, progFD: N, pinpath: .../<podIdentifier>_handle_ingress, maps: 2
Root cause
Three NPA-side gaps. The map is dropped inside aws-ebpf-sdk-go (filed separately:
aws/aws-ebpf-sdk-go#167); NPA turns that into a pod-startup failure.
1. A partial map set is accepted as a successful load. loadBPFProgram only rejects an
empty set, so a 2-of-3 result passes and is logged as success:
bpf_client.go:994 if len(bpfData.Maps) == 0 { ... }
bpf_client.go:999 log().Infof("Prog Load Succeeded for %s, ... maps: %d", ..., len(bpfData.Maps))
2. No MapFD == 0 guard before the update. The missing key yields a zero-value BpfMap,
and the syscall is issued against fd 0:
bpf_client.go:1269 mapToUpdate = ingressProgInfo.Maps[utils.TC_INGRESS_POD_STATE_MAP] // absent → BpfMap{MapFD:0}
bpf_client.go:1272 mapToUpdate.CreateUpdateMapEntry(...) // fd 0 → EINVAL
3. DeleteBPFProbes deletes the per-identifier mutex while holding it, which lets two
attaches for the same identifier run concurrently and trigger the SDK bug:
bpf_client.go:870-872 lock := podIdentifierLock.LoadOrStore(X, &sync.Mutex{}); lock.Lock()
bpf_client.go:915 policyEndpointeBPFContext.Delete(X) // clears the program reuse cache
bpf_client.go:887 l.podIdentifierLock.Delete(X) // removes the mutex entry, still holding it
bpf_client.go:873 (deferred) lock.Unlock()
Deleting the entry while a waiter is parked on that mutex orphans it — the next caller's
LoadOrStore creates a different mutex. So AttacheBPFProbes(X) from
rpc_handler.EnforceNpToPod (rpc_handler.go:86) and from a reconciler
(policyendpoints_controller.go:336) can be inside the function at the same time. The reuse
cache was just cleared, so the gate at bpf_client.go:788 no longer dedupes and both call
LoadBpfFile with the same identifier — same pin paths, concurrently.
This affects a whole cohort at once: GetPodIdentifier (pkg/utils/utils.go:176-187) strips the
pod name's last -suffix, so all pods of a CronJob or ReplicaSet share one identifier and one
set of pin paths.
Version: v1.4.1 (aws-ebpf-sdk-go v1.0.15) · kernel 5.10 (AL2)
Behavior
A pod occasionally fails network policy enforcement with:
Just before it, the same pod's program load was logged as a success but with a short map set:
Root cause
Three NPA-side gaps. The map is dropped inside aws-ebpf-sdk-go (filed separately:
aws/aws-ebpf-sdk-go#167); NPA turns that into a pod-startup failure.
1. A partial map set is accepted as a successful load.
loadBPFProgramonly rejects anempty set, so a 2-of-3 result passes and is logged as success:
2. No
MapFD == 0guard before the update. The missing key yields a zero-valueBpfMap,and the syscall is issued against fd 0:
3.
DeleteBPFProbesdeletes the per-identifier mutex while holding it, which lets twoattaches for the same identifier run concurrently and trigger the SDK bug:
Deleting the entry while a waiter is parked on that mutex orphans it — the next caller's
LoadOrStorecreates a different mutex. SoAttacheBPFProbes(X)fromrpc_handler.EnforceNpToPod(rpc_handler.go:86) and from a reconciler(
policyendpoints_controller.go:336) can be inside the function at the same time. The reusecache was just cleared, so the gate at
bpf_client.go:788no longer dedupes and both callLoadBpfFilewith the same identifier — same pin paths, concurrently.This affects a whole cohort at once:
GetPodIdentifier(pkg/utils/utils.go:176-187) strips thepod name's last
-suffix, so all pods of a CronJob or ReplicaSet share one identifier and oneset of pin paths.