summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog24
-rw-r--r--array.c7
-rw-r--r--bignum.c71
-rw-r--r--insns.def13
-rw-r--r--internal.h9
-rw-r--r--numeric.c14
-rw-r--r--rational.c7
-rw-r--r--thread.c13
8 files changed, 105 insertions, 53 deletions
diff --git a/ChangeLog b/ChangeLog
index ad4beb49a6..87fae93e51 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,27 @@
+Tue Apr 9 20:38:20 2013 Tanaka Akira <akr@fsij.org>
+
+ * internal.h (MUL_OVERFLOW_SIGNED_INTEGER_P): New macro.
+ (MUL_OVERFLOW_FIXNUM_P): Ditto.
+ (MUL_OVERFLOW_LONG_P): Ditto.
+
+ * array.c (rb_ary_product): Don't overflow on signed integer
+ multiplication.
+
+ * numeric.c (fix_mul): Ditto.
+ (int_pow): Ditto.
+
+ * rational.c (f_imul): Ditto.
+
+ * insns.def (opt_mult): Ditto.
+
+ * thread.c (sleep_timeval): Don't overflow on signed integer addition.
+
+ * bignum.c (rb_int2big): Don't overflow on signed integer negation.
+ (rb_big2ulong): Ditto.
+ (rb_big2long): Ditto.
+ (rb_big2ull): Ditto.
+ (rb_big2ll): Ditto.
+
Tue Apr 9 19:45:44 2013 Tanaka Akira <akr@fsij.org>
* lib/open-uri.rb: Support multiple fields with same field
diff --git a/array.c b/array.c
index 76a3a6279d..2646e904e7 100644
--- a/array.c
+++ b/array.c
@@ -5034,15 +5034,14 @@ rb_ary_product(int argc, VALUE *argv, VALUE ary)
else {
/* Compute the length of the result array; return [] if any is empty */
for (i = 0; i < n; i++) {
- long k = RARRAY_LEN(arrays[i]), l = resultlen;
+ long k = RARRAY_LEN(arrays[i]);
if (k == 0) {
result = rb_ary_new2(0);
goto done;
}
- resultlen *= k;
- if (resultlen < k || resultlen < l || resultlen / k != l) {
+ if (MUL_OVERFLOW_LONG_P(resultlen, k))
rb_raise(rb_eRangeError, "too big to product");
- }
+ resultlen *= k;
}
result = rb_ary_new2(resultlen);
}
diff --git a/bignum.c b/bignum.c
index b2daac75af..7f4a2abaa4 100644
--- a/bignum.c
+++ b/bignum.c
@@ -309,13 +309,17 @@ VALUE
rb_int2big(SIGNED_VALUE n)
{
long neg = 0;
+ VALUE u;
VALUE big;
if (n < 0) {
- n = -n;
+ u = 1 + (VALUE)(-(n + 1)); /* u = -n avoiding overflow */
neg = 1;
}
- big = rb_uint2big(n);
+ else {
+ u = n;
+ }
+ big = rb_uint2big(u);
if (neg) {
RBIGNUM_SET_SIGN(big, 0);
}
@@ -1224,12 +1228,15 @@ rb_big2ulong(VALUE x)
{
VALUE num = big2ulong(x, "unsigned long", TRUE);
- if (!RBIGNUM_SIGN(x)) {
- unsigned long v = (unsigned long)(-(long)num);
-
- if (v <= LONG_MAX)
- rb_raise(rb_eRangeError, "bignum out of range of unsigned long");
- return (VALUE)v;
+ if (RBIGNUM_POSITIVE_P(x)) {
+ return num;
+ }
+ else {
+ if (num <= LONG_MAX)
+ return -(long)num;
+ if (num == 1+(unsigned long)(-(LONG_MIN+1)))
+ return LONG_MIN;
+ rb_raise(rb_eRangeError, "bignum out of range of unsigned long");
}
return num;
}
@@ -1239,12 +1246,18 @@ rb_big2long(VALUE x)
{
VALUE num = big2ulong(x, "long", TRUE);
- if ((long)num < 0 &&
- (RBIGNUM_SIGN(x) || (long)num != LONG_MIN)) {
- rb_raise(rb_eRangeError, "bignum too big to convert into `long'");
+ if (RBIGNUM_POSITIVE_P(x)) {
+ if (LONG_MAX < num)
+ rb_raise(rb_eRangeError, "bignum too big to convert into `long'");
+ return num;
+ }
+ else {
+ if (num <= LONG_MAX)
+ return -(long)num;
+ if (num == 1+(unsigned long)(-(LONG_MIN+1)))
+ return LONG_MIN;
+ rb_raise(rb_eRangeError, "bignum too big to convert into `long'");
}
- if (!RBIGNUM_SIGN(x)) return -(SIGNED_VALUE)num;
- return num;
}
#if HAVE_LONG_LONG
@@ -1272,13 +1285,15 @@ rb_big2ull(VALUE x)
{
unsigned LONG_LONG num = big2ull(x, "unsigned long long");
- if (!RBIGNUM_SIGN(x)) {
- LONG_LONG v = -(LONG_LONG)num;
-
- /* FIXNUM_MIN-1 .. LLONG_MIN mapped into 0xbfffffffffffffff .. LONG_MAX+1 */
- if ((unsigned LONG_LONG)v <= LLONG_MAX)
- rb_raise(rb_eRangeError, "bignum out of range of unsigned long long");
- return v;
+ if (RBIGNUM_POSITIVE_P(x)) {
+ return num;
+ }
+ else {
+ if (num <= LLONG_MAX)
+ return -(LONG_LONG)num;
+ if (num == 1+(unsigned LONG_LONG)(-(LLONG_MIN+1)))
+ return LLONG_MIN;
+ rb_raise(rb_eRangeError, "bignum out of range of unsigned long long");
}
return num;
}
@@ -1288,12 +1303,18 @@ rb_big2ll(VALUE x)
{
unsigned LONG_LONG num = big2ull(x, "long long");
- if ((LONG_LONG)num < 0 && (RBIGNUM_SIGN(x)
- || (LONG_LONG)num != LLONG_MIN)) {
- rb_raise(rb_eRangeError, "bignum too big to convert into `long long'");
+ if (RBIGNUM_POSITIVE_P(x)) {
+ if (LLONG_MAX < num)
+ rb_raise(rb_eRangeError, "bignum too big to convert into `long long'");
+ return num;
+ }
+ else {
+ if (num <= LLONG_MAX)
+ return -(LONG_LONG)num;
+ if (num == 1+(unsigned LONG_LONG)(-(LLONG_MIN+1)))
+ return LLONG_MIN;
+ rb_raise(rb_eRangeError, "bignum too big to convert into `long long'");
}
- if (!RBIGNUM_SIGN(x)) return -(LONG_LONG)num;
- return num;
}
#endif /* HAVE_LONG_LONG */
diff --git a/insns.def b/insns.def
index 979aa1cc6a..d61e99c4d0 100644
--- a/insns.def
+++ b/insns.def
@@ -1418,16 +1418,13 @@ opt_mult
val = recv;
}
else {
- volatile long c;
b = FIX2LONG(obj);
- c = a * b;
-
- if (FIXABLE(c) && c / a == b) {
- val = LONG2FIX(c);
- }
- else {
+ if (MUL_OVERFLOW_FIXNUM_P(a, b)) {
val = rb_big_mul(rb_int2big(a), rb_int2big(b));
- }
+ }
+ else {
+ val = LONG2FIX(a * b);
+ }
}
}
else if (FLONUM_2_P(recv, obj) &&
diff --git a/internal.h b/internal.h
index b832a95c89..5e9ce2460a 100644
--- a/internal.h
+++ b/internal.h
@@ -28,6 +28,15 @@ extern "C" {
#endif
#define TIMET_MAX_PLUS_ONE (2*(double)(TIMET_MAX/2+1))
+#define MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, min, max) ( \
+ (a) == 0 ? 0 : \
+ (a) == -1 ? (b) < -(max) : \
+ (a) > 0 ? \
+ ((b) > 0 ? (max) / (a) < (b) : (min) / (a) > (b)) : \
+ ((b) > 0 ? (min) / (a) < (b) : (max) / (a) > (b)))
+#define MUL_OVERFLOW_FIXNUM_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, FIXNUM_MIN, FIXNUM_MAX)
+#define MUL_OVERFLOW_LONG_P(a, b) MUL_OVERFLOW_SIGNED_INTEGER_P(a, b, LONG_MIN, LONG_MAX)
+
struct rb_deprecated_classext_struct {
char conflict[sizeof(VALUE) * 3];
};
diff --git a/numeric.c b/numeric.c
index 66820eeed1..d142c67138 100644
--- a/numeric.c
+++ b/numeric.c
@@ -2731,7 +2731,6 @@ fix_mul(VALUE x, VALUE y)
#if SIZEOF_LONG * 2 <= SIZEOF_LONG_LONG
LONG_LONG d;
#else
- volatile long c;
VALUE r;
#endif
@@ -2745,13 +2744,11 @@ fix_mul(VALUE x, VALUE y)
#else
if (FIT_SQRT_LONG(a) && FIT_SQRT_LONG(b))
return LONG2FIX(a*b);
- c = a * b;
- r = LONG2FIX(c);
-
if (a == 0) return x;
- if (FIX2LONG(r) != c || c/a != b) {
+ if (MUL_OVERFLOW_FIXNUM_P(a, b))
r = rb_big_mul(rb_int2big(a), rb_int2big(b));
- }
+ else
+ r = LONG2FIX(a * b);
return r;
#endif
}
@@ -2973,11 +2970,10 @@ int_pow(long x, unsigned long y)
y >>= 1;
}
{
- volatile long xz = x * z;
- if (!POSFIXABLE(xz) || xz / x != z) {
+ if (MUL_OVERFLOW_FIXNUM_P(x, z)) {
goto bignum;
}
- z = xz;
+ z = x * z;
}
} while (--y);
if (neg) z = -z;
diff --git a/rational.c b/rational.c
index e96cace65c..7b3e851e7e 100644
--- a/rational.c
+++ b/rational.c
@@ -639,7 +639,6 @@ inline static VALUE
f_imul(long a, long b)
{
VALUE r;
- volatile long c;
if (a == 0 || b == 0)
return ZERO;
@@ -648,10 +647,10 @@ f_imul(long a, long b)
else if (b == 1)
return LONG2NUM(a);
- c = a * b;
- r = LONG2NUM(c);
- if (NUM2LONG(r) != c || (c / a) != b)
+ if (MUL_OVERFLOW_LONG_P(a, b))
r = rb_big_mul(rb_int2big(a), rb_int2big(b));
+ else
+ r = LONG2NUM(a * b);
return r;
}
diff --git a/thread.c b/thread.c
index 72cb69b720..8a1e090160 100644
--- a/thread.c
+++ b/thread.c
@@ -994,10 +994,17 @@ sleep_timeval(rb_thread_t *th, struct timeval tv, int spurious_check)
enum rb_thread_status prev_status = th->status;
getclockofday(&to);
- to.tv_sec += tv.tv_sec;
+ if (TIMET_MAX - tv.tv_sec < to.tv_sec)
+ to.tv_sec = TIMET_MAX;
+ else
+ to.tv_sec += tv.tv_sec;
if ((to.tv_usec += tv.tv_usec) >= 1000000) {
- to.tv_sec++;
- to.tv_usec -= 1000000;
+ if (to.tv_sec == TIMET_MAX)
+ to.tv_usec = 999999;
+ else {
+ to.tv_sec++;
+ to.tv_usec -= 1000000;
+ }
}
th->status = THREAD_STOPPED;