Implement symbol namespace check using new Python testing framework.

This commit is contained in:
Douglas Stebila 2019-02-14 11:29:49 -05:00
parent ff61e1d51d
commit e4c368ee49
5 changed files with 55 additions and 56 deletions

View File

@ -121,8 +121,6 @@ help:
@echo "make run-testvectors SCHEME=scheme Run testvector checks for SCHEME" @echo "make run-testvectors SCHEME=scheme Run testvector checks for SCHEME"
@echo "make run-testvectors-all Run all testvector checks" @echo "make run-testvectors-all Run all testvector checks"
@echo "make run-sanitizer-all Run address sanitizer for all schemes" @echo "make run-sanitizer-all Run address sanitizer for all schemes"
@echo "make run-symbol-namespace SCHEME=scheme Run symbol namespace checks for SCHEME"
@echo "make run-symbol-namespace-all Run all symbol namespace checks"
@echo "make run-valgrind SCHEME=scheme Run valgrind checks for SCHEME" @echo "make run-valgrind SCHEME=scheme Run valgrind checks for SCHEME"
@echo "make run-valgrind-all Run valgrind checks all schemes" @echo "make run-valgrind-all Run valgrind checks all schemes"
@echo "make clean Clean up the bin/ folder" @echo "make clean Clean up the bin/ folder"
@ -155,22 +153,12 @@ run-valgrind-all:
run-testvectors: test/check_tvectors.py | require_scheme run-testvectors: test/check_tvectors.py | require_scheme
python3 test/check_tvectors.py $(SCHEME) || exit 1; \ python3 test/check_tvectors.py $(SCHEME) || exit 1; \
.PHONY: run-symbol-namespace
run-symbol-namespace: test/check_symbol_namespace.py | require_scheme
python3 test/check_symbol_namespace.py $(SCHEME) || exit 1; \
.PHONY: run-testvectors-all .PHONY: run-testvectors-all
run-testvectors-all: test/check_tvectors.py run-testvectors-all: test/check_tvectors.py
@for scheme in $(ALL_SCHEMES); do \ @for scheme in $(ALL_SCHEMES); do \
python3 test/check_tvectors.py $$scheme || exit 1; \ python3 test/check_tvectors.py $$scheme || exit 1; \
done done
.PHONY: run-symbol-namespace-all
run-symbol-namespace-all:
@for scheme in $(ALL_SCHEMES); do \
python3 test/check_symbol_namespace.py $$scheme || exit 1; \
done
.PHONY: run-functest-all .PHONY: run-functest-all
run-functest-all: functest-all run-functest-all: functest-all
@for functest in $$(find bin/ -maxdepth 1 -name 'functest_*' -not -type d) ; do \ @for functest in $$(find bin/ -maxdepth 1 -name 'functest_*' -not -type d) ; do \
@ -196,7 +184,7 @@ check-license-files:
done done
.PHONY: test-all .PHONY: test-all
test-all: run-functest-all run-valgrind-all run-sanitizer-all run-testvectors-all run-symbol-namespace-all test-all: run-functest-all run-valgrind-all run-sanitizer-all run-testvectors-all
.PHONY: tidy-all .PHONY: tidy-all
tidy-all: tidy-all:

View File

@ -1,43 +0,0 @@
import subprocess
import sys
"""
For a given SCHEME, this script verifies that all exported symbols are properly
namespaced, i.e., all start with "PQCLEAN_SCHEMENAME_"
"""
if sys.platform != 'linux':
print("This test is not supported on non-Linux platforms")
exit(0)
if len(sys.argv) != 2:
print("Provide a scheme name (e.g. crypto_kem/kyber768) as argv[1]")
exit(1)
SCHEME = sys.argv[1]
SCHEMEFULL = SCHEME.replace('/', '_') # e.g. crypto_kem_kyber768
SCHEMESHORT = SCHEME.split('/')[1].upper()
namespace = "PQCLEAN_{}_".format(SCHEMESHORT).replace('-', '')
# TODO can we do this using object files instead, to preserve file origin?
sharedlib = "bin/shared_{}_clean.so".format(SCHEMEFULL)
subprocess.run(["make", sharedlib, "SCHEME={}".format(SCHEME)])
p = subprocess.run(["nm", "-D", sharedlib], stdout=subprocess.PIPE)
symbols = p.stdout.decode('utf-8').strip().split("\n")
non_namespaced = []
for symbolstr in symbols:
*_, symtype, symbol = symbolstr.split()
if symtype in 'TR':
if not symbol.startswith(namespace):
non_namespaced.append(symbol)
if non_namespaced:
print("! Not all symbols were properly namespaced.", file=sys.stderr)
print("! Missing namespace literal {}".format(namespace), file=sys.stderr)
for symbol in non_namespaced:
print("\t{}".format(symbol), file=sys.stderr)
sys.exit(1)
else:
print("Checking {} succeeded".format(SCHEME))

View File

@ -11,3 +11,4 @@ def run_subprocess(command, working_dir, expected_returncode = 0):
) )
print(result.stdout.decode('utf-8')) print(result.stdout.decode('utf-8'))
assert(result.returncode == expected_returncode) assert(result.returncode == expected_returncode)
return result.stdout.decode('utf-8')

View File

@ -10,6 +10,9 @@ class Scheme:
def path(self, base='..'): def path(self, base='..'):
return os.path.join(base, 'crypto_' + self.type, self.name) return os.path.join(base, 'crypto_' + self.type, self.name)
def namespace_prefix(self):
return 'PQCLEAN_{}_'.format(self.name.upper()).replace('-', '')
@staticmethod @staticmethod
def by_name(scheme_name): def by_name(scheme_name):
for scheme in Scheme.all_schemes(): for scheme in Scheme.all_schemes():

View File

@ -0,0 +1,50 @@
"""
Checks that the all exported symbols are properly namespaced, i.e., all start with "PQCLEAN_SCHEMENAME_".
"""
import os
import pqclean
import helpers
import sys
import unittest
def test_symbol_namespace():
if sys.platform not in ['linux', 'darwin']: raise unittest.SkipTest()
for scheme in pqclean.Scheme.all_schemes():
for implementation in scheme.implementations:
yield check_symbol_namespace, scheme.name, implementation.name
def check_symbol_namespace(scheme_name, implementation_name):
implementation = pqclean.Implementation.by_name(scheme_name, implementation_name)
helpers.run_subprocess(
['make', 'clean'],
implementation.path()
)
helpers.run_subprocess(
['make'],
implementation.path()
)
out = helpers.run_subprocess(
['nm', '-g', 'lib{}_{}.a'.format(scheme_name, implementation_name)],
implementation.path()
)
lines = out.strip().split("\n")
symbols = []
for line in lines:
if ' T ' in line or ' D ' in line or ' S ' in line:
symbols.append(line)
namespace = implementation.scheme.namespace_prefix()
non_namespaced = []
for symbolstr in symbols:
*_, symtype, symbol = symbolstr.split()
if symtype in 'TR':
if not symbol.startswith(namespace) and not symbol.startswith('_' + namespace):
non_namespaced.append(symbol)
if non_namespaced:
print("Missing namespace literal {}".format(namespace))
for symbol in non_namespaced:
print("\ttype: {}, symbol: {}".format(symtype, symbol))
assert(False)