From 39b3e4d4a2d44bb9e72fc09385296283e0c27ae9 Mon Sep 17 00:00:00 2001 From: Taiki Koshino Date: Wed, 2 Sep 2026 18:50:02 +0900 Subject: [PATCH v3] 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: https://www.postgresql.org/message-id/TY4PR01MB17374DD1C2B89248B8714F84394C22%40TY4PR01MB17374.jpnprd01.prod.outlook.com Backpatch-through: v4.3 --- src/query_cache/pool_memqcache.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/query_cache/pool_memqcache.c b/src/query_cache/pool_memqcache.c index bdd89eacc..7d90a6322 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,26 @@ 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); + 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'; - pool_md5_hash(strkey, strlen(strkey), buf); + /* + * 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.52.0