Skip to content

Commit

Permalink
interpreter: do not use pathlib in validate_within_subproject
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
bonzini committed Jan 31, 2025
1 parent 5c23c89 commit 0b4f9ba
Showing 1 changed file with 18 additions and 16 deletions.
34 changes: 18 additions & 16 deletions mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand All @@ -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)

Expand Down

0 comments on commit 0b4f9ba

Please sign in to comment.