From dbb41fdb19ac15fe5e7660b41ea9a1eba93bb84b Mon Sep 17 00:00:00 2001 From: Taiki Koshino Date: Tue, 21 Jul 2026 13:40:58 +0900 Subject: [PATCH] Delimit query-cache key to prevent collisions I have reviewed the patch you provided. It has also passed all regression tests. encode_key() constructs the cache key using md5(user || query || database) without delimiters. This allows different sessions to produce identical keys (e.g., "admin" + "Q" + "testdb" and "adm" + "Q" + "intestdb"), leading to data leakage between users/databases. Changes: Explicit Delimiters: Insert NUL bytes between the user, query, and database fields in the buffer. Hash Full Range: Updated the hash function to process the entire byte range (including NUL separators) instead of stopping at the first NUL byte. eported-by: Emond Papegaaij Reported-by: Claude code Author: Taiki Koshino Discussion: Backpatch-through: v4.3 --- src/query_cache/pool_memqcache.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/query_cache/pool_memqcache.c b/src/query_cache/pool_memqcache.c index bdd89eacc..88f1efcfb 100644 --- a/src/query_cache/pool_memqcache.c +++ b/src/query_cache/pool_memqcache.c @@ -558,12 +558,16 @@ pool_fetch_cache(POOL_CONNECTION_POOL *backend, const char *query, char **buf, s /* * encode key. - * create cache key as md5(username + query string + database name) + * create cache key as md5(username + NUL + query string + NUL + database name + NUL). + * NUL separators prevent collisions between fields whose concatenations + * coincide (e.g. user="ab"+db="cd" vs user="a"+db="bcd"), which would + * otherwise let one authenticated user read another user's cached results. */ static char * encode_key(const char *s, char *buf, POOL_CONNECTION_POOL *backend) { char *strkey; + char *p; int u_length; int d_length; int q_length; @@ -581,13 +585,23 @@ encode_key(const char *s, char *buf, POOL_CONNECTION_POOL *backend) (errmsg("memcache encode key"), errdetail("query: \"%s\"", s))); - length = u_length + d_length + q_length + 1; + length = u_length + 1 + q_length + 1 + d_length + 1; strkey = (char *) palloc(sizeof(char) * length); - snprintf(strkey, length, "%s%s%s", backend->info->user, s, backend->info->database); - - pool_md5_hash(strkey, strlen(strkey), buf); + p = strkey; + memcpy(p, backend->info->user, u_length); + p += u_length; + *p++ = '\0'; + memcpy(p, s, q_length); + p += q_length; + *p++ = '\0'; + memcpy(p, backend->info->database, d_length); + p += d_length; + *p = '\0'; + + /* Hash the full delimited buffer (length - 1 so the final NUL is excluded). */ + pool_md5_hash(strkey, length - 1, buf); ereport(DEBUG1, (errmsg("memcache encode key"), errdetail("`%s' -> `%s'", strkey, buf))); -- 2.47.3