From 0b4f9ba19dbd02de57ce7587cf76fbba3f03a4d3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 9 Jan 2025 13:53:06 +0100 Subject: [PATCH] interpreter: do not use pathlib in validate_within_subproject pathlib's implementation of Path iteration is expensive; "foo.parents" has quadratic complexity when building it: return self._path._from_parsed_parts(self._drv, self._root, self._tail[:-idx - 1]) and comparisons performed by "path in file.parents" potentially have the same issue. Use the newly introduced "is_parent_path" function to check whether a file is under a path in the same way. This removes Path from its hottest use. Signed-off-by: Paolo Bonzini --- mesonbuild/interpreter/interpreter.py | 34 ++++++++++++++------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index bc9a9f8b841d..2ce17a7a96dd 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -19,7 +19,7 @@ from ..wrap import wrap, WrapMode from .. import mesonlib from ..mesonlib import (EnvironmentVariables, ExecutableSerialisation, MesonBugException, MesonException, HoldableObject, - FileMode, MachineChoice, listify, + FileMode, MachineChoice, is_parent_path, listify, extract_as_list, has_path_sep, path_is_in_root, PerMachine) from ..options import OptionKey from ..programs import ExternalProgram, NonExistingExternalProgram @@ -3133,8 +3133,8 @@ def check_clang_asan_lundef(self) -> None: # subproject than it is defined in (due to e.g. a # declare_dependency). def validate_within_subproject(self, subdir, fname): - srcdir = Path(self.environment.source_dir) - builddir = Path(self.environment.build_dir) + srcdir = self.environment.source_dir + builddir = self.environment.build_dir if isinstance(fname, P_OBJ.DependencyVariableString): def validate_installable_file(fpath: Path) -> bool: installablefiles: T.Set[Path] = set() @@ -3155,34 +3155,36 @@ def validate_installable_file(fpath: Path) -> bool: if validate_installable_file(norm): return - def do_validate_within_subproject(norm: Path) -> None: + def do_validate_within_subproject(norm: str) -> None: if os.path.isdir(norm): inputtype = 'directory' else: inputtype = 'file' - if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and builddir in norm.parents: + if InterpreterRuleRelaxation.ALLOW_BUILD_DIR_FILE_REFERENCES in self.relaxations and is_parent_path(builddir, norm): return - if srcdir not in norm.parents: + + if not is_parent_path(srcdir, norm): # Grabbing files outside the source tree is ok. # This is for vendor stuff like: # # /opt/vendorsdk/src/file_with_license_restrictions.c return - project_root = Path(srcdir, self.root_subdir) - subproject_dir = project_root / self.subproject_dir - if norm == project_root: - return - if project_root not in norm.parents: - raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} outside current (sub)project.') - if subproject_dir == norm or subproject_dir in norm.parents: - raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {norm.name} from a nested subproject.') + + project_root = os.path.join(srcdir, self.root_subdir) + if not is_parent_path(project_root, norm): + name = os.path.basename(norm) + raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {name} outside current (sub)project.') + + subproject_dir = os.path.join(project_root, self.subproject_dir) + if is_parent_path(subproject_dir, norm): + name = os.path.basename(norm) + raise InterpreterException(f'Sandbox violation: Tried to grab {inputtype} {name} from a nested subproject.') fname = os.path.join(subdir, fname) if fname in self.validated_cache: return - # Use os.path.abspath() to eliminate .. segments, but do not resolve symlinks - norm = Path(os.path.abspath(Path(srcdir, fname))) + norm = os.path.abspath(os.path.join(srcdir, fname)) do_validate_within_subproject(norm) self.validated_cache.add(fname)