cpython: ed76dc34b39d (original) (raw)

--- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -252,11 +252,15 @@ always available. :const:verbose :option:-v :const:bytes_warning :option:-b :const:quiet :option:-q

+ .. versionchanged:: 3.3 Removed obsolete division_warning attribute.

--- a/Doc/reference/datamodel.rst +++ b/Doc/reference/datamodel.rst @@ -1277,6 +1277,8 @@ Basic customization inheritance of :meth:__hash__ will be blocked, just as if :attr:__hash__ had been explicitly set to :const:None.

.. method:: object.bool(self)

--- a/Doc/using/cmdline.rst +++ b/Doc/using/cmdline.rst @@ -24,7 +24,7 @@ Command line When invoking Python, you may specify any of these options::

The most common use case is, of course, a simple invocation of a script:: @@ -227,6 +227,29 @@ Miscellaneous options .. versionadded:: 3.2 +.. cmdoption:: -R +

+ .. cmdoption:: -s Don't add the :data:user site-packages directory <site.USER_SITE> to @@ -352,6 +375,7 @@ Options you shouldn't use .. _Jython: http://jython.org[](#l3.44) + .. _using-on-envvars: Environment variables @@ -460,6 +484,27 @@ These environment variables influence Py option. +.. envvar:: PYTHONHASHSEED +

+ .. envvar:: PYTHONIOENCODING If this is set before running the interpreter, it overrides the encoding used

--- a/Include/object.h +++ b/Include/object.h @@ -554,6 +554,12 @@ PyAPI_FUNC(Py_hash_t) _Py_HashPointer(vo PyAPI_FUNC(Py_hash_t) _Py_HashBytes(unsigned char*, Py_ssize_t); #endif +typedef struct {

+} _Py_HashSecret_t; +PyAPI_DATA(_Py_HashSecret_t) _Py_HashSecret; + /* Helper for passing objects to printf and the like */ #define PyObject_REPR(obj) _PyUnicode_AsString(PyObject_Repr(obj))

--- a/Include/pydebug.h +++ b/Include/pydebug.h @@ -19,6 +19,7 @@ PyAPI_DATA(int) Py_IgnoreEnvironmentFlag PyAPI_DATA(int) Py_DontWriteBytecodeFlag; PyAPI_DATA(int) Py_NoUserSiteDirectory; PyAPI_DATA(int) Py_UnbufferedStdioFlag; +PyAPI_DATA(int) Py_HashRandomizationFlag; /* this is a wrapper around getenv() that pays attention to Py_IgnoreEnvironmentFlag. It should be used for getting variables like

--- a/Include/pythonrun.h +++ b/Include/pythonrun.h @@ -246,6 +246,8 @@ typedef void (PyOS_sighandler_t)(int); PyAPI_FUNC(PyOS_sighandler_t) PyOS_getsig(int); PyAPI_FUNC(PyOS_sighandler_t) PyOS_setsig(int, PyOS_sighandler_t); +/ Random */ +PyAPI_FUNC(int) _PyOS_URandom (void *buffer, Py_ssize_t size); #ifdef __cplusplus }

--- a/Lib/json/init.py +++ b/Lib/json/init.py @@ -31,7 +31,9 @@ Encoding basic Python object hierarchies Compact encoding:: >>> import json

--- a/Lib/os.py +++ b/Lib/os.py @@ -852,23 +852,6 @@ try: except NameError: # statvfs_result may not exist pass -if not _exists("urandom"):

-

-

-

Supply os.popen()

def popen(cmd, mode="r", buffering=-1): if not isinstance(cmd, str):

--- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1786,8 +1786,6 @@ class TestDateTime(TestDate): self.assertTrue(abs(from_timestamp - from_now) <= tolerance) def test_strptime(self):

- string = '2004-12-01 13:02:47.197' format = '%Y-%m-%d %H:%M:%S.%f' expected = _strptime._strptime_datetime(self.theclass, string, format)

--- a/Lib/test/mapping_tests.py +++ b/Lib/test/mapping_tests.py @@ -14,7 +14,7 @@ class BasicTestMappingProtocol(unittest. def _reference(self): """Return a dictionary of values which are invariant by storage in the object under test."""

--- a/Lib/test/script_helper.py +++ b/Lib/test/script_helper.py @@ -3,7 +3,6 @@ import sys import os -import re import os.path import tempfile import subprocess @@ -20,11 +19,15 @@ def _assert_python(expected_success, *ar cmd_line = [sys.executable] if not env_vars: cmd_line.append('-E')

--- a/Lib/test/support.py +++ b/Lib/test/support.py @@ -1588,6 +1588,7 @@ def args_from_interpreter_flags(): flag_opt_map = { 'bytes_warning': 'b', 'dont_write_bytecode': 'B',

--- a/Lib/test/test_cmd_line.py +++ b/Lib/test/test_cmd_line.py @@ -324,6 +324,22 @@ class CmdLineTest(unittest.TestCase): def test_no_std_streams(self): self._test_no_stdio(['stdin', 'stdout', 'stderr'])

+

def test_main(): test.support.run_unittest(CmdLineTest)

--- a/Lib/test/test_datetime.py +++ b/Lib/test/test_datetime.py @@ -1,7 +1,9 @@ import unittest import sys from test.support import import_fresh_module, run_unittest + TESTS = 'test.datetimetester' +

XXX: import_fresh_module() is supposed to leave sys.module cache untouched,

XXX: but it does not, so we have to save and restore it ourselves.

save_sys_modules = sys.modules.copy() @@ -15,28 +17,32 @@ finally: sys.modules.update(save_sys_modules) test_modules = [pure_tests, fast_tests] test_suffixes = ["_Pure", "_Fast"] +# XXX(gb) First run all the _Pure tests, then all the _Fast tests. You might +# not believe this, but in spite of all the sys.modules trickery running a _Pure +# test last will leave a mix of pure and native datetime stuff lying around. +test_classes = [] for module, suffix in zip(test_modules, test_suffixes): for name, cls in module.dict.items():

def test_main():

if name == "main": test_main()

--- a/Lib/test/test_dbm_gnu.py +++ b/Lib/test/test_dbm_gnu.py @@ -53,7 +53,7 @@ class TestGdbm(unittest.TestCase): all = set(gdbm.open_flags) # Test standard flags (presumably "crwn"). modes = all - set('fsu')

--- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -350,12 +350,13 @@ Variable names: 6: args 7: kwds Cell variables:

co_tricky_nested_f = tricky.func.code.co_consts[1] @@ -374,12 +375,12 @@ Names: Variable names: 0: c Free variables:

code_info_expr_str = """[](#l18.40) Name:

--- a/Lib/test/test_gdb.py +++ b/Lib/test/test_gdb.py @@ -52,13 +52,18 @@ class DebuggerTests(unittest.TestCase): """Test that the debugger can debug Python."""

Returns its stdout, stderr """

@@ -118,7 +123,7 @@ class DebuggerTests(unittest.TestCase): # print ' '.join(args) # Use "args" to invoke gdb, capturing stdout, stderr:

# Ignore some noise on stderr due to the pending breakpoint: err = err.replace('Function "%s" not defined.\n' % breakpoint, '') @@ -207,7 +212,8 @@ class PrettyPrintTests(DebuggerTests): 'Verify the pretty-printing of dictionaries' self.assertGdbRepr({}) self.assertGdbRepr({'foo': 'bar'})

def test_lists(self): 'Verify the pretty-printing of lists' @@ -269,8 +275,8 @@ class PrettyPrintTests(DebuggerTests): def test_sets(self): 'Verify the pretty-printing of sets' self.assertGdbRepr(set())

# Ensure that we handle sets containing the "dummy" key value, # which happens on deletion: @@ -282,8 +288,8 @@ id(s)''') def test_frozensets(self): 'Verify the pretty-printing of frozensets' self.assertGdbRepr(frozenset())

def test_exceptions(self): # Test a RuntimeError

--- a/Lib/test/test_hash.py +++ b/Lib/test/test_hash.py @@ -3,10 +3,16 @@ #

Also test that hash implementations are inherited as expected

+import datetime +import os +import sys import unittest from test import support +from test.script_helper import assert_python_ok from collections import Hashable +IS_64BIT = sys.maxsize > 2**32 + class HashEqualityTestCase(unittest.TestCase): @@ -117,10 +123,92 @@ class HashBuiltinsTestCase(unittest.Test for obj in self.hashes_to_check: self.assertEqual(hash(obj), _default_hash(obj)) +class HashRandomizationTests(unittest.TestCase): +

+

+

+

+ +class StringlikeHashRandomizationTests(HashRandomizationTests):

+

+

+

+ +class StrHashRandomizationTests(StringlikeHashRandomizationTests):

+

+ +class BytesHashRandomizationTests(StringlikeHashRandomizationTests):

+

+ +class DatetimeTests(HashRandomizationTests):

+ +class DatetimeDateTests(DatetimeTests):

+ +class DatetimeDatetimeTests(DatetimeTests):

+ +class DatetimeTimeTests(DatetimeTests):

+ + def test_main(): support.run_unittest(HashEqualityTestCase,

if name == "main":

--- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -26,6 +26,7 @@ try: import threading except ImportError: threading = None +from test.script_helper import assert_python_ok os.stat_float_times(True) st = os.stat(file) @@ -794,14 +795,33 @@ class DevNullTests(unittest.TestCase): self.assertEqual(f.read(), b'') class URandomTests(unittest.TestCase):

+

+

+

@contextlib.contextmanager def _execvpe_mockup(defpath=None):

--- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -733,6 +733,17 @@ class TestBasicOps(unittest.TestCase): if self.repr is not None: self.assertEqual(repr(self.set), self.repr)

+

+ def test_print(self): try: fo = open(support.TESTFN, "w") @@ -891,7 +902,9 @@ class TestBasicOpsString(TestBasicOps): self.set = set(self.values) self.dup = set(self.values) self.length = 3

+

#------------------------------------------------------------------------------ @@ -902,7 +915,9 @@ class TestBasicOpsBytes(TestBasicOps): self.set = set(self.values) self.dup = set(self.values) self.length = 3

+

#------------------------------------------------------------------------------ @@ -916,11 +931,13 @@ class TestBasicOpsMixedStringBytes(TestB self.set = set(self.values) self.dup = set(self.values) self.length = 4

def tearDown(self): self._warning_filters.exit(None, None, None)

+ #============================================================================== def baditer():

--- a/Lib/test/test_strlit.py +++ b/Lib/test/test_strlit.py @@ -65,7 +65,7 @@ class TestLiterals(unittest.TestCase): sys.path.insert(0, self.tmpdir) def tearDown(self):

def test_template(self):

--- a/Lib/test/test_subprocess.py +++ b/Lib/test/test_subprocess.py @@ -1774,7 +1774,7 @@ class CommandsWithSpaces (BaseTestCase): self.with_spaces([sys.executable, self.fname, "ab cd"]) -class ContextManagerTests(ProcessTestCase): +class ContextManagerTests(BaseTestCase): def test_pipe(self): with subprocess.Popen([sys.executable, "-c",

--- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -513,7 +513,7 @@ class SysModuleTest(unittest.TestCase): attrs = ("debug", "inspect", "interactive", "optimize", "dont_write_bytecode", "no_user_site", "no_site", "ignore_environment", "verbose",

--- a/Lib/test/test_urllib.py +++ b/Lib/test/test_urllib.py @@ -13,6 +13,7 @@ import sys import tempfile from base64 import b64encode +import collections def hexescape(char): """Escape char as RFC 2396 specifies""" @@ -953,8 +954,9 @@ class urlencode_Tests(unittest.TestCase) self.assertEqual("a=1&a=2", urllib.parse.urlencode({"a": [1, 2]}, True)) self.assertEqual("a=None&a=a", urllib.parse.urlencode({"a": [None, "a"]}, True))

def test_urlencode_encoding(self): # ASCII encoding. Expect %3F with errors="replace'

old mode 100644 new mode 100755 --- a/Lib/test/test_urlparse.py +++ b/Lib/test/test_urlparse.py @@ -769,7 +769,8 @@ class UrlParseTestCase(unittest.TestCase # Other tests incidentally urlencode things; test non-covered cases: # Sequence and object values. result = urllib.parse.urlencode({'a': [1, 2], 'b': (3, 4, 5)}, True)

class Trivial: def str(self):

--- a/Lib/tkinter/test/test_ttk/test_functions.py +++ b/Lib/tkinter/test/test_ttk/test_functions.py @@ -143,7 +143,7 @@ class InternalFunctionsTest(unittest.Tes ('a', 'b', 'c')), ("test {a b} c", ())) # state spec and options self.assertEqual(ttk._format_elemcreate('image', False, 'test',

--- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -322,6 +322,7 @@ PYTHON_OBJS= [](#l30.3) Python/pystate.o [](#l30.4) Python/pythonrun.o [](#l30.5) Python/pytime.o [](#l30.6) + Python/random.o [](#l30.7) Python/structmember.o [](#l30.8) Python/symtable.o [](#l30.9) Python/sysmodule.o [](#l30.10)

--- a/Misc/NEWS +++ b/Misc/NEWS @@ -16,6 +16,11 @@ Core and Builtins

--- a/Misc/python.man +++ b/Misc/python.man @@ -37,6 +37,9 @@ python - an interpreted, interactive, o .B -OO ] [ +.B -R +] +[ .B -s ] [ @@ -148,6 +151,18 @@ Discard docstrings in addition to the \f Do not print the version and copyright messages. These messages are also suppressed in non-interactive mode. .TP +.B -R +Turn on "hash randomization", so that the hash() values of str, bytes and +datetime objects are "salted" with an unpredictable pseudo-random value. +Although they remain constant within an individual Python process, they are +not predictable between repeated invocations of Python. +.IP +This is intended to provide protection against a denial of service +caused by carefully-chosen inputs that exploit the worst case performance +of a dict insertion, O(n^2) complexity. See +http://www.ocert.org/advisories/ocert-2011-003.html[](#l32.26) +for details. +.TP .B -s Don't add user site directory to sys.path. .TP @@ -402,6 +417,20 @@ specifying \fB-v\fP multiple times. .IP PYTHONWARNINGS If this is set to a comma-separated string it is equivalent to specifying the \fB-W\fP option for each separate value. +.IP PYTHONHASHSEED +If this variable is set to "random", the effect is the same as specifying +the \fB-R\fP option: a random value is used to seed the hashes of str, +bytes and datetime objects. + +If PYTHONHASHSEED is set to an integer value, it is used as a fixed seed for +generating the hash() of the types covered by the hash randomization. Its +purpose is to allow repeatable hashing, such as for selftests for the +interpreter itself, or to allow a cluster of python processes to share hash +values. + +The integer must be a decimal number in the range [0,4294967295]. Specifying +the value 0 will lead to the same hash values as when hash randomization is +disabled. .SH AUTHOR The Python Software Foundation: http://www.python.org/psf[](#l32.51) .SH INTERNET RESOURCES

--- a/Modules/main.c +++ b/Modules/main.c @@ -47,7 +47,7 @@ static wchar_t *orig_argv; static int orig_argc; / command line options */ -#define BASE_OPTS L"bBc:dEhiJm:OqsStuvVW:xX:?" +#define BASE_OPTS L"bBc:dEhiJm:OqRsStuvVW:xX:?" #define PROGRAM_OPTS BASE_OPTS @@ -73,6 +73,9 @@ static char *usage_2 = "[](#l34.12) -O : optimize generated bytecode slightly; also PYTHONOPTIMIZE=x\n[](#l34.13) -OO : remove doc-strings in addition to the -O optimizations\n[](#l34.14) -q : don't print version and copyright messages on interactive startup\n[](#l34.15) +-R : use a pseudo-random salt to make hash() values of various types be\n[](#l34.16)

-s : don't add user site directory to sys.path; also PYTHONNOUSERSITE\n[](#l34.19) -S : don't imply 'import site' on initialization\n[](#l34.20) "; @@ -101,8 +104,14 @@ static char *usage_5 = " The default module search path uses %s.\n" "PYTHONCASEOK : ignore case in 'import' statements (Windows).\n" "PYTHONIOENCODING: Encoding[:errors] used for stdin/stdout/stderr.\n" -"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors.\n" -; +"PYTHONFAULTHANDLER: dump the Python traceback on fatal errors.\n[](#l34.28) +"; +static char *usage_6 = "[](#l34.30) +PYTHONHASHSEED: if this variable is set to random, the effect is the same \n[](#l34.31)

static int usage(int exitcode, wchar_t* program) @@ -118,6 +127,7 @@ usage(int exitcode, wchar_t* program) fputs(usage_3, f); fprintf(f, usage_4, DELIM); fprintf(f, usage_5, DELIM, PYTHONHOMEHELP);

#if defined(__VMS) if (exitcode == 0) { @@ -431,6 +441,10 @@ Py_Main(int argc, wchar_t **argv) Py_QuietFlag++; break;

+ /* This space reserved for other options */ default:

--- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -9317,82 +9317,6 @@ posix_getloadavg(PyObject *self, PyObjec } #endif -#ifdef MS_WINDOWS - -PyDoc_STRVAR(win32_urandom__doc__, -"urandom(n) -> str\n\n[](#l35.10) -Return n random bytes suitable for cryptographic use."); - -typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,[](#l35.13)

-typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,[](#l35.16)

- -static CRYPTGENRANDOM pCryptGenRandom = NULL; -/* This handle is never explicitly released. Instead, the operating

-static PyObject* -win32_urandom(PyObject *self, PyObject *args) -{

-

-

-

-

-

-

-

-} -#endif - PyDoc_STRVAR(device_encoding__doc__, "device_encoding(fd) -> str\n\n[](#l35.84) Return a string describing the encoding of the device\n[](#l35.85) @@ -10490,6 +10414,36 @@ posix_flistxattr(PyObject self, PyObjec #endif / USE_XATTRS */ +PyDoc_STRVAR(posix_urandom__doc__, +"urandom(n) -> str\n\n[](#l35.91) +Return n random bytes suitable for cryptographic use."); + +static PyObject * +posix_urandom(PyObject *self, PyObject *args) +{

+

+

+} + /* Terminal size querying */ static PyTypeObject TerminalSizeType; @@ -10984,12 +10938,7 @@ static PyMethodDef posix_methods[] = { #ifdef HAVE_GETLOADAVG {"getloadavg", posix_getloadavg, METH_NOARGS, posix_getloadavg__doc__}, #endif

#ifdef HAVE_SETRESUID {"setresuid", posix_setresuid, METH_VARARGS, posix_setresuid__doc__}, #endif

--- a/Objects/object.c +++ b/Objects/object.c @@ -759,10 +759,19 @@ Py_hash_t Py_uhash_t x; Py_ssize_t i;

+_Py_HashSecret_t _Py_HashSecret; + Py_hash_t PyObject_Hash(PyObject *v) {

--- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -11221,11 +11221,12 @@ unicode_hash(PyObject self) len = PyUnicode_GET_LENGTH(self); / The hash function as a macro, gets expanded three times below. */ -#define HASH(P) [](#l38.7)

- +#define HASH(P) [](#l38.12)

+

@@ -11246,7 +11247,8 @@ unicode_hash(PyObject *self) break; } }

if (x == -1) x = -2;

--- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -1890,6 +1890,10 @@ RelativePath="..\Python\pythonrun.c" >

--- a/Python/pythonrun.c +++ b/Python/pythonrun.c @@ -73,6 +73,7 @@ extern int _PyLong_Init(void); extern void PyLong_Fini(void); extern int _PyFaulthandler_Init(void); extern void _PyFaulthandler_Fini(void); +extern void _PyRandom_Init(void); #ifdef WITH_THREAD extern void _PyGILState_Init(PyInterpreterState , PyThreadState ); @@ -92,6 +93,7 @@ int Py_FrozenFlag; / Needed by getpath. int Py_IgnoreEnvironmentFlag; / e.g. PYTHONPATH, PYTHONHOME / int Py_NoUserSiteDirectory = 0; / for -s and site.py / int Py_UnbufferedStdioFlag = 0; / Unbuffered binary std{in,out,err} / +int Py_HashRandomizationFlag = 0; / for -R and PYTHONHASHSEED */ PyThreadState *_Py_Finalizing = NULL; @@ -218,6 +220,12 @@ Py_InitializeEx(int install_sigs) Py_OptimizeFlag = add_flag(Py_OptimizeFlag, p); if ((p = Py_GETENV("PYTHONDONTWRITEBYTECODE")) && *p != '\0') Py_DontWriteBytecodeFlag = add_flag(Py_DontWriteBytecodeFlag, p);

+

interp = PyInterpreterState_New(); if (interp == NULL)

new file mode 100644 --- /dev/null +++ b/Python/random.c @@ -0,0 +1,302 @@ +#include "Python.h" +#ifdef MS_WINDOWS +#include <windows.h> +#else +#include <fcntl.h> +#endif + +static int random_initialized = 0; + +#ifdef MS_WINDOWS +typedef BOOL (WINAPI *CRYPTACQUIRECONTEXTA)(HCRYPTPROV *phProv,[](#l41.15)

+typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,[](#l41.18)

+ +static CRYPTGENRANDOM pCryptGenRandom = NULL; +/* This handle is never explicitly released. Instead, the operating

+static int +win32_urandom_init(int raise) +{

+

+

+

+

+

+ +error:

+} + +/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen

+

+

+} +#endif /* MS_WINDOWS / + + +#ifdef __VMS +/ Use openssl random routine */ +#include <openssl/rand.h> +static int +vms_urandom(unsigned char *buffer, Py_ssize_t size, int raise) +{

+} +#endif /* __VMS / + + +#if !defined(MS_WINDOWS) && !defined(__VMS) + +/ Read size bytes from /dev/urandom into buffer.

+

+

+

+} + +/* Read size bytes from /dev/urandom into buffer.

+

+

+

+

+} +#endif /* !defined(MS_WINDOWS) && !defined(__VMS) / + +/ Fill buffer with pseudo-random bytes generated by a linear congruent

+

+

+} + +/* Fill buffer with size pseudo-random bytes, not suitable for cryptographic

+ +#ifdef MS_WINDOWS

+#else +# ifdef __VMS

+# else

+# endif +#endif +} + +void +_PyRandom_Init(void) +{

+

+

+

+

+#ifdef MS_WINDOWS

+#else /* #ifdef MS_WINDOWS */ +# ifdef __VMS

+# else

+# endif +#endif

+}

--- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1332,6 +1332,7 @@ static PyStructSequence_Field flags_fiel /* {"skip_first", "-x"}, */ {"bytes_warning", "-b"}, {"quiet", "-q"},

#ifdef RISCOS

#else

#endif }; @@ -1375,6 +1376,7 @@ make_flags(void) /* SetFlag(skipfirstline); */ SetFlag(Py_BytesWarningFlag); SetFlag(Py_QuietFlag);

#undef SetFlag if (PyErr_Occurred()) {

--- a/Tools/scripts/run_tests.py +++ b/Tools/scripts/run_tests.py @@ -25,6 +25,7 @@ def main(regrtest_args): '-W', 'default', # Warnings set to 'default' '-bb', # Warnings about bytes/bytearray '-E', # Ignore environment variables