diff options
author | Roman Zippel <zippel@linux-m68k.org> | 2008-08-20 19:46:08 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-08-22 00:40:18 -0400 |
commit | 916c7a855174e3b53d182b97a26b2e27a29726a1 (patch) | |
tree | 791b9975b6de18f0120d962b08f74f088386b44d /kernel/time | |
parent | 6a55617ed5d1aa62b850de2cf66f5ede2eef4825 (diff) |
ntp: fix ADJ_OFFSET_SS_READ bug and do_adjtimex() cleanup
Thanks to the review by Michael Kerrisk a bug in the recent
ADJ_OFFSET_SS_READ option was discovered, where the ntp time_offset was
inadvertently set by it. This fixes this by making the adjtime code
more separate from the ntp_adjtime code (both of which really want to
be separate syscalls).
Signed-off-by: Roman Zippel <zippel@linux-m68k.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Acked-by: John Stultz <johnstul@us.ibm.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'kernel/time')
-rw-r--r-- | kernel/time/ntp.c | 76 |
1 files changed, 40 insertions, 36 deletions
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c index 5125ddd8196b..c6921aa1a42a 100644 --- a/kernel/time/ntp.c +++ b/kernel/time/ntp.c | |||
@@ -277,38 +277,50 @@ static inline void notify_cmos_timer(void) { } | |||
277 | int do_adjtimex(struct timex *txc) | 277 | int do_adjtimex(struct timex *txc) |
278 | { | 278 | { |
279 | struct timespec ts; | 279 | struct timespec ts; |
280 | long save_adjust, sec; | ||
281 | int result; | 280 | int result; |
282 | 281 | ||
283 | /* In order to modify anything, you gotta be super-user! */ | 282 | /* Validate the data before disabling interrupts */ |
284 | if (txc->modes && !capable(CAP_SYS_TIME)) | 283 | if (txc->modes & ADJ_ADJTIME) { |
285 | return -EPERM; | ||
286 | |||
287 | /* Now we validate the data before disabling interrupts */ | ||
288 | |||
289 | if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT) { | ||
290 | /* singleshot must not be used with any other mode bits */ | 284 | /* singleshot must not be used with any other mode bits */ |
291 | if (txc->modes & ~ADJ_OFFSET_SS_READ) | 285 | if (!(txc->modes & ADJ_OFFSET_SINGLESHOT)) |
292 | return -EINVAL; | 286 | return -EINVAL; |
287 | if (!(txc->modes & ADJ_OFFSET_READONLY) && | ||
288 | !capable(CAP_SYS_TIME)) | ||
289 | return -EPERM; | ||
290 | } else { | ||
291 | /* In order to modify anything, you gotta be super-user! */ | ||
292 | if (txc->modes && !capable(CAP_SYS_TIME)) | ||
293 | return -EPERM; | ||
294 | |||
295 | /* if the quartz is off by more than 10% something is VERY wrong! */ | ||
296 | if (txc->modes & ADJ_TICK && | ||
297 | (txc->tick < 900000/USER_HZ || | ||
298 | txc->tick > 1100000/USER_HZ)) | ||
299 | return -EINVAL; | ||
300 | |||
301 | if (txc->modes & ADJ_STATUS && time_state != TIME_OK) | ||
302 | hrtimer_cancel(&leap_timer); | ||
293 | } | 303 | } |
294 | 304 | ||
295 | /* if the quartz is off by more than 10% something is VERY wrong ! */ | ||
296 | if (txc->modes & ADJ_TICK) | ||
297 | if (txc->tick < 900000/USER_HZ || | ||
298 | txc->tick > 1100000/USER_HZ) | ||
299 | return -EINVAL; | ||
300 | |||
301 | if (time_state != TIME_OK && txc->modes & ADJ_STATUS) | ||
302 | hrtimer_cancel(&leap_timer); | ||
303 | getnstimeofday(&ts); | 305 | getnstimeofday(&ts); |
304 | 306 | ||
305 | write_seqlock_irq(&xtime_lock); | 307 | write_seqlock_irq(&xtime_lock); |
306 | 308 | ||
307 | /* Save for later - semantics of adjtime is to return old value */ | ||
308 | save_adjust = time_adjust; | ||
309 | |||
310 | /* If there are input parameters, then process them */ | 309 | /* If there are input parameters, then process them */ |
310 | if (txc->modes & ADJ_ADJTIME) { | ||
311 | long save_adjust = time_adjust; | ||
312 | |||
313 | if (!(txc->modes & ADJ_OFFSET_READONLY)) { | ||
314 | /* adjtime() is independent from ntp_adjtime() */ | ||
315 | time_adjust = txc->offset; | ||
316 | ntp_update_frequency(); | ||
317 | } | ||
318 | txc->offset = save_adjust; | ||
319 | goto adj_done; | ||
320 | } | ||
311 | if (txc->modes) { | 321 | if (txc->modes) { |
322 | long sec; | ||
323 | |||
312 | if (txc->modes & ADJ_STATUS) { | 324 | if (txc->modes & ADJ_STATUS) { |
313 | if ((time_status & STA_PLL) && | 325 | if ((time_status & STA_PLL) && |
314 | !(txc->status & STA_PLL)) { | 326 | !(txc->status & STA_PLL)) { |
@@ -375,13 +387,8 @@ int do_adjtimex(struct timex *txc) | |||
375 | if (txc->modes & ADJ_TAI && txc->constant > 0) | 387 | if (txc->modes & ADJ_TAI && txc->constant > 0) |
376 | time_tai = txc->constant; | 388 | time_tai = txc->constant; |
377 | 389 | ||
378 | if (txc->modes & ADJ_OFFSET) { | 390 | if (txc->modes & ADJ_OFFSET) |
379 | if (txc->modes == ADJ_OFFSET_SINGLESHOT) | 391 | ntp_update_offset(txc->offset); |
380 | /* adjtime() is independent from ntp_adjtime() */ | ||
381 | time_adjust = txc->offset; | ||
382 | else | ||
383 | ntp_update_offset(txc->offset); | ||
384 | } | ||
385 | if (txc->modes & ADJ_TICK) | 392 | if (txc->modes & ADJ_TICK) |
386 | tick_usec = txc->tick; | 393 | tick_usec = txc->tick; |
387 | 394 | ||
@@ -389,19 +396,16 @@ int do_adjtimex(struct timex *txc) | |||
389 | ntp_update_frequency(); | 396 | ntp_update_frequency(); |
390 | } | 397 | } |
391 | 398 | ||
399 | txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ, | ||
400 | NTP_SCALE_SHIFT); | ||
401 | if (!(time_status & STA_NANO)) | ||
402 | txc->offset /= NSEC_PER_USEC; | ||
403 | |||
404 | adj_done: | ||
392 | result = time_state; /* mostly `TIME_OK' */ | 405 | result = time_state; /* mostly `TIME_OK' */ |
393 | if (time_status & (STA_UNSYNC|STA_CLOCKERR)) | 406 | if (time_status & (STA_UNSYNC|STA_CLOCKERR)) |
394 | result = TIME_ERROR; | 407 | result = TIME_ERROR; |
395 | 408 | ||
396 | if ((txc->modes == ADJ_OFFSET_SINGLESHOT) || | ||
397 | (txc->modes == ADJ_OFFSET_SS_READ)) | ||
398 | txc->offset = save_adjust; | ||
399 | else { | ||
400 | txc->offset = shift_right(time_offset * NTP_INTERVAL_FREQ, | ||
401 | NTP_SCALE_SHIFT); | ||
402 | if (!(time_status & STA_NANO)) | ||
403 | txc->offset /= NSEC_PER_USEC; | ||
404 | } | ||
405 | txc->freq = shift_right((s32)(time_freq >> PPM_SCALE_INV_SHIFT) * | 409 | txc->freq = shift_right((s32)(time_freq >> PPM_SCALE_INV_SHIFT) * |
406 | (s64)PPM_SCALE_INV, | 410 | (s64)PPM_SCALE_INV, |
407 | NTP_SCALE_SHIFT); | 411 | NTP_SCALE_SHIFT); |