From 1824079fca90c12f258009ed9374d22d12200279 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 24 Oct 2025 07:46:53 +0200 Subject: [PATCH] BUG/MINOR: stick-tables: properly index string-type keys This is one of the rare pleasant surprises of fixing an almost 16-years old bug that remained unnoticed since the feature was implemented. In 1.4-dev7, commit 3bd697e071 ("[MEDIUM] Add stick table (persistence) management functions and types") introduced stick-tables with multiple key types, including strings, IP addresses and integers. Entries are coded in binary and their binary representation is indexed. A special case was made for strings in order to index them as zero-terminated strings. However, there's one subtlety. While strings indeed have a zero appended, they're still indexed using ebmb_insert(), which means that all the bytes till the configured size are indexed as well. And while these bytes generally come from a temporary storage that often contains zeroes, or that is longer than the configured string length and will result in truncation, it's not always the case and certain traffic patterns with certain configurations manage to occasionally present unpadded strings resulting in apparent duplicate keys appearing in the dump, as shown in GH issue #3161. It seems to be essentially reproducible at boot, and not to be particularly affected by mixed patterns. These keys are in fact not exact duplicates in memory, but everywhere they're used (including during synchronization), they are equal. What's interesting is that when this happens, one key can be presented to a peer with its own data and will be indexed as the only one, possibly replacing contents from the previous key, which might replace them again later once updated in turn. This is visible in the dump of the issue above, where key "localhost:8001" was split into two entries, one with a request count of one and the other with a request count of 499999, and indeed, all peers see only that last value, which overwrote the first one. This fix must be backported to all stable branches. Special kudos to Mark Wort for undelining that one. --- src/stick_table.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/stick_table.c b/src/stick_table.c index a328f45aa..9d4a75c19 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -703,7 +703,10 @@ struct stksess *__stktable_store(struct stktable *t, struct stksess *ts, uint sh { struct ebmb_node *eb; - eb = ebmb_insert(&t->shards[shard].keys, &ts->key, t->key_size); + if (t->type == SMP_T_STR) + eb = ebst_insert(&t->shards[shard].keys, &ts->key); + else + eb = ebmb_insert(&t->shards[shard].keys, &ts->key, t->key_size); if (likely(eb == &ts->key)) { ts->exp.key = ts->expire; eb32_insert(&t->shards[shard].exps, &ts->exp);