From: Martin Willi Date: Fri, 16 Feb 2024 09:59:11 +0000 (+0100) Subject: child-sa: Handle refcount overflow for unique mark/if_id allocation gracefully X-Git-Tag: android-2.5.0~9^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1a740bf3f3e20c8a7ff88ca9dcf043fb051670a7;p=thirdparty%2Fstrongswan.git child-sa: Handle refcount overflow for unique mark/if_id allocation gracefully The refcount_t for allocating unique marks and interface IDs may overflow or hit the special value for unique marks/if_ids, in the worst case not setting it on CHILD_SAs that should have one. As (potentially two) marks/if_ids are allocated only for newly created CHILD_SAs, but not for rekeying, this not very likely. Still, if a setup uses aggressive re-authentication and or re-creates CHILD_SAs every minute, a gateway with 100'000 tunnels may hit the overflow within a month uptime. --- diff --git a/src/libstrongswan/ipsec/ipsec_types.c b/src/libstrongswan/ipsec/ipsec_types.c index 6f10adf703..7d93f275a4 100644 --- a/src/libstrongswan/ipsec/ipsec_types.c +++ b/src/libstrongswan/ipsec/ipsec_types.c @@ -150,30 +150,44 @@ bool mark_from_string(const char *value, mark_op_t ops, mark_t *mark) return TRUE; } +/** + * Allocate a unique mark that is non-zero and not one of the special values. + */ +static uint32_t allocate_unique_mark() +{ + static refcount_t unique_mark = 0; + uint32_t m; + + m = ref_get_nonzero(&unique_mark); + while (MARK_IS_UNIQUE(m)) + { + m = ref_get_nonzero(&unique_mark); + } + return m; +} + /* * Described in header */ void allocate_unique_marks(uint32_t *in, uint32_t *out) { - static refcount_t unique_mark = 0; - if (MARK_IS_UNIQUE(*in) || MARK_IS_UNIQUE(*out)) { - refcount_t mark = 0; + uint32_t mark = 0; bool unique_dir = *in == MARK_UNIQUE_DIR || *out == MARK_UNIQUE_DIR; if (!unique_dir) { - mark = ref_get(&unique_mark); + mark = allocate_unique_mark(); } if (MARK_IS_UNIQUE(*in)) { - *in = unique_dir ? ref_get(&unique_mark) : mark; + *in = unique_dir ? allocate_unique_mark() : mark; } if (MARK_IS_UNIQUE(*out)) { - *out = unique_dir ? ref_get(&unique_mark) : mark; + *out = unique_dir ? allocate_unique_mark() : mark; } } } @@ -219,30 +233,46 @@ bool if_id_from_string(const char *value, uint32_t *if_id) return TRUE; } +/** + * Allocate a unique interface ID that is non-zero and not one of the special + * values. + */ +static uint32_t allocate_unique_if_id() +{ + static refcount_t unique_if_id = 0; + uint32_t if_id; + + if_id = ref_get_nonzero(&unique_if_id); + while (IF_ID_IS_UNIQUE(if_id)) + { + if_id = ref_get_nonzero(&unique_if_id); + } + return if_id; +} + /* * Described in header */ void allocate_unique_if_ids(uint32_t *in, uint32_t *out) { - static refcount_t unique_if_id = 0; if (IF_ID_IS_UNIQUE(*in) || IF_ID_IS_UNIQUE(*out)) { - refcount_t if_id = 0; + uint32_t if_id = 0; bool unique_dir = *in == IF_ID_UNIQUE_DIR || *out == IF_ID_UNIQUE_DIR; if (!unique_dir) { - if_id = ref_get(&unique_if_id); + if_id = allocate_unique_if_id(); } if (IF_ID_IS_UNIQUE(*in)) { - *in = unique_dir ? ref_get(&unique_if_id) : if_id; + *in = unique_dir ? allocate_unique_if_id() : if_id; } if (IF_ID_IS_UNIQUE(*out)) { - *out = unique_dir ? ref_get(&unique_if_id) : if_id; + *out = unique_dir ? allocate_unique_if_id() : if_id; } } }