Merge bitcoin/bitcoin#22381: guix: Test security-check sanity before performing them (with macOS)

5b4703c6a7 guix: Test security-check sanity before performing them (Carl Dong)
6cf3345297 scripts: adjust test-symbol-check for guix release environment (fanquake)
1946b5f77c scripts: more robustly test macOS symbol checks (fanquake)
a8127b34bc build: Use and test PE binutils with --reloc-section (Carl Dong)
678348db51 guix: Patch binutils to add security-related disable flags (Carl Dong)
9fdc8afe11 devtools: Improve *-check.py tool detection (Carl Dong)
bda62eab38 ci: skip running the Linux test-security-check target for now (fanquake)
d6ef3543ae lint: Run mypy with --show-error-codes (Carl Dong)

Pull request description:

  This is #20980 rebased (to include the Boost Process fix), and with an additional commit (892d6897f1e613084aa0517a660eab2412308e6e) to fix running the `test-security-check` target for the macOS build. It should pass inside Guix, as well as when cross-compiling on Ubuntu, or building natively on macOS.

  Note that the `test-security-check` may output some warnings (similar too):
  ```bash
  ld: warning: passed two min versions (10.14, 11.4) for platform macOS. Using 11.4.
  ld: warning: passed two min versions (10.14, 11.4) for platform macOS. Using 11.4.
  ld: warning: passed two min versions (10.14, 10.14) for platform macOS. Using 10.14.
  ```
  but those can be ignored, and come about due to us passing `-platform_version` when `-mmacosx-version-min` is already part of `CC`.

  Guix builds:
  ```bash
  71ed0c7a13a4726300779ffc87f7d271086a2744c36896fe6dc51fe3dc33df2e  guix-build-5b4703c6a70d/output/aarch64-linux-gnu/SHA256SUMS.part
  9273980a17052c8ec45b77579781c14ab5d189fa25aa29907d5115513dd302b1  guix-build-5b4703c6a70d/output/aarch64-linux-gnu/bitcoin-5b4703c6a70d-aarch64-linux-gnu-debug.tar.gz
  9c042179af43c8896eb95a34294df15d4910308dcdba40b2010cd36e192938b8  guix-build-5b4703c6a70d/output/aarch64-linux-gnu/bitcoin-5b4703c6a70d-aarch64-linux-gnu.tar.gz
  1ceddecac113f50a952ba6a201cdcdb722e3dc804e663f219bfac8268ce42bf0  guix-build-5b4703c6a70d/output/arm-linux-gnueabihf/SHA256SUMS.part
  759597c4e925e75db4a2381c06cda9b9f4e4674c23436148676b31c9be05c7aa  guix-build-5b4703c6a70d/output/arm-linux-gnueabihf/bitcoin-5b4703c6a70d-arm-linux-gnueabihf-debug.tar.gz
  34e3b6beabaf8c95d7c2ca0d2c3ac4411766694ef43e00bd9783badbbaf045a7  guix-build-5b4703c6a70d/output/arm-linux-gnueabihf/bitcoin-5b4703c6a70d-arm-linux-gnueabihf.tar.gz
  e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855  guix-build-5b4703c6a70d/output/dist-archive/SKIPATTEST.TAG
  3664f6ceee7898caa374281fd877a7597fe491fa2e9f0c174c28d889d60b559c  guix-build-5b4703c6a70d/output/dist-archive/bitcoin-5b4703c6a70d.tar.gz
  d6bc35ba0750c1440bb32831b8c12cddee62f6dce10fec2650897444c2bf4748  guix-build-5b4703c6a70d/output/powerpc64-linux-gnu/SHA256SUMS.part
  a836edf6474ba0c16c19bb217549bac7936c1b44306ed512df58f607ee5568f2  guix-build-5b4703c6a70d/output/powerpc64-linux-gnu/bitcoin-5b4703c6a70d-powerpc64-linux-gnu-debug.tar.gz
  7cc91c6805d5069ca3bd1771e77d95f83eb184b137198cbf84d1d11d0a5c5afe  guix-build-5b4703c6a70d/output/powerpc64-linux-gnu/bitcoin-5b4703c6a70d-powerpc64-linux-gnu.tar.gz
  93b4cb7b83c4975120ad5de5a92f050f5760a2a3f2c37c204c647f5a581c924a  guix-build-5b4703c6a70d/output/powerpc64le-linux-gnu/SHA256SUMS.part
  2266e2c5d0dafa28c6c057ccfc1c439baeab1d714d8c3f64a83015d2827116d2  guix-build-5b4703c6a70d/output/powerpc64le-linux-gnu/bitcoin-5b4703c6a70d-powerpc64le-linux-gnu-debug.tar.gz
  85f41f42c319b83d049d6fd2e2278c07b40a1e28a2eac596427822c0eef9dc3f  guix-build-5b4703c6a70d/output/powerpc64le-linux-gnu/bitcoin-5b4703c6a70d-powerpc64le-linux-gnu.tar.gz
  1499ca9119926083d8c3714ca10d8d4c8d864cbeee8848fd8445b7a1d081222d  guix-build-5b4703c6a70d/output/riscv64-linux-gnu/SHA256SUMS.part
  1995fc1a2e45c49d4b0718aff5dcdac931917e8ae9e762fd23f1126abcecc248  guix-build-5b4703c6a70d/output/riscv64-linux-gnu/bitcoin-5b4703c6a70d-riscv64-linux-gnu-debug.tar.gz
  266889eb58429a470f0fd7bb123f2ae09b0aef86c47b0390938b3634a8f748a9  guix-build-5b4703c6a70d/output/riscv64-linux-gnu/bitcoin-5b4703c6a70d-riscv64-linux-gnu.tar.gz
  cdc3a0dcf80b110443dac5ddf8bc951001a776a651c898c5ea49bb2d487bfe29  guix-build-5b4703c6a70d/output/x86_64-apple-darwin18/SHA256SUMS.part
  8538d1eab96c97866b24546c453d95822f24cf9c6638b42ba523eb7aa441cb26  guix-build-5b4703c6a70d/output/x86_64-apple-darwin18/bitcoin-5b4703c6a70d-osx-unsigned.dmg
  d1b73133f1da68586b07292a8425f7f851e93f599c016376f23728c041cf39cc  guix-build-5b4703c6a70d/output/x86_64-apple-darwin18/bitcoin-5b4703c6a70d-osx-unsigned.tar.gz
  5ad94c5f8a5f29405955ff3ab35d137de1acc04398d6c8298fb187b57a6e316a  guix-build-5b4703c6a70d/output/x86_64-apple-darwin18/bitcoin-5b4703c6a70d-osx64.tar.gz
  8c6d7b3f847faa7b4d16ceecf228f26f146ea982615c1d7a00c57f9230a0c484  guix-build-5b4703c6a70d/output/x86_64-linux-gnu/SHA256SUMS.part
  d0a8c99750319ad8046cfa132a54e5c13a08351f94439ae9af0f8e5486c2c2ea  guix-build-5b4703c6a70d/output/x86_64-linux-gnu/bitcoin-5b4703c6a70d-x86_64-linux-gnu-debug.tar.gz
  d816bb26dd4b0e309f2f576b1cccc6d78743fb2f357daad2da09bb1177330971  guix-build-5b4703c6a70d/output/x86_64-linux-gnu/bitcoin-5b4703c6a70d-x86_64-linux-gnu.tar.gz
  65caaa7f648c7eab1eb82c3331a2ca25b8cd4fe41439de55604501e02571de55  guix-build-5b4703c6a70d/output/x86_64-w64-mingw32/SHA256SUMS.part
  5bf6f7328cbceb0db22a2d7babb07b60cb6dcc19a6db84a1698589b7f5173a06  guix-build-5b4703c6a70d/output/x86_64-w64-mingw32/bitcoin-5b4703c6a70d-win-unsigned.tar.gz
  7aabcb56115decef78d3797840b6e49dbc9b202d56f892490e92616fb06fec9e  guix-build-5b4703c6a70d/output/x86_64-w64-mingw32/bitcoin-5b4703c6a70d-win64-debug.zip
  2f369694648ff9dc5ca1261a1e5874b1c7408ccf2802f9caef56c1334e8a5b7c  guix-build-5b4703c6a70d/output/x86_64-w64-mingw32/bitcoin-5b4703c6a70d-win64-setup-unsigned.exe
  1c1f92513c4aad38419ff49a7b80bf10e6b1eca01ee8c5e3b2acd1768cf1e3d5  guix-build-5b4703c6a70d/output/x86_64-w64-mingw32/bitcoin-5b4703c6a70d-win64.zip
  ```

ACKs for top commit:
  hebasto:
    Approach ACK 5b4703c6a7.

Tree-SHA512: 2cd92a245ea64ef7176cf402a1fa5348a9421c30a4d30d01c950c48f6dcc15cf22ce69ffe1657be97e5fccc14bd933d64683c4439b695528ce3dc34d72dda927
This commit is contained in:
fanquake 2021-07-09 10:17:57 +08:00
commit 34d1d6a112
No known key found for this signature in database
GPG key ID: 2EEB9F5CC09526C1
12 changed files with 262 additions and 51 deletions

View file

@ -58,6 +58,7 @@ DIST_SHARE = \
BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \ BIN_CHECKS=$(top_srcdir)/contrib/devtools/symbol-check.py \
$(top_srcdir)/contrib/devtools/security-check.py \ $(top_srcdir)/contrib/devtools/security-check.py \
$(top_srcdir)/contrib/devtools/utils.py \
$(top_srcdir)/contrib/devtools/pixie.py $(top_srcdir)/contrib/devtools/pixie.py
WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/bitcoin.ico \ WINDOWS_PACKAGING = $(top_srcdir)/share/pixmaps/bitcoin.ico \
@ -366,14 +367,14 @@ clean-local: clean-docs
test-security-check: test-security-check:
if TARGET_DARWIN if TARGET_DARWIN
$(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_MACHO $(AM_V_at) CC='$(CC)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_MACHO
$(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_MACHO $(AM_V_at) CC='$(CC)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_MACHO
endif endif
if TARGET_WINDOWS if TARGET_WINDOWS
$(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_PE $(AM_V_at) CC='$(CC)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_PE
$(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_PE $(AM_V_at) CC='$(CC)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_PE
endif endif
if TARGET_LINUX if TARGET_LINUX
$(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_ELF $(AM_V_at) CC='$(CC)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-security-check.py TestSecurityChecks.test_ELF
$(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_ELF $(AM_V_at) CC='$(CC)' CPPFILT='$(CPPFILT)' $(PYTHON) $(top_srcdir)/contrib/devtools/test-symbol-check.py TestSymbolChecks.test_ELF
endif endif

View file

@ -13,5 +13,4 @@ export DEP_OPTS="DEBUG=1 MULTIPROCESS=1"
export GOAL="install" export GOAL="install"
export BITCOIN_CONFIG="--enable-debug CC=clang CXX=clang++" # Use clang to avoid OOM export BITCOIN_CONFIG="--enable-debug CC=clang CXX=clang++" # Use clang to avoid OOM
export TEST_RUNNER_ENV="BITCOIND=bitcoin-node" export TEST_RUNNER_ENV="BITCOIND=bitcoin-node"
export RUN_SECURITY_TESTS="true"
export PIP_PACKAGES="lief" export PIP_PACKAGES="lief"

View file

@ -900,6 +900,7 @@ if test x$use_hardening != xno; then
]) ])
fi fi
AX_CHECK_LINK_FLAG([[-Wl,--enable-reloc-section]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--enable-reloc-section"],, [[$LDFLAG_WERROR]])
AX_CHECK_LINK_FLAG([[-Wl,--dynamicbase]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--dynamicbase"],, [[$LDFLAG_WERROR]]) AX_CHECK_LINK_FLAG([[-Wl,--dynamicbase]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--dynamicbase"],, [[$LDFLAG_WERROR]])
AX_CHECK_LINK_FLAG([[-Wl,--nxcompat]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--nxcompat"],, [[$LDFLAG_WERROR]]) AX_CHECK_LINK_FLAG([[-Wl,--nxcompat]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--nxcompat"],, [[$LDFLAG_WERROR]])
AX_CHECK_LINK_FLAG([[-Wl,--high-entropy-va]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--high-entropy-va"],, [[$LDFLAG_WERROR]]) AX_CHECK_LINK_FLAG([[-Wl,--high-entropy-va]], [HARDENED_LDFLAGS="$HARDENED_LDFLAGS -Wl,--high-entropy-va"],, [[$LDFLAG_WERROR]])

View file

@ -12,12 +12,13 @@ Example usage:
''' '''
import subprocess import subprocess
import sys import sys
import os
from typing import List, Optional from typing import List, Optional
import lief import lief
import pixie import pixie
from utils import determine_wellknown_cmd
# Debian 8 (Jessie) EOL: 2020. https://wiki.debian.org/DebianReleases#Production_Releases # Debian 8 (Jessie) EOL: 2020. https://wiki.debian.org/DebianReleases#Production_Releases
# #
# - g++ version 4.9.2 (https://packages.debian.org/search?suite=jessie&arch=any&searchon=names&keywords=g%2B%2B) # - g++ version 4.9.2 (https://packages.debian.org/search?suite=jessie&arch=any&searchon=names&keywords=g%2B%2B)
@ -60,7 +61,6 @@ 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',
} }
CPPFILT_CMD = os.getenv('CPPFILT', '/usr/bin/c++filt')
# Allowed NEEDED libraries # Allowed NEEDED libraries
ELF_ALLOWED_LIBRARIES = { ELF_ALLOWED_LIBRARIES = {
@ -140,7 +140,7 @@ 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, universal_newlines=True) self.proc = subprocess.Popen(determine_wellknown_cmd('CPPFILT', 'c++filt'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True)
def __call__(self, mangled): def __call__(self, mangled):
self.proc.stdin.write(mangled + '\n') self.proc.stdin.write(mangled + '\n')

View file

@ -9,6 +9,8 @@ import os
import subprocess import subprocess
import unittest import unittest
from utils import determine_wellknown_cmd
def write_testcode(filename): def write_testcode(filename):
with open(filename, 'w', encoding="utf8") as f: with open(filename, 'w', encoding="utf8") as f:
f.write(''' f.write('''
@ -25,7 +27,7 @@ def clean_files(source, executable):
os.remove(executable) os.remove(executable)
def call_security_check(cc, source, executable, options): def call_security_check(cc, source, executable, options):
subprocess.run([cc,source,'-o',executable] + options, check=True) subprocess.run([*cc,source,'-o',executable] + options, check=True)
p = subprocess.run(['./contrib/devtools/security-check.py',executable], stdout=subprocess.PIPE, universal_newlines=True) p = subprocess.run(['./contrib/devtools/security-check.py',executable], stdout=subprocess.PIPE, universal_newlines=True)
return (p.returncode, p.stdout.rstrip()) return (p.returncode, p.stdout.rstrip())
@ -33,7 +35,7 @@ class TestSecurityChecks(unittest.TestCase):
def test_ELF(self): def test_ELF(self):
source = 'test1.c' source = 'test1.c'
executable = 'test1' executable = 'test1'
cc = 'gcc' cc = determine_wellknown_cmd('CC', 'gcc')
write_testcode(source) write_testcode(source)
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']), self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-zexecstack','-fno-stack-protector','-Wl,-znorelro','-no-pie','-fno-PIE', '-Wl,-z,separate-code']),
@ -54,18 +56,20 @@ class TestSecurityChecks(unittest.TestCase):
def test_PE(self): def test_PE(self):
source = 'test1.c' source = 'test1.c'
executable = 'test1.exe' executable = 'test1.exe'
cc = 'x86_64-w64-mingw32-gcc' cc = determine_wellknown_cmd('CC', 'x86_64-w64-mingw32-gcc')
write_testcode(source) write_testcode(source)
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--no-nxcompat','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']), self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--no-nxcompat','-Wl,--disable-reloc-section','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']),
(1, executable+': failed DYNAMIC_BASE HIGH_ENTROPY_VA NX RELOC_SECTION')) (1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA NX RELOC_SECTION'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']), self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--disable-reloc-section','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']),
(1, executable+': failed DYNAMIC_BASE HIGH_ENTROPY_VA RELOC_SECTION')) (1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA RELOC_SECTION'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']), self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va','-no-pie','-fno-PIE']),
(1, executable+': failed HIGH_ENTROPY_VA RELOC_SECTION')) (1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--high-entropy-va','-no-pie','-fno-PIE']), self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--no-dynamicbase','-Wl,--no-high-entropy-va','-pie','-fPIE']),
(1, executable+': failed RELOC_SECTION')) (1, executable+': failed PIE DYNAMIC_BASE HIGH_ENTROPY_VA')) # -pie -fPIE does nothing unless --dynamicbase is also supplied
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--dynamicbase','-Wl,--high-entropy-va','-pie','-fPIE']), self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--dynamicbase','-Wl,--no-high-entropy-va','-pie','-fPIE']),
(1, executable+': failed HIGH_ENTROPY_VA'))
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat','-Wl,--enable-reloc-section','-Wl,--dynamicbase','-Wl,--high-entropy-va','-pie','-fPIE']),
(0, '')) (0, ''))
clean_files(source, executable) clean_files(source, executable)
@ -73,7 +77,7 @@ class TestSecurityChecks(unittest.TestCase):
def test_MACHO(self): def test_MACHO(self):
source = 'test1.c' source = 'test1.c'
executable = 'test1' executable = 'test1'
cc = 'clang' cc = determine_wellknown_cmd('CC', 'clang')
write_testcode(source) write_testcode(source)
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fno-stack-protector']), self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie','-Wl,-flat_namespace','-Wl,-allow_stack_execute','-fno-stack-protector']),
@ -95,4 +99,3 @@ class TestSecurityChecks(unittest.TestCase):
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View file

@ -7,41 +7,51 @@ Test script for symbol-check.py
''' '''
import os import os
import subprocess import subprocess
from typing import List
import unittest import unittest
def call_symbol_check(cc, source, executable, options): from utils import determine_wellknown_cmd
subprocess.run([cc,source,'-o',executable] + options, check=True)
def call_symbol_check(cc: List[str], source, executable, options):
subprocess.run([*cc,source,'-o',executable] + options, check=True)
p = subprocess.run(['./contrib/devtools/symbol-check.py',executable], stdout=subprocess.PIPE, universal_newlines=True) p = subprocess.run(['./contrib/devtools/symbol-check.py',executable], stdout=subprocess.PIPE, universal_newlines=True)
os.remove(source) os.remove(source)
os.remove(executable) os.remove(executable)
return (p.returncode, p.stdout.rstrip()) return (p.returncode, p.stdout.rstrip())
def get_machine(cc: List[str]):
p = subprocess.run([*cc,'-dumpmachine'], stdout=subprocess.PIPE, universal_newlines=True)
return p.stdout.rstrip()
class TestSymbolChecks(unittest.TestCase): class TestSymbolChecks(unittest.TestCase):
def test_ELF(self): def test_ELF(self):
source = 'test1.c' source = 'test1.c'
executable = 'test1' executable = 'test1'
cc = 'gcc' cc = determine_wellknown_cmd('CC', 'gcc')
# renameat2 was introduced in GLIBC 2.28, so is newer than the upper limit # there's no way to do this test for RISC-V at the moment; we build for
# of glibc for all platforms # RISC-V in a glibc 2.27 envinonment and we allow all symbols from 2.27.
if 'riscv' in get_machine(cc):
self.skipTest("test not available for RISC-V")
# nextup was introduced in GLIBC 2.24, so is newer than our supported
# glibc (2.17), and available in our release build environment (2.24).
with open(source, 'w', encoding="utf8") as f: with open(source, 'w', encoding="utf8") as f:
f.write(''' f.write('''
#define _GNU_SOURCE #define _GNU_SOURCE
#include <stdio.h> #include <math.h>
#include <linux/fs.h>
int renameat2(int olddirfd, const char *oldpath, double nextup(double x);
int newdirfd, const char *newpath, unsigned int flags);
int main() int main()
{ {
renameat2(0, "test", 0, "test_", RENAME_EXCHANGE); nextup(3.14);
return 0; return 0;
} }
''') ''')
self.assertEqual(call_symbol_check(cc, source, executable, []), self.assertEqual(call_symbol_check(cc, source, executable, ['-lm']),
(1, executable + ': symbol renameat2 from unsupported version GLIBC_2.28\n' + (1, executable + ': symbol nextup from unsupported version GLIBC_2.24\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
@ -82,7 +92,7 @@ class TestSymbolChecks(unittest.TestCase):
def test_MACHO(self): def test_MACHO(self):
source = 'test1.c' source = 'test1.c'
executable = 'test1' executable = 'test1'
cc = 'clang' cc = determine_wellknown_cmd('CC', 'clang')
with open(source, 'w', encoding="utf8") as f: with open(source, 'w', encoding="utf8") as f:
f.write(''' f.write('''
@ -96,7 +106,7 @@ class TestSymbolChecks(unittest.TestCase):
''') ''')
self.assertEqual(call_symbol_check(cc, source, executable, ['-lexpat']), self.assertEqual(call_symbol_check(cc, source, executable, ['-lexpat', '-Wl,-platform_version','-Wl,macos', '-Wl,11.4', '-Wl,11.4']),
(1, 'libexpat.1.dylib is not in ALLOWED_LIBRARIES!\n' + (1, 'libexpat.1.dylib is not in ALLOWED_LIBRARIES!\n' +
f'{executable}: failed DYNAMIC_LIBRARIES MIN_OS SDK')) f'{executable}: failed DYNAMIC_LIBRARIES MIN_OS SDK'))
@ -113,7 +123,7 @@ class TestSymbolChecks(unittest.TestCase):
} }
''') ''')
self.assertEqual(call_symbol_check(cc, source, executable, ['-framework', 'CoreGraphics']), self.assertEqual(call_symbol_check(cc, source, executable, ['-framework', 'CoreGraphics', '-Wl,-platform_version','-Wl,macos', '-Wl,11.4', '-Wl,11.4']),
(1, f'{executable}: failed MIN_OS SDK')) (1, f'{executable}: failed MIN_OS SDK'))
source = 'test3.c' source = 'test3.c'
@ -126,13 +136,13 @@ class TestSymbolChecks(unittest.TestCase):
} }
''') ''')
self.assertEqual(call_symbol_check(cc, source, executable, ['-mmacosx-version-min=10.14']), self.assertEqual(call_symbol_check(cc, source, executable, ['-Wl,-platform_version','-Wl,macos', '-Wl,10.14', '-Wl,11.4']),
(1, f'{executable}: failed SDK')) (1, f'{executable}: failed SDK'))
def test_PE(self): def test_PE(self):
source = 'test1.c' source = 'test1.c'
executable = 'test1.exe' executable = 'test1.exe'
cc = 'x86_64-w64-mingw32-gcc' cc = determine_wellknown_cmd('CC', 'x86_64-w64-mingw32-gcc')
with open(source, 'w', encoding="utf8") as f: with open(source, 'w', encoding="utf8") as f:
f.write(''' f.write('''
@ -182,4 +192,3 @@ class TestSymbolChecks(unittest.TestCase):
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

22
contrib/devtools/utils.py Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env python3
# Copyright (c) 2021 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
'''
Common utility functions
'''
import shutil
import sys
import os
from typing import List
def determine_wellknown_cmd(envvar, progname) -> List[str]:
maybe_env = os.getenv(envvar)
maybe_which = shutil.which(progname)
if maybe_env:
return maybe_env.split(' ') # Well-known vars are often meant to be word-split
elif maybe_which:
return [ maybe_which ]
else:
sys.exit(f"{progname} not found")

View file

@ -299,10 +299,11 @@ mkdir -p "$DISTSRC"
# Build Bitcoin Core # Build Bitcoin Core
make --jobs="$JOBS" ${V:+V=1} make --jobs="$JOBS" ${V:+V=1}
# Perform basic ELF security checks on a series of executables. # Check that symbol/security checks tools are sane.
make test-security-check ${V:+V=1}
# Perform basic security checks on a series of executables.
make -C src --jobs=1 check-security ${V:+V=1} make -C src --jobs=1 check-security ${V:+V=1}
# Check that executables only contain allowed gcc, glibc and libstdc++ # Check that executables only contain allowed version symbols.
# version symbols for Linux distro back-compatibility.
make -C src --jobs=1 check-symbols ${V:+V=1} make -C src --jobs=1 check-symbols ${V:+V=1}
mkdir -p "$OUTDIR" mkdir -p "$OUTDIR"

View file

@ -80,6 +80,10 @@ http://www.linuxfromscratch.org/hlfs/view/development/chapter05/gcc-pass1.html"
(("-rpath=") "-rpath-link=")) (("-rpath=") "-rpath-link="))
#t)))))))) #t))))))))
(define (make-binutils-with-mingw-w64-disable-flags xbinutils)
(package-with-extra-patches xbinutils
(search-our-patches "binutils-mingw-w64-disable-flags.patch")))
(define (make-cross-toolchain target (define (make-cross-toolchain target
base-gcc-for-libc base-gcc-for-libc
base-kernel-headers base-kernel-headers
@ -168,7 +172,7 @@ desirable for building Bitcoin Core release binaries."
(define (make-mingw-pthreads-cross-toolchain target) (define (make-mingw-pthreads-cross-toolchain target)
"Create a cross-compilation toolchain package for TARGET" "Create a cross-compilation toolchain package for TARGET"
(let* ((xbinutils (cross-binutils target)) (let* ((xbinutils (make-binutils-with-mingw-w64-disable-flags (cross-binutils target)))
(pthreads-xlibc mingw-w64-x86_64-winpthreads) (pthreads-xlibc mingw-w64-x86_64-winpthreads)
(pthreads-xgcc (make-gcc-with-pthreads (pthreads-xgcc (make-gcc-with-pthreads
(cross-gcc target (cross-gcc target

View file

@ -0,0 +1,171 @@
Description: Add disable opposites to the security-related flags
Author: Stephen Kitt <skitt@debian.org>
This patch adds "no-" variants to disable the various security flags:
"no-dynamicbase", "no-nxcompat", "no-high-entropy-va", "disable-reloc-section".
--- a/ld/emultempl/pe.em
+++ b/ld/emultempl/pe.em
@@ -259,9 +261,11 @@
(OPTION_ENABLE_LONG_SECTION_NAMES + 1)
/* DLLCharacteristics flags. */
#define OPTION_DYNAMIC_BASE (OPTION_DISABLE_LONG_SECTION_NAMES + 1)
-#define OPTION_FORCE_INTEGRITY (OPTION_DYNAMIC_BASE + 1)
+#define OPTION_NO_DYNAMIC_BASE (OPTION_DYNAMIC_BASE + 1)
+#define OPTION_FORCE_INTEGRITY (OPTION_NO_DYNAMIC_BASE + 1)
#define OPTION_NX_COMPAT (OPTION_FORCE_INTEGRITY + 1)
-#define OPTION_NO_ISOLATION (OPTION_NX_COMPAT + 1)
+#define OPTION_NO_NX_COMPAT (OPTION_NX_COMPAT + 1)
+#define OPTION_NO_ISOLATION (OPTION_NO_NX_COMPAT + 1)
#define OPTION_NO_SEH (OPTION_NO_ISOLATION + 1)
#define OPTION_NO_BIND (OPTION_NO_SEH + 1)
#define OPTION_WDM_DRIVER (OPTION_NO_BIND + 1)
@@ -271,6 +275,7 @@
#define OPTION_NO_INSERT_TIMESTAMP (OPTION_INSERT_TIMESTAMP + 1)
#define OPTION_BUILD_ID (OPTION_NO_INSERT_TIMESTAMP + 1)
#define OPTION_ENABLE_RELOC_SECTION (OPTION_BUILD_ID + 1)
+#define OPTION_DISABLE_RELOC_SECTION (OPTION_ENABLE_RELOC_SECTION + 1)
static void
gld${EMULATION_NAME}_add_options
@@ -342,8 +347,10 @@
{"enable-long-section-names", no_argument, NULL, OPTION_ENABLE_LONG_SECTION_NAMES},
{"disable-long-section-names", no_argument, NULL, OPTION_DISABLE_LONG_SECTION_NAMES},
{"dynamicbase",no_argument, NULL, OPTION_DYNAMIC_BASE},
+ {"no-dynamicbase", no_argument, NULL, OPTION_NO_DYNAMIC_BASE},
{"forceinteg", no_argument, NULL, OPTION_FORCE_INTEGRITY},
{"nxcompat", no_argument, NULL, OPTION_NX_COMPAT},
+ {"no-nxcompat", no_argument, NULL, OPTION_NO_NX_COMPAT},
{"no-isolation", no_argument, NULL, OPTION_NO_ISOLATION},
{"no-seh", no_argument, NULL, OPTION_NO_SEH},
{"no-bind", no_argument, NULL, OPTION_NO_BIND},
@@ -351,6 +358,7 @@
{"tsaware", no_argument, NULL, OPTION_TERMINAL_SERVER_AWARE},
{"build-id", optional_argument, NULL, OPTION_BUILD_ID},
{"enable-reloc-section", no_argument, NULL, OPTION_ENABLE_RELOC_SECTION},
+ {"disable-reloc-section", no_argument, NULL, OPTION_DISABLE_RELOC_SECTION},
{NULL, no_argument, NULL, 0}
};
@@ -485,9 +494,12 @@
in object files\n"));
fprintf (file, _(" --dynamicbase Image base address may be relocated using\n\
address space layout randomization (ASLR)\n"));
+ fprintf (file, _(" --no-dynamicbase Image base address may not be relocated\n"));
fprintf (file, _(" --enable-reloc-section Create the base relocation table\n"));
+ fprintf (file, _(" --disable-reloc-section Disable the base relocation table\n"));
fprintf (file, _(" --forceinteg Code integrity checks are enforced\n"));
fprintf (file, _(" --nxcompat Image is compatible with data execution prevention\n"));
+ fprintf (file, _(" --no-nxcompat Image is not compatible with data execution prevention\n"));
fprintf (file, _(" --no-isolation Image understands isolation but do not isolate the image\n"));
fprintf (file, _(" --no-seh Image does not use SEH. No SE handler may\n\
be called in this image\n"));
@@ -862,12 +874,21 @@
case OPTION_ENABLE_RELOC_SECTION:
pe_dll_enable_reloc_section = 1;
break;
+ case OPTION_DISABLE_RELOC_SECTION:
+ pe_dll_enable_reloc_section = 0;
+ /* fall through */
+ case OPTION_NO_DYNAMIC_BASE:
+ pe_dll_characteristics &= ~IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
+ break;
case OPTION_FORCE_INTEGRITY:
pe_dll_characteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY;
break;
case OPTION_NX_COMPAT:
pe_dll_characteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
break;
+ case OPTION_NO_NX_COMPAT:
+ pe_dll_characteristics &= ~IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
+ break;
case OPTION_NO_ISOLATION:
pe_dll_characteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
break;
--- a/ld/emultempl/pep.em
+++ b/ld/emultempl/pep.em
@@ -237,9 +240,12 @@
OPTION_ENABLE_LONG_SECTION_NAMES,
OPTION_DISABLE_LONG_SECTION_NAMES,
OPTION_HIGH_ENTROPY_VA,
+ OPTION_NO_HIGH_ENTROPY_VA,
OPTION_DYNAMIC_BASE,
+ OPTION_NO_DYNAMIC_BASE,
OPTION_FORCE_INTEGRITY,
OPTION_NX_COMPAT,
+ OPTION_NO_NX_COMPAT,
OPTION_NO_ISOLATION,
OPTION_NO_SEH,
OPTION_NO_BIND,
@@ -248,7 +254,8 @@
OPTION_NO_INSERT_TIMESTAMP,
OPTION_TERMINAL_SERVER_AWARE,
OPTION_BUILD_ID,
- OPTION_ENABLE_RELOC_SECTION
+ OPTION_ENABLE_RELOC_SECTION,
+ OPTION_DISABLE_RELOC_SECTION
};
static void
@@ -315,9 +322,12 @@
{"enable-long-section-names", no_argument, NULL, OPTION_ENABLE_LONG_SECTION_NAMES},
{"disable-long-section-names", no_argument, NULL, OPTION_DISABLE_LONG_SECTION_NAMES},
{"high-entropy-va", no_argument, NULL, OPTION_HIGH_ENTROPY_VA},
+ {"no-high-entropy-va", no_argument, NULL, OPTION_NO_HIGH_ENTROPY_VA},
{"dynamicbase",no_argument, NULL, OPTION_DYNAMIC_BASE},
+ {"no-dynamicbase", no_argument, NULL, OPTION_NO_DYNAMIC_BASE},
{"forceinteg", no_argument, NULL, OPTION_FORCE_INTEGRITY},
{"nxcompat", no_argument, NULL, OPTION_NX_COMPAT},
+ {"no-nxcompat", no_argument, NULL, OPTION_NO_NX_COMPAT},
{"no-isolation", no_argument, NULL, OPTION_NO_ISOLATION},
{"no-seh", no_argument, NULL, OPTION_NO_SEH},
{"no-bind", no_argument, NULL, OPTION_NO_BIND},
@@ -327,6 +337,7 @@
{"no-insert-timestamp", no_argument, NULL, OPTION_NO_INSERT_TIMESTAMP},
{"build-id", optional_argument, NULL, OPTION_BUILD_ID},
{"enable-reloc-section", no_argument, NULL, OPTION_ENABLE_RELOC_SECTION},
+ {"disable-reloc-section", no_argument, NULL, OPTION_DISABLE_RELOC_SECTION},
{NULL, no_argument, NULL, 0}
};
@@ -448,11 +461,15 @@
in object files\n"));
fprintf (file, _(" --high-entropy-va Image is compatible with 64-bit address space\n\
layout randomization (ASLR)\n"));
+ fprintf (file, _(" --no-high-entropy-va Image is not compatible with 64-bit ASLR\n"));
fprintf (file, _(" --dynamicbase Image base address may be relocated using\n\
address space layout randomization (ASLR)\n"));
+ fprintf (file, _(" --no-dynamicbase Image base address may not be relocated\n"));
fprintf (file, _(" --enable-reloc-section Create the base relocation table\n"));
+ fprintf (file, _(" --disable-reloc-section Disable the base relocation table\n"));
fprintf (file, _(" --forceinteg Code integrity checks are enforced\n"));
fprintf (file, _(" --nxcompat Image is compatible with data execution prevention\n"));
+ fprintf (file, _(" --no-nxcompat Image is not compatible with data execution prevention\n"));
fprintf (file, _(" --no-isolation Image understands isolation but do not isolate the image\n"));
fprintf (file, _(" --no-seh Image does not use SEH; no SE handler may\n\
be called in this image\n"));
@@ -809,12 +826,24 @@
case OPTION_ENABLE_RELOC_SECTION:
pep_dll_enable_reloc_section = 1;
break;
+ case OPTION_DISABLE_RELOC_SECTION:
+ pep_dll_enable_reloc_section = 0;
+ /* fall through */
+ case OPTION_NO_DYNAMIC_BASE:
+ pe_dll_characteristics &= ~IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE;
+ /* fall through */
+ case OPTION_NO_HIGH_ENTROPY_VA:
+ pe_dll_characteristics &= ~IMAGE_DLL_CHARACTERISTICS_HIGH_ENTROPY_VA;
+ break;
case OPTION_FORCE_INTEGRITY:
pe_dll_characteristics |= IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY;
break;
case OPTION_NX_COMPAT:
pe_dll_characteristics |= IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
break;
+ case OPTION_NO_NX_COMPAT:
+ pe_dll_characteristics &= ~IMAGE_DLL_CHARACTERISTICS_NX_COMPAT;
+ break;
case OPTION_NO_ISOLATION:
pe_dll_characteristics |= IMAGE_DLLCHARACTERISTICS_NO_ISOLATION;
break;

View file

@ -814,23 +814,23 @@ clean-local:
check-symbols: $(bin_PROGRAMS) check-symbols: $(bin_PROGRAMS)
if TARGET_DARWIN if TARGET_DARWIN
@echo "Checking macOS dynamic libraries..." @echo "Checking macOS dynamic libraries..."
$(AM_V_at) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS) $(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
endif endif
if TARGET_WINDOWS if TARGET_WINDOWS
@echo "Checking Windows dynamic libraries..." @echo "Checking Windows dynamic libraries..."
$(AM_V_at) OBJDUMP=$(OBJDUMP) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS) $(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
endif endif
if TARGET_LINUX if TARGET_LINUX
@echo "Checking glibc back compat..." @echo "Checking glibc back compat..."
$(AM_V_at) CPPFILT=$(CPPFILT) $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS) $(AM_V_at) CPPFILT='$(CPPFILT)' $(PYTHON) $(top_srcdir)/contrib/devtools/symbol-check.py $(bin_PROGRAMS)
endif endif
check-security: $(bin_PROGRAMS) check-security: $(bin_PROGRAMS)
if HARDEN if HARDEN
@echo "Checking binary security..." @echo "Checking binary security..."
$(AM_V_at) OBJDUMP=$(OBJDUMP) OTOOL=$(OTOOL) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS) $(AM_V_at) $(PYTHON) $(top_srcdir)/contrib/devtools/security-check.py $(bin_PROGRAMS)
endif endif
libbitcoin_ipc_mpgen_input = \ libbitcoin_ipc_mpgen_input = \

View file

@ -102,7 +102,7 @@ if ! PYTHONWARNINGS="ignore" flake8 --ignore=B,C,E,F,I,N,W --select=$(IFS=","; e
EXIT_CODE=1 EXIT_CODE=1
fi fi
if ! mypy --ignore-missing-imports $(git ls-files "test/functional/*.py" "contrib/devtools/*.py"); then if ! mypy --ignore-missing-imports --show-error-codes $(git ls-files "test/functional/*.py" "contrib/devtools/*.py"); then
EXIT_CODE=1 EXIT_CODE=1
fi fi