Skip to content

Commit 434dbf6

Browse files
committed
oauth: Fix postcondition for set_timer on macOS
On macOS, readding an EVFILT_TIMER to a kqueue does not appear to clear out previously queued timer events, so checks for timer expiration do not work correctly during token retrieval. Switching to IPv4-only communication exposes the problem, because libcurl is no longer clearing out other timeouts related to Happy Eyeballs dual-stack handling. Fully remove and re-register the kqueue timer events during each call to set_timer(), to clear out any stale expirations. Author: Jacob Champion <jacob.champion@enterprisedb.com> Discussion: https://postgr.es/m/CAOYmi%2Bn4EDOOUL27_OqYT2-F2rS6S%2B3mK-ppWb2Ec92UEoUbYA%40mail.gmail.com
1 parent 8d9d584 commit 434dbf6

File tree

1 file changed

+35
-13
lines changed

1 file changed

+35
-13
lines changed

src/interfaces/libpq/fe-auth-oauth-curl.c

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,6 +1326,10 @@ register_socket(CURL *curl, curl_socket_t socket, int what, void *ctx,
13261326
* in the set at all times and just disarm it when it's not needed. For kqueue,
13271327
* the timer is removed completely when disabled to prevent stale timeouts from
13281328
* remaining in the queue.
1329+
*
1330+
* To meet Curl requirements for the CURLMOPT_TIMERFUNCTION, implementations of
1331+
* set_timer must handle repeated calls by fully discarding any previous running
1332+
* or expired timer.
13291333
*/
13301334
static bool
13311335
set_timer(struct async_ctx *actx, long timeout)
@@ -1373,26 +1377,44 @@ set_timer(struct async_ctx *actx, long timeout)
13731377
timeout = 1;
13741378
#endif
13751379

1376-
/* Enable/disable the timer itself. */
1377-
EV_SET(&ev, 1, EVFILT_TIMER, timeout < 0 ? EV_DELETE : (EV_ADD | EV_ONESHOT),
1378-
0, timeout, 0);
1380+
/*
1381+
* Always disable the timer, and remove it from the multiplexer, to clear
1382+
* out any already-queued events. (On some BSDs, adding an EVFILT_TIMER to
1383+
* a kqueue that already has one will clear stale events, but not on
1384+
* macOS.)
1385+
*
1386+
* If there was no previous timer set, the kevent calls will result in
1387+
* ENOENT, which is fine.
1388+
*/
1389+
EV_SET(&ev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, 0);
13791390
if (kevent(actx->timerfd, &ev, 1, NULL, 0, NULL) < 0 && errno != ENOENT)
13801391
{
1381-
actx_error(actx, "setting kqueue timer to %ld: %m", timeout);
1392+
actx_error(actx, "deleting kqueue timer: %m", timeout);
13821393
return false;
13831394
}
13841395

1385-
/*
1386-
* Add/remove the timer to/from the mux. (In contrast with epoll, if we
1387-
* allowed the timer to remain registered here after being disabled, the
1388-
* mux queue would retain any previous stale timeout notifications and
1389-
* remain readable.)
1390-
*/
1391-
EV_SET(&ev, actx->timerfd, EVFILT_READ, timeout < 0 ? EV_DELETE : EV_ADD,
1392-
0, 0, 0);
1396+
EV_SET(&ev, actx->timerfd, EVFILT_READ, EV_DELETE, 0, 0, 0);
13931397
if (kevent(actx->mux, &ev, 1, NULL, 0, NULL) < 0 && errno != ENOENT)
13941398
{
1395-
actx_error(actx, "could not update timer on kqueue: %m");
1399+
actx_error(actx, "removing kqueue timer from multiplexer: %m");
1400+
return false;
1401+
}
1402+
1403+
/* If we're not adding a timer, we're done. */
1404+
if (timeout < 0)
1405+
return true;
1406+
1407+
EV_SET(&ev, 1, EVFILT_TIMER, (EV_ADD | EV_ONESHOT), 0, timeout, 0);
1408+
if (kevent(actx->timerfd, &ev, 1, NULL, 0, NULL) < 0)
1409+
{
1410+
actx_error(actx, "setting kqueue timer to %ld: %m", timeout);
1411+
return false;
1412+
}
1413+
1414+
EV_SET(&ev, actx->timerfd, EVFILT_READ, EV_ADD, 0, 0, 0);
1415+
if (kevent(actx->mux, &ev, 1, NULL, 0, NULL) < 0)
1416+
{
1417+
actx_error(actx, "adding kqueue timer to multiplexer: %m");
13961418
return false;
13971419
}
13981420

0 commit comments

Comments
 (0)