diff --git a/ci/lint/04_install.sh b/ci/lint/04_install.sh index f2d390bd1b9..c1e39dc93e2 100755 --- a/ci/lint/04_install.sh +++ b/ci/lint/04_install.sh @@ -38,7 +38,7 @@ python3 --version ${CI_RETRY_EXE} pip3 install \ codespell==2.4.1 \ - lief==0.13.2 \ + lief==0.16.6 \ mypy==1.4.1 \ pyzmq==25.1.0 \ ruff==0.5.5 \ diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm index 92fc1791f03..a230eea991b 100644 --- a/contrib/guix/manifest.scm +++ b/contrib/guix/manifest.scm @@ -15,8 +15,10 @@ (gnu packages ninja) (gnu packages pkg-config) ((gnu packages python) #:select (python-minimal)) - ((gnu packages python-build) #:select (python-tomli python-poetry-core)) + ((gnu packages python-build) #:select (python-poetry-core)) ((gnu packages python-crypto) #:select (python-asn1crypto)) + ((gnu packages python-science) #:select (python-scikit-build-core)) + ((gnu packages python-xyz) #:select (python-pydantic-2 python-pydantic-core)) ((gnu packages tls) #:select (openssl)) ((gnu packages version-control) #:select (git-minimal)) (guix build-system cmake) @@ -158,37 +160,35 @@ chain for " target " development.")) (define-public python-lief (package (name "python-lief") - (version "0.13.2") + (version "0.16.6") (source (origin (method git-fetch) (uri (git-reference (url "https://github.com/lief-project/LIEF") (commit version))) (file-name (git-file-name name version)) - (modules '((guix build utils))) - (snippet - '(begin - ;; Configure build for Python bindings. - (substitute* "api/python/config-default.toml" - (("(ninja = )true" all m) - (string-append m "false")) - (("(parallel-jobs = )0" all m) - (string-append m (number->string (parallel-job-count))))))) (sha256 (base32 - "0y48x358ppig5xp97ahcphfipx7cg9chldj2q5zrmn610fmi4zll")))) - (build-system python-build-system) - (native-inputs (list cmake-minimal python-tomli)) + "1pq9nagrnkl1x943bqnpiyxmkd9vk99znfxiwqp6vf012b50bz2a")) + (patches (search-our-patches "lief-scikit-0-9.patch")))) + (build-system pyproject-build-system) + (native-inputs (list cmake-minimal + ninja + python-scikit-build-core + python-pydantic-core + python-pydantic-2)) (arguments (list #:tests? #f ;needs network #:phases #~(modify-phases %standard-phases - (add-before 'build 'change-directory + (add-before 'build 'set-pythonpath (lambda _ - (chdir "api/python"))) - (replace 'build + (setenv "PYTHONPATH" + (string-append (string-append (getcwd) "/api/python/backend") + ":" (or (getenv "PYTHONPATH") ""))))) + (add-after 'set-pythonpath 'change-directory (lambda _ - (invoke "python" "setup.py" "build")))))) + (chdir "api/python")))))) (home-page "https://github.com/lief-project/LIEF") (synopsis "Library to instrument executable formats") (description diff --git a/contrib/guix/patches/lief-scikit-0-9.patch b/contrib/guix/patches/lief-scikit-0-9.patch new file mode 100644 index 00000000000..71e617834f0 --- /dev/null +++ b/contrib/guix/patches/lief-scikit-0-9.patch @@ -0,0 +1,21 @@ +Partially revert f23ced2f4ffc170d0a6f40ff4a1bee575e3447cf + +Restore compat with python-scikit-build-core 0.9.x +Can be dropped when using python-scikit-build-core >= 0.10.x + +--- a/api/python/backend/setup.py ++++ b/api/python/backend/setup.py +@@ -101,12 +101,12 @@ def _get_hooked_config(is_editable: bool) -> Optional[dict[str, Union[str, List[ + config_settings = { + "logging.level": "DEBUG", + "build-dir": config.build_dir, +- "build.targets": config.build.targets, + "install.strip": config.strip, + "backport.find-python": "0", + "wheel.py-api": config.build.py_api, + "cmake.source-dir": SRC_DIR.as_posix(), + "cmake.build-type": config.build.build_type, ++ "cmake.targets": config.build.targets, + "cmake.args": [ + *config.cmake_generator, + *config.get_cmake_args(is_editable), diff --git a/contrib/guix/security-check.py b/contrib/guix/security-check.py index 04185a2fae1..b6ec191141a 100755 --- a/contrib/guix/security-check.py +++ b/contrib/guix/security-check.py @@ -30,13 +30,13 @@ def check_ELF_RELRO(binary) -> bool: # However, the dynamic linker need to write to this area so these are RW. # Glibc itself takes care of mprotecting this area R after relocations are finished. # See also https://marc.info/?l=binutils&m=1498883354122353 - if segment.type == lief.ELF.SEGMENT_TYPES.GNU_RELRO: + if segment.type == lief.ELF.Segment.TYPE.GNU_RELRO: have_gnu_relro = True have_bindnow = False try: - flags = binary.get(lief.ELF.DYNAMIC_TAGS.FLAGS) - if flags.value & lief.ELF.DYNAMIC_FLAGS.BIND_NOW: + flags = binary.get(lief.ELF.DynamicEntry.TAG.FLAGS) + if flags.has(lief.ELF.DynamicEntryFlags.FLAG.BIND_NOW): have_bindnow = True except Exception: have_bindnow = False @@ -55,9 +55,9 @@ def check_ELF_SEPARATE_CODE(binary): based on their permissions. This checks for missing -Wl,-z,separate-code and potentially other problems. ''' - R = lief.ELF.SEGMENT_FLAGS.R - W = lief.ELF.SEGMENT_FLAGS.W - E = lief.ELF.SEGMENT_FLAGS.X + R = lief.ELF.Segment.FLAGS.R + W = lief.ELF.Segment.FLAGS.W + E = lief.ELF.Segment.FLAGS.X EXPECTED_FLAGS = { # Read + execute '.init': R | E, @@ -99,7 +99,7 @@ def check_ELF_SEPARATE_CODE(binary): # and for each section, remember the flags of the associated program header. flags_per_section = {} for segment in binary.segments: - if segment.type == lief.ELF.SEGMENT_TYPES.LOAD: + if segment.type == lief.ELF.Segment.TYPE.LOAD: for section in segment.sections: flags_per_section[section.name] = segment.flags # Spot-check ELF LOAD program header flags per section @@ -144,13 +144,13 @@ def check_ELF_FORTIFY(binary) -> bool: def check_PE_DYNAMIC_BASE(binary) -> bool: '''PIE: DllCharacteristics bit 0x40 signifies dynamicbase (ASLR)''' - return lief.PE.DLL_CHARACTERISTICS.DYNAMIC_BASE in binary.optional_header.dll_characteristics_lists + return lief.PE.OptionalHeader.DLL_CHARACTERISTICS.DYNAMIC_BASE in binary.optional_header.dll_characteristics_lists # Must support high-entropy 64-bit address space layout randomization # in addition to DYNAMIC_BASE to have secure ASLR. def check_PE_HIGH_ENTROPY_VA(binary) -> bool: '''PIE: DllCharacteristics bit 0x20 signifies high-entropy ASLR''' - return lief.PE.DLL_CHARACTERISTICS.HIGH_ENTROPY_VA in binary.optional_header.dll_characteristics_lists + return lief.PE.OptionalHeader.DLL_CHARACTERISTICS.HIGH_ENTROPY_VA in binary.optional_header.dll_characteristics_lists def check_PE_RELOC_SECTION(binary) -> bool: '''Check for a reloc section. This is required for functional ASLR.''' @@ -181,7 +181,7 @@ def check_MACHO_NOUNDEFS(binary) -> bool: ''' Check for no undefined references. ''' - return binary.header.has(lief.MachO.HEADER_FLAGS.NOUNDEFS) + return binary.header.has(lief.MachO.Header.FLAGS.NOUNDEFS) def check_MACHO_FIXUP_CHAINS(binary) -> bool: ''' @@ -206,7 +206,13 @@ def check_NX(binary) -> bool: ''' Check for no stack execution ''' - return binary.has_nx + + # binary.has_nx checks are only for the stack, but MachO binaries might + # have executable heaps. + if binary.format == lief.Binary.FORMATS.MACHO: + return binary.concrete.has_nx_stack and binary.concrete.has_nx_heap + else: + return binary.has_nx def check_MACHO_CONTROL_FLOW(binary) -> bool: ''' @@ -253,21 +259,21 @@ BASE_MACHO = [ ] CHECKS = { - lief.EXE_FORMATS.ELF: { - lief.ARCHITECTURES.X86: BASE_ELF + [('CONTROL_FLOW', check_ELF_CONTROL_FLOW), ('FORTIFY', check_ELF_FORTIFY)], - lief.ARCHITECTURES.ARM: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)], - lief.ARCHITECTURES.ARM64: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)], - lief.ARCHITECTURES.PPC: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)], - lief.ARCHITECTURES.RISCV: BASE_ELF, # Skip FORTIFY. See https://github.com/lief-project/LIEF/issues/1082. + lief.Binary.FORMATS.ELF: { + lief.Header.ARCHITECTURES.X86_64: BASE_ELF + [('CONTROL_FLOW', check_ELF_CONTROL_FLOW), ('FORTIFY', check_ELF_FORTIFY)], + lief.Header.ARCHITECTURES.ARM: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)], + lief.Header.ARCHITECTURES.ARM64: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)], + lief.Header.ARCHITECTURES.PPC64: BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)], + lief.Header.ARCHITECTURES.RISCV: BASE_ELF, # Skip FORTIFY. See https://github.com/lief-project/LIEF/issues/1082. }, - lief.EXE_FORMATS.PE: { - lief.ARCHITECTURES.X86: BASE_PE, + lief.Binary.FORMATS.PE: { + lief.Header.ARCHITECTURES.X86_64: BASE_PE, }, - lief.EXE_FORMATS.MACHO: { - lief.ARCHITECTURES.X86: BASE_MACHO + [('PIE', check_PIE), + lief.Binary.FORMATS.MACHO: { + lief.Header.ARCHITECTURES.X86_64: BASE_MACHO + [('PIE', check_PIE), ('NX', check_NX), ('CONTROL_FLOW', check_MACHO_CONTROL_FLOW)], - lief.ARCHITECTURES.ARM64: BASE_MACHO + [('BRANCH_PROTECTION', check_MACHO_BRANCH_PROTECTION)], + lief.Header.ARCHITECTURES.ARM64: BASE_MACHO + [('BRANCH_PROTECTION', check_MACHO_BRANCH_PROTECTION)], } } @@ -275,9 +281,9 @@ if __name__ == '__main__': retval: int = 0 for filename in sys.argv[1:]: binary = lief.parse(filename) + etype = binary.format arch = binary.abstract.header.architecture - binary.concrete failed: list[str] = [] for (name, func) in CHECKS[etype][arch]: diff --git a/contrib/guix/symbol-check.py b/contrib/guix/symbol-check.py index 61e656114ab..91241edaf9d 100755 --- a/contrib/guix/symbol-check.py +++ b/contrib/guix/symbol-check.py @@ -34,7 +34,7 @@ import lief MAX_VERSIONS = { 'GCC': (4,3,0), 'GLIBC': { - lief.ELF.ARCH.x86_64: (2,31), + lief.ELF.ARCH.X86_64: (2,31), lief.ELF.ARCH.ARM: (2,31), lief.ELF.ARCH.AARCH64:(2,31), lief.ELF.ARCH.PPC64: (2,31), @@ -52,41 +52,41 @@ IGNORE_EXPORTS = { # Expected linker-loader names can be found here: # https://sourceware.org/glibc/wiki/ABIList?action=recall&rev=16 -ELF_INTERPRETER_NAMES: dict[lief.ELF.ARCH, dict[lief.ENDIANNESS, str]] = { - lief.ELF.ARCH.x86_64: { - lief.ENDIANNESS.LITTLE: "/lib64/ld-linux-x86-64.so.2", +ELF_INTERPRETER_NAMES: dict[lief.ELF.ARCH, dict[lief.Header.ENDIANNESS, str]] = { + lief.ELF.ARCH.X86_64: { + lief.Header.ENDIANNESS.LITTLE: "/lib64/ld-linux-x86-64.so.2", }, lief.ELF.ARCH.ARM: { - lief.ENDIANNESS.LITTLE: "/lib/ld-linux-armhf.so.3", + lief.Header.ENDIANNESS.LITTLE: "/lib/ld-linux-armhf.so.3", }, lief.ELF.ARCH.AARCH64: { - lief.ENDIANNESS.LITTLE: "/lib/ld-linux-aarch64.so.1", + lief.Header.ENDIANNESS.LITTLE: "/lib/ld-linux-aarch64.so.1", }, lief.ELF.ARCH.PPC64: { - lief.ENDIANNESS.BIG: "/lib64/ld64.so.1", - lief.ENDIANNESS.LITTLE: "/lib64/ld64.so.2", + lief.Header.ENDIANNESS.BIG: "/lib64/ld64.so.1", + lief.Header.ENDIANNESS.LITTLE: "/lib64/ld64.so.2", }, lief.ELF.ARCH.RISCV: { - lief.ENDIANNESS.LITTLE: "/lib/ld-linux-riscv64-lp64d.so.1", + lief.Header.ENDIANNESS.LITTLE: "/lib/ld-linux-riscv64-lp64d.so.1", }, } -ELF_ABIS: dict[lief.ELF.ARCH, dict[lief.ENDIANNESS, list[int]]] = { - lief.ELF.ARCH.x86_64: { - lief.ENDIANNESS.LITTLE: [3,2,0], +ELF_ABIS: dict[lief.ELF.ARCH, dict[lief.Header.ENDIANNESS, list[int]]] = { + lief.ELF.ARCH.X86_64: { + lief.Header.ENDIANNESS.LITTLE: [3,2,0], }, lief.ELF.ARCH.ARM: { - lief.ENDIANNESS.LITTLE: [3,2,0], + lief.Header.ENDIANNESS.LITTLE: [3,2,0], }, lief.ELF.ARCH.AARCH64: { - lief.ENDIANNESS.LITTLE: [3,7,0], + lief.Header.ENDIANNESS.LITTLE: [3,7,0], }, lief.ELF.ARCH.PPC64: { - lief.ENDIANNESS.LITTLE: [3,10,0], - lief.ENDIANNESS.BIG: [3,2,0], + lief.Header.ENDIANNESS.LITTLE: [3,10,0], + lief.Header.ENDIANNESS.BIG: [3,2,0], }, lief.ELF.ARCH.RISCV: { - lief.ENDIANNESS.LITTLE: [4,15,0], + lief.Header.ENDIANNESS.LITTLE: [4,15,0], }, } @@ -223,13 +223,13 @@ def check_exported_symbols(binary) -> bool: name = symbol.name if binary.header.machine_type == lief.ELF.ARCH.RISCV or name in IGNORE_EXPORTS: continue - print(f'{binary.name}: export of symbol {name} not allowed!') + print(f'{filename}: export of symbol {name} not allowed!') ok = False return ok def check_RUNPATH(binary) -> bool: - assert binary.get(lief.ELF.DYNAMIC_TAGS.RUNPATH) is None - assert binary.get(lief.ELF.DYNAMIC_TAGS.RPATH) is None + assert binary.get(lief.ELF.DynamicEntry.TAG.RUNPATH) is None + assert binary.get(lief.ELF.DynamicEntry.TAG.RPATH) is None return True def check_ELF_libraries(binary) -> bool: @@ -294,12 +294,12 @@ def check_ELF_interpreter(binary) -> bool: def check_ELF_ABI(binary) -> bool: expected_abi = ELF_ABIS[binary.header.machine_type][binary.abstract.header.endianness] - note = binary.concrete.get(lief.ELF.NOTE_TYPES.ABI_TAG) - assert note.details.abi == lief.ELF.NOTE_ABIS.LINUX - return note.details.version == expected_abi + note = binary.concrete.get(lief.ELF.Note.TYPE.GNU_ABI_TAG) + assert note.abi == lief.ELF.NoteAbi.ABI.LINUX + return note.version == expected_abi CHECKS = { -lief.EXE_FORMATS.ELF: [ +lief.Binary.FORMATS.ELF: [ ('IMPORTED_SYMBOLS', check_imported_symbols), ('EXPORTED_SYMBOLS', check_exported_symbols), ('LIBRARY_DEPENDENCIES', check_ELF_libraries), @@ -307,13 +307,13 @@ lief.EXE_FORMATS.ELF: [ ('ABI', check_ELF_ABI), ('RUNPATH', check_RUNPATH), ], -lief.EXE_FORMATS.MACHO: [ +lief.Binary.FORMATS.MACHO: [ ('DYNAMIC_LIBRARIES', check_MACHO_libraries), ('MIN_OS', check_MACHO_min_os), ('SDK', check_MACHO_sdk), ('LLD', check_MACHO_lld), ], -lief.EXE_FORMATS.PE: [ +lief.Binary.FORMATS.PE: [ ('DYNAMIC_LIBRARIES', check_PE_libraries), ('SUBSYSTEM_VERSION', check_PE_subsystem_version), ('APPLICATION_MANIFEST', check_PE_application_manifest), @@ -324,6 +324,7 @@ if __name__ == '__main__': retval: int = 0 for filename in sys.argv[1:]: binary = lief.parse(filename) + etype = binary.format failed: list[str] = []