Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 19 Dec 1997 19:26:35 +1100
From:      Bruce Evans <bde@zeta.org.au>
To:        jmb@FreeBSD.ORG, lars@fredriks-1.pr.mcs.net
Cc:        current@FreeBSD.ORG
Subject:   Re: EDOM and hz
Message-ID:  <199712190826.TAA03871@godzilla.zeta.org.au>

next in thread | raw e-mail | index | archive | help
>>                         if (tv->tv_sec > SHRT_MAX / hz - hz) {
>>                                 error = EDOM;
>>                                 goto bad;
>>                         }
>>...
>	<hmmm....better grab stevens v2....ah, let's see on p 543,4>
>
>Rich Stevens recommends:
>
>	if (tv->tv_sec*hz + tv_usec/tick > SHRT_MAX) {
>		error = EDOM;
>		goto bad;
>	}
>
>
>	where tick = 1,000,000 / hz
>	and   SHRT_MAX = 32767

As pointed out in Stevens, the original code should be changed because
it is wrong.  I had comments in my mailbox about this bug, but seem to
have lost the mail, and I thought that -current has a fix from Lite2,
but it doesn't.

The suggested change is wronger.  It fails when tv_sec*hz overflows,
and when the addition overflows.

Checking for overflow of the multiplication is not easy:

Method 1:
Convert everything to u_long and check that the values don't change, so
that the multiplication can't trap.  Then after doing the multiplication
(m = a * b), check that ((m / a) == b && (m % a) == 0), except when a
== 0.  This would be slower than doing the division in the original code,
especially if hz is constant.

Method 2:
Avoid possibile overflow by checking that (tv->tv_sec < LONG_MAX / hz).
When combined with other checks, this gives a check much like the one
in the original code.

A more correct version of the original code:

	if (tv->tv_sec > SHRT_MAX / hz - 1) {
		error = EDOM;
		goto bad;
	}

The -1 term is unnecessary in some cases, e.g., when tv->tv_usec == 0,
but it's probably not worth doing extra checks to avoid these cases.
The -1 term needs to be -2 in some cases where hz doesn't divide
1000000 evenly and tick = 1000000 / hz is rounded in a certain way.
The rounding becomes more significant if hz is a large fraction of
1000000 and can't work right if hz > 1000000.

Perhaps the best way is to combine the checks:

	u_long val;
	...
	/* assert(hz > 0); */
	if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000 ||
	    tv->tv_sec > SHRT_MAX / hz) {
		error = EDOM;
		goto bad;
	}
	/* assert(tick > 0); */
	/* assert(ULONG_MAX - SHRT_MAX >= 1000000); */
	val = (u_long)(tv->tv_sec * hz) + tv_usec / tick;
	if (val > SHRT_MAX) {
		error = EDOM;
		goto bad;
	}

Bruce



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199712190826.TAA03871>