cbor: install allocation cap to mitigate oversized cbor_load() alloc#985
Open
nabbi wants to merge 1 commit into
Open
cbor: install allocation cap to mitigate oversized cbor_load() alloc#985nabbi wants to merge 1 commit into
nabbi wants to merge 1 commit into
Conversation
…tions A malicious or malfunctioning authenticator can craft a tiny CBOR message that declares a huge array/map element count, causing cbor_load() to attempt a multi-gigabyte allocation before any element data is read. See PJK/libcbor#418 (closed won't-fix) and PJK/libcbor#422 (which documents cbor_set_allocs() as the consumer-side mitigation). fido_init() now installs a capping allocator (src/cbor_alloc.c) that bounds total live libcbor allocations to FIDO_CBOR_MAX_ALLOC (64MB by default), causing cbor_load() to fail with CBOR_ERR_MEMERROR instead of attempting an oversized allocation. This is independent of fuzz/README's libcbor patch, which is a much stricter, fuzzing-only mitigation. Documented in SECURITY-CONSIDERATIONS.md and fido_init(3), with a regression test in regress/dev.c.
Contributor
|
Is there something wrong with |
Author
Following upstream libcbor guidance of cbor_set_allocs. |
Contributor
|
Hello, Thanks for your contribution, but I'd vote against it. libfido2 is a library, and the capping of the allocated memory via libfido2 already does what it is supposed to do: properly handle the error condition when Additionally, the CBOR object is read from the authenticator, which is supposed to be trusted. |
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
cbor_load()(libcbor) pre-allocates storage for definite-length CBORarrays/maps sized by the element count declared in the message header,
before reading any element data. The only check on that count is an
integer-overflow check — there's no bound relative to the size of the
input itself. A handful of bytes can therefore make
cbor_load()attemptan allocation of an arbitrary size.
In libfido2,
cbor_load()is reachable from a FIDO authenticator's CTAPresponses (
src/assert.c,src/cbor.c,src/cred.c,src/credman.c,src/largeblob.c,src/winhello.c). A malicious or malfunctioningauthenticator can therefore use a tiny CBOR message to make a host
process attempt a multi-gigabyte (or larger) allocation. The existing
FIDO_MAXMSG/FIDO_MAXMSG_CREDlimits don't help here — they bound thenumber of bytes read, not the element count declared inside those
bytes.
This was reported upstream as
PJK/libcbor#418 (a 5-byte
input triggering a ~2.3GB allocation, a 9-byte input triggering a ~128GB
allocation) and closed won't-fix — libcbor considers this the
consumer's responsibility.
PJK/libcbor#422 (merged in
0.14.0) documents the recommended mitigation: install a capping allocator
via
cbor_set_allocs()(seeexamples/capped_alloc.cin libcbor).Fix
fido_init()now callsfido_init_cbor_allocs()(new filesrc/cbor_alloc.c), which installs a capping allocator viacbor_set_allocs(). It tracks the total size of live libcbor allocationsand rejects any allocation that would push the running total above
FIDO_CBOR_MAX_ALLOC(src/fido/param.h, 64MB by default, overridableat build time). Once the budget is exhausted,
cbor_load()returnsNULLwith
result.error.code == CBOR_ERR_MEMERROR, which everycbor_load()caller in libfido2 already treats as an ordinary decode failure — no
caller-side changes needed.
64MB is well above
FIDO_MAXMSG(2048B),FIDO_MAXMSG_CRED(4096B), andany realistic
largeBlobarray, but far below a level that could exhaustprocess memory or trigger the OOM killer.
Since
cbor_set_allocs()configures a single, process-wide set offunction pointers, and
fido_init()is documented (fido_init(3)) to becalled once per thread, the allocation counter is
TLS— a per-threadbudget, per the alternative suggested in PJK/libcbor#422.
Not related to
fuzz/README's libcbor patchfuzz/READMEdocuments a separate, fuzzing-only libcbor patch that caps_cbor_alloc_multipleat 1000 items, to bound ASAN/MSAN/UBSAN memoryduring fuzzing. That patch is far too restrictive for production (it'd
reject legitimate messages with >1000 elements) and only applies to the
local libcbor build used by the fuzz harness. This PR's mitigation ships
in libfido2 itself, applies to every build, and uses a memory budget
rather than an element-count limit so it doesn't reject legitimate large
messages.
That
fuzz/READMEpatch was first added in 2018 (~7 years ago, relativeto libcbor 0.10.1) — the underlying allocation-sizing issue it works
around upstream has been around at least that long, and PJK/libcbor#418
is simply the most recent (2024) report of it.
Changes
src/cbor_alloc.c(new): capping allocator, installed viacbor_set_allocs()src/dev.c:fido_init()callsfido_init_cbor_allocs()src/fido/param.h: newFIDO_CBOR_MAX_ALLOC(64MB default)src/extern.h,src/CMakeLists.txt: wire up the new TUregress/dev.c: newcbor_alloc_cap()regression test — feedscbor_load()a 9-byte array header declaring 268M elements (wouldotherwise provoke a ~2GB allocation) and asserts it's rejected with
CBOR_ERR_MEMERRORSECURITY-CONSIDERATIONS.md(new),README.adoc,man/fido_init.3:document the threat model and mitigation
Test plan
cbor_alloc_cap()inregress/dev.cpassesregress/devsuite still passes (capping allocator doesn'taffect normal-sized CBOR messages)
References
cbor_load()allocations from tiny inputs(won't-fix upstream)
cbor_set_allocs()capping-allocator mitigation(
examples/capped_alloc.c)