diff options
| author | Gregory P. Smith <greg@krypto.org> | 2022-09-06 05:24:36 (GMT) |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-06 05:24:36 (GMT) |
| commit | 15ec1afd4fcd2da1e2d2b256c562fb42d8d886a2 (patch) | |
| tree | 2d57338531c6e22098a8a76f26262894920a85bc /Python | |
| parent | d5fe9951fe6e69e9175eca24170a220747ec9047 (diff) | |
| download | cpython-15ec1afd4fcd2da1e2d2b256c562fb42d8d886a2.zip cpython-15ec1afd4fcd2da1e2d2b256c562fb42d8d886a2.tar.gz cpython-15ec1afd4fcd2da1e2d2b256c562fb42d8d886a2.tar.bz2 | |
[3.7] gh-95778: CVE-2020-10735: Prevent DoS by very large int() (GH-96504)
Converting between `int` and `str` in bases other than 2
(binary), 4, 8 (octal), 16 (hexadecimal), or 32 such as base 10 (decimal) now
raises a `ValueError` if the number of digits in string form is above a
limit to avoid potential denial of service attacks due to the algorithmic
complexity. This is a mitigation for CVE-2020-10735
(https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10735).
This new limit can be configured or disabled by environment variable, command
line flag, or :mod:`sys` APIs. See the `Integer String Conversion Length
Limitation` documentation. The default limit is 4300
digits in string form.
Patch by Gregory P. Smith [Google] and Christian Heimes [Red Hat] with feedback
from Victor Stinner, Thomas Wouters, Steve Dower, Ned Deily, and Mark Dickinson.
Diffstat (limited to 'Python')
| -rw-r--r-- | Python/ast.c | 33 | ||||
| -rw-r--r-- | Python/clinic/sysmodule.c.h | 50 | ||||
| -rw-r--r-- | Python/pylifecycle.c | 4 | ||||
| -rw-r--r-- | Python/sysmodule.c | 44 |
4 files changed, 127 insertions, 4 deletions
diff --git a/Python/ast.c b/Python/ast.c index 9d8a354..d67e2b2 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -9,6 +9,11 @@ #include "ast.h" #include "token.h" #include "pythonrun.h" +/* A Windows header defines its own Yield macro, so we don't use the one + * from Python-ast.h and instead call _Py_Yield() directly. [ugh] */ +#undef Yield +#include "internal/pystate.h" +#undef Yield #include <assert.h> #include <stdbool.h> @@ -2138,8 +2143,32 @@ ast_for_atom(struct compiling *c, const node *n) } case NUMBER: { PyObject *pynum = parsenumber(c, STR(ch)); - if (!pynum) + if (!pynum) { + PyThreadState *tstate = PyThreadState_GET(); + // The only way a ValueError should happen in _this_ code is via + // PyLong_FromString hitting a length limit. + if (tstate->curexc_type == PyExc_ValueError && + tstate->curexc_value != NULL) { + PyObject *type, *value, *tb; + // This acts as PyErr_Clear() as we're replacing curexc. + PyErr_Fetch(&type, &value, &tb); + Py_XDECREF(tb); + Py_DECREF(type); + PyObject *helpful_msg = PyUnicode_FromFormat( + "%S - Consider hexadecimal for huge integer literals " + "to avoid decimal conversion limits.", + value); + if (helpful_msg) { + const char* error_msg = PyUnicode_AsUTF8(helpful_msg); + if (error_msg) { + ast_error(c, ch, error_msg); + } + Py_DECREF(helpful_msg); + } + Py_DECREF(value); + } return NULL; + } if (PyArena_AddPyObject(c->c_arena, pynum) < 0) { Py_DECREF(pynum); @@ -2678,7 +2707,7 @@ ast_for_expr(struct compiling *c, const node *n) } if (is_from) return YieldFrom(exp, LINENO(n), n->n_col_offset, c->c_arena); - return Yield(exp, LINENO(n), n->n_col_offset, c->c_arena); + return _Py_Yield(exp, LINENO(n), n->n_col_offset, c->c_arena); } case factor: if (NCH(n) == 1) { diff --git a/Python/clinic/sysmodule.c.h b/Python/clinic/sysmodule.c.h index 3e14805..bc6c99a 100644 --- a/Python/clinic/sysmodule.c.h +++ b/Python/clinic/sysmodule.c.h @@ -63,4 +63,52 @@ sys_get_coroutine_origin_tracking_depth(PyObject *module, PyObject *Py_UNUSED(ig exit: return return_value; } -/*[clinic end generated code: output=4a3ac42b97d710ff input=a9049054013a1b77]*/ + +PyDoc_STRVAR(sys_get_int_max_str_digits__doc__, +"get_int_max_str_digits($module, /)\n" +"--\n" +"\n" +"Set the maximum string digits limit for non-binary int<->str conversions."); + +#define SYS_GET_INT_MAX_STR_DIGITS_METHODDEF \ + {"get_int_max_str_digits", (PyCFunction)sys_get_int_max_str_digits, METH_NOARGS, sys_get_int_max_str_digits__doc__}, + +static PyObject * +sys_get_int_max_str_digits_impl(PyObject *module); + +static PyObject * +sys_get_int_max_str_digits(PyObject *module, PyObject *Py_UNUSED(ignored)) +{ + return sys_get_int_max_str_digits_impl(module); +} + +PyDoc_STRVAR(sys_set_int_max_str_digits__doc__, +"set_int_max_str_digits($module, /, maxdigits)\n" +"--\n" +"\n" +"Set the maximum string digits limit for non-binary int<->str conversions."); + +#define SYS_SET_INT_MAX_STR_DIGITS_METHODDEF \ + {"set_int_max_str_digits", (PyCFunction)sys_set_int_max_str_digits, METH_FASTCALL|METH_KEYWORDS, sys_set_int_max_str_digits__doc__}, + +static PyObject * +sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits); + +static PyObject * +sys_set_int_max_str_digits(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"maxdigits", NULL}; + static _PyArg_Parser _parser = {"i:set_int_max_str_digits", _keywords, 0}; + int maxdigits; + + if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, + &maxdigits)) { + goto exit; + } + return_value = sys_set_int_max_str_digits_impl(module, maxdigits); + +exit: + return return_value; +} +/*[clinic end generated code: output=c566fcdbb8f6ae2c input=a9049054013a1b77]*/ diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 55d1ba5..ff0087e 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -6,6 +6,7 @@ #undef Yield /* undefine macro conflicting with winbase.h */ #include "internal/context.h" #include "internal/hamt.h" +#include "internal/pycore_long.h" #include "internal/pystate.h" #include "grammar.h" #include "node.h" @@ -130,6 +131,9 @@ int Py_LegacyWindowsFSEncodingFlag = 0; /* Uses mbcs instead of utf-8 */ int Py_LegacyWindowsStdioFlag = 0; /* Uses FileIO instead of WindowsConsoleIO */ #endif +/* Unusual name compared to the above for backporting from 3.12 reasons. */ +int _Py_global_config_int_max_str_digits = -1; /* -X int_max_str_digits or PYTHONINTMAXSTRDIGITS */ + /* Hack to force loading of object files */ int (*_PyOS_mystrnicmp_hack)(const char *, const char *, Py_ssize_t) = \ PyOS_mystrnicmp; /* Python/pystrcmp.o */ diff --git a/Python/sysmodule.c b/Python/sysmodule.c index b953a00..82e029f 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -15,6 +15,7 @@ Data members: */ #include "Python.h" +#include "internal/pycore_long.h" // _PY_LONG_MAX_STR_DIGITS_THRESHOLD #include "internal/pystate.h" #include "code.h" #include "frameobject.h" @@ -1218,6 +1219,43 @@ sys_mdebug(PyObject *self, PyObject *args) } #endif /* USE_MALLOPT */ + +/*[clinic input] +sys.get_int_max_str_digits + +Set the maximum string digits limit for non-binary int<->str conversions. +[clinic start generated code]*/ + +static PyObject * +sys_get_int_max_str_digits_impl(PyObject *module) +/*[clinic end generated code: output=0042f5e8ae0e8631 input=8dab13e2023e60d5]*/ +{ + return PyLong_FromSsize_t(_PyRuntime.int_max_str_digits); +} + +/*[clinic input] +sys.set_int_max_str_digits + + maxdigits: int + +Set the maximum string digits limit for non-binary int<->str conversions. +[clinic start generated code]*/ + +static PyObject * +sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits) +/*[clinic end generated code: output=734d4c2511f2a56d input=d7e3f325db6910c5]*/ +{ + if ((!maxdigits) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) { + _PyRuntime.int_max_str_digits = maxdigits; + Py_RETURN_NONE; + } else { + PyErr_Format( + PyExc_ValueError, "maxdigits must be 0 or larger than %d", + _PY_LONG_MAX_STR_DIGITS_THRESHOLD); + return NULL; + } +} + size_t _PySys_GetSizeOf(PyObject *o) { @@ -1605,6 +1643,8 @@ static PyMethodDef sys_methods[] = { {"getandroidapilevel", (PyCFunction)sys_getandroidapilevel, METH_NOARGS, getandroidapilevel_doc}, #endif + SYS_GET_INT_MAX_STR_DIGITS_METHODDEF + SYS_SET_INT_MAX_STR_DIGITS_METHODDEF {NULL, NULL} /* sentinel */ }; @@ -2051,6 +2091,7 @@ static PyStructSequence_Field flags_fields[] = { {"isolated", "-I"}, {"dev_mode", "-X dev"}, {"utf8_mode", "-X utf8"}, + {"int_max_str_digits", "-X int_max_str_digits"}, {0} }; @@ -2058,7 +2099,7 @@ static PyStructSequence_Desc flags_desc = { "sys.flags", /* name */ flags__doc__, /* doc */ flags_fields, /* fields */ - 15 + 16 }; static PyObject* @@ -2092,6 +2133,7 @@ make_flags(void) SetFlag(Py_IsolatedFlag); PyStructSequence_SET_ITEM(seq, pos++, PyBool_FromLong(core_config->dev_mode)); SetFlag(Py_UTF8Mode); + SetFlag(_Py_global_config_int_max_str_digits); #undef SetFlag if (PyErr_Occurred()) { |
