From: Tobias Brunner Date: Mon, 11 Aug 2025 12:24:16 +0000 (+0200) Subject: ike-sa-manager: Avoid deadlock due to race condition during shutdown X-Git-Tag: 6.0.3dr1~9 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d662a69d9dd2d9cb8816307d30ec052e69102f73;p=thirdparty%2Fstrongswan.git ike-sa-manager: Avoid deadlock due to race condition during shutdown If an entry is added while we wait for a checked out SA in flush() (e.g. due to an action performed by that SA), new entries might get inserted before the one we wait for. If that was the first entry in the row, we didn't correctly update the table and the new entries were basically lost by overwriting the first entry in the row. As the SA count was still increased but the new entries couldn't get enumerated, the daemon wasn't terminated properly but was stuck in the loop in flush(). --- diff --git a/src/libcharon/sa/ike_sa_manager.c b/src/libcharon/sa/ike_sa_manager.c index fca61ce7c8..7796efb658 100644 --- a/src/libcharon/sa/ike_sa_manager.c +++ b/src/libcharon/sa/ike_sa_manager.c @@ -677,14 +677,15 @@ static void remove_entry(private_ike_sa_manager_t *this, entry_t *entry) } /** - * Remove the entry at the current enumerator position. + * Remove the entry at the current enumerator position. This only works for a + * single concurrent thread. */ static void remove_entry_at(private_enumerator_t *this) { this->entry = NULL; if (this->current) { - table_item_t *current = this->current; + table_item_t *current = this->current, *prev; ignore_result(ref_put(&this->manager->total_sa_count)); this->current = this->prev; @@ -695,7 +696,22 @@ static void remove_entry_at(private_enumerator_t *this) } else { - this->manager->ike_sa_table[this->row] = current->next; + /* we started from the beginning of the row, but while we waited + * for the entry in flush(), one or more entries might have been + * added, start from the beginning again in either case */ + prev = this->manager->ike_sa_table[this->row]; + if (prev != current) + { + while (prev->next != current) + { + prev = prev->next; + } + prev->next = current->next; + } + else + { + this->manager->ike_sa_table[this->row] = current->next; + } unlock_single_segment(this->manager, this->segment); } free(current);