[PATCH] Defensive hardening: Replace sprintf with snprintf in pg_stat_statements
Hello,
I'm submitting a defensive hardening patch for contrib/pg_stat_statements
that replaces sprintf with snprintf when generating normalised query
placeholders.
## Background
While reviewing pg_stat_statements normalisation code, I noticed that
generate_normalized_query() uses unbounded sprintf to format parameter
placeholders ($1, $2, etc.) into the normalised query buffer:
n_quer_loc += sprintf(norm_query + n_quer_loc, "$%d%s",
num_constants_replaced + 1 +
jstate->highest_extern_param_id,
locs[i].squashed ? " /*, ... */" : "");
## Analysis
The current buffer sizing logic appears to account for worst-case growth.
Each constant contributes at least one byte, while each generated
placeholder is bounded to at most 11 bytes ("$2147483647" for INT_MAX
parameter IDs, plus the optional squash comment). Given this invariant, an
actual overflow is unlikely under normal circumstances.
However, the use of unbounded sprintf makes the safety of this code
dependent on reasoning about buffer sizing logic elsewhere in the function,
rather than being locally verifiable.
## Proposed Change
This patch replaces sprintf with snprintf, making the write bound explicit
at the formatting site:
n_quer_loc += snprintf(norm_query + n_quer_loc,
norm_query_buflen - n_quer_loc + 1,
"$%d%s",
num_constants_replaced + 1 +
jstate->highest_extern_param_id,
locs[i].squashed ? " /*, ... */" : "");
This is defensive hardening only - I am not claiming this fixes a
demonstrated overflow vulnerability. The change simply makes the local
write bound explicit and protects against potential future modifications to
the buffer sizing logic.
## Additional Safety Improvements
The patch also includes related NULL pointer safety improvements in the
same file:
- Initialise qbuffer to NULL in gc_qtexts()
- Set qbuffer to NULL after pfree() in gc_qtexts() (both success and error
paths)
- Set qbuffer to NULL after pfree() in pgss_shmem_shutdown() error path
These follow defensive programming practices and guard against potential
use-after-free scenarios.
## Testing
Built and tested on macOS (Darwin 25.4.0):
- pg_stat_statements module compiles cleanly without errors or warnings
- No whitespace issues (git diff --check passed)
- No performance impact expected (same code path, just bounded write)
- Total changes: 13 insertions, 4 deletions in one file
## Context
This improvement was suggested by a downstream PostgreSQL-based project
(Apache Cloudberry) maintainer, who recommended that defensive hardening
belongs in PostgreSQL upstream rather than being maintained as a downstream
patch. Reference:
https://github.com/apache/cloudberry/pull/1744#issuecomment-4458061490
Patch attached (pg_stat_statements-sprintf-snprintf-v1.patch). I'm happy to
address any feedback or concerns.
Best Regards,
Anupam