Merge #12829: Python3 fixup
f50975b
[contrib] fixup symbol-check.py Python3 support (John Newbery)5de2b18
[contrib] fixup security-check.py Python3 support (John Newbery) Pull request description: security-check.py and symbol-check.py were broken by #11881. Fix them. Tree-SHA512: 86de3d6dc3292b1ae4cc04c2d7d7dbbf39c9270551d7b224b8d8b19e3184c30c897dbf823200403706d06bb405c0decad5cfd690cb2c0312992a235a4ffcf6bf
This commit is contained in:
commit
252c1b0fae
2 changed files with 55 additions and 55 deletions
|
@ -20,38 +20,38 @@ def check_ELF_PIE(executable):
|
||||||
'''
|
'''
|
||||||
Check for position independent executable (PIE), allowing for address space randomization.
|
Check for position independent executable (PIE), allowing for address space randomization.
|
||||||
'''
|
'''
|
||||||
p = subprocess.Popen([READELF_CMD, '-h', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
p = subprocess.Popen([READELF_CMD, '-h', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
|
||||||
(stdout, stderr) = p.communicate()
|
(stdout, stderr) = p.communicate()
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
raise IOError('Error opening file')
|
raise IOError('Error opening file')
|
||||||
|
|
||||||
ok = False
|
ok = False
|
||||||
for line in stdout.split(b'\n'):
|
for line in stdout.splitlines():
|
||||||
line = line.split()
|
line = line.split()
|
||||||
if len(line)>=2 and line[0] == b'Type:' and line[1] == b'DYN':
|
if len(line)>=2 and line[0] == 'Type:' and line[1] == 'DYN':
|
||||||
ok = True
|
ok = True
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
def get_ELF_program_headers(executable):
|
def get_ELF_program_headers(executable):
|
||||||
'''Return type and flags for ELF program headers'''
|
'''Return type and flags for ELF program headers'''
|
||||||
p = subprocess.Popen([READELF_CMD, '-l', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
p = subprocess.Popen([READELF_CMD, '-l', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
|
||||||
(stdout, stderr) = p.communicate()
|
(stdout, stderr) = p.communicate()
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
raise IOError('Error opening file')
|
raise IOError('Error opening file')
|
||||||
in_headers = False
|
in_headers = False
|
||||||
count = 0
|
count = 0
|
||||||
headers = []
|
headers = []
|
||||||
for line in stdout.split(b'\n'):
|
for line in stdout.splitlines():
|
||||||
if line.startswith(b'Program Headers:'):
|
if line.startswith('Program Headers:'):
|
||||||
in_headers = True
|
in_headers = True
|
||||||
if line == b'':
|
if line == '':
|
||||||
in_headers = False
|
in_headers = False
|
||||||
if in_headers:
|
if in_headers:
|
||||||
if count == 1: # header line
|
if count == 1: # header line
|
||||||
ofs_typ = line.find(b'Type')
|
ofs_typ = line.find('Type')
|
||||||
ofs_offset = line.find(b'Offset')
|
ofs_offset = line.find('Offset')
|
||||||
ofs_flags = line.find(b'Flg')
|
ofs_flags = line.find('Flg')
|
||||||
ofs_align = line.find(b'Align')
|
ofs_align = line.find('Align')
|
||||||
if ofs_typ == -1 or ofs_offset == -1 or ofs_flags == -1 or ofs_align == -1:
|
if ofs_typ == -1 or ofs_offset == -1 or ofs_flags == -1 or ofs_align == -1:
|
||||||
raise ValueError('Cannot parse elfread -lW output')
|
raise ValueError('Cannot parse elfread -lW output')
|
||||||
elif count > 1:
|
elif count > 1:
|
||||||
|
@ -68,9 +68,9 @@ def check_ELF_NX(executable):
|
||||||
have_wx = False
|
have_wx = False
|
||||||
have_gnu_stack = False
|
have_gnu_stack = False
|
||||||
for (typ, flags) in get_ELF_program_headers(executable):
|
for (typ, flags) in get_ELF_program_headers(executable):
|
||||||
if typ == b'GNU_STACK':
|
if typ == 'GNU_STACK':
|
||||||
have_gnu_stack = True
|
have_gnu_stack = True
|
||||||
if b'W' in flags and b'E' in flags: # section is both writable and executable
|
if 'W' in flags and 'E' in flags: # section is both writable and executable
|
||||||
have_wx = True
|
have_wx = True
|
||||||
return have_gnu_stack and not have_wx
|
return have_gnu_stack and not have_wx
|
||||||
|
|
||||||
|
@ -87,17 +87,17 @@ def check_ELF_RELRO(executable):
|
||||||
# However, the dynamic linker need to write to this area so these are RW.
|
# 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.
|
# Glibc itself takes care of mprotecting this area R after relocations are finished.
|
||||||
# See also http://permalink.gmane.org/gmane.comp.gnu.binutils/71347
|
# See also http://permalink.gmane.org/gmane.comp.gnu.binutils/71347
|
||||||
if typ == b'GNU_RELRO':
|
if typ == 'GNU_RELRO':
|
||||||
have_gnu_relro = True
|
have_gnu_relro = True
|
||||||
|
|
||||||
have_bindnow = False
|
have_bindnow = False
|
||||||
p = subprocess.Popen([READELF_CMD, '-d', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
p = subprocess.Popen([READELF_CMD, '-d', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
|
||||||
(stdout, stderr) = p.communicate()
|
(stdout, stderr) = p.communicate()
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
raise IOError('Error opening file')
|
raise IOError('Error opening file')
|
||||||
for line in stdout.split(b'\n'):
|
for line in stdout.splitlines():
|
||||||
tokens = line.split()
|
tokens = line.split()
|
||||||
if len(tokens)>1 and tokens[1] == b'(BIND_NOW)' or (len(tokens)>2 and tokens[1] == b'(FLAGS)' and b'BIND_NOW' in tokens[2]):
|
if len(tokens)>1 and tokens[1] == '(BIND_NOW)' or (len(tokens)>2 and tokens[1] == '(FLAGS)' and 'BIND_NOW' in tokens[2]):
|
||||||
have_bindnow = True
|
have_bindnow = True
|
||||||
return have_gnu_relro and have_bindnow
|
return have_gnu_relro and have_bindnow
|
||||||
|
|
||||||
|
@ -105,13 +105,13 @@ def check_ELF_Canary(executable):
|
||||||
'''
|
'''
|
||||||
Check for use of stack canary
|
Check for use of stack canary
|
||||||
'''
|
'''
|
||||||
p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
|
||||||
(stdout, stderr) = p.communicate()
|
(stdout, stderr) = p.communicate()
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
raise IOError('Error opening file')
|
raise IOError('Error opening file')
|
||||||
ok = False
|
ok = False
|
||||||
for line in stdout.split(b'\n'):
|
for line in stdout.splitlines():
|
||||||
if b'__stack_chk_fail' in line:
|
if '__stack_chk_fail' in line:
|
||||||
ok = True
|
ok = True
|
||||||
return ok
|
return ok
|
||||||
|
|
||||||
|
@ -121,13 +121,13 @@ def get_PE_dll_characteristics(executable):
|
||||||
Returns a tuple (arch,bits) where arch is 'i386:x86-64' or 'i386'
|
Returns a tuple (arch,bits) where arch is 'i386:x86-64' or 'i386'
|
||||||
and bits is the DllCharacteristics value.
|
and bits is the DllCharacteristics value.
|
||||||
'''
|
'''
|
||||||
p = subprocess.Popen([OBJDUMP_CMD, '-x', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
p = subprocess.Popen([OBJDUMP_CMD, '-x', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
|
||||||
(stdout, stderr) = p.communicate()
|
(stdout, stderr) = p.communicate()
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
raise IOError('Error opening file')
|
raise IOError('Error opening file')
|
||||||
arch = ''
|
arch = ''
|
||||||
bits = 0
|
bits = 0
|
||||||
for line in stdout.split('\n'):
|
for line in stdout.splitlines():
|
||||||
tokens = line.split()
|
tokens = line.split()
|
||||||
if len(tokens)>=2 and tokens[0] == 'architecture:':
|
if len(tokens)>=2 and tokens[0] == 'architecture:':
|
||||||
arch = tokens[1].rstrip(',')
|
arch = tokens[1].rstrip(',')
|
||||||
|
|
|
@ -46,28 +46,28 @@ 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 = {
|
||||||
b'_edata', b'_end', b'_init', b'__bss_start', b'_fini', b'_IO_stdin_used'
|
'_edata', '_end', '_init', '__bss_start', '_fini', '_IO_stdin_used'
|
||||||
}
|
}
|
||||||
READELF_CMD = os.getenv('READELF', '/usr/bin/readelf')
|
READELF_CMD = os.getenv('READELF', '/usr/bin/readelf')
|
||||||
CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt')
|
CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt')
|
||||||
# Allowed NEEDED libraries
|
# Allowed NEEDED libraries
|
||||||
ALLOWED_LIBRARIES = {
|
ALLOWED_LIBRARIES = {
|
||||||
# bitcoind and bitcoin-qt
|
# bitcoind and bitcoin-qt
|
||||||
b'libgcc_s.so.1', # GCC base support
|
'libgcc_s.so.1', # GCC base support
|
||||||
b'libc.so.6', # C library
|
'libc.so.6', # C library
|
||||||
b'libpthread.so.0', # threading
|
'libpthread.so.0', # threading
|
||||||
b'libanl.so.1', # DNS resolve
|
'libanl.so.1', # DNS resolve
|
||||||
b'libm.so.6', # math library
|
'libm.so.6', # math library
|
||||||
b'librt.so.1', # real-time (clock)
|
'librt.so.1', # real-time (clock)
|
||||||
b'ld-linux-x86-64.so.2', # 64-bit dynamic linker
|
'ld-linux-x86-64.so.2', # 64-bit dynamic linker
|
||||||
b'ld-linux.so.2', # 32-bit dynamic linker
|
'ld-linux.so.2', # 32-bit dynamic linker
|
||||||
# bitcoin-qt only
|
# bitcoin-qt only
|
||||||
b'libX11-xcb.so.1', # part of X11
|
'libX11-xcb.so.1', # part of X11
|
||||||
b'libX11.so.6', # part of X11
|
'libX11.so.6', # part of X11
|
||||||
b'libxcb.so.1', # part of X11
|
'libxcb.so.1', # part of X11
|
||||||
b'libfontconfig.so.1', # font support
|
'libfontconfig.so.1', # font support
|
||||||
b'libfreetype.so.6', # font parsing
|
'libfreetype.so.6', # font parsing
|
||||||
b'libdl.so.2' # programming interface to dynamic linker
|
'libdl.so.2' # programming interface to dynamic linker
|
||||||
}
|
}
|
||||||
|
|
||||||
class CPPFilt(object):
|
class CPPFilt(object):
|
||||||
|
@ -77,10 +77,10 @@ class CPPFilt(object):
|
||||||
Use a pipe to the 'c++filt' command.
|
Use a pipe to the 'c++filt' command.
|
||||||
'''
|
'''
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
self.proc = subprocess.Popen(CPPFILT_CMD, stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
|
||||||
|
|
||||||
def __call__(self, mangled):
|
def __call__(self, mangled):
|
||||||
self.proc.stdin.write(mangled + b'\n')
|
self.proc.stdin.write(mangled + '\n')
|
||||||
self.proc.stdin.flush()
|
self.proc.stdin.flush()
|
||||||
return self.proc.stdout.readline().rstrip()
|
return self.proc.stdout.readline().rstrip()
|
||||||
|
|
||||||
|
@ -94,43 +94,43 @@ def read_symbols(executable, imports=True):
|
||||||
Parse an ELF executable and return a list of (symbol,version) tuples
|
Parse an ELF executable and return a list of (symbol,version) tuples
|
||||||
for dynamic, imported symbols.
|
for dynamic, imported symbols.
|
||||||
'''
|
'''
|
||||||
p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
p = subprocess.Popen([READELF_CMD, '--dyn-syms', '-W', executable], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
|
||||||
(stdout, stderr) = p.communicate()
|
(stdout, stderr) = p.communicate()
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip()))
|
raise IOError('Could not read symbols for %s: %s' % (executable, stderr.strip()))
|
||||||
syms = []
|
syms = []
|
||||||
for line in stdout.split(b'\n'):
|
for line in stdout.splitlines():
|
||||||
line = line.split()
|
line = line.split()
|
||||||
if len(line)>7 and re.match(b'[0-9]+:$', line[0]):
|
if len(line)>7 and re.match('[0-9]+:$', line[0]):
|
||||||
(sym, _, version) = line[7].partition(b'@')
|
(sym, _, version) = line[7].partition('@')
|
||||||
is_import = line[6] == b'UND'
|
is_import = line[6] == 'UND'
|
||||||
if version.startswith(b'@'):
|
if version.startswith('@'):
|
||||||
version = version[1:]
|
version = version[1:]
|
||||||
if is_import == imports:
|
if is_import == imports:
|
||||||
syms.append((sym, version))
|
syms.append((sym, version))
|
||||||
return syms
|
return syms
|
||||||
|
|
||||||
def check_version(max_versions, version):
|
def check_version(max_versions, version):
|
||||||
if b'_' in version:
|
if '_' in version:
|
||||||
(lib, _, ver) = version.rpartition(b'_')
|
(lib, _, ver) = version.rpartition('_')
|
||||||
else:
|
else:
|
||||||
lib = version
|
lib = version
|
||||||
ver = '0'
|
ver = '0'
|
||||||
ver = tuple([int(x) for x in ver.split(b'.')])
|
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
|
||||||
return ver <= max_versions[lib]
|
return ver <= max_versions[lib]
|
||||||
|
|
||||||
def read_libraries(filename):
|
def read_libraries(filename):
|
||||||
p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
|
p = subprocess.Popen([READELF_CMD, '-d', '-W', filename], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True)
|
||||||
(stdout, stderr) = p.communicate()
|
(stdout, stderr) = p.communicate()
|
||||||
if p.returncode:
|
if p.returncode:
|
||||||
raise IOError('Error opening file')
|
raise IOError('Error opening file')
|
||||||
libraries = []
|
libraries = []
|
||||||
for line in stdout.split(b'\n'):
|
for line in stdout.splitlines():
|
||||||
tokens = line.split()
|
tokens = line.split()
|
||||||
if len(tokens)>2 and tokens[1] == b'(NEEDED)':
|
if len(tokens)>2 and tokens[1] == '(NEEDED)':
|
||||||
match = re.match(b'^Shared library: \[(.*)\]$', b' '.join(tokens[2:]))
|
match = re.match('^Shared library: \[(.*)\]$', ' '.join(tokens[2:]))
|
||||||
if match:
|
if match:
|
||||||
libraries.append(match.group(1))
|
libraries.append(match.group(1))
|
||||||
else:
|
else:
|
||||||
|
@ -144,18 +144,18 @@ if __name__ == '__main__':
|
||||||
# Check imported symbols
|
# Check imported symbols
|
||||||
for sym,version in read_symbols(filename, True):
|
for sym,version in read_symbols(filename, True):
|
||||||
if version and not check_version(MAX_VERSIONS, version):
|
if version and not check_version(MAX_VERSIONS, version):
|
||||||
print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym).decode('utf-8'), version.decode('utf-8')))
|
print('%s: symbol %s from unsupported version %s' % (filename, cppfilt(sym), version))
|
||||||
retval = 1
|
retval = 1
|
||||||
# Check exported symbols
|
# Check exported symbols
|
||||||
for sym,version in read_symbols(filename, False):
|
for sym,version in read_symbols(filename, False):
|
||||||
if sym in IGNORE_EXPORTS:
|
if sym in IGNORE_EXPORTS:
|
||||||
continue
|
continue
|
||||||
print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym).decode('utf-8')))
|
print('%s: export of symbol %s not allowed' % (filename, cppfilt(sym)))
|
||||||
retval = 1
|
retval = 1
|
||||||
# Check dependency libraries
|
# Check dependency libraries
|
||||||
for library_name in read_libraries(filename):
|
for library_name in read_libraries(filename):
|
||||||
if library_name not in ALLOWED_LIBRARIES:
|
if library_name not in ALLOWED_LIBRARIES:
|
||||||
print('%s: NEEDED library %s is not allowed' % (filename, library_name.decode('utf-8')))
|
print('%s: NEEDED library %s is not allowed' % (filename, library_name))
|
||||||
retval = 1
|
retval = 1
|
||||||
|
|
||||||
sys.exit(retval)
|
sys.exit(retval)
|
||||||
|
|
Loading…
Add table
Reference in a new issue