summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorGregory P. Smith <greg@krypto.org>2022-09-06 05:24:36 (GMT)
committerGitHub <noreply@github.com>2022-09-06 05:24:36 (GMT)
commit15ec1afd4fcd2da1e2d2b256c562fb42d8d886a2 (patch)
tree2d57338531c6e22098a8a76f26262894920a85bc /Python
parentd5fe9951fe6e69e9175eca24170a220747ec9047 (diff)
downloadcpython-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.c33
-rw-r--r--Python/clinic/sysmodule.c.h50
-rw-r--r--Python/pylifecycle.c4
-rw-r--r--Python/sysmodule.c44
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()) {