Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c08b8a49 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Linus Torvalds
Browse files

[PATCH] sys_alarm() unsigned signed conversion fixup



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: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 185ae6d7
Loading
Loading
Loading
Loading
+1 −13
Original line number Diff line number Diff line
@@ -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
+1 −21
Original line number Diff line number Diff line
@@ -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)
+2 −14
Original line number Diff line number Diff line
@@ -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
+1 −0
Original line number Diff line number Diff line
@@ -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);

+37 −0
Original line number Diff line number Diff line
@@ -226,6 +226,43 @@ again:
	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)
Loading