pgpool2.git / summary / log / commit / refs

commit    3fb1d89cea0a6d7a098ac0a8851663c1a531b3e9
Author:   Tatsuo Ishii <ishii@postgresql.org>
Date:     Tue Sep 29 08:22:42 2026 +0000

    Reject negative read length in pool_read2.
    
    pool_read2() allocates buf2 sized to cp->len + len, consumes that many
    bytes from the pending buffer, then loops reading from the socket
    while len > 0. When len is zero or negative the loop never executes a
    fresh read and the function falls straight through to "return
    cp->buf2", handing the caller a pointer to whatever the previous
    pool_read2() invocation happened to leave in the per-connection
    buffer.
    
    Callers compute len from on-the-wire length fields, e.g.
    len = ntohl(hdr) - sizeof(hdr);
    and several do so without bounds-checking. A backend that ships a
    malformed message with a length smaller than the header (or a
    negative value once cast to int32) drives pool_read2() into the
    stale-buffer path. The stale bytes are then forwarded to the
    frontend as ParameterStatus / ErrorResponse / etc. payloads,
    leaking whatever the previous reply contained on this same
    backend connection.
    
    Add a guard at the top of pool_read2() that logs and returns NULL when
    len < 0, before any buffer setup runs.
    
    When len == 0, just retuns cp->buf2, rather than returning NULL to not
    break existing callers relying on the behavior.
    
    Reported-by: Emond Papegaaij <emond.papegaaij@gmail.com>
    Reported-by: Claude code
    Author: Tatsuo Ishii <ishii@postgresql.org>
    Discussion: https://github.com/pgpool/pgpool2/issues/168
    Backpatch-through: v4.3


src/utils/pool_stream.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/utils/pool_stream.c b/src/utils/pool_stream.c index 0a9cc3576..df88c87f7 100644 --- a/src/utils/pool_stream.c +++ b/src/utils/pool_stream.c @@ -303,9 +303,9 @@ pool_read(POOL_CONNECTION *cp, void *buf, int len) } /* -* read exactly len bytes from cp -* returns buffer address on success otherwise NULL. -*/ + * read exactly len bytes from cp returns buffer address on success otherwise + * NULL. Returns NULL too when len < 0. + */ char * pool_read2(POOL_CONNECTION *cp, int len) { @@ -314,7 +314,16 @@ pool_read2(POOL_CONNECTION *cp, int len) int alloc_size; int consume_size; int readlen; - MemoryContext oldContext = SwitchToConnectionContext(cp->isbackend); + MemoryContext oldContext; + + if (len < 0) + { + ereport(LOG, + (errmsg("pool_read2: negative len %d, returning NULL", len))); + return NULL; + } + + oldContext = SwitchToConnectionContext(cp->isbackend); req_size = cp->len + len; [parent: 2bb4b1738191]