| Summary: | LONG_MIN / 1 sends a "Floating exception" | ||||||
|---|---|---|---|---|---|---|---|
| Product: | Base System | Reporter: | Patrick Pelissier <ppelissi> | ||||
| Component: | alpha | Assignee: | freebsd-alpha (Nobody) <alpha> | ||||
| Status: | Closed FIXED | ||||||
| Severity: | Affects Only Me | ||||||
| Priority: | Normal | ||||||
| Version: | Unspecified | ||||||
| Hardware: | Any | ||||||
| OS: | Any | ||||||
| Attachments: |
|
||||||
Ooh a good excuse to look at Alpha assembler. (/me googles for the alpha
instruction set.)
(Can someone on the -alpha list tell me if this reasoning is correct,
and if I should commit the patch?)
Alpha appears not to have an integer division instruction, so this is
implemented by divq(), the source of which is generated from
"src/lib/libc/alpha/gen/divrem4"
(For reading the macros when preprocessing as divq, S is true, OP is
"div", and WORDSIZE is 64)
If I'm reading it correctly, the start of this function decides what
sign the result should have, then gets the absolute value of each
operand, (search for "subq zero, A, A" and "subq zero, B, B".) Note, for
"LONG_MIN", or 0x8000000000000000, 2's compliment subtraction for zero
gives back the 0x8000000000000000
The body of the work is done with logical shifts, so everything still
works when treating the dividend as an unsigned long.
At the end of the function, the code takes the calculated result, and
decides if it needs to negate it:
> /* Check to see if we should negate it. */
> subqv zero, RESULT, T_0
> cmovlbs NEG, T_0, RESULT
The "v" here means "trap overflows", which is why we blow up. LONG_MIN
is the only value that _can_ blow up here, and you can only reach this
case if you start with LONG_MIN / 1, if my reasoning isn't flawed.
So, I think it's perfectly safe to remove the "v" from the subq
instruction. i.e., I think the attached patch is correct.
Er, After realising that this source came from NetBSD, I checked their CVS repo, and found it already fixed there with an equivalent patch. There's also an equivalent in the kernel: /usr/src/sys/alpha/alpha/divrem.m4. The kernel case might be a security issue if you could find a syscall that would do arbitrary (assuming not by zero!) division of 64-bit values. State Changed From-To: open->closed Fix committed to 6-current and 5-stable |
The following program: #include <limits.h> int f(long a, long b); int main() { return f(LONG_MIN, 1); } int f(long a, long b) { return a / b; } produces a "Floating exception (core dumped)" on alpha-unknown-freebsd5.2.1 (spe149.testdrive.hp.com) with: gcc version 3.4.2 and gcc version 3.3.3 [FreeBSD] 20031106. The test program was compiled without any flags: spe149.testdrive.hp.com> /tmp/make/bin/gcc test3.c spe149.testdrive.hp.com> ./a.out Floating exception (core dumped) spe149.testdrive.hp.com> /tmp/make/bin/gcc -v Reading specs from /tmp/make/lib/gcc/alpha-unknown-freebsd5.2.1/3.4.2/specs Configured with: ./configure --prefix=/tmp/gcc : (reconfigured) ./configure --prefix=/tmp/gcc : (reconfigured) ./configure --prefix=/tmp/make --enable-languages=c Thread model: posix gcc version 3.4.2 The problem seems to be in the internal function "__divq" which produces the exception. (See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=17613 )