changeset 30142:a4d1c080e445

fix construction of ranges with extreme integer values (bug #61132) * ov.cc (check_colon_operand<T>): New template. (make_range<T>): Use it. * range.tst: New test.
author John W. Eaton <jwe@octave.org>
date Thu, 09 Sep 2021 10:08:12 -0400
parents 58f043641a78
children 45c45295a84a
files libinterp/octave-value/ov.cc test/range.tst
diffstat 2 files changed, 46 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/octave-value/ov.cc	Wed Sep 08 16:02:04 2021 -0700
+++ b/libinterp/octave-value/ov.cc	Thu Sep 09 10:08:12 2021 -0400
@@ -27,6 +27,8 @@
 #  include "config.h"
 #endif
 
+#include <cmath>
+
 #include "data-conv.h"
 #include "quit.h"
 #include "str-vec.h"
@@ -3001,7 +3003,26 @@
     return get_colon_op_type (typ, limit.builtin_type ());
   }
 
-  // Templated version used for various integer types (int8, uint16, ...)
+  // This check depends on the type of VAL either being the expected
+  // integer type or a double value.
+
+  template <typename T>
+  static void
+  check_colon_operand (const octave_value& val, const char *op_str)
+  {
+    if (! val.is_double_type ())
+      return;
+
+    double dval = val.double_value ();
+    double intpart;
+
+    if (dval > std::numeric_limits<typename T::val_type>::max ()
+        || dval < std::numeric_limits<typename T::val_type>::min ()
+        || std::modf (dval, &intpart) != 0.0)
+      error ("colon operator %s invalid (not an integer or out of range for given integer type)", op_str);
+  }
+
+// Templated version used for various integer types (int8, uint16, ...)
   template <typename T>
   octave_value
   make_range (const octave_value& base, const octave_value& increment,
@@ -3010,22 +3031,15 @@
     if (base.isempty () || increment.isempty () || limit.isempty ())
       return octave_value (range<T> (), for_cmd_expr);
 
-    double dval;
+    check_colon_operand<T> (base, "lower bound");
+    check_colon_operand<T> (increment, "increment");
+    check_colon_operand<T> (limit, "upper bound");
 
     T base_val = octave_value_extract<T> (base);
-    dval = base.double_value ();
-    if (base_val != dval)
-      error ("colon operator lower bound invalid (not an integer or out of range for given integer type)");
 
     T increment_val = octave_value_extract<T> (increment);
-    dval = increment.double_value ();
-    if (increment_val != dval)
-      error ("colon operator increment invalid (not an integer or out of range for given integer type)");
 
     T limit_val = octave_value_extract<T> (limit);
-    dval = limit.double_value ();
-    if (limit_val != dval)
-      error ("colon operator upper bound invalid (not an integer or out of range for given integer type)");
 
     range<T> r (base_val, increment_val, limit_val);
 
--- a/test/range.tst	Wed Sep 08 16:02:04 2021 -0700
+++ b/test/range.tst	Thu Sep 09 10:08:12 2021 -0400
@@ -79,6 +79,9 @@
 %!assert ([ r ; uint32(z)          ], uint32 (expect))
 %!assert ([ r ; uint64(z)          ], uint64 (expect))
 
+
+
+
 ## Test corner cases of ranges (base and limit)
 
 %!shared r, rrev, rneg
@@ -483,6 +486,7 @@
 %!error <invalid types found in range> (1:1:{5})
 %!error <incompatible types found in range> (int8(1):int16(1):5)
 %!error <incompatible types found in range> (int8(1):1:int16(5))
+
 ## Tests with mixed integer/floating point values
 %!error <colon operator lower bound invalid> (1.5:uint8(1):5)
 %!error <colon operator lower bound invalid> (-1:uint8(1):5)
@@ -490,3 +494,20 @@
 %!error <colon operator increment invalid> (uint8(1):-1:5)
 %!error <colon operator upper bound invalid> (uint8(1):1:5.5)
 %!error <colon operator upper bound invalid> (uint8(1):1:256)
+
+## Extreme integer values.
+%!test <61132>
+%! types = {"int8", "int16", "int32", "int64", ...
+%!          "uint8", "uint16", "uint32", "uint64"};
+%! for i = 1:numel (types)
+%!   cls = types{i};
+%!   lo = intmin (cls);
+%!   hi = intmax (cls);
+%!   n = 99;
+%!   rlo = lo:(lo+n);
+%!   rhi = (hi-n):hi;
+%!   assert (class (rlo), cls);
+%!   assert (class (rhi), cls);
+%!   assert (numel (rlo), n+1);
+%!   assert (numel (rhi), n+1);
+%! endfor