From 04a67f8a840a1f6e24d30b9daab11434c0a34f20 Mon Sep 17 00:00:00 2001 From: Martin Vogel Date: Wed, 14 Oct 2026 19:11:39 +0200 Subject: [PATCH] fix(cypher): build the DISTINCT row key without snprintf-derived sizes CodeQL cpp/overflowing-snprintf (alert 85) flagged rb_row_key: it advanced `used` by snprintf's return value and passed `cap - used` as the next size, which would underflow if a call ever truncated. The buffer is sized for every column first, so it never did; the key is now copied with explicit bounds instead. The key bytes are unchanged, so DISTINCT results are identical. Signed-off-by: Martin Vogel --- src/cypher/cypher.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/cypher/cypher.c b/src/cypher/cypher.c index 5f29be2b7..b37ef6c0d 100644 --- a/src/cypher/cypher.c +++ b/src/cypher/cypher.c @@ -4016,10 +4016,20 @@ static const char *rb_row_key(const result_builder_t *rb, const char **row, char *cap = need; } size_t used = 0; - (*buf)[0] = '\0'; for (int c = 0; c < rb->col_count; c++) { - used += (size_t)snprintf(*buf + used, *cap - used, "%zu:%s", strlen(row[c]), row[c]); + size_t len = strlen(row[c]); + char prefix[CBM_SZ_32]; + int prefix_len = snprintf(prefix, sizeof(prefix), "%zu:", len); + if (prefix_len <= 0 || (size_t)prefix_len >= sizeof(prefix) || + (size_t)prefix_len + len >= *cap - used) { + return NULL; /* cannot happen: `need` reserved this */ + } + memcpy(*buf + used, prefix, (size_t)prefix_len); + used += (size_t)prefix_len; + memcpy(*buf + used, row[c], len); + used += len; } + (*buf)[used] = '\0'; return *buf; }