Re: 2.0.3x: select returns dubious tv.tv_sec?

Ville Herva (vherva@babbage.tky.hut.fi)
Wed, 28 Apr 1999 07:17:47 +0300


Yesterday I wrote:
> vherva@turing:/home/vherva>cd
> vherva@turing:/home/vherva>gcc u.c -o u;./u
> ... because if one does, he'll get suprised: 13124461.
> No data within five seconds.
>
>
> The problem is somewhere in the fs/select.c, I suspect.
>
> if ((long) timeout < 0)
> timeout = 0;
> if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
> put_user(timeout/HZ, &tvp->tv_sec);
> timeout %= HZ;
> timeout *= (1000000/HZ);
> put_user(timeout, &tvp->tv_usec);
> }
>
> It seems clear that timeout (unsigned) is somewhere between 2**31 and 2**32
> when those erranous values are returned. It is not clear to me how
> timeout gets those values.

I believe I found out what's wrong with that.

/usr/src/linux-2.0.3x/fs/select.c:sys_select():

unsigned long timeout;

/* ... */
timeout = ~0UL;

if (tvp) {
error = verify_area(VERIFY_WRITE, tvp, sizeof(*tvp));
if (error)
goto out;
timeout = ROUND_UP(get_user(&tvp->tv_usec),(1000000/HZ));
timeout += get_user(&tvp->tv_sec) * (unsigned long) HZ;
if (timeout)
timeout += jiffies + 1;
}
zero_fd_set(n, &res_in);
zero_fd_set(n, &res_out);
zero_fd_set(n, &res_ex);
current->timeout = timeout;
error = do_select(n,
(fd_set *) &in,
(fd_set *) &out,
(fd_set *) &ex,
(fd_set *) &res_in,
(fd_set *) &res_out,
(fd_set *) &res_ex,
(fd_set *) &locked);
timeout = current->timeout - jiffies - 1;
current->timeout = 0;
if ((long) timeout < 0)
timeout = 0;
if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
put_user(timeout/HZ, &tvp->tv_sec);
timeout %= HZ;
timeout *= (1000000/HZ);
put_user(timeout, &tvp->tv_usec);
}

Now, if the timeout is reached during do_select, current->timeout becomes zero.
The line

if ((long) timeout < 0)
timeout = 0;

is propably for that. However, if jiffies > 2**31, that wont work (since
timeout wraps around and actually becomes positive), and select returns
false values. I would suggest something like

timeout = current->timeout ? current->timeout - jiffies - 1 : 0;

instead of

timeout = current->timeout - jiffies - 1;

2.2 seems to do

if (tvp && !(current->personality & STICKY_TIMEOUTS)) {
time_t sec = 0, usec = 0;
if (timeout) {
sec = timeout / HZ;
usec = timeout % HZ;
usec *= (1000000/HZ);
}
put_user(sec, &tvp->tv_sec);
put_user(usec, &tvp->tv_usec);
}

So I think it has not got this bug. I'm not sure, however.

-- v --

v@iki.fi

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/