Implement symbol namespace check using new Python testing framework.
This commit is contained in:
parent
ff61e1d51d
commit
e4c368ee49
14
Makefile
14
Makefile
@ -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:
|
||||||
|
@ -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))
|
|
@ -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')
|
||||||
|
@ -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():
|
||||||
|
50
test/test_symbol_namespace.py
Normal file
50
test/test_symbol_namespace.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user