Skip to content

Commit

Permalink
[PATCH] sys_alarm() unsigned signed conversion fixup
Browse files Browse the repository at this point in the history
alarm() calls the kernel with an unsigend int timeout in seconds.  The
value is stored in the tv_sec field of a struct timeval to setup the
itimer.  The tv_sec field of struct timeval is of type long, which causes
the tv_sec value to be negative on 32 bit machines if seconds > INT_MAX.

Before the hrtimer merge (pre 2.6.16) such a negative value was converted
to the maximum jiffies timeout by the timeval_to_jiffies conversion.  It's
not clear whether this was intended or just happened to be done by the
timeval_to_jiffies code.

hrtimers expect a timeval in canonical form and treat a negative timeout as
already expired.  This breaks the legitimate usage of alarm() with a
timeout value > INT_MAX seconds.

For 32 bit machines it is therefor necessary to limit the internal seconds
value to avoid API breakage.  Instead of doing this in all implementations
of sys_alarm the duplicated sys_alarm code is moved into a common function
in itimer.c

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
  • Loading branch information
Thomas Gleixner authored and Linus Torvalds committed Mar 25, 2006
1 parent 185ae6d commit c08b8a4
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 61 deletions.
14 changes: 1 addition & 13 deletions arch/ia64/ia32/sys_ia32.c
Original file line number Diff line number Diff line change
Expand Up @@ -1166,19 +1166,7 @@ put_tv32 (struct compat_timeval __user *o, struct timeval *i)
asmlinkage unsigned long
sys32_alarm (unsigned int seconds)
{
struct itimerval it_new, it_old;
unsigned int oldalarm;

it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;
it_new.it_value.tv_sec = seconds;
it_new.it_value.tv_usec = 0;
do_setitimer(ITIMER_REAL, &it_new, &it_old);
oldalarm = it_old.it_value.tv_sec;
/* ehhh.. We can't return 0 if we have an alarm pending.. */
/* And we'd better return too much than too little anyway */
if (it_old.it_value.tv_usec)
oldalarm++;
return oldalarm;
return alarm_setitimer(seconds);
}

/* Translations due to time_t size differences. Which affects all
Expand Down
22 changes: 1 addition & 21 deletions arch/mips/kernel/sysirix.c
Original file line number Diff line number Diff line change
Expand Up @@ -645,27 +645,7 @@ static inline void getitimer_real(struct itimerval *value)

asmlinkage unsigned int irix_alarm(unsigned int seconds)
{
struct itimerval it_new, it_old;
unsigned int oldalarm;

if (!seconds) {
getitimer_real(&it_old);
del_timer(&current->real_timer);
} else {
it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;
it_new.it_value.tv_sec = seconds;
it_new.it_value.tv_usec = 0;
do_setitimer(ITIMER_REAL, &it_new, &it_old);
}
oldalarm = it_old.it_value.tv_sec;
/*
* ehhh.. We can't return 0 if we have an alarm pending ...
* And we'd better return too much than too little anyway
*/
if (it_old.it_value.tv_usec)
oldalarm++;

return oldalarm;
return alarm_setitimer(seconds);
}

asmlinkage int irix_pause(void)
Expand Down
16 changes: 2 additions & 14 deletions arch/x86_64/ia32/sys_ia32.c
Original file line number Diff line number Diff line change
Expand Up @@ -430,24 +430,12 @@ put_tv32(struct compat_timeval __user *o, struct timeval *i)
return err;
}

extern int do_setitimer(int which, struct itimerval *, struct itimerval *);
extern unsigned int alarm_setitimer(unsigned int seconds);

asmlinkage long
sys32_alarm(unsigned int seconds)
{
struct itimerval it_new, it_old;
unsigned int oldalarm;

it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;
it_new.it_value.tv_sec = seconds;
it_new.it_value.tv_usec = 0;
do_setitimer(ITIMER_REAL, &it_new, &it_old);
oldalarm = it_old.it_value.tv_sec;
/* ehhh.. We can't return 0 if we have an alarm pending.. */
/* And we'd better return too much than too little anyway */
if (it_old.it_value.tv_usec)
oldalarm++;
return oldalarm;
return alarm_setitimer(seconds);
}

/* Translations due to time_t size differences. Which affects all
Expand Down
1 change: 1 addition & 0 deletions include/linux/time.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ extern long do_utimes(int dfd, char __user *filename, struct timeval *times);
struct itimerval;
extern int do_setitimer(int which, struct itimerval *value,
struct itimerval *ovalue);
extern unsigned int alarm_setitimer(unsigned int seconds);
extern int do_getitimer(int which, struct itimerval *value);
extern void getnstimeofday(struct timespec *tv);

Expand Down
37 changes: 37 additions & 0 deletions kernel/itimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,43 @@ int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
return 0;
}

/**
* alarm_setitimer - set alarm in seconds
*
* @seconds: number of seconds until alarm
* 0 disables the alarm
*
* Returns the remaining time in seconds of a pending timer or 0 when
* the timer is not active.
*
* On 32 bit machines the seconds value is limited to (INT_MAX/2) to avoid
* negative timeval settings which would cause immediate expiry.
*/
unsigned int alarm_setitimer(unsigned int seconds)
{
struct itimerval it_new, it_old;

#if BITS_PER_LONG < 64
if (seconds > INT_MAX)
seconds = INT_MAX;
#endif
it_new.it_value.tv_sec = seconds;
it_new.it_value.tv_usec = 0;
it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;

do_setitimer(ITIMER_REAL, &it_new, &it_old);

/*
* We can't return 0 if we have an alarm pending ... And we'd
* better return too much than too little anyway
*/
if ((!it_old.it_value.tv_sec && it_old.it_value.tv_usec) ||
it_old.it_value.tv_usec >= 500000)
it_old.it_value.tv_sec++;

return it_old.it_value.tv_sec;
}

asmlinkage long sys_setitimer(int which,
struct itimerval __user *value,
struct itimerval __user *ovalue)
Expand Down
14 changes: 1 addition & 13 deletions kernel/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -956,19 +956,7 @@ void do_timer(struct pt_regs *regs)
*/
asmlinkage unsigned long sys_alarm(unsigned int seconds)
{
struct itimerval it_new, it_old;
unsigned int oldalarm;

it_new.it_interval.tv_sec = it_new.it_interval.tv_usec = 0;
it_new.it_value.tv_sec = seconds;
it_new.it_value.tv_usec = 0;
do_setitimer(ITIMER_REAL, &it_new, &it_old);
oldalarm = it_old.it_value.tv_sec;
/* ehhh.. We can't return 0 if we have an alarm pending.. */
/* And we'd better return too much than too little anyway */
if ((!oldalarm && it_old.it_value.tv_usec) || it_old.it_value.tv_usec >= 500000)
oldalarm++;
return oldalarm;
return alarm_setitimer(seconds);
}

#endif
Expand Down

0 comments on commit c08b8a4

Please sign in to comment.