diff --git a/chrony-select-timeout.patch b/chrony-select-timeout.patch new file mode 100644 index 0000000..4b94ca3 --- /dev/null +++ b/chrony-select-timeout.patch @@ -0,0 +1,94 @@ +commit d0b24860363a3704e28569ce9a6987717834edea +Author: Miroslav Lichvar +Date: Tue Dec 5 11:08:24 2017 +0100 + + client: don't call select() with invalid timeout + + If the system clock was stepped forward after chronyc sent a request and + before it read the clock in order to calculate the receive timeout, + select() could be called with a negative timeout, which resulted in an + infinite loop waiting for select() to succeed. + + Fix the submit_request() function to not call select() with a negative + timeout. Also, return immediately on any error of select(). + +diff --git a/client.c b/client.c +index 5c3a99e..4e23158 100644 +--- a/client.c ++++ b/client.c +@@ -1394,9 +1394,16 @@ submit_request(CMD_Request *request, CMD_Reply *reply) + + timeout = initial_timeout / 1000.0 * (1U << (n_attempts - 1)) - + UTI_DiffTimespecsToDouble(&ts_now, &ts_start); +- UTI_DoubleToTimeval(timeout, &tv); + DEBUG_LOG("Timeout %f seconds", timeout); + ++ /* Avoid calling select() with an invalid timeout */ ++ if (timeout <= 0.0) { ++ new_attempt = 1; ++ continue; ++ } ++ ++ UTI_DoubleToTimeval(timeout, &tv); ++ + FD_ZERO(&rdfd); + FD_ZERO(&wrfd); + FD_ZERO(&exfd); +@@ -1410,6 +1417,7 @@ submit_request(CMD_Request *request, CMD_Reply *reply) + + if (select_status < 0) { + DEBUG_LOG("select failed : %s", strerror(errno)); ++ return 0; + } else if (select_status == 0) { + /* Timeout must have elapsed, try a resend? */ + new_attempt = 1; + +commit 6863e43269fe27ce2744eb643295f31c00ec176d +Author: Miroslav Lichvar +Date: Tue Dec 12 11:03:04 2017 +0100 + + client: avoid reading clock after sending request + + If chronyc sent a request which caused chronyd to step the clock (e.g. + makestep, settime) and the second reading of the clock before calling + select() to wait for a response happened after the clock was stepped, a + new request could be sent immediately and chronyd would process the same + command twice. If the second request failed (e.g. a settime request too + close to the first request), chronyc would report an error. + + Change the submit_request() function to read the clock only once per + select() to wait for the first response even when the clock was stepped. + +diff --git a/client.c b/client.c +index a04dcb8..7d1e346 100644 +--- a/client.c ++++ b/client.c +@@ -1347,15 +1347,15 @@ submit_request(CMD_Request *request, CMD_Reply *reply) + new_attempt = 1; + + do { ++ if (gettimeofday(&tv, NULL)) ++ return 0; ++ + if (new_attempt) { + new_attempt = 0; + + if (n_attempts > max_retries) + return 0; + +- if (gettimeofday(&tv, NULL)) +- return 0; +- + UTI_TimevalToTimespec(&tv, &ts_start); + + UTI_GetRandomBytes(&request->sequence, sizeof (request->sequence)); +@@ -1383,9 +1383,6 @@ submit_request(CMD_Request *request, CMD_Reply *reply) + DEBUG_LOG("Sent %d bytes", command_length); + } + +- if (gettimeofday(&tv, NULL)) +- return 0; +- + UTI_TimevalToTimespec(&tv, &ts_now); + + /* Check if the clock wasn't stepped back */ diff --git a/chrony.spec b/chrony.spec index 893ef3a..837bfb5 100644 --- a/chrony.spec +++ b/chrony.spec @@ -21,6 +21,8 @@ Source10: https://github.com/mlichvar/clknetsim/archive/%{clknetsim_ver}/c # add NTP servers from DHCP when starting service Patch1: chrony-service-helper.patch +# fix chronyc getting stuck in infinite loop after clock step +Patch2: chrony-select-timeout.patch BuildRequires: libcap-devel libedit-devel nss-devel pps-tools-devel %ifarch %{ix86} x86_64 %{arm} aarch64 mipsel mips64el ppc64 ppc64le s390 s390x @@ -54,6 +56,7 @@ clocks, system real-time clock or manual input as time references. %setup -q -n %{name}-%{version}%{?prerelease} -a 10 %{?gitpatch:%patch0 -p1} %patch1 -p1 -b .service-helper +%patch2 -p1 -b .select-timeout %{?gitpatch: echo %{version}-%{gitpatch} > version.txt}