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>