mirror of
https://github.com/Retropex/bitcoin.git
synced 2025-05-12 19:20:42 +02:00
scripts: use LIEF for ELF checks in symbol-check.py
Co-authored-by: Carl Dong <contact@carldong.me>
This commit is contained in:
parent
610a8a8e39
commit
309eac9019
@ -376,5 +376,5 @@ if TARGET_WINDOWS
|
|||||||
endif
|
endif
|
||||||
if TARGET_LINUX
|
if TARGET_LINUX
|
||||||
$(AM_V_at) CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_ELF
|
$(AM_V_at) CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_ELF
|
||||||
$(AM_V_at) CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' CPPFILT='$(CPPFILT)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_ELF
|
$(AM_V_at) CC='$(CC)' CFLAGS='$(CFLAGS)' CPPFLAGS='$(CPPFLAGS)' LDFLAGS='$(LDFLAGS)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_ELF
|
||||||
endif
|
endif
|
||||||
|
@ -113,7 +113,6 @@ AC_PATH_PROG([GIT], [git])
|
|||||||
AC_PATH_PROG(CCACHE,ccache)
|
AC_PATH_PROG(CCACHE,ccache)
|
||||||
AC_PATH_PROG(XGETTEXT,xgettext)
|
AC_PATH_PROG(XGETTEXT,xgettext)
|
||||||
AC_PATH_PROG(HEXDUMP,hexdump)
|
AC_PATH_PROG(HEXDUMP,hexdump)
|
||||||
AC_PATH_TOOL(CPPFILT, c++filt)
|
|
||||||
AC_PATH_TOOL(OBJCOPY, objcopy)
|
AC_PATH_TOOL(OBJCOPY, objcopy)
|
||||||
AC_PATH_PROG(DOXYGEN, doxygen)
|
AC_PATH_PROG(DOXYGEN, doxygen)
|
||||||
AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
|
AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
|
||||||
|
@ -10,14 +10,14 @@ Example usage:
|
|||||||
|
|
||||||
find ../path/to/binaries -type f -executable | xargs python3 contrib/devtools/symbol-check.py
|
find ../path/to/binaries -type f -executable | xargs python3 contrib/devtools/symbol-check.py
|
||||||
'''
|
'''
|
||||||
import subprocess
|
|
||||||
import sys
|
import sys
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
import lief
|
import lief
|
||||||
import pixie
|
|
||||||
|
|
||||||
from utils import determine_wellknown_cmd
|
# temporary constant, to be replaced with lief.ELF.ARCH.RISCV
|
||||||
|
# https://github.com/lief-project/LIEF/pull/562
|
||||||
|
LIEF_ELF_ARCH_RISCV = lief.ELF.ARCH(243)
|
||||||
|
|
||||||
# Debian 8 (Jessie) EOL: 2020. https://wiki.debian.org/DebianReleases#Production_Releases
|
# Debian 8 (Jessie) EOL: 2020. https://wiki.debian.org/DebianReleases#Production_Releases
|
||||||
#
|
#
|
||||||
@ -43,12 +43,12 @@ from utils import determine_wellknown_cmd
|
|||||||
MAX_VERSIONS = {
|
MAX_VERSIONS = {
|
||||||
'GCC': (4,8,0),
|
'GCC': (4,8,0),
|
||||||
'GLIBC': {
|
'GLIBC': {
|
||||||
pixie.EM_386: (2,17),
|
lief.ELF.ARCH.i386: (2,17),
|
||||||
pixie.EM_X86_64: (2,17),
|
lief.ELF.ARCH.x86_64: (2,17),
|
||||||
pixie.EM_ARM: (2,17),
|
lief.ELF.ARCH.ARM: (2,17),
|
||||||
pixie.EM_AARCH64:(2,17),
|
lief.ELF.ARCH.AARCH64:(2,17),
|
||||||
pixie.EM_PPC64: (2,17),
|
lief.ELF.ARCH.PPC64: (2,17),
|
||||||
pixie.EM_RISCV: (2,27),
|
LIEF_ELF_ARCH_RISCV: (2,27),
|
||||||
},
|
},
|
||||||
'LIBATOMIC': (1,0),
|
'LIBATOMIC': (1,0),
|
||||||
'V': (0,5,0), # xkb (bitcoin-qt only)
|
'V': (0,5,0), # xkb (bitcoin-qt only)
|
||||||
@ -58,7 +58,8 @@ MAX_VERSIONS = {
|
|||||||
|
|
||||||
# Ignore symbols that are exported as part of every executable
|
# Ignore symbols that are exported as part of every executable
|
||||||
IGNORE_EXPORTS = {
|
IGNORE_EXPORTS = {
|
||||||
'_edata', '_end', '__end__', '_init', '__bss_start', '__bss_start__', '_bss_end__', '__bss_end__', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr',
|
'_edata', '_end', '__end__', '_init', '__bss_start', '__bss_start__', '_bss_end__',
|
||||||
|
'__bss_end__', '_fini', '_IO_stdin_used', 'stdin', 'stdout', 'stderr',
|
||||||
'environ', '_environ', '__environ',
|
'environ', '_environ', '__environ',
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,31 +134,8 @@ PE_ALLOWED_LIBRARIES = {
|
|||||||
'WTSAPI32.dll',
|
'WTSAPI32.dll',
|
||||||
}
|
}
|
||||||
|
|
||||||
class CPPFilt(object):
|
|
||||||
'''
|
|
||||||
Demangle C++ symbol names.
|
|
||||||
|
|
||||||
Use a pipe to the 'c++filt' command.
|
|
||||||
'''
|
|
||||||
def __init__(self):
|
|
||||||
self.proc = subprocess.Popen(determine_wellknown_cmd('CPPFILT', 'c++filt'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
|
|
||||||
|
|
||||||
def __call__(self, mangled):
|
|
||||||
self.proc.stdin.write(mangled + '\n')
|
|
||||||
self.proc.stdin.flush()
|
|
||||||
return self.proc.stdout.readline().rstrip()
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self.proc.stdin.close()
|
|
||||||
self.proc.stdout.close()
|
|
||||||
self.proc.wait()
|
|
||||||
|
|
||||||
def check_version(max_versions, version, arch) -> bool:
|
def check_version(max_versions, version, arch) -> bool:
|
||||||
if '_' in version:
|
(lib, _, ver) = version.rpartition('_')
|
||||||
(lib, _, ver) = version.rpartition('_')
|
|
||||||
else:
|
|
||||||
lib = version
|
|
||||||
ver = '0'
|
|
||||||
ver = tuple([int(x) for x in ver.split('.')])
|
ver = tuple([int(x) for x in ver.split('.')])
|
||||||
if not lib in max_versions:
|
if not lib in max_versions:
|
||||||
return False
|
return False
|
||||||
@ -167,41 +145,42 @@ def check_version(max_versions, version, arch) -> bool:
|
|||||||
return ver <= max_versions[lib][arch]
|
return ver <= max_versions[lib][arch]
|
||||||
|
|
||||||
def check_imported_symbols(filename) -> bool:
|
def check_imported_symbols(filename) -> bool:
|
||||||
elf = pixie.load(filename)
|
|
||||||
cppfilt = CPPFilt()
|
|
||||||
ok: bool = True
|
ok: bool = True
|
||||||
|
binary = lief.parse(filename)
|
||||||
|
|
||||||
for symbol in elf.dyn_symbols:
|
for symbol in binary.imported_symbols:
|
||||||
if not symbol.is_import:
|
if not symbol.imported:
|
||||||
continue
|
continue
|
||||||
sym = symbol.name.decode()
|
|
||||||
version = symbol.version.decode() if symbol.version is not None else None
|
version = symbol.symbol_version if symbol.has_version else None
|
||||||
if version and not check_version(MAX_VERSIONS, version, elf.hdr.e_machine):
|
|
||||||
print('{}: symbol {} from unsupported version {}'.format(filename, cppfilt(sym), version))
|
if version:
|
||||||
ok = False
|
aux_version = version.symbol_version_auxiliary.name if version.has_auxiliary_version else None
|
||||||
|
if aux_version and not check_version(MAX_VERSIONS, aux_version, binary.header.machine_type):
|
||||||
|
print(f'{filename}: symbol {symbol.name} from unsupported version {version}')
|
||||||
|
ok = False
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
def check_exported_symbols(filename) -> bool:
|
def check_exported_symbols(filename) -> bool:
|
||||||
elf = pixie.load(filename)
|
|
||||||
cppfilt = CPPFilt()
|
|
||||||
ok: bool = True
|
ok: bool = True
|
||||||
for symbol in elf.dyn_symbols:
|
binary = lief.parse(filename)
|
||||||
if not symbol.is_export:
|
|
||||||
|
for symbol in binary.dynamic_symbols:
|
||||||
|
if not symbol.exported:
|
||||||
continue
|
continue
|
||||||
sym = symbol.name.decode()
|
name = symbol.name
|
||||||
if elf.hdr.e_machine == pixie.EM_RISCV or sym in IGNORE_EXPORTS:
|
if binary.header.machine_type == LIEF_ELF_ARCH_RISCV or name in IGNORE_EXPORTS:
|
||||||
continue
|
continue
|
||||||
print('{}: export of symbol {} not allowed'.format(filename, cppfilt(sym)))
|
print(f'{filename}: export of symbol {name} not allowed!')
|
||||||
ok = False
|
ok = False
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
def check_ELF_libraries(filename) -> bool:
|
def check_ELF_libraries(filename) -> bool:
|
||||||
ok: bool = True
|
ok: bool = True
|
||||||
elf = pixie.load(filename)
|
binary = lief.parse(filename)
|
||||||
for library_name in elf.query_dyn_tags(pixie.DT_NEEDED):
|
for library in binary.libraries:
|
||||||
assert(isinstance(library_name, bytes))
|
if library not in ELF_ALLOWED_LIBRARIES:
|
||||||
if library_name.decode() not in ELF_ALLOWED_LIBRARIES:
|
print(f'{filename}: {library} is not in ALLOWED_LIBRARIES!')
|
||||||
print('{}: NEEDED library {} is not allowed'.format(filename, library_name.decode()))
|
|
||||||
ok = False
|
ok = False
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class TestSymbolChecks(unittest.TestCase):
|
|||||||
''')
|
''')
|
||||||
|
|
||||||
self.assertEqual(call_symbol_check(cc, source, executable, ['-lm']),
|
self.assertEqual(call_symbol_check(cc, source, executable, ['-lm']),
|
||||||
(1, executable + ': symbol nextup from unsupported version GLIBC_2.24\n' +
|
(1, executable + ': symbol nextup from unsupported version GLIBC_2.24(3)\n' +
|
||||||
executable + ': failed IMPORTED_SYMBOLS'))
|
executable + ': failed IMPORTED_SYMBOLS'))
|
||||||
|
|
||||||
# -lutil is part of the libc6 package so a safe bet that it's installed
|
# -lutil is part of the libc6 package so a safe bet that it's installed
|
||||||
@ -79,7 +79,7 @@ class TestSymbolChecks(unittest.TestCase):
|
|||||||
''')
|
''')
|
||||||
|
|
||||||
self.assertEqual(call_symbol_check(cc, source, executable, ['-lutil']),
|
self.assertEqual(call_symbol_check(cc, source, executable, ['-lutil']),
|
||||||
(1, executable + ': NEEDED library libutil.so.1 is not allowed\n' +
|
(1, executable + ': libutil.so.1 is not in ALLOWED_LIBRARIES!\n' +
|
||||||
executable + ': failed LIBRARY_DEPENDENCIES'))
|
executable + ': failed LIBRARY_DEPENDENCIES'))
|
||||||
|
|
||||||
# finally, check a simple conforming binary
|
# finally, check a simple conforming binary
|
||||||
|
@ -808,20 +808,8 @@ clean-local:
|
|||||||
$(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@
|
$(AM_V_GEN) $(WINDRES) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) -DWINDRES_PREPROC -i $< -o $@
|
||||||
|
|
||||||
check-symbols: $(bin_PROGRAMS)
|
check-symbols: $(bin_PROGRAMS)
|
||||||
if TARGET_DARWIN
|
@echo "Running symbol and dynamic library checks..."
|
||||||
@echo "Checking macOS dynamic libraries..."
|
|
||||||
$(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
|
$(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
|
||||||
endif
|
|
||||||
|
|
||||||
if TARGET_WINDOWS
|
|
||||||
@echo "Checking Windows dynamic libraries..."
|
|
||||||
$(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
if TARGET_LINUX
|
|
||||||
@echo "Checking glibc back compat..."
|
|
||||||
$(AM_V_at) CPPFILT='$(CPPFILT)' $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
|
|
||||||
endif
|
|
||||||
|
|
||||||
check-security: $(bin_PROGRAMS)
|
check-security: $(bin_PROGRAMS)
|
||||||
if HARDEN
|
if HARDEN
|
||||||
|
Loading…
Reference in New Issue
Block a user