Changeset View
Changeset View
Standalone View
Standalone View
contrib/devtools/test-security-check.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2015-2017 The Bitcoin Core developers | # Copyright (c) 2015-2017 The Bitcoin Core developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
''' | """ | ||||
Test script for security-check.py | Test script for security-check.py | ||||
''' | """ | ||||
import os | import os | ||||
import subprocess | import subprocess | ||||
import unittest | import unittest | ||||
from utils import determine_wellknown_cmd | 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(""" | ||||
#include <stdio.h> | #include <stdio.h> | ||||
int main() | int main() | ||||
{ | { | ||||
printf("the quick brown fox jumps over the lazy god\\n"); | printf("the quick brown fox jumps over the lazy god\\n"); | ||||
return 0; | return 0; | ||||
} | } | ||||
''') | """) | ||||
def clean_files(source, executable): | def clean_files(source, executable): | ||||
os.remove(source) | os.remove(source) | ||||
os.remove(executable) | os.remove(executable) | ||||
def call_security_check(cc, source, executable, options): | def call_security_check(cc, source, executable, options): | ||||
subprocess.check_call([*cc, source, '-o', executable] + options) | subprocess.check_call([*cc, source, "-o", executable] + options) | ||||
p = subprocess.Popen(['./security-check.py', executable], stdout=subprocess.PIPE, | p = subprocess.Popen( | ||||
stderr=subprocess.PIPE, stdin=subprocess.PIPE, universal_newlines=True) | ["./security-check.py", executable], | ||||
stdout=subprocess.PIPE, | |||||
stderr=subprocess.PIPE, | |||||
stdin=subprocess.PIPE, | |||||
universal_newlines=True, | |||||
) | |||||
(stdout, stderr) = p.communicate() | (stdout, stderr) = p.communicate() | ||||
return (p.returncode, stdout.rstrip()) | return (p.returncode, stdout.rstrip()) | ||||
class TestSecurityChecks(unittest.TestCase): | class TestSecurityChecks(unittest.TestCase): | ||||
def test_ELF(self): | def test_ELF(self): | ||||
source = 'test1.c' | source = "test1.c" | ||||
executable = 'test1' | executable = "test1" | ||||
cc = determine_wellknown_cmd('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( | ||||
(1, executable + ': failed PIE NX RELRO Canary')) | call_security_check( | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack', '-fno-stack-protector', '-Wl,-znorelro', '-no-pie', '-fno-PIE', '-Wl,-z,separate-code']), | cc, | ||||
(1, executable + ': failed PIE RELRO Canary')) | source, | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack', '-fstack-protector-all', '-Wl,-znorelro', '-no-pie', '-fno-PIE', '-Wl,-z,separate-code']), | executable, | ||||
(1, executable + ': failed PIE RELRO')) | [ | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack', '-fstack-protector-all', '-Wl,-znorelro', '-pie', '-fPIE', '-Wl,-z,separate-code']), | "-Wl,-zexecstack", | ||||
(1, executable + ': failed RELRO')) | "-fno-stack-protector", | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack', '-fstack-protector-all', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,noseparate-code']), | "-Wl,-znorelro", | ||||
(1, executable + ': failed separate_code')) | "-no-pie", | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-znoexecstack', '-fstack-protector-all', '-Wl,-zrelro', '-Wl,-z,now', '-pie', '-fPIE', '-Wl,-z,separate-code']), | "-fno-PIE", | ||||
(0, '')) | "-Wl,-z,separate-code", | ||||
], | |||||
), | |||||
(1, executable + ": failed PIE NX RELRO Canary"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, | |||||
source, | |||||
executable, | |||||
[ | |||||
"-Wl,-znoexecstack", | |||||
"-fno-stack-protector", | |||||
"-Wl,-znorelro", | |||||
"-no-pie", | |||||
"-fno-PIE", | |||||
"-Wl,-z,separate-code", | |||||
], | |||||
), | |||||
(1, executable + ": failed PIE RELRO Canary"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, | |||||
source, | |||||
executable, | |||||
[ | |||||
"-Wl,-znoexecstack", | |||||
"-fstack-protector-all", | |||||
"-Wl,-znorelro", | |||||
"-no-pie", | |||||
"-fno-PIE", | |||||
"-Wl,-z,separate-code", | |||||
], | |||||
), | |||||
(1, executable + ": failed PIE RELRO"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, | |||||
source, | |||||
executable, | |||||
[ | |||||
"-Wl,-znoexecstack", | |||||
"-fstack-protector-all", | |||||
"-Wl,-znorelro", | |||||
"-pie", | |||||
"-fPIE", | |||||
"-Wl,-z,separate-code", | |||||
], | |||||
), | |||||
(1, executable + ": failed RELRO"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, | |||||
source, | |||||
executable, | |||||
[ | |||||
"-Wl,-znoexecstack", | |||||
"-fstack-protector-all", | |||||
"-Wl,-zrelro", | |||||
"-Wl,-z,now", | |||||
"-pie", | |||||
"-fPIE", | |||||
"-Wl,-z,noseparate-code", | |||||
], | |||||
), | |||||
(1, executable + ": failed separate_code"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, | |||||
source, | |||||
executable, | |||||
[ | |||||
"-Wl,-znoexecstack", | |||||
"-fstack-protector-all", | |||||
"-Wl,-zrelro", | |||||
"-Wl,-z,now", | |||||
"-pie", | |||||
"-fPIE", | |||||
"-Wl,-z,separate-code", | |||||
], | |||||
), | |||||
(0, ""), | |||||
) | |||||
clean_files(source, executable) | clean_files(source, executable) | ||||
def test_PE(self): | def test_PE(self): | ||||
source = 'test1.c' | source = "test1.c" | ||||
executable = 'test1.exe' | executable = "test1.exe" | ||||
cc = determine_wellknown_cmd('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( | ||||
(1, executable + ': failed DYNAMIC_BASE HIGH_ENTROPY_VA NX RELOC_SECTION')) | call_security_check( | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat', '-Wl,--no-dynamicbase', '-Wl,--no-high-entropy-va', '-no-pie', '-fno-PIE']), | cc, | ||||
(1, executable + ': failed DYNAMIC_BASE HIGH_ENTROPY_VA RELOC_SECTION')) | source, | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat', '-Wl,--dynamicbase', '-Wl,--no-high-entropy-va', '-no-pie', '-fno-PIE']), | executable, | ||||
(1, executable + ': failed HIGH_ENTROPY_VA RELOC_SECTION')) | [ | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat', '-Wl,--dynamicbase', '-Wl,--high-entropy-va', '-no-pie', '-fno-PIE']), | "-Wl,--no-nxcompat", | ||||
(1, executable + ': failed RELOC_SECTION')) | "-Wl,--no-dynamicbase", | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,--nxcompat', '-Wl,--dynamicbase', '-Wl,--high-entropy-va', '-pie', '-fPIE']), | "-Wl,--no-high-entropy-va", | ||||
(0, '')) | "-no-pie", | ||||
"-fno-PIE", | |||||
], | |||||
), | |||||
(1, executable + ": failed 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", | |||||
], | |||||
), | |||||
(1, executable + ": failed 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", | |||||
], | |||||
), | |||||
(1, executable + ": failed HIGH_ENTROPY_VA RELOC_SECTION"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, | |||||
source, | |||||
executable, | |||||
[ | |||||
"-Wl,--nxcompat", | |||||
"-Wl,--dynamicbase", | |||||
"-Wl,--high-entropy-va", | |||||
"-no-pie", | |||||
"-fno-PIE", | |||||
], | |||||
), | |||||
(1, executable + ": failed RELOC_SECTION"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, | |||||
source, | |||||
executable, | |||||
[ | |||||
"-Wl,--nxcompat", | |||||
"-Wl,--dynamicbase", | |||||
"-Wl,--high-entropy-va", | |||||
"-pie", | |||||
"-fPIE", | |||||
], | |||||
), | |||||
(0, ""), | |||||
) | |||||
clean_files(source, executable) | clean_files(source, executable) | ||||
def test_MACHO(self): | def test_MACHO(self): | ||||
source = 'test1.c' | source = "test1.c" | ||||
executable = 'test1' | executable = "test1" | ||||
cc = determine_wellknown_cmd('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( | ||||
(1, executable + ': failed PIE NOUNDEFS NX Canary')) | call_security_check( | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie', '-Wl,-flat_namespace', '-Wl,-allow_stack_execute', '-fstack-protector-all']), | cc, | ||||
(1, executable + ': failed PIE NOUNDEFS NX')) | source, | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie', '-Wl,-flat_namespace', '-fstack-protector-all']), | executable, | ||||
(1, executable + ': failed PIE NOUNDEFS')) | [ | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-no_pie', '-fstack-protector-all']), | "-Wl,-no_pie", | ||||
(1, executable + ': failed PIE')) | "-Wl,-flat_namespace", | ||||
self.assertEqual(call_security_check(cc, source, executable, ['-Wl,-pie', '-fstack-protector-all']), | "-Wl,-allow_stack_execute", | ||||
(0, '')) | "-fno-stack-protector", | ||||
], | |||||
), | |||||
(1, executable + ": failed PIE NOUNDEFS NX Canary"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, | |||||
source, | |||||
executable, | |||||
[ | |||||
"-Wl,-no_pie", | |||||
"-Wl,-flat_namespace", | |||||
"-Wl,-allow_stack_execute", | |||||
"-fstack-protector-all", | |||||
], | |||||
), | |||||
(1, executable + ": failed PIE NOUNDEFS NX"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, | |||||
source, | |||||
executable, | |||||
["-Wl,-no_pie", "-Wl,-flat_namespace", "-fstack-protector-all"], | |||||
), | |||||
(1, executable + ": failed PIE NOUNDEFS"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, source, executable, ["-Wl,-no_pie", "-fstack-protector-all"] | |||||
), | |||||
(1, executable + ": failed PIE"), | |||||
) | |||||
self.assertEqual( | |||||
call_security_check( | |||||
cc, source, executable, ["-Wl,-pie", "-fstack-protector-all"] | |||||
), | |||||
(0, ""), | |||||
) | |||||
clean_files(source, executable) | clean_files(source, executable) | ||||
if __name__ == '__main__': | if __name__ == "__main__": | ||||
unittest.main() | unittest.main() |