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
- :const:
hash_randomization
:option:-R
============================= ============================= .. versionchanged:: 3.2 Addedquiet
attribute for the new :option:-q
flag. - .. versionadded:: 3.2.3
The ``hash_randomization`` attribute.[](#l1.14)
+
.. 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
.
--- 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 +
- Turn on hash randomization, so that the :meth:
__hash__
values of str, bytes - and datetime objects are "salted" with an unpredictable random value.
- Although they remain constant within an individual Python process, they are
- not predictable between repeated invocations of Python. +
- 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 for details. +
- Changing hash values affects the order in which keys are retrieved from a
- dict. Although Python has never made guarantees about this ordering (and it
- typically varies between 32-bit and 64-bit builds), enough real-world code
- implicitly relies on this non-guaranteed behavior that the randomization is
- disabled by default. +
- See also :envvar:
PYTHONHASHSEED
. + - .. versionadded:: 3.2.3 +
+
.. 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
+
- If this variable is set to
random
, the effect is the same as specifying - the :option:
-R
option: a random value is used to seed the hashes of str, - bytes and datetime objects. +
- If :envvar:
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. +
- .. versionadded:: 3.2.3 +
+ .. 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"):
Return a string of n random bytes suitable for cryptographic use.[](#l8.11)
"""[](#l8.13)
try:[](#l8.14)
_urandomfd = open("/dev/urandom", O_RDONLY)[](#l8.15)
except (OSError, IOError):[](#l8.16)
raise NotImplementedError("/dev/urandom (or equivalent) not found")[](#l8.17)
bs = b""[](#l8.18)
while len(bs) < n:[](#l8.19)
bs += read(_urandomfd, n - len(bs))[](#l8.20)
close(_urandomfd)[](#l8.21)
return bs[](#l8.22)
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):
import _strptime[](#l9.7)
- 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."""
return {1:2, "key1":"value1", "key2":(1,2,3)}[](#l10.7)
def _empty_mapping(self): """Return an empty mapping object""" return self.type2test()return {"1": "2", "key1":"value1", "key2":(1,2,3)}[](#l10.8)
--- 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')
- cmd_line.extend(args)
Need to preserve the original environment, for in-place testing of
env = os.environ.copy() shared library builds.
But a special flag that can be set to override -- in this case, the
caller is responsible to pass the full environment.
- if env_vars.pop('__cleanenv', None):
env.update(env_vars)env = {}[](#l12.22)
- cmd_line.extend(args) p = subprocess.Popen(cmd_line, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
--- 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',
'hash_randomization': 'R',[](#l13.7) 'ignore_environment': 'E',[](#l13.8) 'no_user_site': 's',[](#l13.9) 'no_site': 'S',[](#l13.10)
--- 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_hash_randomization(self):
# Verify that -R enables hash randomization:[](#l14.8)
self.verify_valid_flag('-R')[](#l14.9)
hashes = [][](#l14.10)
for i in range(2):[](#l14.11)
code = 'print(hash("spam"))'[](#l14.12)
rc, out, err = assert_python_ok('-R', '-c', code)[](#l14.13)
self.assertEqual(rc, 0)[](#l14.14)
hashes.append(out)[](#l14.15)
self.assertNotEqual(hashes[0], hashes[1])[](#l14.16)
# Verify that sys.flags contains hash_randomization[](#l14.18)
code = 'import sys; print("random is", sys.flags.hash_randomization)'[](#l14.19)
rc, out, err = assert_python_ok('-R', '-c', code)[](#l14.20)
self.assertEqual(rc, 0)[](#l14.21)
self.assertIn(b'random is 1', out)[](#l14.22)
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():
if isinstance(cls, type) and issubclass(cls, unittest.TestCase):[](#l15.24)
name += suffix[](#l15.25)
cls.__name__ = name[](#l15.26)
globals()[name] = cls[](#l15.27)
def setUp(self, module=module, setup=cls.setUp):[](#l15.28)
self._save_sys_modules = sys.modules.copy()[](#l15.29)
sys.modules[TESTS] = module[](#l15.30)
sys.modules['datetime'] = module.datetime_module[](#l15.31)
sys.modules['_strptime'] = module._strptime[](#l15.32)
setup(self)[](#l15.33)
def tearDown(self, teardown=cls.tearDown):[](#l15.34)
teardown(self)[](#l15.35)
sys.modules.clear()[](#l15.36)
sys.modules.update(self._save_sys_modules)[](#l15.37)
cls.setUp = setUp[](#l15.38)
cls.tearDown = tearDown[](#l15.39)
if not (isinstance(cls, type) and issubclass(cls, unittest.TestCase)):[](#l15.40)
continue[](#l15.41)
cls.__name__ = name + suffix[](#l15.42)
@classmethod[](#l15.43)
def setUpClass(cls_, module=module):[](#l15.44)
cls_._save_sys_modules = sys.modules.copy()[](#l15.45)
sys.modules[TESTS] = module[](#l15.46)
sys.modules['datetime'] = module.datetime_module[](#l15.47)
sys.modules['_strptime'] = module._strptime[](#l15.48)
@classmethod[](#l15.49)
def tearDownClass(cls_):[](#l15.50)
sys.modules.clear()[](#l15.51)
sys.modules.update(cls_._save_sys_modules)[](#l15.52)
cls.setUpClass = setUpClass[](#l15.53)
cls.tearDownClass = tearDownClass[](#l15.54)
test_classes.append(cls)[](#l15.55)
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')
for mode in modes:[](#l16.7)
for mode in sorted(modes): # put "c" mode first[](#l16.8) self.g = gdbm.open(filename, mode)[](#l16.9) self.g.close()[](#l16.10)
--- a/Lib/test/test_dis.py +++ b/Lib/test/test_dis.py @@ -350,12 +350,13 @@ Variable names: 6: args 7: kwds Cell variables:
- 0: [edfxyz]
- 1: [edfxyz]
- 2: [edfxyz]
- 3: [edfxyz]
- 4: [edfxyz]
- 5: [edfxyz]"""
+# NOTE: the order of the cell variables above depends on dictionary order
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 """
if env_vars:[](#l19.13)
env = os.environ.copy()[](#l19.14)
env.update(env_vars)[](#l19.15)
else:[](#l19.16)
env = None[](#l19.17) out, err = subprocess.Popen([](#l19.18)
args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,[](#l19.19)
args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env,[](#l19.20) ).communicate()[](#l19.21) return out.decode('utf-8', 'replace'), err.decode('utf-8', 'replace')[](#l19.22)
@@ -118,7 +123,7 @@ class DebuggerTests(unittest.TestCase): # print ' '.join(args) # Use "args" to invoke gdb, capturing stdout, stderr:
out, err = self.run_gdb(*args)[](#l19.28)
out, err = self.run_gdb(*args, PYTHONHASHSEED='0')[](#l19.29)
# 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'})
self.assertGdbRepr({'foo': 'bar', 'douglas':42})[](#l19.37)
self.assertGdbRepr({'foo': 'bar', 'douglas': 42},[](#l19.38)
"{'foo': 'bar', 'douglas': 42}")[](#l19.39)
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())
self.assertGdbRepr(set(['a', 'b']))[](#l19.47)
self.assertGdbRepr(set([4, 5, 6]))[](#l19.48)
self.assertGdbRepr(set(['a', 'b']), "{'a', 'b'}")[](#l19.49)
self.assertGdbRepr(set([4, 5, 6]), "{4, 5, 6}")[](#l19.50)
# 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())
self.assertGdbRepr(frozenset(['a', 'b']))[](#l19.58)
self.assertGdbRepr(frozenset([4, 5, 6]))[](#l19.59)
self.assertGdbRepr(frozenset(['a', 'b']), "frozenset({'a', 'b'})")[](#l19.60)
self.assertGdbRepr(frozenset([4, 5, 6]), "frozenset({4, 5, 6})")[](#l19.61)
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): +
- def get_hash(self, repr_, seed=None):
env = os.environ.copy()[](#l20.33)
env['__cleanenv'] = True # signal to assert_python not to do a copy[](#l20.34)
# of os.environ on its own[](#l20.35)
if seed is not None:[](#l20.36)
env['PYTHONHASHSEED'] = str(seed)[](#l20.37)
else:[](#l20.38)
env.pop('PYTHONHASHSEED', None)[](#l20.39)
out = assert_python_ok([](#l20.40)
'-c', self.get_hash_command(repr_),[](#l20.41)
**env)[](#l20.42)
stdout = out[1].strip()[](#l20.43)
return int(stdout)[](#l20.44)
- def test_randomized_hash(self):
# two runs should return different hashes[](#l20.47)
run1 = self.get_hash(self.repr_, seed='random')[](#l20.48)
run2 = self.get_hash(self.repr_, seed='random')[](#l20.49)
self.assertNotEqual(run1, run2)[](#l20.50)
+ +class StringlikeHashRandomizationTests(HashRandomizationTests):
- def test_null_hash(self):
# PYTHONHASHSEED=0 disables the randomized hash[](#l20.54)
if IS_64BIT:[](#l20.55)
known_hash_of_obj = 1453079729188098211[](#l20.56)
else:[](#l20.57)
known_hash_of_obj = -1600925533[](#l20.58)
# Randomization is disabled by default:[](#l20.60)
self.assertEqual(self.get_hash(self.repr_), known_hash_of_obj)[](#l20.61)
# It can also be disabled by setting the seed to 0:[](#l20.63)
self.assertEqual(self.get_hash(self.repr_, seed=0), known_hash_of_obj)[](#l20.64)
- def test_fixed_hash(self):
# test a fixed seed for the randomized hash[](#l20.67)
# Note that all types share the same values:[](#l20.68)
if IS_64BIT:[](#l20.69)
h = -4410911502303878509[](#l20.70)
else:[](#l20.71)
h = -206076799[](#l20.72)
self.assertEqual(self.get_hash(self.repr_, seed=42), h)[](#l20.73)
+ +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,
HashInheritanceTestCase,[](#l20.103)
HashBuiltinsTestCase)[](#l20.104)
HashInheritanceTestCase,[](#l20.105)
HashBuiltinsTestCase,[](#l20.106)
StrHashRandomizationTests,[](#l20.107)
BytesHashRandomizationTests,[](#l20.108)
DatetimeDateTests,[](#l20.109)
DatetimeDatetimeTests,[](#l20.110)
DatetimeTimeTests)[](#l20.111)
--- 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):
- def test_urandom(self):
try:[](#l22.16)
self.assertEqual(len(os.urandom(1)), 1)[](#l22.17)
self.assertEqual(len(os.urandom(10)), 10)[](#l22.18)
self.assertEqual(len(os.urandom(100)), 100)[](#l22.19)
self.assertEqual(len(os.urandom(1000)), 1000)[](#l22.20)
except NotImplementedError:[](#l22.21)
pass[](#l22.22)
- def test_urandom_length(self):
self.assertEqual(len(os.urandom(0)), 0)[](#l22.24)
self.assertEqual(len(os.urandom(1)), 1)[](#l22.25)
self.assertEqual(len(os.urandom(10)), 10)[](#l22.26)
self.assertEqual(len(os.urandom(100)), 100)[](#l22.27)
self.assertEqual(len(os.urandom(1000)), 1000)[](#l22.28)
- def test_urandom_value(self):
data1 = os.urandom(16)[](#l22.31)
data2 = os.urandom(16)[](#l22.32)
self.assertNotEqual(data1, data2)[](#l22.33)
- def get_urandom_subprocess(self, count):
code = '\n'.join(([](#l22.36)
'import os, sys',[](#l22.37)
'data = os.urandom(%s)' % count,[](#l22.38)
'sys.stdout.buffer.write(data)',[](#l22.39)
'sys.stdout.buffer.flush()'))[](#l22.40)
out = assert_python_ok('-c', code)[](#l22.41)
stdout = out[1][](#l22.42)
self.assertEqual(len(stdout), 16)[](#l22.43)
return stdout[](#l22.44)
- def test_urandom_subprocess(self):
data1 = self.get_urandom_subprocess(16)[](#l22.47)
data2 = self.get_urandom_subprocess(16)[](#l22.48)
self.assertNotEqual(data1, data2)[](#l22.49)
@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 check_repr_against_values(self):
text = repr(self.set)[](#l23.8)
self.assertTrue(text.startswith('{'))[](#l23.9)
self.assertTrue(text.endswith('}'))[](#l23.10)
result = text[1:-1].split(', ')[](#l23.12)
result.sort()[](#l23.13)
sorted_repr_values = [repr(value) for value in self.values][](#l23.14)
sorted_repr_values.sort()[](#l23.15)
self.assertEqual(result, sorted_repr_values)[](#l23.16)
+ 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
self.repr = "{'a', 'c', 'b'}"[](#l23.25)
#------------------------------------------------------------------------------ @@ -902,7 +915,9 @@ class TestBasicOpsBytes(TestBasicOps): self.set = set(self.values) self.dup = set(self.values) self.length = 3
self.repr = "{b'a', b'c', b'b'}"[](#l23.36)
#------------------------------------------------------------------------------ @@ -916,11 +931,13 @@ class TestBasicOpsMixedStringBytes(TestB self.set = set(self.values) self.dup = set(self.values) self.length = 4
self.repr = "{'a', b'a', 'b', b'b'}"[](#l23.47)
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):
sys.path = self.save_path[](#l24.7)
sys.path[:] = self.save_path[](#l24.8) shutil.rmtree(self.tmpdir, ignore_errors=True)[](#l24.9)
--- 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",
"bytes_warning", "quiet")[](#l26.7)
"bytes_warning", "quiet", "hash_randomization")[](#l26.8) for attr in attrs:[](#l26.9) self.assertTrue(hasattr(sys.flags, attr), attr)[](#l26.10) self.assertEqual(type(getattr(sys.flags, attr)), int, attr)[](#l26.11)
--- 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))
data = collections.OrderedDict([("a", 1), ("b", 1)])[](#l27.15) self.assertEqual("a=a&a=b",[](#l27.16)
urllib.parse.urlencode({"a": {"a": 1, "b": 1}}, True))[](#l27.17)
urllib.parse.urlencode({"a": data}, True))[](#l27.18)
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)
self.assertEqual(result, 'a=1&a=2&b=3&b=4&b=5')[](#l28.9)
# we cannot rely on ordering here[](#l28.10)
assert set(result.split('&')) == {'a=1', 'a=2', 'b=3', 'b=4', 'b=5'}[](#l28.11)
--- 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', 'b'), a='x', b='y'), ("test a b", ("-a", "x", "-b", "y")))[](#l29.7)
('a', 'b'), a='x'), ("test a b", ("-a", "x")))[](#l29.8) # format returned values as a tcl script[](#l29.9) # state spec with multiple states and an option with a multivalue[](#l29.10) self.assertEqual(ttk._format_elemcreate('image', True, 'test',[](#l29.11)
--- 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
- Issue #14051: Allow arbitrary attributes to be set of classmethod and staticmethod. +- Issue #13703: oCERT-2011-003: add -R command-line option and PYTHONHASHSEED
- environment variable, to provide an opt-in way to protect against denial of
- service attacks due to hash collisions within the dict and set types. Patch
- by David Malcolm, based on work by Victor Stinner. +
- Issue #13020: Fix a reference leak when allocating a structsequence object fails. Patch by Suman Saha.
--- 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)
unpredictable between separate invocations of the interpreter, as\n\[](#l34.17)
a defence against denial-of-service attacks\n\[](#l34.18)
-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)
- as specifying the :option:
-R
option: a random value is used to seed the\n[](#l34.32) - hashes of str, bytes and datetime objects. It can also be set to an integer\n[](#l34.33)
- in the range [0,4294967295] to get hash values with a predictable seed.\n[](#l34.34) +";
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;
case 'R':[](#l34.51)
Py_HashRandomizationFlag++;[](#l34.52)
break;[](#l34.53)
+ /* 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)
LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\[](#l35.14)
DWORD dwFlags );[](#l35.15)
-typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,[](#l35.16)
BYTE *pbBuffer );[](#l35.17)
- -static CRYPTGENRANDOM pCryptGenRandom = NULL; -/* This handle is never explicitly released. Instead, the operating
-static PyObject* -win32_urandom(PyObject *self, PyObject *args) -{
- /* Read arguments */
- if (! PyArg_ParseTuple(args, "i:urandom", &howMany))
return NULL;[](#l35.32)
- if (howMany < 0)
return PyErr_Format(PyExc_ValueError,[](#l35.34)
"negative argument not allowed");[](#l35.35)
- if (hCryptProv == 0) {
HINSTANCE hAdvAPI32 = NULL;[](#l35.38)
CRYPTACQUIRECONTEXTA pCryptAcquireContext = NULL;[](#l35.39)
/* Obtain handle to the DLL containing CryptoAPI[](#l35.41)
This should not fail */[](#l35.42)
hAdvAPI32 = GetModuleHandleW(L"advapi32.dll");[](#l35.43)
if(hAdvAPI32 == NULL)[](#l35.44)
return win32_error("GetModuleHandle", NULL);[](#l35.45)
/* Obtain pointers to the CryptoAPI functions[](#l35.47)
This will fail on some early versions of Win95 */[](#l35.48)
pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress([](#l35.49)
hAdvAPI32,[](#l35.50)
"CryptAcquireContextA");[](#l35.51)
if (pCryptAcquireContext == NULL)[](#l35.52)
return PyErr_Format(PyExc_NotImplementedError,[](#l35.53)
"CryptAcquireContextA not found");[](#l35.54)
pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress([](#l35.56)
hAdvAPI32, "CryptGenRandom");[](#l35.57)
if (pCryptGenRandom == NULL)[](#l35.58)
return PyErr_Format(PyExc_NotImplementedError,[](#l35.59)
"CryptGenRandom not found");[](#l35.60)
/* Acquire context */[](#l35.62)
if (! pCryptAcquireContext(&hCryptProv, NULL, NULL,[](#l35.63)
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))[](#l35.64)
return win32_error("CryptAcquireContext", NULL);[](#l35.65)
- }
- /* Allocate bytes */
- result = PyBytes_FromStringAndSize(NULL, howMany);
- if (result != NULL) {
/* Get random data */[](#l35.71)
memset(PyBytes_AS_STRING(result), 0, howMany); /* zero seed */[](#l35.72)
if (! pCryptGenRandom(hCryptProv, howMany, (unsigned char*)[](#l35.73)
PyBytes_AS_STRING(result))) {[](#l35.74)
Py_DECREF(result);[](#l35.75)
return win32_error("CryptGenRandom", NULL);[](#l35.76)
}[](#l35.77)
- }
- return result;
-} -#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) +{
/* Read arguments */[](#l35.101)
- if (!PyArg_ParseTuple(args, "n:urandom", &size))
return NULL;[](#l35.103)
- if (size < 0)
return PyErr_Format(PyExc_ValueError,[](#l35.105)
"negative argument not allowed");[](#l35.106)
- result = PyBytes_FromStringAndSize(NULL, size);
- if (result == NULL)
return NULL;[](#l35.109)
- ret = _PyOS_URandom(PyBytes_AS_STRING(result),
PyBytes_GET_SIZE(result));[](#l35.112)
- if (ret == -1) {
Py_DECREF(result);[](#l35.114)
return NULL;[](#l35.115)
- }
- return result;
+} + /* 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 MS_WINDOWS
- {"urandom", win32_urandom, METH_VARARGS, win32_urandom__doc__},
- #endif
- #ifdef __VMS
- {"urandom", vms_urandom, METH_VARARGS, vms_urandom__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;
- /*
We make the hash of the empty string be 0, rather than using[](#l37.9)
(prefix ^ suffix), since this slightly obfuscates the hash secret[](#l37.10)
- */
- if (len == 0) {
return 0;[](#l37.13)
- }
- x = (Py_uhash_t) _Py_HashSecret.prefix;
- x ^= (Py_uhash_t) *p << 7; for (i = 0; i < len; i++) x = (_PyHASH_MULTIPLIER * x) ^ (Py_uhash_t) *p++; x ^= (Py_uhash_t) len;
- x ^= (Py_uhash_t) _Py_HashSecret.suffix; if (x == -1) x = -2; return x; @@ -776,6 +785,8 @@ PyObject_HashNotImplemented(PyObject *v) return -1; }
+_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)
- x = (Py_uhash_t)*P << 7; [](#l38.8)
- while (--len >= 0) [](#l38.9)
x = (_PyHASH_MULTIPLIER*x) ^ (Py_uhash_t)*P++;[](#l38.10)
- +#define HASH(P) [](#l38.12)
- x ^= (Py_uhash_t) *P << 7; [](#l38.13)
- while (--len >= 0) [](#l38.14)
x = (_PyHASH_MULTIPLIER * x) ^ (Py_uhash_t) *P++; \[](#l38.15)
- x = (Py_uhash_t) _Py_HashSecret.prefix; switch (PyUnicode_KIND(self)) { case PyUnicode_1BYTE_KIND: { const unsigned char *c = PyUnicode_1BYTE_DATA(self);
@@ -11246,7 +11247,8 @@ unicode_hash(PyObject *self) break; } }
--- a/PCbuild/pythoncore.vcproj +++ b/PCbuild/pythoncore.vcproj @@ -1890,6 +1890,10 @@ RelativePath="..\Python\pythonrun.c" >
<File[](#l39.7)
RelativePath="..\Python\random.c"[](#l39.8)
>[](#l39.9)
</File>[](#l39.10) <File[](#l39.11) RelativePath="..\Python\structmember.c"[](#l39.12) >[](#l39.13)
--- 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);
- /* The variable is only tested for existence here; _PyRandom_Init will
check its value further. */[](#l40.24)
- if ((p = Py_GETENV("PYTHONHASHSEED")) && *p != '\0')
Py_HashRandomizationFlag = add_flag(Py_HashRandomizationFlag, p);[](#l40.26)
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)
LPCSTR pszContainer, LPCSTR pszProvider, DWORD dwProvType,\[](#l41.16)
DWORD dwFlags );[](#l41.17)
+typedef BOOL (WINAPI *CRYPTGENRANDOM)(HCRYPTPROV hProv, DWORD dwLen,[](#l41.18)
BYTE *pbBuffer );[](#l41.19)
+ +static CRYPTGENRANDOM pCryptGenRandom = NULL; +/* This handle is never explicitly released. Instead, the operating
+static int +win32_urandom_init(int raise) +{
- /* Obtain handle to the DLL containing CryptoAPI. This should not fail. */
- hAdvAPI32 = GetModuleHandle("advapi32.dll");
- if(hAdvAPI32 == NULL)
goto error;[](#l41.35)
- /* Obtain pointers to the CryptoAPI functions. This will fail on some early
versions of Win95. */[](#l41.38)
- pCryptAcquireContext = (CRYPTACQUIRECONTEXTA)GetProcAddress(
hAdvAPI32, "CryptAcquireContextA");[](#l41.40)
- if (pCryptAcquireContext == NULL)
goto error;[](#l41.42)
- pCryptGenRandom = (CRYPTGENRANDOM)GetProcAddress(hAdvAPI32,
"CryptGenRandom");[](#l41.45)
- if (pCryptGenRandom == NULL)
goto error;[](#l41.47)
- /* Acquire context */
- if (! pCryptAcquireContext(&hCryptProv, NULL, NULL,
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))[](#l41.51)
goto error;[](#l41.52)
- if (raise)
PyErr_SetFromWindowsErr(0);[](#l41.58)
- else
Py_FatalError("Failed to initialize Windows random API (CryptoGen)");[](#l41.60)
- return -1;
+} + +/* Fill buffer with size pseudo-random bytes generated by the Windows CryptoGen
- API. Return 0 on success, or -1 on error. */ +static int +win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise) +{
- Py_ssize_t chunk;
- while (size > 0)
- {
chunk = size > INT_MAX ? INT_MAX : size;[](#l41.79)
if (!pCryptGenRandom(hCryptProv, chunk, buffer))[](#l41.80)
{[](#l41.81)
/* CryptGenRandom() failed */[](#l41.82)
if (raise)[](#l41.83)
PyErr_SetFromWindowsErr(0);[](#l41.84)
else[](#l41.85)
Py_FatalError("Failed to initialized the randomized hash "[](#l41.86)
"secret using CryptoGen)");[](#l41.87)
return -1;[](#l41.88)
}[](#l41.89)
buffer += chunk;[](#l41.90)
size -= chunk;[](#l41.91)
- }
- return 0;
+} +#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) +{
- if (RAND_pseudo_bytes(buffer, size) < 0) {
if (raise) {[](#l41.105)
PyErr_Format(PyExc_ValueError,[](#l41.106)
"RAND_pseudo_bytes");[](#l41.107)
} else {[](#l41.108)
Py_FatalError("Failed to initialize the randomized hash "[](#l41.109)
"secret using RAND_pseudo_bytes");[](#l41.110)
}[](#l41.111)
return -1;[](#l41.112)
- }
- return 0;
+} +#endif /* __VMS / + + +#if !defined(MS_WINDOWS) && !defined(__VMS) + +/ Read size bytes from /dev/urandom into buffer.
- Call Py_FatalError() on error. */ +static void +dev_urandom_noraise(char *buffer, Py_ssize_t size) +{
- int fd;
- Py_ssize_t n;
- fd = open("/dev/urandom", O_RDONLY);
- if (fd < 0)
Py_FatalError("Failed to open /dev/urandom");[](#l41.133)
- while (0 < size)
- {
do {[](#l41.137)
n = read(fd, buffer, (size_t)size);[](#l41.138)
} while (n < 0 && errno == EINTR);[](#l41.139)
if (n <= 0)[](#l41.140)
{[](#l41.141)
/* stop on error or if read(size) returned 0 */[](#l41.142)
Py_FatalError("Failed to read bytes from /dev/urandom");[](#l41.143)
break;[](#l41.144)
}[](#l41.145)
buffer += n;[](#l41.146)
size -= (Py_ssize_t)n;[](#l41.147)
- }
- close(fd);
+} + +/* Read size bytes from /dev/urandom into buffer.
- Return 0 on success, raise an exception and return -1 on error. */ +static int +dev_urandom_python(char *buffer, Py_ssize_t size) +{
- int fd;
- Py_ssize_t n;
- Py_BEGIN_ALLOW_THREADS
- fd = open("/dev/urandom", O_RDONLY);
- Py_END_ALLOW_THREADS
- if (fd < 0)
- {
PyErr_SetFromErrnoWithFilename(PyExc_OSError, "/dev/urandom");[](#l41.168)
return -1;[](#l41.169)
- }
- Py_BEGIN_ALLOW_THREADS
- do {
do {[](#l41.174)
n = read(fd, buffer, (size_t)size);[](#l41.175)
} while (n < 0 && errno == EINTR);[](#l41.176)
if (n <= 0)[](#l41.177)
break;[](#l41.178)
buffer += n;[](#l41.179)
size -= (Py_ssize_t)n;[](#l41.180)
- } while (0 < size);
- Py_END_ALLOW_THREADS
- if (n <= 0)
- {
/* stop on error or if read(size) returned 0 */[](#l41.186)
if (n < 0)[](#l41.187)
PyErr_SetFromErrno(PyExc_OSError);[](#l41.188)
else[](#l41.189)
PyErr_Format(PyExc_RuntimeError,[](#l41.190)
"Failed to read %zi bytes from /dev/urandom",[](#l41.191)
size);[](#l41.192)
close(fd);[](#l41.193)
return -1;[](#l41.194)
- }
- close(fd);
- return 0;
+} +#endif /* !defined(MS_WINDOWS) && !defined(__VMS) / + +/ Fill buffer with pseudo-random bytes generated by a linear congruent
- Use bits 23..16 of x(n) to generate a byte. */ +static void +lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size) +{
- size_t index;
- unsigned int x;
- x = x0;
- for (index=0; index < size; index++) {
x *= 214013;[](#l41.215)
x += 2531011;[](#l41.216)
/* modulo 2 ^ (8 * sizeof(int)) */[](#l41.217)
buffer[index] = (x >> 16) & 0xff;[](#l41.218)
- }
+} + +/* Fill buffer with size pseudo-random bytes, not suitable for cryptographic
- use, from the operating random number generator (RNG). +
- Return 0 on success, raise an exception and return -1 on error. */ +int +_PyOS_URandom(void *buffer, Py_ssize_t size) +{
- if (size < 0) {
PyErr_Format(PyExc_ValueError,[](#l41.230)
"negative argument not allowed");[](#l41.231)
return -1;[](#l41.232)
- }
- if (size == 0)
return 0;[](#l41.235)
+# endif +#endif +} + +void +_PyRandom_Init(void) +{
- /*
By default, hash randomization is disabled, and only[](#l41.260)
enabled if PYTHONHASHSEED is set to non-empty or if[](#l41.261)
"-R" is provided at the command line:[](#l41.262)
- */
- if (!Py_HashRandomizationFlag) {
/* Disable the randomized hash: */[](#l41.265)
memset(secret, 0, secret_size);[](#l41.266)
return;[](#l41.267)
- }
- /*
Hash randomization is enabled. Generate a per-process secret,[](#l41.271)
using PYTHONHASHSEED if provided.[](#l41.272)
- */
- env = Py_GETENV("PYTHONHASHSEED");
- if (env && *env != '\0' && strcmp(env, "random") != 0) {
char *endptr = env;[](#l41.277)
unsigned long seed;[](#l41.278)
seed = strtoul(env, &endptr, 10);[](#l41.279)
if (*endptr != '\0'[](#l41.280)
|| seed > 4294967295UL[](#l41.281)
|| (errno == ERANGE && seed == ULONG_MAX))[](#l41.282)
{[](#l41.283)
Py_FatalError("PYTHONHASHSEED must be \"random\" or an integer "[](#l41.284)
"in range [0; 4294967295]");[](#l41.285)
}[](#l41.286)
if (seed == 0) {[](#l41.287)
/* disable the randomized hash */[](#l41.288)
memset(secret, 0, secret_size);[](#l41.289)
}[](#l41.290)
else {[](#l41.291)
lcg_urandom(seed, (unsigned char*)secret, secret_size);[](#l41.292)
}[](#l41.293)
- }
- else {
(void)win32_urandom((unsigned char *)secret, secret_size, 0);[](#l41.297)
+#else /* #ifdef MS_WINDOWS */ +# ifdef __VMS
vms_urandom((unsigned char *)secret, secret_size, 0);[](#l41.300)
dev_urandom_noraise((char*)secret, secret_size);[](#l41.302)
--- 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"},
- {"hash_randomization", "-R"}, {0} }; @@ -1340,9 +1341,9 @@ static PyStructSequence_Desc flags_desc flags__doc__, /* doc / flags_fields, / fields */
#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