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.
This commit is contained in:
Victor Stinner 2017-12-15 00:51:22 +01:00 committed by GitHub
parent 96a5e50a5d
commit 358e5e17a5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 63 additions and 39 deletions

View file

@ -277,8 +277,9 @@ Miscellaneous options
.. cmdoption:: -R .. cmdoption:: -R
Kept for compatibility. On Python 3.3 and greater, hash randomization is Turn on hash randomization. This option only has an effect if the
turned on by default. :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, On previous versions of Python, this option turns on hash randomization,
so that the :meth:`__hash__` values of str, bytes and datetime so that the :meth:`__hash__` values of str, bytes and datetime

View file

@ -136,7 +136,13 @@ PyAPI_FUNC(_PyInitError) _PyImportHooks_Init(void);
PyAPI_FUNC(int) _PyFrame_Init(void); PyAPI_FUNC(int) _PyFrame_Init(void);
PyAPI_FUNC(int) _PyFloat_Init(void); PyAPI_FUNC(int) _PyFloat_Init(void);
PyAPI_FUNC(int) PyByteArray_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 #endif
/* Various internal finalizers */ /* Various internal finalizers */

View file

@ -432,8 +432,16 @@ class CmdLineTest(unittest.TestCase):
# Verify that sys.flags contains hash_randomization # Verify that sys.flags contains hash_randomization
code = 'import sys; print("random is", sys.flags.hash_randomization)' code = 'import sys; print("random is", sys.flags.hash_randomization)'
rc, out, err = assert_python_ok('-c', code) rc, out, err = assert_python_ok('-c', code, PYTHONHASHSEED='')
self.assertEqual(rc, 0) 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) self.assertIn(b'random is 1', out)
def test_del___main__(self): def test_del___main__(self):

View file

@ -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``.

View file

@ -726,7 +726,7 @@ pymain_parse_cmdline_impl(_PyMain *pymain)
break; break;
case 'R': case 'R':
/* Ignored */ pymain->core_config.use_hash_seed = 0;
break; break;
/* This space reserved for other options */ /* This space reserved for other options */
@ -1293,6 +1293,10 @@ pymain_set_global_config(_PyMain *pymain)
Py_IgnoreEnvironmentFlag = pymain->core_config.ignore_environment; Py_IgnoreEnvironmentFlag = pymain->core_config.ignore_environment;
Py_UTF8Mode = pymain->core_config.utf8_mode; 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 _PyInitError
_PyCoreConfig_ReadEnv(_PyCoreConfig *config) _PyCoreConfig_ReadEnv(_PyCoreConfig *config)
{ {
@ -1712,6 +1734,11 @@ _PyCoreConfig_ReadEnv(_PyCoreConfig *config)
return err; return err;
} }
err = config_init_hash_seed(config);
if (_Py_INIT_FAILED(err)) {
return err;
}
return _Py_INIT_OK(); return _Py_INIT_OK();
} }
@ -1777,12 +1804,6 @@ pymain_parse_envvars(_PyMain *pymain)
/* Get environment variables */ /* Get environment variables */
pymain_set_flags_from_env(pymain); 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) { if (pymain_warnings_envvar(pymain) < 0) {
return -1; return -1;
} }

View file

@ -533,7 +533,8 @@ _PyOS_URandomNonblock(void *buffer, Py_ssize_t size)
return pyurandom(buffer, size, 0, 1); return pyurandom(buffer, size, 0, 1);
} }
int Py_ReadHashSeed(const char *seed_text, int
_Py_ReadHashSeed(const char *seed_text,
int *use_hash_seed, int *use_hash_seed,
unsigned long *hash_seed) unsigned long *hash_seed)
{ {
@ -561,9 +562,9 @@ int Py_ReadHashSeed(const char *seed_text,
return 0; return 0;
} }
static _PyInitError
init_hash_secret(int use_hash_seed, _PyInitError
unsigned long hash_seed) _Py_HashRandomization_Init(const _PyCoreConfig *config)
{ {
void *secret = &_Py_HashSecret; void *secret = &_Py_HashSecret;
Py_ssize_t secret_size = sizeof(_Py_HashSecret_t); Py_ssize_t secret_size = sizeof(_Py_HashSecret_t);
@ -573,14 +574,14 @@ init_hash_secret(int use_hash_seed,
} }
_Py_HashSecret_Initialized = 1; _Py_HashSecret_Initialized = 1;
if (use_hash_seed) { if (config->use_hash_seed) {
if (hash_seed == 0) { if (config->hash_seed == 0) {
/* disable the randomized hash */ /* disable the randomized hash */
memset(secret, 0, secret_size); memset(secret, 0, secret_size);
} }
else { else {
/* use the specified hash seed */ /* use the specified hash seed */
lcg_urandom(hash_seed, secret, secret_size); lcg_urandom(config->hash_seed, secret, secret_size);
} }
} }
else { else {
@ -601,24 +602,6 @@ init_hash_secret(int use_hash_seed,
return _Py_INIT_OK(); 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 void
_Py_HashRandomization_Fini(void) _Py_HashRandomization_Fini(void)