Skip to content

Commit 933f35f

Browse files
committed
BUG/MEDIUM: stick-table: Decrement the ref count inside lock to kill a session
When we try to kill a session, the shard must be locked before decrementing the ref count on the session. Otherwise, the ref count can fall to 0 and a purge task (stktable_trash_oldest or process_table_expire) may release the session before we have the opportunity to acquire the lock on the shard to effectively kill the session. This could lead to a double free. Here is the scenario: Thread 1 Thread 2 sktsess_kill(ts) if (ATOMIC_DEC(&ts->ref_cnt) != 0) return /* here the ref count is 0 */ stktable_trash_oldest() LOCK(&sh_lock) if (!ATOMIC_LOAD(&ts->ref_cnf)) __stksess_free(ts) UNLOCK(&sh_lock) /* here the session was released */ LOCK(&sh_lock) __stksess_free(ts) <--- double free UNLOCK(&sh_lock) The bug was introduced in 2.9 by the commit 7968fe3 ("MEDIUM: stick-table: change the ref_cnt atomically"). The ref count must be decremented inside the lock for stksess_kill() and sktsess_kill_if_expired() function. This patch should fix the issue #2611. It must be backported as far as 2.9. On the 2.9, there is no sharding. All the table is locked. The patch will have to be adapted. (cherry picked from commit 9357873) Signed-off-by: Christopher Faulet <[email protected]>
1 parent 08cc270 commit 933f35f

File tree

2 files changed

+7
-9
lines changed

2 files changed

+7
-9
lines changed

include/haproxy/stick_table.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,6 @@ static inline void stksess_kill_if_expired(struct stktable *t, struct stksess *t
220220
uint shard;
221221
size_t len;
222222

223-
if (decrefcnt && HA_ATOMIC_SUB_FETCH(&ts->ref_cnt, 1) != 0)
224-
return;
225-
226223
if (t->expire != TICK_ETERNITY && tick_is_expired(ts->expire, now_ms)) {
227224
if (t->type == SMP_T_STR)
228225
len = strlen((const char *)ts->key.key);
@@ -235,9 +232,12 @@ static inline void stksess_kill_if_expired(struct stktable *t, struct stksess *t
235232
ALREADY_CHECKED(shard);
236233

237234
HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->shards[shard].sh_lock);
238-
__stksess_kill_if_expired(t, ts);
235+
if (!decrefcnt || !HA_ATOMIC_SUB_FETCH(&ts->ref_cnt, 1))
236+
__stksess_kill_if_expired(t, ts);
239237
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->shards[shard].sh_lock);
240238
}
239+
else if (decrefcnt)
240+
HA_ATOMIC_SUB_FETCH(&ts->ref_cnt, 1);
241241
}
242242

243243
/* sets the stick counter's entry pointer */

src/stick_table.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,10 +170,7 @@ int stksess_kill(struct stktable *t, struct stksess *ts, int decrefcnt)
170170
{
171171
uint shard;
172172
size_t len;
173-
int ret;
174-
175-
if (decrefcnt && HA_ATOMIC_SUB_FETCH(&ts->ref_cnt, 1) != 0)
176-
return 0;
173+
int ret = 0;
177174

178175
if (t->type == SMP_T_STR)
179176
len = strlen((const char *)ts->key.key);
@@ -186,7 +183,8 @@ int stksess_kill(struct stktable *t, struct stksess *ts, int decrefcnt)
186183
ALREADY_CHECKED(shard);
187184

188185
HA_RWLOCK_WRLOCK(STK_TABLE_LOCK, &t->shards[shard].sh_lock);
189-
ret = __stksess_kill(t, ts);
186+
if (!decrefcnt || !HA_ATOMIC_SUB_FETCH(&ts->ref_cnt, 1))
187+
ret = __stksess_kill(t, ts);
190188
HA_RWLOCK_WRUNLOCK(STK_TABLE_LOCK, &t->shards[shard].sh_lock);
191189

192190
return ret;

0 commit comments

Comments
 (0)