public inbox for [email protected]  
help / color / mirror / Atom feed
From: Daniel Schreiber <[email protected]>
To: [email protected]
Subject: PostgreSQL 17: Bug in libpq when libpq is dlopened/closed multiple times
Date: Wed, 15 Apr 2026 17:55:01 +0200
Message-ID: <[email protected]> (raw)

Dear PostgreSQL developers,

my colleagues and I probably found a bug in libpq when libpq is dlopened 
and closed multiple times during the lifetime of a process. In our setup 
we use a PAM module which links to libpq. The process using PAM is 
linked against openssl, so openssl is loaded during the complete 
lifetime of the process whereas libpq is loaded only during PAM 
authentication (and unloaded when PAM has finished).

We observed the bug on a Debian 13 system using libpq from Debian. To 
reproduce the bug, compile the attached c file using the following gcc 
command line:

gcc libpq1-dlopen.c -Wall -Wextra -o libpq1-dlopen -ldl -lssl -lcrypto

Then run the binary with a postgresql connection string as an argument. 
The connection string has to include 'sslmode=require'. The program will 
in a loop try to dlopen libpq, then connect to the server, finish the 
connection and unload libpq.

According to our findings every time a connection is established after 
dlopening libpq one of the 127 available BIO_METHOD structures in 
OpenSSL is consumed:
https://github.com/postgres/postgres/blob/REL_17_9/src/interfaces/libpq/fe-secure-openssl.c#L1987

So after 127 cycles registering the callbacks fails and in our use case 
the application is no longer able to authenticate using PAM. As a 
workaround we LD_PRELOAD libpq in the application.

I am not subscribed yet to the mailing list, so please CC me.

Thank you,

Daniel
-- 
Daniel Schreiber
Facharbeitsgruppe Systemsoftware
Universitaetsrechenzentrum

Technische Universität Chemnitz
Straße der Nationen 62 (Raum B303)
09111 Chemnitz
Germany

Tel:     +49 371 531 35444



Attachments:

  [text/x-csrc] libpq1-dlopen.c (3.0K, 2-libpq1-dlopen.c)
  download | inline:
/*
 * src/test/examples/testlibpq.c
 *
 *
 * testlibpq.c
 *
 *      Test the C version of libpq, the PostgreSQL frontend library.
 */
#include <stdio.h>
#include <stdlib.h>
#include <postgresql/libpq-fe.h>
#include <openssl/ssl.h>
#include <dlfcn.h>

typedef PGconn* (*PQconnectdb_func)(const char *conninfo);
typedef void (*PQfinish_func)(PGconn *conn);
typedef ConnStatusType (*PQstatus_func)(const PGconn *conn);
typedef char* (*PQerrorMessage_func)(const PGconn *conn);


static void
exit_nicely(PGconn *conn, PQfinish_func finish_func)
{
    finish_func(conn);
    exit(1);
}

// Dummy to force linking of openssl
void dummy_openssl() {
    SSL_library_init();
}

int
main2(int argc, char **argv)
{
    const char *conninfo;
    PGconn     *conn;
    PGresult   *res;
    int         nFields;
    int         i,
                j;
    void *handle;
    PQconnectdb_func conn_func;
    PQfinish_func finish_func;
    PQstatus_func status_func;
    PQerrorMessage_func errorMessage_func;
    char* error;

    handle = dlopen("libpq.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "Fehler beim Laden: %s\n", dlerror());
        return 1;
    }
    
    conn_func = (PQconnectdb_func)dlsym(handle, "PQconnectdb");
    error = dlerror();
    if (error != NULL) {
        fprintf(stderr, "error loading function: %s\n", error);
        dlclose(handle);
        return 1;
    }
    finish_func = (PQfinish_func)dlsym(handle, "PQfinish");
    error = dlerror();
    if (error != NULL) {
        fprintf(stderr, "error loading function: %s\n", error);
        dlclose(handle);
        return 1;
    }
    status_func = (PQstatus_func)dlsym(handle, "PQstatus");
    error = dlerror();
    if (error != NULL) {
        fprintf(stderr, "error loading function: %s\n", error);
        dlclose(handle);
        return 1;
    }
    errorMessage_func = (PQerrorMessage_func)dlsym(handle, "PQerrorMessage");
    error = dlerror();
    if (error != NULL) {
        fprintf(stderr, "error loading function: %s\n", error);
        dlclose(handle);
        return 1;
    }

    /*
     * If the user supplies a parameter on the command line, use it as the
     * conninfo string; otherwise default to setting dbname=postgres and using
     * environment variables or defaults for all other connection parameters.
     */
    if (argc > 1)
        conninfo = argv[1];
    else
        conninfo = "dbname = postgres";

    /* Make a connection to the database */
    conn = conn_func(conninfo);

    /* Check to see that the backend connection was successfully made */
    if (status_func(conn) != CONNECTION_OK)
    {
        fprintf(stderr, "%s", errorMessage_func(conn));
        exit_nicely(conn, finish_func);
    }

    /* close the connection to the database and cleanup */
    finish_func(conn);
    /* unload libpq */
    dlclose(handle);

    return 0;
}

int
main(int argc, char **argv) {
	int i=0;
	dummy_openssl();
	for (i=0; i < 229; i++) {
		printf("%d\n", i);
		/* will fail at i==127 */
		main2(argc, argv);
	}
}


  [application/pkcs7-signature] smime.p7s (4.8K, 3-smime.p7s)
  download

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: [email protected]
  Cc: [email protected], [email protected]
  Subject: Re: PostgreSQL 17: Bug in libpq when libpq is dlopened/closed multiple times
  In-Reply-To: <[email protected]>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox