[Python-checkins] bpo-32329: Fix -R option for hash randomization (#4873)

Victor Stinner webhook-mailer at python.org
Thu Dec 14 18:51:25 EST 2017


https://github.com/python/cpython/commit/358e5e17a51ba00742bfaee4557a94c3c4179c22
commit: 358e5e17a51ba00742bfaee4557a94c3c4179c22
branch: master
author: Victor Stinner <victor.stinner at gmail.com>
committer: GitHub <noreply at github.com>
date: 2017-12-15T00:51:22+01:00
summary:

bpo-32329: Fix -R option for hash randomization (#4873)

bpo-32329, bpo-32030:

* The -R option now turns on hash randomization when the
  PYTHONHASHSEED environment variable is set to 0 Previously, the
  option was ignored.
* sys.flags.hash_randomization is now properly set to 0 when hash
  randomization is turned off by PYTHONHASHSEED=0.
* _PyCoreConfig_ReadEnv() now reads the PYTHONHASHSEED environment
  variable. _Py_HashRandomization_Init() now only apply the
  configuration, it doesn't read PYTHONHASHSEED anymore.

files:
A Misc/NEWS.d/next/Core and Builtins/2017-12-15-00-13-04.bpo-32329.q47IN2.rst
M Doc/using/cmdline.rst
M Include/pylifecycle.h
M Lib/test/test_cmd_line.py
M Modules/main.c
M Python/bootstrap_hash.c

diff --git a/Doc/using/cmdline.rst b/Doc/using/cmdline.rst
index 5cb90717705..598eb2583d3 100644
--- a/Doc/using/cmdline.rst
+++ b/Doc/using/cmdline.rst
@@ -277,8 +277,9 @@ Miscellaneous options
 
 .. cmdoption:: -R
 
-   Kept for compatibility.  On Python 3.3 and greater, hash randomization is
-   turned on by default.
+   Turn on hash randomization. This option only has an effect if the
+   :envvar:`PYTHONHASHSEED` environment variable is set to ``0``, since hash
+   randomization is enabled by default.
 
    On previous versions of Python, this option turns on hash randomization,
    so that the :meth:`__hash__` values of str, bytes and datetime
diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h
index 61ed6ccf021..0416bfae0ea 100644
--- a/Include/pylifecycle.h
+++ b/Include/pylifecycle.h
@@ -136,7 +136,13 @@ PyAPI_FUNC(_PyInitError) _PyImportHooks_Init(void);
 PyAPI_FUNC(int) _PyFrame_Init(void);
 PyAPI_FUNC(int) _PyFloat_Init(void);
 PyAPI_FUNC(int) PyByteArray_Init(void);
-PyAPI_FUNC(_PyInitError) _Py_HashRandomization_Init(_PyCoreConfig *core_config);
+PyAPI_FUNC(_PyInitError) _Py_HashRandomization_Init(const _PyCoreConfig *);
+#endif
+#ifdef Py_BUILD_CORE
+PyAPI_FUNC(int) _Py_ReadHashSeed(
+    const char *seed_text,
+    int *use_hash_seed,
+    unsigned long *hash_seed);
 #endif
 
 /* Various internal finalizers */
diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index 2aff51bdc13..2b14c301c7d 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -432,8 +432,16 @@ def test_hash_randomization(self):
 
         # Verify that sys.flags contains hash_randomization
         code = 'import sys; print("random is", sys.flags.hash_randomization)'
-        rc, out, err = assert_python_ok('-c', code)
-        self.assertEqual(rc, 0)
+        rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='')
+        self.assertIn(b'random is 1', out)
+
+        rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='random')
+        self.assertIn(b'random is 1', out)
+
+        rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='0')
+        self.assertIn(b'random is 0', out)
+
+        rc, out, err = assert_python_ok('-R', '-c', code, PYTHONHASHSEED='0')
         self.assertIn(b'random is 1', out)
 
     def test_del___main__(self):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2017-12-15-00-13-04.bpo-32329.q47IN2.rst b/Misc/NEWS.d/next/Core and Builtins/2017-12-15-00-13-04.bpo-32329.q47IN2.rst
new file mode 100644
index 00000000000..86bcf234477
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2017-12-15-00-13-04.bpo-32329.q47IN2.rst	
@@ -0,0 +1,5 @@
+The :option:`-R` option now turns on hash randomization when the
+:envvar:`PYTHONHASHSEED` environment variable is set to ``0``. Previously,
+the option was ignored. Moreover, ``sys.flags.hash_randomization`` is now
+properly set to 0 when hash randomization is turned off by
+``PYTHONHASHSEED=0``.
diff --git a/Modules/main.c b/Modules/main.c
index 6db7e5f7088..e1a2f98dc6a 100644
--- a/Modules/main.c
+++ b/Modules/main.c
@@ -726,7 +726,7 @@ pymain_parse_cmdline_impl(_PyMain *pymain)
             break;
 
         case 'R':
-            /* Ignored */
+            pymain->core_config.use_hash_seed = 0;
             break;
 
         /* This space reserved for other options */
@@ -1293,6 +1293,10 @@ pymain_set_global_config(_PyMain *pymain)
 
     Py_IgnoreEnvironmentFlag = pymain->core_config.ignore_environment;
     Py_UTF8Mode = pymain->core_config.utf8_mode;
+
+    /* Random or non-zero hash seed */
+    Py_HashRandomizationFlag = (pymain->core_config.use_hash_seed == 0 ||
+                                pymain->core_config.hash_seed != 0);
 }
 
 
@@ -1694,6 +1698,24 @@ config_init_home(_PyCoreConfig *config)
 }
 
 
+static _PyInitError
+config_init_hash_seed(_PyCoreConfig *config)
+{
+    if (config->use_hash_seed < 0) {
+        const char *seed_text = pymain_get_env_var("PYTHONHASHSEED");
+        int use_hash_seed;
+        unsigned long hash_seed;
+        if (_Py_ReadHashSeed(seed_text, &use_hash_seed, &hash_seed) < 0) {
+            return _Py_INIT_USER_ERR("PYTHONHASHSEED must be \"random\" "
+                                     "or an integer in range [0; 4294967295]");
+        }
+        config->use_hash_seed = use_hash_seed;
+        config->hash_seed = hash_seed;
+    }
+    return _Py_INIT_OK();
+}
+
+
 _PyInitError
 _PyCoreConfig_ReadEnv(_PyCoreConfig *config)
 {
@@ -1712,6 +1734,11 @@ _PyCoreConfig_ReadEnv(_PyCoreConfig *config)
         return err;
     }
 
+    err = config_init_hash_seed(config);
+    if (_Py_INIT_FAILED(err)) {
+        return err;
+    }
+
     return _Py_INIT_OK();
 }
 
@@ -1777,12 +1804,6 @@ pymain_parse_envvars(_PyMain *pymain)
     /* Get environment variables */
     pymain_set_flags_from_env(pymain);
 
-    /* The variable is only tested for existence here;
-       _Py_HashRandomization_Init will check its value further. */
-    if (pymain_get_env_var("PYTHONHASHSEED")) {
-        Py_HashRandomizationFlag = 1;
-    }
-
     if (pymain_warnings_envvar(pymain) < 0) {
         return -1;
     }
diff --git a/Python/bootstrap_hash.c b/Python/bootstrap_hash.c
index 2762f4656e2..9fd5cfb200e 100644
--- a/Python/bootstrap_hash.c
+++ b/Python/bootstrap_hash.c
@@ -533,9 +533,10 @@ _PyOS_URandomNonblock(void *buffer, Py_ssize_t size)
     return pyurandom(buffer, size, 0, 1);
 }
 
-int Py_ReadHashSeed(const char *seed_text,
-                    int *use_hash_seed,
-                    unsigned long *hash_seed)
+int
+_Py_ReadHashSeed(const char *seed_text,
+                 int *use_hash_seed,
+                 unsigned long *hash_seed)
 {
     Py_BUILD_ASSERT(sizeof(_Py_HashSecret_t) == sizeof(_Py_HashSecret.uc));
     /* Convert a text seed to a numeric one */
@@ -561,9 +562,9 @@ int Py_ReadHashSeed(const char *seed_text,
     return 0;
 }
 
-static _PyInitError
-init_hash_secret(int use_hash_seed,
-                 unsigned long hash_seed)
+
+_PyInitError
+_Py_HashRandomization_Init(const _PyCoreConfig *config)
 {
     void *secret = &_Py_HashSecret;
     Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
@@ -573,14 +574,14 @@ init_hash_secret(int use_hash_seed,
     }
     _Py_HashSecret_Initialized = 1;
 
-    if (use_hash_seed) {
-        if (hash_seed == 0) {
+    if (config->use_hash_seed) {
+        if (config->hash_seed == 0) {
             /* disable the randomized hash */
             memset(secret, 0, secret_size);
         }
         else {
             /* use the specified hash seed */
-            lcg_urandom(hash_seed, secret, secret_size);
+            lcg_urandom(config->hash_seed, secret, secret_size);
         }
     }
     else {
@@ -601,24 +602,6 @@ init_hash_secret(int use_hash_seed,
     return _Py_INIT_OK();
 }
 
-_PyInitError
-_Py_HashRandomization_Init(_PyCoreConfig *core_config)
-{
-    const char *seed_text;
-    int use_hash_seed = core_config->use_hash_seed;
-    unsigned long hash_seed = core_config->hash_seed;
-
-    if (use_hash_seed < 0) {
-        seed_text = Py_GETENV("PYTHONHASHSEED");
-        if (Py_ReadHashSeed(seed_text, &use_hash_seed, &hash_seed) < 0) {
-            return _Py_INIT_USER_ERR("PYTHONHASHSEED must be \"random\" "
-                                     "or an integer in range [0; 4294967295]");
-        }
-        core_config->use_hash_seed = use_hash_seed;
-        core_config->hash_seed = hash_seed;
-    }
-    return init_hash_secret(use_hash_seed, hash_seed);
-}
 
 void
 _Py_HashRandomization_Fini(void)



More information about the Python-checkins mailing list