summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2014-11-25 18:21:45 +0000
committerPeter Maydell <peter.maydell@linaro.org>2014-11-27 11:31:58 +0000
commit490309fcfbed9fa1ed357541f609975016a34628 (patch)
treec8caabfb128d467474687a414affcb93441cf3bb
parent3ef4ebcc5c0360629bb05f49d9b961774247d6ca (diff)
downloadqemu-490309fcfbed9fa1ed357541f609975016a34628.tar.gz
qemu-timer: Avoid overflows when converting timeout to struct timespec
In qemu_poll_ns(), when we convert an int64_t nanosecond timeout into a struct timespec, we may accidentally run into overflow problems if the timeout is very long. This happens because the tv_sec field is a time_t, which is signed, so we might end up setting it to a negative value by mistake. This will result in what was intended to be a near-infinite timeout turning into an instantaneous timeout, and we'll busy loop. Cap the maximum timeout at INT32_MAX seconds (about 68 years) to avoid this problem. This specifically manifested on ARM hosts as an extreme slowdown on guest shutdown (when the guest reprogrammed the PL031 RTC to not generate alarms using a very long timeout) but could happen on other hosts and guests too. Reported-by: Christoffer Dall <christoffer.dall@linaro.org> Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Fam Zheng <famz@redhat.com> Message-id: 1416939705-1272-1-git-send-email-peter.maydell@linaro.org
-rw-r--r--qemu-timer.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/qemu-timer.c b/qemu-timer.c
index 00a5d35c3f..c77de64301 100644
--- a/qemu-timer.c
+++ b/qemu-timer.c
@@ -314,7 +314,14 @@ int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout)
return ppoll((struct pollfd *)fds, nfds, NULL, NULL);
} else {
struct timespec ts;
- ts.tv_sec = timeout / 1000000000LL;
+ int64_t tvsec = timeout / 1000000000LL;
+ /* Avoid possibly overflowing and specifying a negative number of
+ * seconds, which would turn a very long timeout into a busy-wait.
+ */
+ if (tvsec > (int64_t)INT32_MAX) {
+ tvsec = INT32_MAX;
+ }
+ ts.tv_sec = tvsec;
ts.tv_nsec = timeout % 1000000000LL;
return ppoll((struct pollfd *)fds, nfds, &ts, NULL);
}