From 105d964ab08257e15cd7fd946bc15e6ff62074ac Mon Sep 17 00:00:00 2001 From: "R. Elliott Childre" Date: Mon, 18 May 2026 00:53:24 -0400 Subject: [PATCH] identification: Fix double-free when cloning empty IDs The clone() method was missing a branch when there is an encoded chunk of length 0 that still needed to be cloned. Otherwise, the destruction of the clone frees the same pointer that the original owns. This double free was found with an improved `fuzz_ids` fuzz harness and a two byte input to create an identification from "@#" or [0x40, 0x23]. It can also be triggered with `:#` e.g. `dns:#`. One of the problematic constructors is used to parse EAP-Identities, which are cloned before storing them in the auth-cfg. So this can be triggered by an unauthenticated attacker. Note that while the length check was already added with 418dbd624363 ("cloning %any ID without zero-byte memleak") and identities that trigger this can be created since 86ab5636c2c9 ("support for @#hex ID_KEY_ID identification_t"), it was the referenced commit that made the length check problematic. Fixes: 2147da40a5d7 ("simplified identification_t.clone() using memcpy") Fixes: CVE-2026-47895 --- .../tests/suites/test_identification.c | 23 +++++++++++++++++++ src/libstrongswan/utils/identification.c | 5 +--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/libstrongswan/tests/suites/test_identification.c b/src/libstrongswan/tests/suites/test_identification.c index db4043769239..565971a767d8 100644 --- a/src/libstrongswan/tests/suites/test_identification.c +++ b/src/libstrongswan/tests/suites/test_identification.c @@ -1408,6 +1408,28 @@ START_TEST(test_clone) } END_TEST +START_TEST(test_clone_empty) +{ + identification_t *a, *b; + chunk_t a_enc, b_enc; + + /* this produces an empty but non-NULL encoding, which previously caused a + * double-free when destroying a clone */ + a = identification_create_from_string("@#"); + ck_assert(a != NULL); + a_enc = a->get_encoding(a); + + b = a->clone(a); + ck_assert(b != NULL); + ck_assert(a != b); + b_enc = b->get_encoding(b); + ck_assert(a_enc.ptr != b_enc.ptr); + + b->destroy(b); + a->destroy(a); +} +END_TEST + Suite *identification_suite_create() { Suite *s; @@ -1465,6 +1487,7 @@ Suite *identification_suite_create() tc = tcase_create("clone"); tcase_add_test(tc, test_clone); + tcase_add_test(tc, test_clone_empty); suite_add_tcase(s, tc); return s; diff --git a/src/libstrongswan/utils/identification.c b/src/libstrongswan/utils/identification.c index d31955b38061..b8e68f8d53f5 100644 --- a/src/libstrongswan/utils/identification.c +++ b/src/libstrongswan/utils/identification.c @@ -1586,10 +1586,7 @@ METHOD(identification_t, clone_, identification_t*, private_identification_t *clone = malloc_thing(private_identification_t); memcpy(clone, this, sizeof(private_identification_t)); - if (this->encoded.len) - { - clone->encoded = chunk_clone(this->encoded); - } + clone->encoded = chunk_clone(this->encoded); return &clone->public; } -- 2.43.0