]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
child-sa: Handle refcount overflow for unique mark/if_id allocation gracefully
authorMartin Willi <martin@strongswan.org>
Fri, 16 Feb 2024 09:59:11 +0000 (10:59 +0100)
committerTobias Brunner <tobias@strongswan.org>
Fri, 16 Feb 2024 13:04:17 +0000 (14:04 +0100)
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.

src/libstrongswan/ipsec/ipsec_types.c

index 6f10adf703fbf855888ac5e1a72973b5d8693472..7d93f275a4ef87aa8a0307ae8cd9227a9bd9f13c 100644 (file)
@@ -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;
                }
        }
 }