Skip to content
File

Blob: src/pyodide/internal/topLevelEntropy/entropy_patches.py

python86 lines
1"""
2Handle the top level getentropy() mess:
3
4The C stdlib function getentropy() `getentropy()` calls
5`crpyto.getRandomValues()` but this throws an error at top level which causes a
6fatal error.
7
8Goals:
9
101. Avoid top-level calls to the C stdlib function getentropy(), these fatally
11 fail. Patch these to raise Python errors instead.
122. Allow top level import of `random` and `numpy.random` modules. These seed
13 themselves with the functions that we patched in step 1, we temporarily
14 replace the `getentropy()` calls with no-ops to let them through.
153. Install wrapper modules at top level that only allow calls to a whitelisted
16 set of functions from `random` and `numpy.random` that don't use the bad
17 seeds that came from step 2.
184. Put it all back.
195. Reseed the rng before entering the request scope for the first time.
20
21Steps 1, part of 4, and 5 are handled here, steps 2, 3, and part of 4 are
22handled in _cloudflare_random_overlays.
23"""
24 
25import os
26from functools import wraps
27 
28# Import entropy_import_context for side effects
29from . import entropy_import_context # noqa: F401
30from .allow_entropy import _set_in_request_context, raise_unless_entropy_allowed
31from .import_patch_manager import (
32 after_snapshot_handlers,
33 before_first_request_handlers,
34 install_import_patch_manager,
35 remove_import_patch_manager,
36)
37 
38# Prevent calls to getentropy(). The intended way for `getentropy()` to fail is
39# to set an EIO error, which turns into a Python OSError, so we raise this same
40# error so that if we patch `getentropy` from the Emscripten C stdlib we can
41# remove these patches without changing the behavior.
42 
43 
44orig_urandom = os.urandom
45 
46 
47@wraps(orig_urandom)
48def patch_urandom(*args):
49 raise_unless_entropy_allowed()
50 return orig_urandom(*args)
51 
52 
53def disable_urandom():
54 """
55 Python os.urandom() calls C getentropy() which calls JS
56 crypto.getRandomValues() which throws at top level, fatally crashing the
57 interpreter.
58
59 TODO: Patch Emscripten's getentropy() to return EIO if
60 `crypto.getRandomValues()` throws. Then we can remove this.
61 """
62 os.urandom = patch_urandom
63 
64 
65def restore_urandom():
66 os.urandom = orig_urandom
67 
68 
69def before_top_level():
70 disable_urandom()
71 install_import_patch_manager()
72 
73 
74def after_snapshot():
75 remove_import_patch_manager()
76 for cb in after_snapshot_handlers:
77 cb()
78 
79 
80def before_first_request():
81 _set_in_request_context()
82 restore_urandom()
83 remove_import_patch_manager()
84 for cb in before_first_request_handlers:
85 cb()