python/cast_mode: Fix integer overflow of LONG_MAX+1.

In SWIG_CanCastAsInteger, we check for double(value) <= double(LONG_MAX).

However LONG_MAX cannot be represented as double if sizeof(long)=8, so it is usually rounded
to LONG_MAX+1. So SWIG_CanCastAsInteger returned true for LONG_MAX+1 and the next cast ended
up in an integer overflow.

Worse for unsigned long: It is called by smaller unsigned integers with range checks after
the convertion to unsigned long. As the integer overflow yields a 0x0, this is valid for all
small unsigned integers. Smaller signed integers will always fail this range check after the
integer overflow.

This fixes the tests on my machine:
  as_l(lmaxd + LSB)
  as_ll(llmaxd + LSB)
  as_ul(ulmax + LSB)
  as_ull(ullmax + LSB)

Wrong old results before this commit:
  as_l(float(2**63)) := -0x8000000000000000
  as_ll(float(2**63)) := -0x8000000000000000
  as_uc(float(2**64)) := 0x0
  as_us(float(2**64)) := 0x0
  as_ui(float(2**64)) := 0x0
  as_ul(float(2**64)) := 0x0
  as_ull(float(2**64)) := 0x0

Now all of them throw an exception.
This commit is contained in:
Markus Wick 2023-01-26 13:33:49 +01:00
parent 02c45b9145
commit 5c008f0c52
2 changed files with 17 additions and 2 deletions

View File

@ -7,6 +7,17 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/
Version 4.2.0 (in progress)
===========================
2023-XX-XX: degasus
[Python] #2494 Fix integer overflow / undefined behavior in the python cast_mode on
sizeof(long)==8 platforms for the implicit conversion of the edge cases:
long(double(LONG_MAX + 1))
long long(double(LONG_MAX + 1))
unsigned char(double(ULONG_MAX + 1))
unsigned short(double(ULONG_MAX + 1))
unsigned int(double(ULONG_MAX + 1))
unsigned long(double(ULONG_MAX + 1))
unsigned long long(double(ULONG_MAX + 1))
2023-12-03: olly
[Ocaml] Remove -suffix command line option which has emitted a
deprecation warning since SWIG 3.0.4 - if you want to specify

View File

@ -104,7 +104,9 @@ SWIG_AsVal_dec(long)(PyObject *obj, long* val)
if (!dispatch) {
double d;
int res = SWIG_AddCast(SWIG_AsVal(double)(obj,&d));
if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, LONG_MAX)) {
// largest double not larger than LONG_MAX
const double long_max = sizeof(long) == 8 ? 0x7ffffffffffffc00LL : LONG_MAX;
if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, LONG_MIN, long_max)) {
if (val) *val = (long)(d);
return res;
}
@ -166,7 +168,9 @@ SWIG_AsVal_dec(unsigned long)(PyObject *obj, unsigned long *val)
if (!dispatch) {
double d;
int res = SWIG_AddCast(SWIG_AsVal(double)(obj,&d));
if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ULONG_MAX)) {
// largest double not larger than ULONG_MAX
const double ulong_max = sizeof(unsigned long) == 8 ? 0xfffffffffffff800uLL : ULONG_MAX;
if (SWIG_IsOK(res) && SWIG_CanCastAsInteger(&d, 0, ulong_max)) {
if (val) *val = (unsigned long)(d);
return res;
}