Bug #7371

Fix undefined overflow checking in bigdecimal

Added by Xi Wang over 1 year ago. Updated 4 months ago.

[ruby-core:49411]
Status:Closed
Priority:Normal
Assignee:Kenta Murata
Category:ext
Target version:-
ruby -v:1.9.x Backport:

Description

In AddExponent() at ext/bigdecimal/bigdecimal.c:3677, the overflow checks rely on signed integer overflow, which is undefined behavior in C.

SIGNED_VALUE m = e+n;
SIGNED_VALUE eb, mb;
if(e>0) {
    if(n>0) {
        mb = m*(SIGNED_VALUE)BASE_FIG;
        eb = e*(SIGNED_VALUE)BASE_FIG;
        if(mb<eb) goto overflow;
    }

Some compilers (e.g., gcc 4.8) will optimize away such overflow checks due to undefined behavior. Ruby currently uses "-fno-strict-overflow" to disable such offending optimizations in gcc, but this workaround option is not supported by other compilers, thus not portable.

The attached patch uses unsigned multiplication for overflow checking, which is well defined in C.

0001-bigdecimal.c-avoid-undefined-overflow-checking.patch Magnifier (1.31 KB) Xi Wang, 11/16/2012 05:24 PM

History

#1 Updated by Kenta Murata over 1 year ago

  • Category set to ext
  • Assignee set to Kenta Murata

#2 Updated by Usaku NAKAMURA over 1 year ago

  • Status changed from Open to Assigned

#3 Updated by Xi Wang over 1 year ago

To see how it works, try to compile the following (simplified) code with gcc 4.8. The entire function will be optimized away with "gcc -O2" (just grep "bar" in the resulting assembly code); gcc 4.7 or earlier doesn't do that.

#define SIGNEDVALUE long
#define BASE
FIG 9

void bar(void);

static void AddExponent(SIGNEDVALUE e, SIGNEDVALUE n)
{
SIGNEDVALUE m = e+n;
SIGNED
VALUE eb, mb;
if(e>0) {
if(n>0) {
mb = m(SIGNEDVALUE)BASEFIG;
eb = e
(SIGNEDVALUE)BASEFIG;
if(mb<eb) goto overflow;
}
}
return;
overflow:
bar();
}

void foo(SIGNED_VALUE e)
{
AddExponent(e, 1);
}

#4 Updated by Kenta Murata 4 months ago

  • Status changed from Assigned to Closed

I think this issue had been fixed by akr in r40214.

Also available in: Atom PDF