summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRasmus Villemoes2017-04-26 13:03:10 +0200
committerRasmus Villemoes2017-04-26 13:35:26 +0200
commite2cd1072b44fe4fe269ee0e9ee51e05e946a011d (patch)
tree5b696b708dc1f8beb4b491310d99e80e9a028899
parentlib/timer.c: remove bogus SA_SIGINFO from timer_settime call (diff)
downloadkernel-qcow2-util-linux-e2cd1072b44fe4fe269ee0e9ee51e05e946a011d.tar.gz
kernel-qcow2-util-linux-e2cd1072b44fe4fe269ee0e9ee51e05e946a011d.tar.xz
kernel-qcow2-util-linux-e2cd1072b44fe4fe269ee0e9ee51e05e946a011d.zip
lib/timer.c: prevent pathological race condition
flock(1) uses the timer facility to interrupt a blocking flock(2) call. However, in a pathological case (or with a sufficiently short timeout), the timer may fire and the signal be delivered after the timer is set up, but before we get around to doing the flock(2) call. In that case, we'd block forever. Checking timeout_expired right before calling flock(2) does not eliminate that race, so the only option is to make the timer fire repeatedly. Having the timer fire after we've returned from flock(2) is not a problem, since we only check timeout_expired in case of EINTR (also, this firing after return could also happen with the current code). There is currently one other user of setup_timer (misc-utils/uuidd.c), but in that case the signal handler simply exits. Future users of setup_timer obviously need to ensure that they can tolerate multiple signal deliveries. Choosing 1% of the initial timeout as the repeating interval is somewhat arbitrary. However, I put a lower bound of 0.01s, since setting the interval much smaller than this may end up effectively live-locking the process, handling a never-ending stream of signals. Signed-off-by: Rasmus Villemoes <rasmus.villemoes@prevas.dk>
-rw-r--r--lib/timer.c10
1 files changed, 6 insertions, 4 deletions
diff --git a/lib/timer.c b/lib/timer.c
index 143c3a63a..813eade94 100644
--- a/lib/timer.c
+++ b/lib/timer.c
@@ -13,16 +13,18 @@
int setup_timer(timer_t * t_id, struct itimerval *timeout,
void (*timeout_handler)(int, siginfo_t *, void *))
{
+ time_t sec = timeout->it_value.tv_sec;
+ long usec = timeout->it_value.tv_usec;
struct sigaction sig_a;
static struct sigevent sig_e = {
.sigev_notify = SIGEV_SIGNAL,
.sigev_signo = SIGALRM
};
struct itimerspec val = {
- .it_value.tv_sec = timeout->it_value.tv_sec,
- .it_value.tv_nsec = timeout->it_value.tv_usec * 1000,
- .it_interval.tv_sec = 0,
- .it_interval.tv_nsec = 0
+ .it_value.tv_sec = sec,
+ .it_value.tv_nsec = usec * 1000,
+ .it_interval.tv_sec = sec / 100,
+ .it_interval.tv_nsec = (sec ? sec % 100 : 1) * 10*1000*1000
};
if (sigemptyset(&sig_a.sa_mask))