From: Frederic Lecaille Date: Fri, 23 Oct 2026 16:01:06 +0000 (+0200) Subject: BUG/MINOR: peers: don't use remote table definitions from a previous session X-Git-Tag: v3.5-dev10~44 X-Git-Url: https://git.kaiwu.me/$%7BGITURL%7D$%7Bcid%7D?a=commitdiff_plain;h=eaeeb9e9a49dcb53e340dd8d71fc3bc12c823c59;p=haproxy.git BUG/MINOR: peers: don't use remote table definitions from a previous session The remote table ID and the list of data types announced by the remote peer in table definition messages are stored in the shared table and are never reset when the session ends. Since remote_id defaults to 0, a table switch message designating table ID 0 selects any table the peer never defined, and after a reconnection, a table switch message referencing a table ID defined during the previous session selects it with the previous session's data types, without the checks performed on definition messages (e.g. the key type). In both cases the update that follows is still decoded: the entry key is inserted into the local table even when no data type is announced, so such a peer can create entries carrying only a key and no value. On a small table this can evict legitimate entries. Let's reset the remote definitions when the session ends, and never match table ID 0 in switch messages. Reproducibility: both issues require a received "stick-table switch" message (type 131). HAProxy has never emitted this message: since the peers protocol 2.0 was introduced in 2015 (b3971ab06), the function in charge of building it, peer_prepare_switchmsg(), actually emits a table definition message (type 130), and doc/peers.txt documents type 131 as unused. However, the message is part of the documented protocol and its type is still assigned, so a third-party or modified implementation may well emit it, possibly in good faith, since its name and semantics describe exactly a table selection by ID. It is therefore reachable by a non-HAProxy peer, but not by any version of HAProxy itself. Note that the statement in the original report about a conforming peer which "always defines a table before switching to it" does not hold either, as HAProxy never switches at all. This must be backported as far as 3.0. --- diff --git a/src/peers.c b/src/peers.c index 2ef410806..4fffcf352 100644 --- a/src/peers.c +++ b/src/peers.c @@ -1096,6 +1096,7 @@ static int peer_prepare_ackmsg(char *msg, size_t size, struct peer_prep_params * void __peer_session_deinit(struct peer *peer) { struct peers *peers = peer->peers; + struct shared_table *st; int thr; if (!peers || !peer->appctx) @@ -1115,6 +1116,14 @@ void __peer_session_deinit(struct peer *peer) peer->remote_table = peer->last_local_table = peer->stop_local_table = NULL; peer->appctx = NULL; + /* table definitions received from the peer are only valid for the + * session's duration. + */ + for (st = peer->tables; st; st = st->next) { + st->remote_id = 0; + st->remote_data = 0; + } + /* reset teaching flags to 0 */ peer->flags &= ~PEER_TEACH_FLAGS; @@ -2324,7 +2333,8 @@ static inline int peer_treat_switchmsg(struct appctx *appctx, struct peer *p, p->remote_table = NULL; for (st = p->tables; st; st = st->next) { - if (st->remote_id == table_id) { + /* 0 is the ID of tables not defined by the peer */ + if (table_id && st->remote_id == table_id) { p->remote_table = st; TRACE_PRINTF(TRACE_LEVEL_DEVELOPER, PEERS_EV_PROTO_SWITCH, appctx, p, NULL, NULL, "table switch message successfully process (table=%s)", st->table->id);