From d34af6078912015fd3aba55e6273c0901c422917 Mon Sep 17 00:00:00 2001 From: shihao zhong Date: Thu, 5 Nov 2026 22:56:50 -0500 Subject: [PATCH v2 1/2] Limit the length of reported string GUCs Reject values too long for a ParameterStatus message, since libpq drops the connection on them. Share the limit through protocol.h. Reported-by: Daniel Gustafsson Discussion: https://postgr.es/m/3538A21B-9257-42A9-A363-058DD5AE51D3@yesql.se --- src/backend/utils/misc/guc.c | 37 ++++++++++++++++++++++++++--- src/include/libpq/protocol.h | 8 +++++++ src/interfaces/libpq/fe-protocol3.c | 4 ++-- 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 21c9ccec1e2..c9f811692e5 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -38,6 +38,7 @@ #include "guc_internal.h" #include "libpq/pqformat.h" #include "libpq/protocol.h" +#include "mb/pg_wchar.h" #include "miscadmin.h" #include "parser/scansup.h" #include "port/pg_bitutils.h" @@ -6804,14 +6805,32 @@ call_real_check_hook(const struct config_generic *conf, double *newval, void **e return true; } +/* + * Maximum length in bytes of a GUC_REPORT string parameter. + * + * Each new value is sent in a ParameterStatus message, which must fit in + * PQ_SHORT_MESSAGE_MAX_LEN, or libpq drops the connection. The message holds + * a 4-byte length word and the parameter name and value, each with a + * trailing null byte. Both strings are converted to the client encoding + * before they are sent, which can make them up to MAX_CONVERSION_GROWTH + * times longer. A show_hook could also change the value that is sent, but + * we don't try to account for that. + */ +static int +max_reported_string_len(const struct config_generic *conf) +{ + return (PQ_SHORT_MESSAGE_MAX_LEN - 4) / MAX_CONVERSION_GROWTH - + (int) strlen(conf->name) - 2; +} + static bool call_string_check_hook(const struct config_generic *conf, char **newval, void **extra, GucSource source, int elevel) { volatile bool result = true; - /* Quick success if no hook */ - if (!conf->_string.check_hook) + /* Quick success if no hook and no length limit */ + if (!conf->_string.check_hook && !(conf->flags & GUC_REPORT)) return true; /* @@ -6827,7 +6846,19 @@ call_string_check_hook(const struct config_generic *conf, char **newval, void ** GUC_check_errdetail_string = NULL; GUC_check_errhint_string = NULL; - if (!conf->_string.check_hook(newval, extra, source)) + if ((conf->flags & GUC_REPORT) && *newval && + strlen(*newval) > max_reported_string_len(conf)) + { + ereport(elevel, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("value for parameter \"%s\" is too long", + conf->name), + errdetail("The maximum length is %d bytes.", + max_reported_string_len(conf)))); + result = false; + } + else if (conf->_string.check_hook && + !conf->_string.check_hook(newval, extra, source)) { ereport(elevel, (errcode(GUC_check_errcode_value), diff --git a/src/include/libpq/protocol.h b/src/include/libpq/protocol.h index 503852ca988..e77ea0d031f 100644 --- a/src/include/libpq/protocol.h +++ b/src/include/libpq/protocol.h @@ -111,4 +111,12 @@ #define AUTH_REQ_SASL_FIN 12 /* Final SASL message */ #define AUTH_REQ_MAX AUTH_REQ_SASL_FIN /* maximum AUTH_REQ_* value */ + +/* + * libpq treats a backend message longer than this, including the length + * word, as a loss of sync, unless its type is one that can be long. The + * backend must not send a longer message of any other type. + */ +#define PQ_SHORT_MESSAGE_MAX_LEN 30000 + #endif /* PROTOCOL_H */ diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c index 1fd427cb9be..53cc5425b9d 100644 --- a/src/interfaces/libpq/fe-protocol3.c +++ b/src/interfaces/libpq/fe-protocol3.c @@ -99,7 +99,7 @@ pqParseInput3(PGconn *conn) handleSyncLoss(conn, id, msgLength); return; } - if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) + if (msgLength > PQ_SHORT_MESSAGE_MAX_LEN && !VALID_LONG_MESSAGE_TYPE(id)) { handleSyncLoss(conn, id, msgLength); return; @@ -2259,7 +2259,7 @@ pqFunctionCall3(PGconn *conn, Oid fnid, handleSyncLoss(conn, id, msgLength); break; } - if (msgLength > 30000 && !VALID_LONG_MESSAGE_TYPE(id)) + if (msgLength > PQ_SHORT_MESSAGE_MAX_LEN && !VALID_LONG_MESSAGE_TYPE(id)) { handleSyncLoss(conn, id, msgLength); break; -- 2.37.1 (Apple Git-137.1)