Skip to content

Commit 0217a74

Browse files
committed
Fix race condition with BIO methods initialization in libpq with threads
The libpq code in charge of creating per-connection SSL objects was prone to a race condition when loading the custom BIO methods needed by my_SSL_set_fd(). As BIO methods are stored as a static variable, the initialization of a connection could fail because it could be possible to have one thread refer to my_bio_methods while it is being manipulated by a second concurrent thread. This error has been introduced by 8bb14cd, that has removed ssl_config_mutex around the call of my_SSL_set_fd(), that itself sets the custom BIO methods used in libpq. Like previously, the BIO method initialization is now protected by the existing ssl_config_mutex, itself initialized earlier for WIN32. While on it, document that my_bio_methods is protected by ssl_config_mutex, as this can be easy to miss. Reported-by: Willi Mann Author: Willi Mann, Michael Paquier Discussion: https://postgr.es/m/e77abc4c-4d03-4058-a9d7-ef0035657e04@celonis.com Backpatch-through: 12
1 parent 450ad65 commit 0217a74

File tree

1 file changed

+51
-22
lines changed

1 file changed

+51
-22
lines changed

src/interfaces/libpq/fe-secure-openssl.c

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,7 @@ PQsslAttribute(PGconn *conn, const char *attribute_name)
14961496
#define BIO_set_data(bio, data) (bio->ptr = data)
14971497
#endif
14981498

1499+
/* protected by ssl_config_mutex */
14991500
static BIO_METHOD *my_bio_methods;
15001501

15011502
static int
@@ -1561,6 +1562,15 @@ my_sock_write(BIO *h, const char *buf, int size)
15611562
static BIO_METHOD *
15621563
my_BIO_s_socket(void)
15631564
{
1565+
BIO_METHOD *res;
1566+
1567+
#ifdef ENABLE_THREAD_SAFETY
1568+
if (pthread_mutex_lock(&ssl_config_mutex))
1569+
return NULL;
1570+
#endif
1571+
1572+
res = my_bio_methods;
1573+
15641574
if (!my_bio_methods)
15651575
{
15661576
BIO_METHOD *biom = (BIO_METHOD *) BIO_s_socket();
@@ -1569,39 +1579,58 @@ my_BIO_s_socket(void)
15691579

15701580
my_bio_index = BIO_get_new_index();
15711581
if (my_bio_index == -1)
1572-
return NULL;
1582+
goto err;
15731583
my_bio_index |= (BIO_TYPE_DESCRIPTOR | BIO_TYPE_SOURCE_SINK);
1574-
my_bio_methods = BIO_meth_new(my_bio_index, "libpq socket");
1575-
if (!my_bio_methods)
1576-
return NULL;
1584+
res = BIO_meth_new(my_bio_index, "libpq socket");
1585+
if (!res)
1586+
goto err;
15771587

15781588
/*
15791589
* As of this writing, these functions never fail. But check anyway,
15801590
* like OpenSSL's own examples do.
15811591
*/
1582-
if (!BIO_meth_set_write(my_bio_methods, my_sock_write) ||
1583-
!BIO_meth_set_read(my_bio_methods, my_sock_read) ||
1584-
!BIO_meth_set_gets(my_bio_methods, BIO_meth_get_gets(biom)) ||
1585-
!BIO_meth_set_puts(my_bio_methods, BIO_meth_get_puts(biom)) ||
1586-
!BIO_meth_set_ctrl(my_bio_methods, BIO_meth_get_ctrl(biom)) ||
1587-
!BIO_meth_set_create(my_bio_methods, BIO_meth_get_create(biom)) ||
1588-
!BIO_meth_set_destroy(my_bio_methods, BIO_meth_get_destroy(biom)) ||
1589-
!BIO_meth_set_callback_ctrl(my_bio_methods, BIO_meth_get_callback_ctrl(biom)))
1592+
if (!BIO_meth_set_write(res, my_sock_write) ||
1593+
!BIO_meth_set_read(res, my_sock_read) ||
1594+
!BIO_meth_set_gets(res, BIO_meth_get_gets(biom)) ||
1595+
!BIO_meth_set_puts(res, BIO_meth_get_puts(biom)) ||
1596+
!BIO_meth_set_ctrl(res, BIO_meth_get_ctrl(biom)) ||
1597+
!BIO_meth_set_create(res, BIO_meth_get_create(biom)) ||
1598+
!BIO_meth_set_destroy(res, BIO_meth_get_destroy(biom)) ||
1599+
!BIO_meth_set_callback_ctrl(res, BIO_meth_get_callback_ctrl(biom)))
15901600
{
1591-
BIO_meth_free(my_bio_methods);
1592-
my_bio_methods = NULL;
1593-
return NULL;
1601+
goto err;
15941602
}
15951603
#else
1596-
my_bio_methods = malloc(sizeof(BIO_METHOD));
1597-
if (!my_bio_methods)
1598-
return NULL;
1599-
memcpy(my_bio_methods, biom, sizeof(BIO_METHOD));
1600-
my_bio_methods->bread = my_sock_read;
1601-
my_bio_methods->bwrite = my_sock_write;
1604+
res = malloc(sizeof(BIO_METHOD));
1605+
if (!res)
1606+
goto err;
1607+
memcpy(res, biom, sizeof(BIO_METHOD));
1608+
res->bread = my_sock_read;
1609+
res->bwrite = my_sock_write;
16021610
#endif
16031611
}
1604-
return my_bio_methods;
1612+
1613+
my_bio_methods = res;
1614+
1615+
#ifdef ENABLE_THREAD_SAFETY
1616+
pthread_mutex_unlock(&ssl_config_mutex);
1617+
#endif
1618+
1619+
return res;
1620+
1621+
err:
1622+
#ifdef HAVE_BIO_METH_NEW
1623+
if (res)
1624+
BIO_meth_free(res);
1625+
#else
1626+
if (res)
1627+
free(res);
1628+
#endif
1629+
1630+
#ifdef ENABLE_THREAD_SAFETY
1631+
pthread_mutex_unlock(&ssl_config_mutex);
1632+
#endif
1633+
return NULL;
16051634
}
16061635

16071636
/* This should exactly match OpenSSL's SSL_set_fd except for using my BIO */

0 commit comments

Comments
 (0)