Issue #1621: Avoid signed overflow in list and tuple operations

Patch by Xiang Zhang.
diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py
index f20fdc0..26e9368 100644
--- a/Lib/test/list_tests.py
+++ b/Lib/test/list_tests.py
@@ -266,9 +266,21 @@
         self.assertEqual(a, list("spameggs"))
 
         self.assertRaises(TypeError, a.extend, None)
-
         self.assertRaises(TypeError, a.extend)
 
+        # overflow test. issue1621
+        class CustomIter:
+            def __iter__(self):
+                return self
+            def __next__(self):
+                raise StopIteration
+            def __length_hint__(self):
+                return sys.maxsize
+        a = self.type2test([1,2,3,4])
+        a.extend(CustomIter())
+        self.assertEqual(a, [1,2,3,4])
+
+
     def test_insert(self):
         a = self.type2test([0, 1, 2])
         a.insert(0, -2)
diff --git a/Misc/NEWS b/Misc/NEWS
index 80c6573..67ee549 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -16,6 +16,9 @@
 - Issue #27581: Don't rely on wrapping for overflow check in
   PySequence_Tuple().  Patch by Xiang Zhang.
 
+- Issue #1621: Avoid signed integer overflow in list and tuple operations.
+  Patch by Xiang Zhang.
+
 - Issue #27419: Standard __import__() no longer look up "__import__" in globals
   or builtins for importing submodules or "from import".  Fixed a crash if
   raise a warning about unabling to resolve package from __spec__ or
diff --git a/Objects/listobject.c b/Objects/listobject.c
index ddc0fee..0b2c8c1 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -488,9 +488,9 @@
         return NULL;
     }
 #define b ((PyListObject *)bb)
-    size = Py_SIZE(a) + Py_SIZE(b);
-    if (size < 0)
+    if (Py_SIZE(a) > PY_SSIZE_T_MAX - Py_SIZE(b))
         return PyErr_NoMemory();
+    size = Py_SIZE(a) + Py_SIZE(b);
     np = (PyListObject *) PyList_New(size);
     if (np == NULL) {
         return NULL;
@@ -841,18 +841,20 @@
         return NULL;
     }
     m = Py_SIZE(self);
-    mn = m + n;
-    if (mn >= m) {
+    if (m > PY_SSIZE_T_MAX - n) {
+        /* m + n overflowed; on the chance that n lied, and there really
+         * is enough room, ignore it.  If n was telling the truth, we'll
+         * eventually run out of memory during the loop.
+         */
+    }
+    else {
+        mn = m + n;
         /* Make room. */
         if (list_resize(self, mn) < 0)
             goto error;
         /* Make the list sane again. */
         Py_SIZE(self) = m;
     }
-    /* Else m + n overflowed; on the chance that n lied, and there really
-     * is enough room, ignore it.  If n was telling the truth, we'll
-     * eventually run out of memory during the loop.
-     */
 
     /* Run iterator to exhaustion. */
     for (;;) {
diff --git a/Objects/tupleobject.c b/Objects/tupleobject.c
index 1b41258..c0ff499 100644
--- a/Objects/tupleobject.c
+++ b/Objects/tupleobject.c
@@ -453,9 +453,9 @@
         return NULL;
     }
 #define b ((PyTupleObject *)bb)
-    size = Py_SIZE(a) + Py_SIZE(b);
-    if (size < 0)
+    if (Py_SIZE(a) > PY_SSIZE_T_MAX - Py_SIZE(b))
         return PyErr_NoMemory();
+    size = Py_SIZE(a) + Py_SIZE(b);
     np = (PyTupleObject *) PyTuple_New(size);
     if (np == NULL) {
         return NULL;