https://github.com/cilium/cilium
Revision cc50936a67901f04b83210c821264e8769bd3551 authored by Joe Stringer on 07 August 2023, 20:25:21 UTC, committed by Joe Stringer on 10 August 2023, 16:13:05 UTC
[ upstream commit 74530b1880ec080959eb95aa71d6ce1d7101570b ]

Commit c96b9d81a8a6 ("ipcache: Remove superfluous if condition")
introduced a double-free for cases where the ipcache allocates a new
(reference to a) security identity and there is already an identity
corresponding to those labels which another subsystem injected directly
into the ipcache via Upsert() and friends. This commit fixes the issue.

IPCache has two sets of APIs:
- Older APIs like Upsert() / UpsertGeneratedIdentities which require the
  caller to retain reference counts against each Identity and balance
  allocate/release calls.
- Newer APIs like UpsertMetadata() / UpsertLabels() / UpsertPrefixes()
  which delegate the reference counting responsibility to the ipcache
  itself, which will hold one reference to the identity for all callers
  of the APIs.

InjectLabels() is responsible for reference counting the identities
associated with ipcache entries that it manages. In order to ensure
balanced allocate and release, this function allocates and retains one
reference to the identity for itself internally within this function.
Under normal operation it will typically release this reference again
once it has performed the ipcache update operations. It may be triggered
multiple times for the same prefix, in which case it should only ever
acquire one reference to the identity, otherwise the references could
leak and the identity would not be freed.

There is one exception to the "always allocate" + "always" release
behaviour in InjectLabels(): Given that the ipcache needs to hold
exactly one reference to the identity over time to ensure it remains
live while in use by the ipcache, the very first time that
InjectLabels() allocates the identity reference, it must hold onto the
reference and not immediately release it. In particular, if another
subsystem injected an ipcache entry via older APIs, then the first time
that InjectLabels() runs, it also needs to acquire and hold a reference
to the identity, even though the underlying ipcache already contains an
CIDR -> Identity mapping that was injected via the older Upsert() API.

In commit c96b9d81a8a6 ("ipcache: Remove superfluous if condition"), the
check which was ensuring that ipcache entries that transition from being
managed by older APIs towards newer APIs would not trigger allocation of
a new identity reference to be held by the ipcache itself. The result
would be that the resting number of references to the identity would be
one less than what is necessary to keep the identity alive. If the other
subsystem ever released its reference to the identity, then that would
be the last reference and the identity would be freed, despite still
being in use.

This scenario leads to policy recalculation that removes any datapath
allow rules for the corresponding CIDRs, ultimately resulting in packet
loss for the impacted CIDRs. One such example involves CIDR identity
restore startup logic in the daemon. That path allocates identities then
injects them into the ipcache using older APIs. If any such CIDRs are
used by network policies, then the network policies subsystem will
insert the CIDR into the ipcache using newer ipcache APIs. Given this
mix of API usage and the bug introduced in the identity reference
counting internally in ipcache, this triggers the bug.

This commit fixes it by checking whether InjectLabels() already assumed
ownership over the underlying ipcache entry, which implies that the
ipcache already holds a reference to the identity on behalf of all newer
API users. During subsequent calls to InjectLabels(), the logic checks
that the ipcache entry was installed by this function and accounts for
the fact that a prior iteration had allocated an identity reference. It
can then safely release references that were acquired during the current
execution of the function.

Fixes: c96b9d81a8a6 ("ipcache: Remove superfluous if condition")
Reported-by: Boris Petrovic <carnerito.b@gmail.com>
Reported-by: Kim-Eirik Karlsen <kim.eirik@gmail.com>
Reported-by: Jason Witkowski <jason@witkow.ski>
Signed-off-by: Joe Stringer <joe@cilium.io>
Signed-off-by: Maciej Kwiek <maciej@isovalent.com>
1 parent 89c5f6a
History
Tip revision: cc50936a67901f04b83210c821264e8769bd3551 authored by Joe Stringer on 07 August 2023, 20:25:21 UTC
ipcache: Fix refcounting with mix of APIs
Tip revision: cc50936
File Mode Size
.devcontainer
.github
.nvim
.travis
.vscode
Documentation
api
bpf
bugtool
cilium
cilium-health
clustermesh-apiserver
contrib
daemon
envoy
examples
hack
hubble-relay
images
install
jenkinsfiles
kvstoremesh
operator
pkg
plugins
test
tools
vendor
.authors.aux -rw-r--r-- 416 bytes
.clang-format -rw-r--r-- 3.9 KB
.clomonitor.yml -rw-r--r-- 984 bytes
.gitattributes -rw-r--r-- 887 bytes
.gitignore -rw-r--r-- 1.8 KB
.golangci.yaml -rw-r--r-- 3.6 KB
.mailmap -rw-r--r-- 6.5 KB
.travis.yml -rw-r--r-- 506 bytes
AUTHORS -rw-r--r-- 41.5 KB
CHANGELOG.md -rw-r--r-- 112.0 KB
CODEOWNERS -rw-r--r-- 11.2 KB
CODE_OF_CONDUCT.md -rw-r--r-- 2.2 KB
CONTRIBUTING.md -rw-r--r-- 691 bytes
FURTHER_READINGS.rst -rw-r--r-- 6.4 KB
LICENSE -rw-r--r-- 11.1 KB
MAINTAINERS.md -rw-r--r-- 4.3 KB
Makefile -rw-r--r-- 34.9 KB
Makefile.defs -rw-r--r-- 6.9 KB
Makefile.docker -rw-r--r-- 7.3 KB
Makefile.quiet -rw-r--r-- 818 bytes
README.rst -rw-r--r-- 19.4 KB
SECURITY.md -rw-r--r-- 1.0 KB
USERS.md -rw-r--r-- 28.5 KB
VERSION -rw-r--r-- 7 bytes
Vagrantfile -rw-r--r-- 14.9 KB
go.mod -rw-r--r-- 12.2 KB
go.sum -rw-r--r-- 152.7 KB
netlify.toml -rw-r--r-- 92 bytes
vagrant_box_defaults.rb -rw-r--r-- 334 bytes

README.rst

back to top