Skip to content

Commit 1ee8231

Browse files
committed
pythongh-115382: Remove foreign import paths from cross compiles
Previously, when a build was configured to use a host interpreter via --with-build-python, the PYTHON_FOR_BUILD config value included a path in PYTHONPATH that pointed to the target's built external modules. For "normal" foreign architecture cross compiles, when loading compiled external libraries, the target libraries were processed first due to their precedence in sys.path. These libraries are then ruled out because of a mismatch in the SOABI so the import mechanism continues searching in sys.path for modules until it finds the host's native modules. However, if the host interpreter and the target python are on the same version + SOABI combination, the host interpreter would attempt to load the target's external modules due precedence in sys.path. Despite the "match", the target build may be linked against a different libc or may include instructions that are not supported on the host, so loading/executing the target's external modules can lead to crashes. Now, the path to the target's external modules is no longer in PYTHONPATH to prevent accidentally loading these foreign modules. Some build scripts need to interrogate sysconfig via `get_config_var{s}` to determine what target modules were built as well as other target specific config values. This was previously done by specifying _PYTHON_SYSCONFIGDATA_NAME in the environment and leveraging the target's module path in PYTHONPATH so it could be imported in sysconfig._init_posix. These build scripts now check if the environment is configured to use a host interpreter and will now load the target's sysconfigdata module based on the information in the environment and query it as necessary. Signed-off-by: Vincent Fazio <[email protected]>
1 parent 6abddd9 commit 1ee8231

File tree

4 files changed

+66
-4
lines changed

4 files changed

+66
-4
lines changed

Lib/ensurepip/__init__.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,29 @@
2424
# policies recommend against bundling dependencies. For example, Fedora
2525
# installs wheel packages in the /usr/share/python-wheels/ directory and don't
2626
# install the ensurepip._bundled package.
27-
_WHEEL_PKG_DIR = sysconfig.get_config_var('WHEEL_PKG_DIR')
27+
if '_PYTHON_HOST_PLATFORM' in os.environ:
28+
# When invoked during a cross compile, use the sysconfigdata file from the build directory.
29+
_WHEEL_PKG_DIR = None
30+
31+
from importlib.machinery import FileFinder, SourceFileLoader, SOURCE_SUFFIXES
32+
from importlib.util import module_from_spec
33+
34+
build_dir = os.environ.get("_PYTHON_PROJECT_BASE")
35+
pybuild = os.path.join(build_dir, "pybuilddir.txt")
36+
if os.path.exists(pybuild):
37+
with open(pybuild, encoding="utf-8") as f:
38+
builddir = f.read()
39+
target_lib_dir = os.path.join(build_dir, builddir)
40+
spec = FileFinder(target_lib_dir ,(SourceFileLoader, SOURCE_SUFFIXES)).find_spec(
41+
os.environ.get("_PYTHON_SYSCONFIGDATA_NAME")
42+
)
43+
if spec is not None:
44+
target_module = module_from_spec(spec)
45+
spec.loader.exec_module(target_module)
46+
47+
_WHEEL_PKG_DIR = target_module.build_time_vars.get('WHEEL_PKG_DIR')
48+
else:
49+
_WHEEL_PKG_DIR = sysconfig.get_config_var('WHEEL_PKG_DIR')
2850

2951

3052
def _find_packages(path):

Tools/build/check_extension_modules.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,41 @@ def __bool__(self):
125125

126126
ModuleInfo = collections.namedtuple("ModuleInfo", "name state")
127127

128+
class _SysConfigShim:
129+
"""
130+
A class to shim the sysconfig data from the cross compile build directory.
131+
132+
It's not safe to include foreign import directories in sys.path as the host
133+
compatible interpreter may attempt to load libraries it's incompatible with.
134+
135+
However, we need to interrogate the target build to check that modules were
136+
compiled correctly so we need to load data from sysconfigdata that resides
137+
in the build directory.
138+
"""
139+
def __init__(self, lib_path: pathlib.Path):
140+
from importlib.machinery import FileFinder, SourceFileLoader, SOURCE_SUFFIXES
141+
from importlib.util import module_from_spec
142+
143+
configdata = os.environ.get("_PYTHON_SYSCONFIGDATA_NAME")
144+
if configdata is None:
145+
raise RuntimeError(f'_PYTHON_SYSCONFIGDATA_NAME is required for cross compilation')
146+
spec = FileFinder(str(lib_path) ,(SourceFileLoader, SOURCE_SUFFIXES)).find_spec(configdata)
147+
if spec is None:
148+
raise RuntimeError(f'Could not find find sysconfig data for {configdata}')
149+
150+
self.target_module = module_from_spec(spec)
151+
spec.loader.exec_module(self.target_module)
152+
153+
def get_config_var(self, name: str):
154+
return self.get_config_vars().get(name)
155+
156+
def get_config_vars(self, *args):
157+
if args:
158+
vals = []
159+
for name in args:
160+
vals.append(self.target_module.build_time_vars.get(name))
161+
return vals
162+
return self.target_module.build_time_vars
128163

129164
class ModuleChecker:
130165
pybuilddir_txt = "pybuilddir.txt"
@@ -139,10 +174,15 @@ class ModuleChecker:
139174

140175
def __init__(self, cross_compiling: bool = False, strict: bool = False):
141176
self.cross_compiling = cross_compiling
177+
self.builddir = self.get_builddir()
178+
if self.cross_compiling:
179+
shim = _SysConfigShim(self.builddir)
180+
sysconfig.get_config_var = shim.get_config_var
181+
sysconfig.get_config_vars = shim.get_config_vars
182+
142183
self.strict_extensions_build = strict
143184
self.ext_suffix = sysconfig.get_config_var("EXT_SUFFIX")
144185
self.platform = sysconfig.get_platform()
145-
self.builddir = self.get_builddir()
146186
self.modules = self.get_modules()
147187

148188
self.builtin_ok = []

configure

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

configure.ac

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ AC_ARG_WITH([build-python],
164164
dnl Build Python interpreter is used for regeneration and freezing.
165165
ac_cv_prog_PYTHON_FOR_REGEN=$with_build_python
166166
PYTHON_FOR_FREEZE="$with_build_python"
167-
PYTHON_FOR_BUILD='_PYTHON_PROJECT_BASE=$(abs_builddir) _PYTHON_HOST_PLATFORM=$(_PYTHON_HOST_PLATFORM) PYTHONPATH=$(shell test -f pybuilddir.txt && echo $(abs_builddir)/`cat pybuilddir.txt`:)$(srcdir)/Lib _PYTHON_SYSCONFIGDATA_NAME=_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH) '$with_build_python
167+
PYTHON_FOR_BUILD='_PYTHON_PROJECT_BASE=$(abs_builddir) _PYTHON_HOST_PLATFORM=$(_PYTHON_HOST_PLATFORM) PYTHONPATH=$(srcdir)/Lib _PYTHON_SYSCONFIGDATA_NAME=_sysconfigdata_$(ABIFLAGS)_$(MACHDEP)_$(MULTIARCH) '$with_build_python
168168
AC_MSG_RESULT([$with_build_python])
169169
], [
170170
AS_VAR_IF([cross_compiling], [yes],

0 commit comments

Comments
 (0)