diff mbox

[RFC] time: Fix negative offset handling in timekeeping_inject_offset

Message ID 1392847670-7053-1-git-send-email-john.stultz@linaro.org
State New
Headers show

Commit Message

John Stultz Feb. 19, 2014, 10:07 p.m. UTC
Kay, Richard,
	Scratched out a fix for this below and it seems to pass
my trivial test, which you can find here:
https://raw2.github.com/johnstultz-work/timetests/master/adj-setoffset.c

I need to validate that it doesn't cause other problems in the
time accounting code, but wanted to send it out for your initial
thoughts and any further testing you might have before I send it
to a wider audience.

thanks
-john


The adjtimex() ADJ_SETOFFSET feature allows a offset in timespec
format to be added to the current time. This value can be positive
or negative. However, representing a negative value in a timespec
can be confusing, as there may be numerous ways to represent the
same amount of time.

Positive values are most obviously represented:
1)	{ 0,  500000000}
2)	{ 3,  0}
3)	{ 3,  500000000}

While negative values are more complex and confusing:
4)	{-5,  0}
5)	{ 0, -500000000}
6)	{-5, -500000000}
7)	{-6,  500000000}

Note that the last two values (#6 and #7) actually represent the
same amount of time.

In timekeeping_inject_offset, a naieve validation check on the
timespec value resulted in -EINVAL being returned if the nsec
portion of the timespec was negative. This resulted in the
ADJ_SETOFFSET interface to consider examples #5 and #6 above
as invalid, forcing users to use the more awkward representations
of {sec - 1, NSEC_PER_SEC + nsec} for negative values (like
example #7 above.

Initially I suspected the extra logic for underflow handling
was the reason this was done, but in looking at it,
set_normalized_timespec() handles both overflows and underflows
properly.

Thus this patch allows for all of the representations above
to be considered valid.

There is of course, one missing example from the list above:
8)	{ 4, -500000000}

But I consider this is too ackward and misformed to be reasonably
supported, so it will still trigger EINVAL errors.

Cc: Richard Cochran <richardcochran@gmail.com>
Cc: Kay Sievers <kay@vrfy.org>
Reported-by: Kay Sievers <kay@vrfy.org>
Signed-off-by: John Stultz <john.stultz@linaro.org>
---
 kernel/time/timekeeping.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
diff mbox

Patch

diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 0aa4ce8..0b5ef8c 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -539,7 +539,11 @@  int timekeeping_inject_offset(struct timespec *ts)
 	struct timespec tmp;
 	int ret = 0;
 
-	if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
+	/* Disallow any {1,-500} style timespecs */
+	if ((ts->tv_sec > 0) && ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC))
+		return -EINVAL;
+	/* While interval may be negative, should still be sane */
+	if (abs(ts->tv_nsec) >= NSEC_PER_SEC)
 		return -EINVAL;
 
 	raw_spin_lock_irqsave(&timekeeper_lock, flags);