Skip to content

Commit

Permalink
python: bump lief to 0.16.2
Browse files Browse the repository at this point in the history
  • Loading branch information
willcl-ark committed Jan 24, 2025
1 parent ede9aa9 commit 01d6c08
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 118 deletions.
2 changes: 1 addition & 1 deletion ci/lint/04_install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ fi

${CI_RETRY_EXE} pip3 install \
codespell==2.2.6 \
lief==0.13.2 \
lief==0.16.2 \
mypy==1.4.1 \
pyzmq==25.1.0 \
ruff==0.5.5 \
Expand Down
55 changes: 24 additions & 31 deletions contrib/devtools/security-check.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,17 @@ 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:
have_bindnow = True
flags_entry = binary.get(lief.ELF.DynamicEntry.TAG.FLAGS)
if flags_entry is not None:
if flags_entry.has(lief.ELF.DynamicEntryFlags.FLAG.BIND_NOW):
have_bindnow = True
except Exception:
have_bindnow = False
pass

return have_gnu_relro and have_bindnow

Expand All @@ -51,9 +52,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,
Expand Down Expand Up @@ -95,7 +96,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
Expand Down Expand Up @@ -138,13 +139,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.'''
Expand Down Expand Up @@ -175,7 +176,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:
'''
Expand Down Expand Up @@ -247,34 +248,26 @@ def check_MACHO_BRANCH_PROTECTION(binary) -> bool:
]

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.EXE_FORMATS.PE: {
lief.ARCHITECTURES.X86: BASE_PE,
},
lief.EXE_FORMATS.MACHO: {
lief.ARCHITECTURES.X86: 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.Binary.FORMATS.ELF, lief.Header.ARCHITECTURES.X86): BASE_ELF + [('CONTROL_FLOW', check_ELF_CONTROL_FLOW), ('FORTIFY', check_ELF_FORTIFY)],
(lief.Binary.FORMATS.ELF, lief.Header.ARCHITECTURES.ARM): BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)],
(lief.Binary.FORMATS.ELF, lief.Header.ARCHITECTURES.ARM64): BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)],
(lief.Binary.FORMATS.ELF, lief.Header.ARCHITECTURES.PPC): BASE_ELF + [('FORTIFY', check_ELF_FORTIFY)],
(lief.Binary.FORMATS.ELF, lief.Header.ARCHITECTURES.RISCV): BASE_ELF,
(lief.Binary.FORMATS.PE, lief.Header.ARCHITECTURES.X86): BASE_PE,
(lief.Binary.FORMATS.MACHO, lief.Header.ARCHITECTURES.X86): BASE_MACHO + [('PIE', check_PIE), ('NX', check_NX), ('CONTROL_FLOW', check_MACHO_CONTROL_FLOW)],
(lief.Binary.FORMATS.MACHO, lief.Header.ARCHITECTURES.ARM64): BASE_MACHO + [('BRANCH_PROTECTION', check_MACHO_BRANCH_PROTECTION)],
}

if __name__ == '__main__':
retval: int = 0
for filename in sys.argv[1:]:
binary = lief.parse(filename)
etype = binary.format
arch = binary.abstract.header.architecture
if not binary:
break
binary.concrete

failed: list[str] = []
for (name, func) in CHECKS[etype][arch]:
for name, func in CHECKS[(binary.format, binary.abstract.header.architecture)]:
if not func(binary):
failed.append(name)
if failed:
Expand Down
142 changes: 60 additions & 82 deletions contrib/devtools/symbol-check.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@
# See https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html for more info.

MAX_VERSIONS = {
'GCC': (4,3,0),
'GLIBC': {
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),
lief.ELF.ARCH.RISCV: (2,31),
},
'LIBATOMIC': (1,0),
'V': (0,5,0), # xkb (bitcoin-qt only)
'GCC': (4,3,0),
'GLIBC': {
int(lief.ELF.ARCH.X86_64): (2,31),
int(lief.ELF.ARCH.ARM): (2,31),
int(lief.ELF.ARCH.AARCH64): (2,31),
int(lief.ELF.ARCH.PPC64): (2,31),
int(lief.ELF.ARCH.RISCV): (2,31),
},
'LIBATOMIC': (1,0),
'V': (0,5,0),
}

# Ignore symbols that are exported as part of every executable
Expand All @@ -52,42 +52,22 @@

# 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",
},
lief.ELF.ARCH.ARM: {
lief.ENDIANNESS.LITTLE: "/lib/ld-linux-armhf.so.3",
},
lief.ELF.ARCH.AARCH64: {
lief.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.ELF.ARCH.RISCV: {
lief.ENDIANNESS.LITTLE: "/lib/ld-linux-riscv64-lp64d.so.1",
},
ELF_INTERPRETER_NAMES = {
(lief.ELF.ARCH.X86_64, lief.Header.ENDIANNESS.LITTLE): "/lib64/ld-linux-x86-64.so.2",
(lief.ELF.ARCH.ARM, lief.Header.ENDIANNESS.LITTLE): "/lib/ld-linux-armhf.so.3",
(lief.ELF.ARCH.AARCH64, lief.Header.ENDIANNESS.LITTLE): "/lib/ld-linux-aarch64.so.1",
(lief.ELF.ARCH.PPC64, lief.Header.ENDIANNESS.BIG): "/lib64/ld64.so.1",
(lief.ELF.ARCH.PPC64, lief.Header.ENDIANNESS.LITTLE): "/lib64/ld64.so.2",
(lief.ELF.ARCH.RISCV, 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],
},
lief.ELF.ARCH.ARM: {
lief.ENDIANNESS.LITTLE: [3,2,0],
},
lief.ELF.ARCH.AARCH64: {
lief.ENDIANNESS.LITTLE: [3,7,0],
},
lief.ELF.ARCH.PPC64: {
lief.ENDIANNESS.LITTLE: [3,10,0],
lief.ENDIANNESS.BIG: [3,2,0],
},
lief.ELF.ARCH.RISCV: {
lief.ENDIANNESS.LITTLE: [4,15,0],
},
ELF_ABIS = {
(lief.ELF.ARCH.X86_64, lief.Header.ENDIANNESS.LITTLE): [3,2,0],
(lief.ELF.ARCH.ARM, lief.Header.ENDIANNESS.LITTLE): [3,2,0],
(lief.ELF.ARCH.AARCH64, lief.Header.ENDIANNESS.LITTLE): [3,7,0],
(lief.ELF.ARCH.PPC64, lief.Header.ENDIANNESS.LITTLE): [3,10,0],
(lief.ELF.ARCH.PPC64, lief.Header.ENDIANNESS.BIG): [3,2,0],
(lief.ELF.ARCH.RISCV, lief.Header.ENDIANNESS.LITTLE): [3,15,0],
}

# Allowed NEEDED libraries
Expand Down Expand Up @@ -174,14 +154,12 @@
}

def check_version(max_versions, version, arch) -> bool:
(lib, _, ver) = version.rpartition('_')
ver = tuple([int(x) for x in ver.split('.')])
if not lib in max_versions:
lib, _, ver = version.rpartition('_')
ver = tuple(int(x) for x in ver.split('.'))
if lib not in max_versions:
return False
if isinstance(max_versions[lib], tuple):
return ver <= max_versions[lib]
else:
return ver <= max_versions[lib][arch]
max_ver = max_versions[lib]
return ver <= (max_ver[int(arch)] if isinstance(max_ver, dict) else max_ver)

def check_imported_symbols(binary) -> bool:
ok: bool = True
Expand All @@ -199,22 +177,21 @@ def check_imported_symbols(binary) -> bool:
ok = False
return ok

def check_exported_symbols(binary) -> bool:
def check_exported_symbols(binary, filename: str) -> bool:
ok: bool = True

for symbol in binary.dynamic_symbols:
if not symbol.exported:
continue
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:
Expand Down Expand Up @@ -265,46 +242,47 @@ def check_PE_subsystem_version(binary) -> bool:
return False

def check_ELF_interpreter(binary) -> bool:
expected_interpreter = ELF_INTERPRETER_NAMES[binary.header.machine_type][binary.abstract.header.endianness]
expected_interpreter = ELF_INTERPRETER_NAMES[(binary.header.machine_type, binary.abstract.header.endianness)]

return binary.concrete.interpreter == expected_interpreter

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
expected_abi = ELF_ABIS[(binary.header.machine_type, binary.abstract.header.endianness)]
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: [
('IMPORTED_SYMBOLS', check_imported_symbols),
('EXPORTED_SYMBOLS', check_exported_symbols),
('LIBRARY_DEPENDENCIES', check_ELF_libraries),
('INTERPRETER_NAME', check_ELF_interpreter),
('ABI', check_ELF_ABI),
('RUNPATH', check_RUNPATH),
],
lief.EXE_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: [
('DYNAMIC_LIBRARIES', check_PE_libraries),
('SUBSYSTEM_VERSION', check_PE_subsystem_version),
]
lief.ELF.Binary: [
('IMPORTED_SYMBOLS', check_imported_symbols),
('EXPORTED_SYMBOLS', check_exported_symbols),
('LIBRARY_DEPENDENCIES', check_ELF_libraries),
('INTERPRETER_NAME', check_ELF_interpreter),
('ABI', check_ELF_ABI),
('RUNPATH', check_RUNPATH),
],
lief.MachO.Binary: [
('DYNAMIC_LIBRARIES', check_MACHO_libraries),
('MIN_OS', check_MACHO_min_os),
('SDK', check_MACHO_sdk),
('LLD', check_MACHO_lld),
],
lief.PE.Binary: [
('DYNAMIC_LIBRARIES', check_PE_libraries),
('SUBSYSTEM_VERSION', check_PE_subsystem_version),
]
}

if __name__ == '__main__':
retval: int = 0
for filename in sys.argv[1:]:
binary = lief.parse(filename)
etype = binary.format

if not binary:
break
etype = type(binary.concrete)
failed: list[str] = []
for (name, func) in CHECKS[etype]:
if not func(binary):
if not func(binary) if name != 'EXPORTED_SYMBOLS' else func(binary, filename):
failed.append(name)
if failed:
print(f'{filename}: failed {" ".join(failed)}')
Expand Down
7 changes: 3 additions & 4 deletions contrib/guix/manifest.scm
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
(gnu packages pkg-config)
((gnu packages python) #:select (python-minimal))
((gnu packages python-build) #:select (python-tomli))
(gnu packages python-science) ; For lief
(gnu packages python-xyz) ; For lief (python-pydantic[-core])
((gnu packages python-crypto) #:select (python-asn1crypto))
((gnu packages tls) #:select (openssl))
((gnu packages version-control) #:select (git-minimal))
(guix build-system cmake)
(guix build-system gnu)
(guix build-system python)
(guix build-system pyproject)
(guix build-system trivial)
(guix download)
(guix gexp)
Expand All @@ -30,10 +33,6 @@
(guix packages)
((guix utils) #:select (cc-for-target substitute-keyword-arguments)))

(use-modules (guix build-system pyproject))
(use-modules (gnu packages python-science))
(use-modules (gnu packages python-xyz)) ; For python-pydantic[-core]

(define-syntax-rule (search-our-patches file-name ...)
"Return the list of absolute file names corresponding to each
FILE-NAME found in ./patches relative to the current file."
Expand Down

0 comments on commit 01d6c08

Please sign in to comment.