Skip to content

Commit e5ccc94

Browse files
bpo-38643: Raise SystemError instead of crashing when PyNumber_ToBase is called with invalid base. (GH-18863)
1 parent 413f013 commit e5ccc94

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
@@ -502,6 +502,20 @@ def test_c_subclass_of_heap_ctype_with_del_modifying_dunder_class_only_decrefs_o
502502
# Test that subtype_dealloc decref the newly assigned __class__ only once
503503
self.assertEqual(new_type_refcnt, sys.getrefcount(_testcapi.HeapCTypeSubclass))
504504

505+
def test_pynumber_tobase(self):
506+
from _testcapi import pynumber_tobase
507+
self.assertEqual(pynumber_tobase(123, 2), '0b1111011')
508+
self.assertEqual(pynumber_tobase(123, 8), '0o173')
509+
self.assertEqual(pynumber_tobase(123, 10), '123')
510+
self.assertEqual(pynumber_tobase(123, 16), '0x7b')
511+
self.assertEqual(pynumber_tobase(-123, 2), '-0b1111011')
512+
self.assertEqual(pynumber_tobase(-123, 8), '-0o173')
513+
self.assertEqual(pynumber_tobase(-123, 10), '-123')
514+
self.assertEqual(pynumber_tobase(-123, 16), '-0x7b')
515+
self.assertRaises(TypeError, pynumber_tobase, 123.0, 10)
516+
self.assertRaises(TypeError, pynumber_tobase, '123', 10)
517+
self.assertRaises(SystemError, pynumber_tobase, 123, 0)
518+
505519

506520
class TestPendingCalls(unittest.TestCase):
507521

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
@@ -5259,6 +5259,19 @@ meth_fastcall_keywords(PyObject* self, PyObject* const* args,
52595259
}
52605260

52615261

5262+
static PyObject*
5263+
pynumber_tobase(PyObject *module, PyObject *args)
5264+
{
5265+
PyObject *obj;
5266+
int base;
5267+
if (!PyArg_ParseTuple(args, "Oi:pynumber_tobase",
5268+
&obj, &base)) {
5269+
return NULL;
5270+
}
5271+
return PyNumber_ToBase(obj, base);
5272+
}
5273+
5274+
52625275
static PyObject *test_buildvalue_issue38913(PyObject *, PyObject *);
52635276

52645277
static PyMethodDef TestMethods[] = {
@@ -5519,6 +5532,7 @@ static PyMethodDef TestMethods[] = {
55195532
{"meth_noargs", meth_noargs, METH_NOARGS},
55205533
{"meth_fastcall", (PyCFunction)(void(*)(void))meth_fastcall, METH_FASTCALL},
55215534
{"meth_fastcall_keywords", (PyCFunction)(void(*)(void))meth_fastcall_keywords, METH_FASTCALL|METH_KEYWORDS},
5535+
{"pynumber_tobase", pynumber_tobase, METH_VARARGS},
55225536
{NULL, NULL} /* sentinel */
55235537
};
55245538

Objects/abstract.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1551,18 +1551,15 @@ PyNumber_Float(PyObject *o)
15511551
PyObject *
15521552
PyNumber_ToBase(PyObject *n, int base)
15531553
{
1554-
PyObject *res = NULL;
1554+
if (!(base == 2 || base == 8 || base == 10 || base == 16)) {
1555+
PyErr_SetString(PyExc_SystemError,
1556+
"PyNumber_ToBase: base must be 2, 8, 10 or 16");
1557+
return NULL;
1558+
}
15551559
PyObject *index = PyNumber_Index(n);
1556-
15571560
if (!index)
15581561
return NULL;
1559-
if (PyLong_Check(index))
1560-
res = _PyLong_Format(index, base);
1561-
else
1562-
/* It should not be possible to get here, as
1563-
PyNumber_Index already has a check for the same
1564-
condition */
1565-
PyErr_SetString(PyExc_ValueError, "PyNumber_ToBase: index not int");
1562+
PyObject *res = _PyLong_Format(index, base);
15661563
Py_DECREF(index);
15671564
return res;
15681565
}

0 commit comments

Comments
 (0)