minstall: Add more safety checks when unpickling installdata

When need to catch exceptions just like we do in coredata.load() to
print proper error message instead of backtrace when user mix meson
versions.

This happens frequently when user has a newer version of meson installed
in their HOME and then "sudo meson install" uses the system version of
meson.
pull/10503/head
Xavier Claessens 3 years ago committed by Eli Schwartz
parent 7229443738
commit 2e3ac3eec0
  1. 19
      mesonbuild/coredata.py
  2. 22
      mesonbuild/mesonlib/universal.py
  3. 15
      mesonbuild/minstall.py

@ -23,6 +23,7 @@ from .mesonlib import (
MesonException, EnvironmentException, MachineChoice, PerMachine,
PerMachineDefaultable, default_libdir, default_libexecdir,
default_prefix, split_args, OptionKey, OptionType, stringlistify,
pickle_load
)
from .wrap import WrapMode
import ast
@ -1047,23 +1048,11 @@ def major_versions_differ(v1: str, v2: str) -> bool:
def load(build_dir: str) -> CoreData:
filename = os.path.join(build_dir, 'meson-private', 'coredata.dat')
load_fail_msg = f'Coredata file {filename!r} is corrupted. Try with a fresh build tree.'
try:
with open(filename, 'rb') as f:
obj = pickle.load(f)
except (pickle.UnpicklingError, EOFError):
raise MesonException(load_fail_msg)
except (TypeError, ModuleNotFoundError, AttributeError):
raise MesonException(
f"Coredata file {filename!r} references functions or classes that don't "
"exist. This probably means that it was generated with an old "
"version of meson.")
if not isinstance(obj, CoreData):
raise MesonException(load_fail_msg)
if major_versions_differ(obj.version, version):
raise MesonVersionMismatchException(obj.version, version)
obj = pickle_load(filename, 'Coredata', CoreData)
assert isinstance(obj, CoreData), 'for mypy'
return obj
def save(obj: CoreData, build_dir: str) -> str:
filename = os.path.join(build_dir, 'meson-private', 'coredata.dat')
prev_filename = filename + '.prev'

@ -31,6 +31,7 @@ import typing as T
import uuid
import textwrap
import copy
import pickle
from mesonbuild import mlog
@ -123,6 +124,7 @@ __all__ = [
'listify',
'partition',
'path_is_in_root',
'pickle_load',
'Popen_safe',
'quiet_git',
'quote_arg',
@ -2232,3 +2234,23 @@ class OptionKey:
def is_base(self) -> bool:
"""Convenience method to check if this is a base option."""
return self.type is OptionType.BASE
def pickle_load(filename: str, object_name: str, object_type: T.Type) -> T.Any:
load_fail_msg = f'{object_name} file {filename!r} is corrupted. Try with a fresh build tree.'
try:
with open(filename, 'rb') as f:
obj = pickle.load(f)
except (pickle.UnpicklingError, EOFError):
raise MesonException(load_fail_msg)
except (TypeError, ModuleNotFoundError, AttributeError):
raise MesonException(
f"{object_name} file {filename!r} references functions or classes that don't "
"exist. This probably means that it was generated with an old "
"version of meson.")
if not isinstance(obj, object_type):
raise MesonException(load_fail_msg)
from ..coredata import version as coredata_version
from ..coredata import major_versions_differ, MesonVersionMismatchException
if major_versions_differ(obj.version, coredata_version):
raise MesonVersionMismatchException(obj.version, coredata_version)
return obj

@ -18,7 +18,6 @@ from pathlib import Path
import argparse
import errno
import os
import pickle
import shlex
import shutil
import subprocess
@ -28,9 +27,7 @@ import typing as T
from . import build
from . import environment
from .backend.backends import InstallData
from .coredata import major_versions_differ, MesonVersionMismatchException
from .coredata import version as coredata_version
from .mesonlib import MesonException, Popen_safe, RealPathAction, is_windows, setup_vsenv
from .mesonlib import MesonException, Popen_safe, RealPathAction, is_windows, setup_vsenv, pickle_load
from .scripts import depfixer, destdir_join
from .scripts.meson_exe import run_exe
try:
@ -133,13 +130,9 @@ class DirMaker:
def load_install_data(fname: str) -> InstallData:
with open(fname, 'rb') as ifile:
obj = pickle.load(ifile)
if not isinstance(obj, InstallData) or not hasattr(obj, 'version'):
raise MesonVersionMismatchException('<unknown>', coredata_version)
if major_versions_differ(obj.version, coredata_version):
raise MesonVersionMismatchException(obj.version, coredata_version)
return obj
obj = pickle_load(fname, 'InstallData', InstallData)
assert isinstance(obj, InstallData), 'fo mypy'
return obj
def is_executable(path: str, follow_symlinks: bool = False) -> bool:
'''Checks whether any of the "x" bits are set in the source file mode.'''

Loading…
Cancel
Save