From aeadae53998b442f97a7027e6750c7977851bea3 Mon Sep 17 00:00:00 2001
From: Max Kellermann <mk@cm4all.com>
Date: Fri, 21 Jul 2023 13:34:02 +0200
Subject: [PATCH] util/IntrusiveHashSet: insert_check() returns the bucket head
 on success

The list head is a stable value that is guaranteed to be still valid
when insert_commit() gets called.

This fixes a linked list corruption bug in class StaticCache which
occurs when the cache item pointed to by the iterator gets evicted
between insert_check() and insert_commit().
---
 src/util/IntrusiveHashSet.hxx | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/util/IntrusiveHashSet.hxx b/src/util/IntrusiveHashSet.hxx
index 0cfd63635..01b86329e 100644
--- a/src/util/IntrusiveHashSet.hxx
+++ b/src/util/IntrusiveHashSet.hxx
@@ -227,7 +227,11 @@ public:
 			if (equal(key, i))
 				return {bucket.iterator_to(i), false};
 
-		return {bucket.begin(), true};
+		/* bucket.end() is a pointer to the bucket's list
+		   head, a stable value that is guaranteed to be still
+		   valid when insert_commit() gets called
+		   eventually */
+		return {bucket.end(), true};
 	}
 
 	/**
@@ -237,7 +241,10 @@ public:
 	 */
 	constexpr void insert_commit(bucket_iterator bucket, reference item) noexcept {
 		++counter;
-		GetBucket(item).insert(bucket, item);
+
+		/* using insert_after() so the new item gets inserted
+		   at the front of the bucket list */
+		GetBucket(item).insert_after(bucket, item);
 	}
 
 	/**