Skip to content

Commit 6f4e7fc

Browse files
[3.7] bpo-38643: Raise SystemError instead of crashing when PyNumber_ToBase is called with invalid base. (GH-18863). (GH-18955)
(cherry picked from commit e5ccc94)
1 parent 500999f commit 6f4e7fc

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
@@ -315,6 +315,20 @@ def items(self):
315315
self.assertRaises(TypeError, _testcapi.get_mapping_values, bad_mapping)
316316
self.assertRaises(TypeError, _testcapi.get_mapping_items, bad_mapping)
317317

318+
def test_pynumber_tobase(self):
319+
from _testcapi import pynumber_tobase
320+
self.assertEqual(pynumber_tobase(123, 2), '0b1111011')
321+
self.assertEqual(pynumber_tobase(123, 8), '0o173')
322+
self.assertEqual(pynumber_tobase(123, 10), '123')
323+
self.assertEqual(pynumber_tobase(123, 16), '0x7b')
324+
self.assertEqual(pynumber_tobase(-123, 2), '-0b1111011')
325+
self.assertEqual(pynumber_tobase(-123, 8), '-0o173')
326+
self.assertEqual(pynumber_tobase(-123, 10), '-123')
327+
self.assertEqual(pynumber_tobase(-123, 16), '-0x7b')
328+
self.assertRaises(TypeError, pynumber_tobase, 123.0, 10)
329+
self.assertRaises(TypeError, pynumber_tobase, '123', 10)
330+
self.assertRaises(SystemError, pynumber_tobase, 123, 0)
331+
318332

319333
class TestPendingCalls(unittest.TestCase):
320334

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
@@ -4653,6 +4653,19 @@ get_main_config(PyObject *self, PyObject *Py_UNUSED(args))
46534653
}
46544654

46554655

4656+
static PyObject*
4657+
pynumber_tobase(PyObject *module, PyObject *args)
4658+
{
4659+
PyObject *obj;
4660+
int base;
4661+
if (!PyArg_ParseTuple(args, "Oi:pynumber_tobase",
4662+
&obj, &base)) {
4663+
return NULL;
4664+
}
4665+
return PyNumber_ToBase(obj, base);
4666+
}
4667+
4668+
46564669
static PyMethodDef TestMethods[] = {
46574670
{"raise_exception", raise_exception, METH_VARARGS},
46584671
{"raise_memoryerror", (PyCFunction)raise_memoryerror, METH_NOARGS},
@@ -4888,6 +4901,7 @@ static PyMethodDef TestMethods[] = {
48884901
{"get_global_config", get_global_config, METH_NOARGS},
48894902
{"get_core_config", get_core_config, METH_NOARGS},
48904903
{"get_main_config", get_main_config, METH_NOARGS},
4904+
{"pynumber_tobase", pynumber_tobase, METH_VARARGS},
48914905
{NULL, NULL} /* sentinel */
48924906
};
48934907

Objects/abstract.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,18 +1480,15 @@ PyNumber_Float(PyObject *o)
14801480
PyObject *
14811481
PyNumber_ToBase(PyObject *n, int base)
14821482
{
1483-
PyObject *res = NULL;
1483+
if (!(base == 2 || base == 8 || base == 10 || base == 16)) {
1484+
PyErr_SetString(PyExc_SystemError,
1485+
"PyNumber_ToBase: base must be 2, 8, 10 or 16");
1486+
return NULL;
1487+
}
14841488
PyObject *index = PyNumber_Index(n);
1485-
14861489
if (!index)
14871490
return NULL;
1488-
if (PyLong_Check(index))
1489-
res = _PyLong_Format(index, base);
1490-
else
1491-
/* It should not be possible to get here, as
1492-
PyNumber_Index already has a check for the same
1493-
condition */
1494-
PyErr_SetString(PyExc_ValueError, "PyNumber_ToBase: index not int");
1491+
PyObject *res = _PyLong_Format(index, base);
14951492
Py_DECREF(index);
14961493
return res;
14971494
}

0 commit comments

Comments
 (0)