Skip to content

Commit ab9c729

Browse files
[3.8] bpo-38643: Raise SystemError instead of crashing when PyNumber_ToBase is called with invalid base. (GH-18863). (GH-18954)
(cherry picked from commit e5ccc94)
1 parent 99ef1ac commit ab9c729

File tree

4 files changed

+36
-9
lines changed

4 files changed

+36
-9
lines changed

Lib/test/test_capi.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,20 @@ def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_o
473473
# Test that subtype_dealloc decref the newly assigned __class__ only once
474474
self.assertEqual(new_type_refcnt, sys.getrefcount(_testcapi.HeapCTypeSubclass))
475475

476+
def test_pynumber_tobase(self):
477+
from _testcapi import pynumber_tobase
478+
self.assertEqual(pynumber_tobase(123, 2), '0b1111011')
479+
self.assertEqual(pynumber_tobase(123, 8), '0o173')
480+
self.assertEqual(pynumber_tobase(123, 10), '123')
481+
self.assertEqual(pynumber_tobase(123, 16), '0x7b')
482+
self.assertEqual(pynumber_tobase(-123, 2), '-0b1111011')
483+
self.assertEqual(pynumber_tobase(-123, 8), '-0o173')
484+
self.assertEqual(pynumber_tobase(-123, 10), '-123')
485+
self.assertEqual(pynumber_tobase(-123, 16), '-0x7b')
486+
self.assertRaises(TypeError, pynumber_tobase, 123.0, 10)
487+
self.assertRaises(TypeError, pynumber_tobase, '123', 10)
488+
self.assertRaises(SystemError, pynumber_tobase, 123, 0)
489+
476490

477491
class TestPendingCalls(unittest.TestCase):
478492

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:c:func:`PyNumber_ToBase` now raises a :exc:`SystemError` instead of
2+
crashing when called with invalid base.

Modules/_testcapimodule.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5063,6 +5063,19 @@ test_write_unraisable_exc(PyObject *self, PyObject *args)
50635063
}
50645064

50655065

5066+
static PyObject*
5067+
pynumber_tobase(PyObject *module, PyObject *args)
5068+
{
5069+
PyObject *obj;
5070+
int base;
5071+
if (!PyArg_ParseTuple(args, "Oi:pynumber_tobase",
5072+
&obj, &base)) {
5073+
return NULL;
5074+
}
5075+
return PyNumber_ToBase(obj, base);
5076+
}
5077+
5078+
50665079
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
50675080

50685081
static PyMethodDef TestMethods[] = {
@@ -5309,6 +5322,7 @@ static PyMethodDef TestMethods[] = {
53095322
{"negative_refcount", negative_refcount, METH_NOARGS},
53105323
#endif
53115324
{"write_unraisable_exc", test_write_unraisable_exc, METH_VARARGS},
5325+
{"pynumber_tobase", pynumber_tobase, METH_VARARGS},
53125326
{NULL, NULL} /* sentinel */
53135327
};
53145328

Objects/abstract.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,18 +1509,15 @@ PyNumber_Float(PyObject *o)
15091509
PyObject *
15101510
PyNumber_ToBase(PyObject *n, int base)
15111511
{
1512-
PyObject *res = NULL;
1512+
if (!(base == 2 || base == 8 || base == 10 || base == 16)) {
1513+
PyErr_SetString(PyExc_SystemError,
1514+
"PyNumber_ToBase: base must be 2, 8, 10 or 16");
1515+
return NULL;
1516+
}
15131517
PyObject *index = PyNumber_Index(n);
1514-
15151518
if (!index)
15161519
return NULL;
1517-
if (PyLong_Check(index))
1518-
res = _PyLong_Format(index, base);
1519-
else
1520-
/* It should not be possible to get here, as
1521-
PyNumber_Index already has a check for the same
1522-
condition */
1523-
PyErr_SetString(PyExc_ValueError, "PyNumber_ToBase: index not int");
1520+
PyObject *res = _PyLong_Format(index, base);
15241521
Py_DECREF(index);
15251522
return res;
15261523
}

0 commit comments

Comments
 (0)