From 017f0b73694150b66e4db6d4399e5e57fe7d7257 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 13:29:20 +0100 Subject: [PATCH 01/24] Stop supporting non-canonical case in mpi_write_string test data We're using the non-standard function strcasecmp() just so that the case of digits beyond 9 can be different in the library and in the test data. Use matching case in the test data, and use a standard function for the comparison. Signed-off-by: Gilles Peskine --- tests/suites/test_suite_bignum.function | 2 +- tests/suites/test_suite_bignum.misc.data | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/suites/test_suite_bignum.function b/tests/suites/test_suite_bignum.function index cefbfc37fb..2f00ff8d59 100644 --- a/tests/suites/test_suite_bignum.function +++ b/tests/suites/test_suite_bignum.function @@ -133,7 +133,7 @@ void mpi_read_write_string(int radix_X, char *input_X, int radix_A, TEST_ASSERT(sign_is_valid(&X)); TEST_ASSERT(mbedtls_mpi_write_string(&X, radix_A, str, output_size, &len) == result_write); if (result_write == 0) { - TEST_ASSERT(strcasecmp(str, input_A) == 0); + TEST_ASSERT(strcmp(str, input_A) == 0); TEST_ASSERT(str[len] == '!'); } } diff --git a/tests/suites/test_suite_bignum.misc.data b/tests/suites/test_suite_bignum.misc.data index 5eda4c11ad..11c5d7b664 100644 --- a/tests/suites/test_suite_bignum.misc.data +++ b/tests/suites/test_suite_bignum.misc.data @@ -56,10 +56,10 @@ Test mpi_read_write_string #5 (Illegal output radix) mpi_read_write_string:16:"-23":17:"-23":4:0:MBEDTLS_ERR_MPI_BAD_INPUT_DATA Test mpi_read_write_string #6 (Output radix of 15) -mpi_read_write_string:10:"29":15:"1e":100:0:0 +mpi_read_write_string:10:"29":15:"1E":100:0:0 Test mpi_read_write_string #7 -mpi_read_write_string:10:"56125680981752282334141896320372489490613963693556392520816017892111350604111697682705498319512049040516698827829292076808006940873974979584527073481012636016353913462376755556720019831187364993587901952757307830896531678727717924":16:"0941379d00fed1491fe15df284dfde4a142f68aa8d412023195cee66883e6290ffe703f4ea5963bf212713cee46b107c09182b5edcd955adac418bf4918e2889af48e1099d513830cec85c26ac1e158b52620e33ba8692f893efbb2f958b4424":200:0:0 +mpi_read_write_string:10:"56125680981752282334141896320372489490613963693556392520816017892111350604111697682705498319512049040516698827829292076808006940873974979584527073481012636016353913462376755556720019831187364993587901952757307830896531678727717924":16:"0941379D00FED1491FE15DF284DFDE4A142F68AA8D412023195CEE66883E6290FFE703F4EA5963BF212713CEE46B107C09182B5EDCD955ADAC418BF4918E2889AF48E1099D513830CEC85C26AC1E158B52620E33BA8692F893EFBB2F958B4424":200:0:0 Test mpi_read_write_string #8 (Empty MPI hex -> hex) mpi_read_write_string:16:"":16:"":4:0:0 From 6c607e5a559521cba2fc2c2b3223c461db9d31ef Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 13:34:50 +0100 Subject: [PATCH 02/24] Remove declarations of the nonstandard function strcasecmp It is no longer used. Signed-off-by: Gilles Peskine --- tests/suites/helpers.function | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function index 60eae9a64f..8f0cc2b5e9 100644 --- a/tests/suites/helpers.function +++ b/tests/suites/helpers.function @@ -24,18 +24,12 @@ typedef UINT8 uint8_t; typedef INT32 int32_t; typedef UINT32 uint32_t; -#define strncasecmp _strnicmp -#define strcasecmp _stricmp #else #include #endif #include -#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) || defined(__MINGW32__) -#include -#endif - #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) #include #endif From 187932639b28d89c10d093fb07fecdc561633a9d Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 13:18:58 +0100 Subject: [PATCH 03/24] Remove stdint.h substitute for older MSVC We now require at least Visual Studio 2013, which has stdint.h per https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2013/y4hta57s(v=vs.120) so the workaround to define C99 types on pre-C99 MSVC is no longer needed. Signed-off-by: Gilles Peskine --- tests/suites/helpers.function | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function index 8f0cc2b5e9..19647315c9 100644 --- a/tests/suites/helpers.function +++ b/tests/suites/helpers.function @@ -8,7 +8,9 @@ #include #include +#include #include +#include #if defined(MBEDTLS_ERROR_C) #include "mbedtls/error.h" @@ -19,17 +21,6 @@ #include "mbedtls/memory_buffer_alloc.h" #endif -#ifdef _MSC_VER -#include -typedef UINT8 uint8_t; -typedef INT32 int32_t; -typedef UINT32 uint32_t; -#else -#include -#endif - -#include - #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) #include #endif From bc3db2e30ad7975dc641dc32b1676e25dc9ee632 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sat, 3 Dec 2022 22:38:14 +0100 Subject: [PATCH 04/24] printf testing: exercise integer parsing in the test framework Signed-off-by: Gilles Peskine --- tests/suites/test_suite_platform_printf.data | 57 +++++++++++++++++++ .../test_suite_platform_printf.function | 25 ++++++++ 2 files changed, 82 insertions(+) create mode 100644 tests/suites/test_suite_platform_printf.data create mode 100644 tests/suites/test_suite_platform_printf.function diff --git a/tests/suites/test_suite_platform_printf.data b/tests/suites/test_suite_platform_printf.data new file mode 100644 index 0000000000..693f06938b --- /dev/null +++ b/tests/suites/test_suite_platform_printf.data @@ -0,0 +1,57 @@ +# The test cases for printf and integers have two purposes: they exercise +# the printf function family, and they exercise the passing of integers +# through the test framework. + +printf "%d", 0 +printf_int:"%d":0:"0" + +printf "%d", -0 +printf_int:"%d":-0:"0" + +printf "%d", 0x0 +printf_int:"%d":0x0:"0" + +printf "%d", 0x00 +printf_int:"%d":0x00:"0" + +printf "%d", 0x000000000000000000000000000000000000000000 +printf_int:"%d":0x000000000000000000000000000000000000000000:"0" + +printf "%d", -0x0 +printf_int:"%d":-0x0:"0" + +printf "%d", 1 +printf_int:"%d":1:"1" + +printf "%d", 0x1 +printf_int:"%d":0x1:"1" + +printf "%d", 0x0000000000000000000000000000000000000000001 +printf_int:"%d":0x0000000000000000000000000000000000000000001:"1" + +printf "%d", -1 +printf_int:"%d":-1:"-1" + +printf "%d", -0x1 +printf_int:"%d":-0x1:"-1" + +printf "%d", -0x0000000000000000000000000000000000000000001 +printf_int:"%d":-0x0000000000000000000000000000000000000000001:"-1" + +printf "%d", 2147483647 +printf_int:"%d":2147483647:"2147483647" + +printf "%d", 0x7fffffff +printf_int:"%d":0x7fffffff:"2147483647" + +printf "%d", -2147483647 +printf_int:"%d":-2147483647:"-2147483647" + +printf "%d", -0x7fffffff +printf_int:"%d":-0x7fffffff:"-2147483647" + +printf "%d", -2147483648 +printf_int:"%d":-2147483648:"-2147483648" + +printf "%d", -0x80000000 +printf_int:"%d":-0x80000000:"-2147483648" diff --git a/tests/suites/test_suite_platform_printf.function b/tests/suites/test_suite_platform_printf.function new file mode 100644 index 0000000000..51d3c6d060 --- /dev/null +++ b/tests/suites/test_suite_platform_printf.function @@ -0,0 +1,25 @@ +/* BEGIN_HEADER */ +#include "mbedtls/platform.h" + +#include +#include +#include +/* END_HEADER */ + +/* BEGIN_CASE */ +void printf_int(char *format, int x, char *result) +{ + char *output = NULL; + const size_t n = strlen(result); + + /* Nominal case: buffer just large enough */ + ASSERT_ALLOC(output, n + 1); + TEST_EQUAL(n, mbedtls_snprintf(output, n + 1, format, x)); + ASSERT_COMPARE(result, n + 1, output, n + 1); + mbedtls_free(output); + output = NULL; + +exit: + mbedtls_free(output); +} +/* END_CASE */ From 8542f5c81f2faf537ba064b5d1d2dd650afda745 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sat, 3 Dec 2022 22:58:52 +0100 Subject: [PATCH 05/24] Add line number to a few error messages This is just a quick improvement, not meant to tackle the problem as a whole. Signed-off-by: Gilles Peskine --- tests/scripts/generate_test_code.py | 14 +++---- tests/scripts/test_generate_test_code.py | 50 ++++++++++++------------ 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index f19d30b61e..be08fbcfda 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -705,7 +705,7 @@ def parse_test_data(data_f): execution. :param data_f: file object of the data file. - :return: Generator that yields test name, function name, + :return: Generator that yields line number, test name, function name, dependency list and function argument list. """ __state_read_name = 0 @@ -748,7 +748,7 @@ def parse_test_data(data_f): parts = escaped_split(line, ':') test_function = parts[0] args = parts[1:] - yield name, test_function, dependencies, args + yield data_f.line_no, name, test_function, dependencies, args dependencies = [] state = __state_read_name if state == __state_read_args: @@ -931,7 +931,7 @@ def gen_from_test_data(data_f, out_data_f, func_info, suite_dependencies): unique_expressions = [] dep_check_code = '' expression_code = '' - for test_name, function_name, test_dependencies, test_args in \ + for line_no, test_name, function_name, test_dependencies, test_args in \ parse_test_data(data_f): out_data_f.write(test_name + '\n') @@ -942,16 +942,16 @@ def gen_from_test_data(data_f, out_data_f, func_info, suite_dependencies): # Write test function name test_function_name = 'test_' + function_name if test_function_name not in func_info: - raise GeneratorInputError("Function %s not found!" % - test_function_name) + raise GeneratorInputError("%d: Function %s not found!" % + (line_no, test_function_name)) func_id, func_args = func_info[test_function_name] out_data_f.write(str(func_id)) # Write parameters if len(test_args) != len(func_args): - raise GeneratorInputError("Invalid number of arguments in test " + raise GeneratorInputError("%d: Invalid number of arguments in test " "%s. See function %s signature." % - (test_name, function_name)) + (line_no, test_name, function_name)) expression_code += write_parameters(out_data_f, test_args, func_args, unique_expressions) diff --git a/tests/scripts/test_generate_test_code.py b/tests/scripts/test_generate_test_code.py index d23d742193..9a78ca1138 100755 --- a/tests/scripts/test_generate_test_code.py +++ b/tests/scripts/test_generate_test_code.py @@ -1264,29 +1264,29 @@ dhm_selftest: # List of (name, function_name, dependencies, args) tests = list(parse_test_data(stream)) test1, test2, test3, test4 = tests - self.assertEqual(test1[0], 'Diffie-Hellman full exchange #1') - self.assertEqual(test1[1], 'dhm_do_dhm') - self.assertEqual(test1[2], []) - self.assertEqual(test1[3], ['10', '"23"', '10', '"5"']) + self.assertEqual(test1[1], 'Diffie-Hellman full exchange #1') + self.assertEqual(test1[2], 'dhm_do_dhm') + self.assertEqual(test1[3], []) + self.assertEqual(test1[4], ['10', '"23"', '10', '"5"']) - self.assertEqual(test2[0], 'Diffie-Hellman full exchange #2') - self.assertEqual(test2[1], 'dhm_do_dhm') - self.assertEqual(test2[2], []) - self.assertEqual(test2[3], ['10', '"93450983094850938450983409623"', + self.assertEqual(test2[1], 'Diffie-Hellman full exchange #2') + self.assertEqual(test2[2], 'dhm_do_dhm') + self.assertEqual(test2[3], []) + self.assertEqual(test2[4], ['10', '"93450983094850938450983409623"', '10', '"9345098304850938450983409622"']) - self.assertEqual(test3[0], 'Diffie-Hellman full exchange #3') - self.assertEqual(test3[1], 'dhm_do_dhm') - self.assertEqual(test3[2], []) - self.assertEqual(test3[3], ['10', + self.assertEqual(test3[1], 'Diffie-Hellman full exchange #3') + self.assertEqual(test3[2], 'dhm_do_dhm') + self.assertEqual(test3[3], []) + self.assertEqual(test3[4], ['10', '"9345098382739712938719287391879381271"', '10', '"9345098792137312973297123912791271"']) - self.assertEqual(test4[0], 'Diffie-Hellman selftest') - self.assertEqual(test4[1], 'dhm_selftest') - self.assertEqual(test4[2], []) + self.assertEqual(test4[1], 'Diffie-Hellman selftest') + self.assertEqual(test4[2], 'dhm_selftest') self.assertEqual(test4[3], []) + self.assertEqual(test4[4], []) def test_with_dependencies(self): """ @@ -1306,15 +1306,15 @@ dhm_do_dhm:10:"93450983094850938450983409623":10:"9345098304850938450983409622" # List of (name, function_name, dependencies, args) tests = list(parse_test_data(stream)) test1, test2 = tests - self.assertEqual(test1[0], 'Diffie-Hellman full exchange #1') - self.assertEqual(test1[1], 'dhm_do_dhm') - self.assertEqual(test1[2], ['YAHOO']) - self.assertEqual(test1[3], ['10', '"23"', '10', '"5"']) + self.assertEqual(test1[1], 'Diffie-Hellman full exchange #1') + self.assertEqual(test1[2], 'dhm_do_dhm') + self.assertEqual(test1[3], ['YAHOO']) + self.assertEqual(test1[4], ['10', '"23"', '10', '"5"']) - self.assertEqual(test2[0], 'Diffie-Hellman full exchange #2') - self.assertEqual(test2[1], 'dhm_do_dhm') - self.assertEqual(test2[2], []) - self.assertEqual(test2[3], ['10', '"93450983094850938450983409623"', + self.assertEqual(test2[1], 'Diffie-Hellman full exchange #2') + self.assertEqual(test2[2], 'dhm_do_dhm') + self.assertEqual(test2[3], []) + self.assertEqual(test2[4], ['10', '"93450983094850938450983409623"', '10', '"9345098304850938450983409622"']) def test_no_args(self): @@ -1335,7 +1335,7 @@ dhm_do_dhm:10:"93450983094850938450983409623":10:"9345098304850938450983409622" stream = StringIOWrapper('test_suite_ut.function', data) err = None try: - for _, _, _, _ in parse_test_data(stream): + for _, _, _, _, _ in parse_test_data(stream): pass except GeneratorInputError as err: self.assertEqual(type(err), GeneratorInputError) @@ -1353,7 +1353,7 @@ depends_on:YAHOO stream = StringIOWrapper('test_suite_ut.function', data) err = None try: - for _, _, _, _ in parse_test_data(stream): + for _, _, _, _, _ in parse_test_data(stream): pass except GeneratorInputError as err: self.assertEqual(type(err), GeneratorInputError) From ca25deee12ad5b6acd5189af85bac512dfb1ba31 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 17:27:25 +0100 Subject: [PATCH 06/24] Factor get_function_info out of gen_from_test_data No intended behavior change. This commit is mainly to satisfy pylint, which complains that gen_from_test_data now has too many variables. But it's a good thing anyway to make the function a little more readable. Signed-off-by: Gilles Peskine --- tests/scripts/generate_test_code.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index be08fbcfda..9759f2ab2d 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -909,6 +909,24 @@ def gen_suite_dep_checks(suite_dependencies, dep_check_code, expression_code): return dep_check_code, expression_code +def get_function_info(func_info, function_name, line_no): + """Look up information about a test function by name. + + Raise an informative expression if function_name is not found. + + :param func_info: dictionary mapping function names to their information. + :param function_name: the function name as written in the .function and + .data files. + :param line_no: line number for error messages. + :return Function information (id, args). + """ + test_function_name = 'test_' + function_name + if test_function_name not in func_info: + raise GeneratorInputError("%d: Function %s not found!" % + (line_no, test_function_name)) + return func_info[test_function_name] + + def gen_from_test_data(data_f, out_data_f, func_info, suite_dependencies): """ This function reads test case name, dependencies and test vectors @@ -940,11 +958,8 @@ def gen_from_test_data(data_f, out_data_f, func_info, suite_dependencies): unique_dependencies) # Write test function name - test_function_name = 'test_' + function_name - if test_function_name not in func_info: - raise GeneratorInputError("%d: Function %s not found!" % - (line_no, test_function_name)) - func_id, func_args = func_info[test_function_name] + func_id, func_args = \ + get_function_info(func_info, function_name, line_no) out_data_f.write(str(func_id)) # Write parameters From 1a24895bfd8ad0bfe0f0592810f403c236f8e164 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sat, 3 Dec 2022 23:48:25 +0100 Subject: [PATCH 07/24] Simplify string escapes Treat backslash as a universal escape character: "\n" is a newline, backslash escapes any non-alphanumeric character. This affects some test cases that had "\," standing for backslash-comma. With the new uniform treatment of backslashes, this needs to be "\\,". Signed-off-by: Gilles Peskine --- tests/suites/host_test.function | 24 ++++++++++++------------ tests/suites/test_suite_x509parse.data | 6 +++--- tests/suites/test_suite_x509write.data | 6 +++--- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function index 475a9c835b..a99a7c7c83 100644 --- a/tests/suites/host_test.function +++ b/tests/suites/host_test.function @@ -180,24 +180,24 @@ static int parse_arguments(char *buf, size_t len, char **params, p++; } - /* Replace newlines, question marks and colons in strings */ + /* Replace backslash escapes in strings */ for (i = 0; i < cnt; i++) { p = params[i]; q = params[i]; while (*p != '\0') { - if (*p == '\\' && *(p + 1) == 'n') { - p += 2; - *(q++) = '\n'; - } else if (*p == '\\' && *(p + 1) == ':') { - p += 2; - *(q++) = ':'; - } else if (*p == '\\' && *(p + 1) == '?') { - p += 2; - *(q++) = '?'; - } else { - *(q++) = *(p++); + if (*p == '\\') { + ++p; + switch (*p) { + case 'n': + *p = '\n'; + break; + default: + // Fall through to copying *p + break; + } } + *(q++) = *(p++); } *q = '\0'; } diff --git a/tests/suites/test_suite_x509parse.data b/tests/suites/test_suite_x509parse.data index a6b001fb15..db2fe56a5f 100644 --- a/tests/suites/test_suite_x509parse.data +++ b/tests/suites/test_suite_x509parse.data @@ -336,7 +336,7 @@ mbedtls_x509_csr_info:"data_files/server1.req.sha512":"CSR version \: 1\nsubje X509 CSR Information RSA with SHA-256, containing commas depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_MD_CAN_SHA256:MBEDTLS_RSA_C:MBEDTS_X509_INFO -mbedtls_x509_csr_info:"data_files/server1.req.commas.sha256":"CSR version \: 1\nsubject name \: C=NL, O=PolarSSL\, Commas, CN=PolarSSL Server 1\nsigned using \: RSA with SHA-256\nRSA key size \: 2048 bits\n" +mbedtls_x509_csr_info:"data_files/server1.req.commas.sha256":"CSR version \: 1\nsubject name \: C=NL, O=PolarSSL\\, Commas, CN=PolarSSL Server 1\nsigned using \: RSA with SHA-256\nRSA key size \: 2048 bits\n" X509 CSR Information EC with SHA1 depends_on:MBEDTLS_PK_CAN_ECDSA_SOME:MBEDTLS_PEM_PARSE_C:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_MD_CAN_SHA1:!MBEDTLS_X509_REMOVE_INFO @@ -437,7 +437,7 @@ mbedtls_x509_dn_gets:"data_files/server2.crt":"issuer":"C=NL, O=PolarSSL, CN=Pol X509 Get Distinguished Name #5 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA1 -mbedtls_x509_dn_gets:"data_files/server1.commas.crt":"subject":"C=NL, O=PolarSSL\, Commas, CN=PolarSSL Server 1" +mbedtls_x509_dn_gets:"data_files/server1.commas.crt":"subject":"C=NL, O=PolarSSL\\, Commas, CN=PolarSSL Server 1" X509 Get Modified DN #1 depends_on:MBEDTLS_PEM_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_MD_CAN_SHA1 @@ -3145,7 +3145,7 @@ x509_get_time:MBEDTLS_ASN1_UTC_TIME:"0002291212+0300":MBEDTLS_ERR_X509_INVALID_D X509 Get time (UTC invalid character in year) depends_on:MBEDTLS_X509_USE_C -x509_get_time:MBEDTLS_ASN1_UTC_TIME:"0\1130231212Z":MBEDTLS_ERR_X509_INVALID_DATE:0:0:0:0:0:0 +x509_get_time:MBEDTLS_ASN1_UTC_TIME:"0\\1130231212Z":MBEDTLS_ERR_X509_INVALID_DATE:0:0:0:0:0:0 X509 Get time (UTC invalid character in month) depends_on:MBEDTLS_X509_USE_C diff --git a/tests/suites/test_suite_x509write.data b/tests/suites/test_suite_x509write.data index bb40029bb9..cd1b0a3bd7 100644 --- a/tests/suites/test_suite_x509write.data +++ b/tests/suites/test_suite_x509write.data @@ -163,7 +163,7 @@ depends_on:MBEDTLS_MD_CAN_SHA256:MBEDTLS_PK_CAN_ECDSA_SIGN:MBEDTLS_ECDSA_DETERMI x509_crt_check:"data_files/server5.key":"":"C=NL,O=PolarSSL,CN=PolarSSL Server 1":"data_files/test-ca2.key":"PolarSSLTest":"C=NL,O=PolarSSL,CN=Polarssl Test EC CA":"01":"20190210144406":"20290210144406":MBEDTLS_MD_SHA256:0:0:"NULL":0:0:1:-1:"":2:0:"data_files/test-ca2.crt" X509 String to Names #1 -mbedtls_x509_string_to_names:"C=NL,O=Offspark\, Inc., OU=PolarSSL":"C=NL, O=Offspark\, Inc., OU=PolarSSL":0 +mbedtls_x509_string_to_names:"C=NL,O=Offspark\\, Inc., OU=PolarSSL":"C=NL, O=Offspark\\, Inc., OU=PolarSSL":0 X509 String to Names #2 mbedtls_x509_string_to_names:"C=NL, O=Offspark, Inc., OU=PolarSSL":"":MBEDTLS_ERR_X509_UNKNOWN_OID @@ -175,10 +175,10 @@ X509 String to Names #4 (Name larger than 255 bytes) mbedtls_x509_string_to_names:"C=NL, O=1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456, OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME X509 String to Names #5 (Escape non-allowed characters) -mbedtls_x509_string_to_names:"C=NL, O=Offspark\a Inc., OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME +mbedtls_x509_string_to_names:"C=NL, O=Offspark\\a Inc., OU=PolarSSL":"":MBEDTLS_ERR_X509_INVALID_NAME X509 String to Names #6 (Escape at end) -mbedtls_x509_string_to_names:"C=NL, O=Offspark\":"":MBEDTLS_ERR_X509_INVALID_NAME +mbedtls_x509_string_to_names:"C=NL, O=Offspark\\":"":MBEDTLS_ERR_X509_INVALID_NAME Check max serial length x509_set_serial_check: From a9946952b4f442f706cb22cd9b3acedb90ef9b6b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sat, 3 Dec 2022 23:50:05 +0100 Subject: [PATCH 08/24] Exercise string parsing in the test framework Signed-off-by: Gilles Peskine --- tests/suites/test_suite_platform_printf.data | 43 ++++++++++++++++++- .../test_suite_platform_printf.function | 25 +++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/tests/suites/test_suite_platform_printf.data b/tests/suites/test_suite_platform_printf.data index 693f06938b..1ed68d5a2e 100644 --- a/tests/suites/test_suite_platform_printf.data +++ b/tests/suites/test_suite_platform_printf.data @@ -1,6 +1,6 @@ # The test cases for printf and integers have two purposes: they exercise # the printf function family, and they exercise the passing of integers -# through the test framework. +# and strings through the test framework. printf "%d", 0 printf_int:"%d":0:"0" @@ -55,3 +55,44 @@ printf_int:"%d":-2147483648:"-2147483648" printf "%d", -0x80000000 printf_int:"%d":-0x80000000:"-2147483648" + +# The next few test cases exercise how the test framework handles special +# characters in strings. +printf "%c", SPACE, SPACE +printf_char2:"%c%c":SPACE_CHAR:SPACE_CHAR:" " + +printf "%c", NEWLINE, SPACE +printf_char2:"%c%c":NEWLINE_CHAR:SPACE_CHAR:"\n " + +printf "%c", DOUBLE QUOTE, SPACE +printf_char2:"%c%c":DOUBLE_QUOTE_CHAR:SPACE_CHAR:"\" " + +printf "%c", COLON, SPACE +printf_char2:"%c%c":COLON_CHAR:SPACE_CHAR:"\: " + +printf "%c", BACKSLASH, SPACE +printf_char2:"%c%c":BACKSLASH_CHAR:SPACE_CHAR:"\\ " + +printf "%c", SPACE, BACKSLASH +printf_char2:"%c%c":SPACE_CHAR:BACKSLASH_CHAR:" \\" + +printf "%c%c", COLON, COLON +printf_char2:"%c%c":COLON_CHAR:COLON_CHAR:"\:\:" + +printf "%c%c", COLON, NEWLINE +printf_char2:"%c%c":COLON_CHAR:NEWLINE_CHAR:"\:\n" + +printf "%c%c", BACKSLASH, NEWLINE +printf_char2:"%c%c":BACKSLASH_CHAR:NEWLINE_CHAR:"\\\n" + +printf "%c%c", BACKSLASH, DOUBLE QUOTE +printf_char2:"%c%c":BACKSLASH_CHAR:DOUBLE_QUOTE_CHAR:"\\\"" + +printf "%c%c", BACKSLASH, COLON +printf_char2:"%c%c":BACKSLASH_CHAR:COLON_CHAR:"\\\:" + +printf "%c%c", BACKSLASH, BACKSLASH +printf_char2:"%c%c":BACKSLASH_CHAR:BACKSLASH_CHAR:"\\\\" + +printf "%c%c", BACKSLASH, n +printf_char2:"%c%c":BACKSLASH_CHAR:LOWERCASE_N_CHAR:"\\n" diff --git a/tests/suites/test_suite_platform_printf.function b/tests/suites/test_suite_platform_printf.function index 51d3c6d060..f050d6e47e 100644 --- a/tests/suites/test_suite_platform_printf.function +++ b/tests/suites/test_suite_platform_printf.function @@ -4,6 +4,13 @@ #include #include #include + +#define NEWLINE_CHAR '\n' +#define SPACE_CHAR ' ' +#define DOUBLE_QUOTE_CHAR '"' +#define COLON_CHAR ':' +#define BACKSLASH_CHAR '\\' +#define LOWERCASE_N_CHAR 'n' /* END_HEADER */ /* BEGIN_CASE */ @@ -23,3 +30,21 @@ exit: mbedtls_free(output); } /* END_CASE */ + +/* BEGIN_CASE */ +void printf_char2(char *format, int arg1, int arg2, char *result) +{ + char *output = NULL; + const size_t n = strlen(result); + + /* Nominal case: buffer just large enough */ + ASSERT_ALLOC(output, n + 1); + TEST_EQUAL(n, mbedtls_snprintf(output, n + 1, format, arg1, arg2)); + ASSERT_COMPARE(result, n + 1, output, n + 1); + mbedtls_free(output); + output = NULL; + +exit: + mbedtls_free(output); +} +/* END_CASE */ From 5226eb5cd39180aac91563c7b5b75dc321bae02b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 00:28:56 +0100 Subject: [PATCH 09/24] Simplify parsing of integers in .datax files In the .datax parser, since we're calling strtol() anyway, rely on it for verification. This makes the .datax parser very slightly more liberal (leading spaces and '+' are now accepted), and changes the interpretation of numbers with leading zeros to octal. Before, an argument like :0123: was parsed as decimal, but an argument like :0123+1: was parsed as a C expression and hence the leading zero marked an octal representation. Now, a leading zero is always interpreted according to C syntax, namely indicating octal. There are no nonzero integer constants with a leading zero in a .data file, so this does not affect existing test cases. In the .datax generator, allow negative arguments to be 'int' (before, they were systematically treated as 'exp' even though they didn't need to be). In the .datax parser, validate the range of integer constants. They have to fit in int32_t. In the .datax generator, use 'exp' instead of 'int' for integer constants that are out of range. Signed-off-by: Gilles Peskine --- tests/scripts/generate_test_code.py | 14 ++++++-- tests/suites/helpers.function | 2 ++ tests/suites/host_test.function | 54 +++++++++-------------------- 3 files changed, 30 insertions(+), 40 deletions(-) diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index 9759f2ab2d..6b5f11bb46 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -846,6 +846,14 @@ def write_dependencies(out_data_f, test_dependencies, unique_dependencies): return dep_check_code +INT_VAL_REGEX = re.compile(r'-?(\d+|0x[0-9a-f]+)$', re.I) +def val_is_int(val: str) -> bool: + """Whether val is suitable as an 'int' parameter in the .datax file.""" + if not INT_VAL_REGEX.match(val): + return False + # Limit the range to what is guaranteed to get through strtol() + return abs(int(val, 0)) <= 0x7fffffff + def write_parameters(out_data_f, test_args, func_args, unique_expressions): """ Writes test parameters to the intermediate data file, replacing @@ -864,9 +872,9 @@ def write_parameters(out_data_f, test_args, func_args, unique_expressions): typ = func_args[i] val = test_args[i] - # check if val is a non literal int val (i.e. an expression) - if typ == 'int' and not re.match(r'(\d+|0x[0-9a-f]+)$', - val, re.I): + # Pass small integer constants literally. This reduces the size of + # the C code. Register anything else as an expression. + if typ == 'int' and not val_is_int(val): typ = 'exp' if val not in unique_expressions: unique_expressions.append(val) diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function index 19647315c9..7615d27153 100644 --- a/tests/suites/helpers.function +++ b/tests/suites/helpers.function @@ -8,6 +8,8 @@ #include #include +#include +#include #include #include #include diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function index a99a7c7c83..565ded3768 100644 --- a/tests/suites/host_test.function +++ b/tests/suites/host_test.function @@ -32,46 +32,26 @@ int verify_string(char **str) * * \return 0 if success else 1 */ -int verify_int(char *str, int32_t *value) +int verify_int(char *str, int32_t *p_value) { - size_t i; - int minus = 0; - int digits = 1; - int hex = 0; - - for (i = 0; i < strlen(str); i++) { - if (i == 0 && str[i] == '-') { - minus = 1; - continue; - } - - if (((minus && i == 2) || (!minus && i == 1)) && - str[i - 1] == '0' && (str[i] == 'x' || str[i] == 'X')) { - hex = 1; - continue; - } - - if (!((str[i] >= '0' && str[i] <= '9') || - (hex && ((str[i] >= 'a' && str[i] <= 'f') || - (str[i] >= 'A' && str[i] <= 'F'))))) { - digits = 0; - break; - } + char *end = NULL; + errno = 0; + long value = strtol(str, &end, 0); + if (errno == EINVAL || *end != '\0') { + mbedtls_fprintf(stderr, + "Expected integer for parameter and got: %s\n", str); + return KEY_VALUE_MAPPING_NOT_FOUND; } - - if (digits) { - if (hex) { - *value = strtol(str, NULL, 16); - } else { - *value = strtol(str, NULL, 10); - } - - return 0; + if (errno == ERANGE +#if LONG_MAX > 0x7fffffff + || value > 0x7fffffffL || value < -0x80000000L +#endif + ) { + mbedtls_fprintf(stderr, "Integer out of range: %s\n", str); + return KEY_VALUE_MAPPING_NOT_FOUND; } - - mbedtls_fprintf(stderr, - "Expected integer for parameter and got: %s\n", str); - return KEY_VALUE_MAPPING_NOT_FOUND; + *p_value = value; + return 0; } From b3c2eaf00fde007eb402dc714bf7ddda37218316 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 13:10:55 +0100 Subject: [PATCH 10/24] Support different types in the parameter store The test framework stores size_t and int32_t values in the parameter store by converting them all to int. This is ok in practice, since we assume int covers int32_t and we don't have test data larger than 2GB. But it's confusing and error-prone. So make the parameter store a union, which allows size_t values not to be potentially truncated and makes the code a little clearer. Signed-off-by: Gilles Peskine --- tests/include/test/arguments.h | 38 ++++++++++++++++++++++++ tests/scripts/generate_test_code.py | 4 +-- tests/scripts/test_generate_test_code.py | 11 +++---- tests/suites/helpers.function | 1 + tests/suites/host_test.function | 13 ++++---- 5 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 tests/include/test/arguments.h diff --git a/tests/include/test/arguments.h b/tests/include/test/arguments.h new file mode 100644 index 0000000000..7ff416e519 --- /dev/null +++ b/tests/include/test/arguments.h @@ -0,0 +1,38 @@ +/** + * \file arguments.h + * + * \brief Manipulation of test arguments. + * + * Much of the code is in host_test.function, to be migrated here later. + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TEST_ARGUMENTS_H +#define TEST_ARGUMENTS_H + +#include "mbedtls/build_info.h" +#include +#include + +typedef union { + size_t len; + int32_t s32; +} mbedtls_test_argument_t; + +#endif /* TEST_ARGUMENTS_H */ diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index 6b5f11bb46..54c7e3a6f8 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -471,7 +471,7 @@ def parse_function_arguments(line): continue if re.search(INT_CHECK_REGEX, arg.strip()): args.append('int') - args_dispatch.append('*( (int *) params[%d] )' % arg_idx) + args_dispatch.append('((mbedtls_test_argument_t*)params[%d])->s32' % arg_idx) elif re.search(CHAR_CHECK_REGEX, arg.strip()): args.append('char*') args_dispatch.append('(char *) params[%d]' % arg_idx) @@ -479,7 +479,7 @@ def parse_function_arguments(line): args.append('hex') # create a structure pointer_initializer = '(uint8_t *) params[%d]' % arg_idx - len_initializer = '*( (uint32_t *) params[%d] )' % (arg_idx+1) + len_initializer = '((mbedtls_test_argument_t*)params[%d])->len' % (arg_idx+1) local_vars += """ data_t data%d = {%s, %s}; """ % (arg_idx, pointer_initializer, len_initializer) diff --git a/tests/scripts/test_generate_test_code.py b/tests/scripts/test_generate_test_code.py index 9a78ca1138..c198ad0e35 100755 --- a/tests/scripts/test_generate_test_code.py +++ b/tests/scripts/test_generate_test_code.py @@ -485,9 +485,10 @@ class ParseFuncSignature(TestCase): args, local, arg_dispatch = parse_function_arguments(line) self.assertEqual(args, ['char*', 'int', 'int']) self.assertEqual(local, '') - self.assertEqual(arg_dispatch, ['(char *) params[0]', - '*( (int *) params[1] )', - '*( (int *) params[2] )']) + self.assertEqual(arg_dispatch, + ['(char *) params[0]', + '((mbedtls_test_argument_t*)params[1])->s32', + '((mbedtls_test_argument_t*)params[2])->s32']) def test_hex_params(self): """ @@ -499,10 +500,10 @@ class ParseFuncSignature(TestCase): self.assertEqual(args, ['char*', 'hex', 'int']) self.assertEqual(local, ' data_t data1 = {(uint8_t *) params[1], ' - '*( (uint32_t *) params[2] )};\n') + '((mbedtls_test_argument_t*)params[2])->len};\n') self.assertEqual(arg_dispatch, ['(char *) params[0]', '&data1', - '*( (int *) params[3] )']) + '((mbedtls_test_argument_t*)params[3])->s32']) def test_unsupported_arg(self): """ diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function index 7615d27153..86ff5b4893 100644 --- a/tests/suites/helpers.function +++ b/tests/suites/helpers.function @@ -2,6 +2,7 @@ /*----------------------------------------------------------------------------*/ /* Headers */ +#include #include #include #include diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function index 565ded3768..59b18d2896 100644 --- a/tests/suites/host_test.function +++ b/tests/suites/host_test.function @@ -28,7 +28,7 @@ int verify_string(char **str) * integer value. * * \param str Input string. - * \param value Pointer to int for output value. + * \param p_value Pointer to output value. * * \return 0 if success else 1 */ @@ -203,7 +203,8 @@ static int parse_arguments(char *buf, size_t len, char **params, * * \return 0 for success else 1 */ -static int convert_params(size_t cnt, char **params, int32_t *int_params_store) +static int convert_params(size_t cnt, char **params, + mbedtls_test_argument_t *int_params_store) { char **cur = params; char **out = params; @@ -221,7 +222,7 @@ static int convert_params(size_t cnt, char **params, int32_t *int_params_store) break; } } else if (strcmp(type, "int") == 0) { - if (verify_int(val, int_params_store) == 0) { + if (verify_int(val, &int_params_store->s32) == 0) { *out++ = (char *) int_params_store++; } else { ret = (DISPATCH_INVALID_TEST_DATA); @@ -235,7 +236,7 @@ static int convert_params(size_t cnt, char **params, int32_t *int_params_store) mbedtls_test_unhexify((unsigned char *) val, strlen(val), val, &len) == 0); - *int_params_store = len; + int_params_store->len = len; *out++ = val; *out++ = (char *) (int_params_store++); } else { @@ -244,7 +245,7 @@ static int convert_params(size_t cnt, char **params, int32_t *int_params_store) } } else if (strcmp(type, "exp") == 0) { int exp_id = strtol(val, NULL, 10); - if (get_expression(exp_id, int_params_store) == 0) { + if (get_expression(exp_id, &int_params_store->s32) == 0) { *out++ = (char *) int_params_store++; } else { ret = (DISPATCH_INVALID_TEST_DATA); @@ -463,7 +464,7 @@ int execute_tests(int argc, const char **argv) char buf[5000]; char *params[50]; /* Store for processed integer params. */ - int32_t int_params[50]; + mbedtls_test_argument_t int_params[50]; void *pointer; #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) int stdout_fd = -1; From 400cde607b524e8e6e4667a1ef87c45bac3e6545 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 14:00:32 +0100 Subject: [PATCH 11/24] parse_function_arguments: make local_vars a list Internal refactoring only, no behavior change. Signed-off-by: Gilles Peskine --- tests/scripts/generate_test_code.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index 54c7e3a6f8..5066a57b7f 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -456,7 +456,7 @@ def parse_function_arguments(line): wrapper function and argument dispatch code. """ args = [] - local_vars = '' + local_vars = [] args_dispatch = [] arg_idx = 0 # Remove characters before arguments @@ -480,9 +480,8 @@ def parse_function_arguments(line): # create a structure pointer_initializer = '(uint8_t *) params[%d]' % arg_idx len_initializer = '((mbedtls_test_argument_t*)params[%d])->len' % (arg_idx+1) - local_vars += """ data_t data%d = {%s, %s}; -""" % (arg_idx, pointer_initializer, len_initializer) - + local_vars.append(' data_t data%d = {%s, %s};\n' % + (arg_idx, pointer_initializer, len_initializer)) args_dispatch.append('&data%d' % arg_idx) arg_idx += 1 else: @@ -490,7 +489,7 @@ def parse_function_arguments(line): "'char *' or 'data_t'\n%s" % line) arg_idx += 1 - return args, local_vars, args_dispatch + return args, ''.join(local_vars), args_dispatch def generate_function_code(name, code, local_vars, args_dispatch, From 096f0ca7e550270725f7af99385cdb544cdd02d8 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 14:10:39 +0100 Subject: [PATCH 12/24] parse_function_arguments: extract per-argument function Internal refactoring only, no behavior change. Signed-off-by: Gilles Peskine --- tests/scripts/generate_test_code.py | 58 +++++++++++++++++++---------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index 5066a57b7f..53efcea846 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -444,6 +444,40 @@ def parse_function_dependencies(line): return dependencies +def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch): + """ + Parses one test function's argument declaration. + + :param arg: argument declaration. + :param arg_idx: current wrapper argument index. + :param args: accumulator of arguments' internal types. + :param local_vars: accumulator of internal variable declarations. + :param args_dispatch: accumulator of argument usage expressions. + :return: the number of new wrapper arguments, + or None if the argument declaration is invalid. + """ + arg = arg.strip() + if arg == '': + return 0 + if re.search(INT_CHECK_REGEX, arg.strip()): + args.append('int') + args_dispatch.append('((mbedtls_test_argument_t*)params[%d])->s32' % arg_idx) + return 1 + if re.search(CHAR_CHECK_REGEX, arg.strip()): + args.append('char*') + args_dispatch.append('(char *) params[%d]' % arg_idx) + return 1 + if re.search(DATA_T_CHECK_REGEX, arg.strip()): + args.append('hex') + # create a structure + pointer_initializer = '(uint8_t *) params[%d]' % arg_idx + len_initializer = '((mbedtls_test_argument_t*)params[%d])->len' % (arg_idx+1) + local_vars.append(' data_t data%d = {%s, %s};\n' % + (arg_idx, pointer_initializer, len_initializer)) + args_dispatch.append('&data%d' % arg_idx) + return 2 + return None + def parse_function_arguments(line): """ Parses test function signature for validation and generates @@ -466,28 +500,12 @@ def parse_function_arguments(line): # i.e. the test functions will not have a function pointer # argument. for arg in line[:line.find(')')].split(','): - arg = arg.strip() - if arg == '': - continue - if re.search(INT_CHECK_REGEX, arg.strip()): - args.append('int') - args_dispatch.append('((mbedtls_test_argument_t*)params[%d])->s32' % arg_idx) - elif re.search(CHAR_CHECK_REGEX, arg.strip()): - args.append('char*') - args_dispatch.append('(char *) params[%d]' % arg_idx) - elif re.search(DATA_T_CHECK_REGEX, arg.strip()): - args.append('hex') - # create a structure - pointer_initializer = '(uint8_t *) params[%d]' % arg_idx - len_initializer = '((mbedtls_test_argument_t*)params[%d])->len' % (arg_idx+1) - local_vars.append(' data_t data%d = {%s, %s};\n' % - (arg_idx, pointer_initializer, len_initializer)) - args_dispatch.append('&data%d' % arg_idx) - arg_idx += 1 - else: + indexes = parse_function_argument(arg, arg_idx, + args, local_vars, args_dispatch) + if indexes is None: raise ValueError("Test function arguments can only be 'int', " "'char *' or 'data_t'\n%s" % line) - arg_idx += 1 + arg_idx += indexes return args, ''.join(local_vars), args_dispatch From 47e2e8817d938bc02ec94be8f01a4755eacca4c5 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 14:29:06 +0100 Subject: [PATCH 13/24] Support (void) as an argument list of a test function Signed-off-by: Gilles Peskine --- tests/scripts/generate_test_code.py | 19 ++++++------ tests/scripts/test_generate_test_code.py | 37 ++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 11 deletions(-) diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index 53efcea846..b72e2cb009 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -457,8 +457,6 @@ def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch): or None if the argument declaration is invalid. """ arg = arg.strip() - if arg == '': - return 0 if re.search(INT_CHECK_REGEX, arg.strip()): args.append('int') args_dispatch.append('((mbedtls_test_argument_t*)params[%d])->s32' % arg_idx) @@ -478,6 +476,7 @@ def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch): return 2 return None +ARGUMENT_LIST_REGEX = re.compile(r'\((.*?)\)', re.S) def parse_function_arguments(line): """ Parses test function signature for validation and generates @@ -489,17 +488,19 @@ def parse_function_arguments(line): :return: argument list, local variables for wrapper function and argument dispatch code. """ - args = [] - local_vars = [] - args_dispatch = [] - arg_idx = 0 - # Remove characters before arguments - line = line[line.find('(') + 1:] # Process arguments, ex: arg1, arg2 ) # This script assumes that the argument list is terminated by ')' # i.e. the test functions will not have a function pointer # argument. - for arg in line[:line.find(')')].split(','): + m = ARGUMENT_LIST_REGEX.search(line) + arg_list = m.group(1).strip() + if arg_list in ['', 'void']: + return [], '', [] + args = [] + local_vars = [] + args_dispatch = [] + arg_idx = 0 + for arg in arg_list.split(','): indexes = parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch) if indexes is None: diff --git a/tests/scripts/test_generate_test_code.py b/tests/scripts/test_generate_test_code.py index c198ad0e35..889b96299c 100755 --- a/tests/scripts/test_generate_test_code.py +++ b/tests/scripts/test_generate_test_code.py @@ -513,9 +513,9 @@ class ParseFuncSignature(TestCase): line = 'void entropy_threshold( char * a, data_t * h, char result )' self.assertRaises(ValueError, parse_function_arguments, line) - def test_no_params(self): + def test_empty_params(self): """ - Test no parameters. + Test no parameters (nothing between parentheses). :return: """ line = 'void entropy_threshold()' @@ -524,6 +524,39 @@ class ParseFuncSignature(TestCase): self.assertEqual(local, '') self.assertEqual(arg_dispatch, []) + def test_blank_params(self): + """ + Test no parameters (space between parentheses). + :return: + """ + line = 'void entropy_threshold( )' + args, local, arg_dispatch = parse_function_arguments(line) + self.assertEqual(args, []) + self.assertEqual(local, '') + self.assertEqual(arg_dispatch, []) + + def test_void_params(self): + """ + Test no parameters (void keyword). + :return: + """ + line = 'void entropy_threshold(void)' + args, local, arg_dispatch = parse_function_arguments(line) + self.assertEqual(args, []) + self.assertEqual(local, '') + self.assertEqual(arg_dispatch, []) + + def test_void_space_params(self): + """ + Test no parameters (void with spaces). + :return: + """ + line = 'void entropy_threshold( void )' + args, local, arg_dispatch = parse_function_arguments(line) + self.assertEqual(args, []) + self.assertEqual(local, '') + self.assertEqual(arg_dispatch, []) + class ParseFunctionCode(TestCase): """ From 4ea4ad082b5a6e54d8928867d6889c94b6276343 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 15:11:00 +0100 Subject: [PATCH 14/24] parse_function_arguments: stricter type parsing Use normalization the equality comparisons instead of loose regular expressions to determine the type of an argument of a test function. Now declarations are parsed in a stricter way: there can't be ignored junk at the beginning or at the end. For example, `long long unsigned int x` was accepted as a test function argument (but not `long long unsigned x`), although this was misleading since the value was truncated to the range of int. Now only recognized types are accepted. The new code is slightly looser in that it accepts `char const*` as well as `const char*`. Signed-off-by: Gilles Peskine --- tests/scripts/generate_test_code.py | 26 ++++++++++++++++----- tests/suites/test_suite_psa_crypto.function | 4 ++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index b72e2cb009..263cd90eb5 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -171,6 +171,13 @@ import string import argparse +# Types regognized as integer arguments in test functions. +INTEGER_TYPES = frozenset(['int']) +# Types recognized as string arguments in test functions. +STRING_TYPES = frozenset(['char*', 'const char*', 'char const*']) +# Types recognized as hex data arguments in test functions. +DATA_TYPES = frozenset(['data_t*', 'const data_t*', 'data_t const*']) + BEGIN_HEADER_REGEX = r'/\*\s*BEGIN_HEADER\s*\*/' END_HEADER_REGEX = r'/\*\s*END_HEADER\s*\*/' @@ -192,9 +199,6 @@ CONDITION_REGEX = r'({})(?:\s*({})\s*({}))?$'.format(C_IDENTIFIER_REGEX, CONDITION_OPERATOR_REGEX, CONDITION_VALUE_REGEX) TEST_FUNCTION_VALIDATION_REGEX = r'\s*void\s+(?P\w+)\s*\(' -INT_CHECK_REGEX = r'int\s+.*' -CHAR_CHECK_REGEX = r'char\s*\*\s*.*' -DATA_T_CHECK_REGEX = r'data_t\s*\*\s*.*' FUNCTION_ARG_LIST_END_REGEX = r'.*\)' EXIT_LABEL_REGEX = r'^exit:' @@ -444,6 +448,7 @@ def parse_function_dependencies(line): return dependencies +ARGUMENT_DECLARATION_REGEX = re.compile(r'(.+?) ?(?:\bconst\b)? ?(\w+)\Z', re.S) def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch): """ Parses one test function's argument declaration. @@ -456,16 +461,25 @@ def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch): :return: the number of new wrapper arguments, or None if the argument declaration is invalid. """ + # Normalize whitespace arg = arg.strip() - if re.search(INT_CHECK_REGEX, arg.strip()): + arg = re.sub(r'\s*\*\s*', r'*', arg) + arg = re.sub(r'\s+', r' ', arg) + # Extract name and type + m = ARGUMENT_DECLARATION_REGEX.search(arg) + if not m: + # E.g. "int x[42]" + return None + typ, _ = m.groups() + if typ in INTEGER_TYPES: args.append('int') args_dispatch.append('((mbedtls_test_argument_t*)params[%d])->s32' % arg_idx) return 1 - if re.search(CHAR_CHECK_REGEX, arg.strip()): + if typ in STRING_TYPES: args.append('char*') args_dispatch.append('(char *) params[%d]' % arg_idx) return 1 - if re.search(DATA_T_CHECK_REGEX, arg.strip()): + if typ in DATA_TYPES: args.append('hex') # create a structure pointer_initializer = '(uint8_t *) params[%d]' % arg_idx diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index cd8a7b5fff..a3cfb35c5d 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -2408,12 +2408,12 @@ exit: /* BEGIN_CASE */ void copy_success(int source_usage_arg, int source_alg_arg, int source_alg2_arg, - unsigned int source_lifetime_arg, + int source_lifetime_arg, int type_arg, data_t *material, int copy_attributes, int target_usage_arg, int target_alg_arg, int target_alg2_arg, - unsigned int target_lifetime_arg, + int target_lifetime_arg, int expected_usage_arg, int expected_alg_arg, int expected_alg2_arg) { From 872948cc72e8238e23ca73cc52542306b00b2548 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 15:32:54 +0100 Subject: [PATCH 15/24] Support larger integer test arguments: C part Change the type of signed integer arguments from int32_t to intmax_t. This allows the C code to work with test function arguments with a range larger than int32_t. A subsequent commit will change the .datax generator to support larger types. Signed-off-by: Gilles Peskine --- tests/include/test/arguments.h | 2 +- tests/scripts/generate_test_code.py | 2 +- tests/scripts/test_generate_test_code.py | 6 +++--- tests/suites/host_test.function | 14 ++++++-------- tests/suites/main_test.function | 2 +- 5 files changed, 12 insertions(+), 14 deletions(-) diff --git a/tests/include/test/arguments.h b/tests/include/test/arguments.h index 7ff416e519..74bbbd5690 100644 --- a/tests/include/test/arguments.h +++ b/tests/include/test/arguments.h @@ -32,7 +32,7 @@ typedef union { size_t len; - int32_t s32; + intmax_t sint; } mbedtls_test_argument_t; #endif /* TEST_ARGUMENTS_H */ diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index 263cd90eb5..a3f6937c70 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -473,7 +473,7 @@ def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch): typ, _ = m.groups() if typ in INTEGER_TYPES: args.append('int') - args_dispatch.append('((mbedtls_test_argument_t*)params[%d])->s32' % arg_idx) + args_dispatch.append('((mbedtls_test_argument_t*)params[%d])->sint' % arg_idx) return 1 if typ in STRING_TYPES: args.append('char*') diff --git a/tests/scripts/test_generate_test_code.py b/tests/scripts/test_generate_test_code.py index 889b96299c..7c0ac0c315 100755 --- a/tests/scripts/test_generate_test_code.py +++ b/tests/scripts/test_generate_test_code.py @@ -487,8 +487,8 @@ class ParseFuncSignature(TestCase): self.assertEqual(local, '') self.assertEqual(arg_dispatch, ['(char *) params[0]', - '((mbedtls_test_argument_t*)params[1])->s32', - '((mbedtls_test_argument_t*)params[2])->s32']) + '((mbedtls_test_argument_t*)params[1])->sint', + '((mbedtls_test_argument_t*)params[2])->sint']) def test_hex_params(self): """ @@ -503,7 +503,7 @@ class ParseFuncSignature(TestCase): '((mbedtls_test_argument_t*)params[2])->len};\n') self.assertEqual(arg_dispatch, ['(char *) params[0]', '&data1', - '((mbedtls_test_argument_t*)params[3])->s32']) + '((mbedtls_test_argument_t*)params[3])->sint']) def test_unsupported_arg(self): """ diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function index 59b18d2896..06f391fa4f 100644 --- a/tests/suites/host_test.function +++ b/tests/suites/host_test.function @@ -32,21 +32,19 @@ int verify_string(char **str) * * \return 0 if success else 1 */ -int verify_int(char *str, int32_t *p_value) +int verify_int(char *str, intmax_t *p_value) { char *end = NULL; errno = 0; + /* Limit the range to long: for large integers, the test framework will + * use expressions anyway. */ long value = strtol(str, &end, 0); if (errno == EINVAL || *end != '\0') { mbedtls_fprintf(stderr, "Expected integer for parameter and got: %s\n", str); return KEY_VALUE_MAPPING_NOT_FOUND; } - if (errno == ERANGE -#if LONG_MAX > 0x7fffffff - || value > 0x7fffffffL || value < -0x80000000L -#endif - ) { + if (errno == ERANGE) { mbedtls_fprintf(stderr, "Integer out of range: %s\n", str); return KEY_VALUE_MAPPING_NOT_FOUND; } @@ -222,7 +220,7 @@ static int convert_params(size_t cnt, char **params, break; } } else if (strcmp(type, "int") == 0) { - if (verify_int(val, &int_params_store->s32) == 0) { + if (verify_int(val, &int_params_store->sint) == 0) { *out++ = (char *) int_params_store++; } else { ret = (DISPATCH_INVALID_TEST_DATA); @@ -245,7 +243,7 @@ static int convert_params(size_t cnt, char **params, } } else if (strcmp(type, "exp") == 0) { int exp_id = strtol(val, NULL, 10); - if (get_expression(exp_id, &int_params_store->s32) == 0) { + if (get_expression(exp_id, &int_params_store->sint) == 0) { *out++ = (char *) int_params_store++; } else { ret = (DISPATCH_INVALID_TEST_DATA); diff --git a/tests/suites/main_test.function b/tests/suites/main_test.function index 11a009afec..6c8d98e8b9 100644 --- a/tests/suites/main_test.function +++ b/tests/suites/main_test.function @@ -69,7 +69,7 @@ __MBEDTLS_TEST_TEMPLATE__FUNCTIONS_CODE * * \return 0 if exp_id is found. 1 otherwise. */ -int get_expression(int32_t exp_id, int32_t *out_value) +int get_expression(int32_t exp_id, intmax_t *out_value) { int ret = KEY_VALUE_MAPPING_FOUND; From 6f5082bf4db21ccdbdc41bb2bc1468e58f72a036 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 15:57:49 +0100 Subject: [PATCH 16/24] Allow more signed integer types in test function arguments Now that the C code supports the full range of intmax_t, allow any size of signed integer type in the .data file parser. Signed-off-by: Gilles Peskine --- tests/scripts/generate_test_code.py | 21 +++++++++++++--- tests/scripts/test_generate_test_code.py | 4 +-- tests/suites/test_suite_platform_printf.data | 4 +++ .../test_suite_platform_printf.function | 25 +++++++++++++++++++ 4 files changed, 49 insertions(+), 5 deletions(-) diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index a3f6937c70..f900fd2ba6 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -171,8 +171,23 @@ import string import argparse -# Types regognized as integer arguments in test functions. -INTEGER_TYPES = frozenset(['int']) +# Types regognized as signed integer arguments in test functions. +SIGNED_INTEGER_TYPES = frozenset([ + 'char', + 'short', + 'short int', + 'int', + 'int8_t', + 'int16_t', + 'int32_t', + 'int64_t', + 'intmax_t', + 'long', + 'long int', + 'long long int', + 'mbedtls_mpi_sint', + 'psa_status_t', +]) # Types recognized as string arguments in test functions. STRING_TYPES = frozenset(['char*', 'const char*', 'char const*']) # Types recognized as hex data arguments in test functions. @@ -471,7 +486,7 @@ def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch): # E.g. "int x[42]" return None typ, _ = m.groups() - if typ in INTEGER_TYPES: + if typ in SIGNED_INTEGER_TYPES: args.append('int') args_dispatch.append('((mbedtls_test_argument_t*)params[%d])->sint' % arg_idx) return 1 diff --git a/tests/scripts/test_generate_test_code.py b/tests/scripts/test_generate_test_code.py index 7c0ac0c315..effa46b589 100755 --- a/tests/scripts/test_generate_test_code.py +++ b/tests/scripts/test_generate_test_code.py @@ -507,10 +507,10 @@ class ParseFuncSignature(TestCase): def test_unsupported_arg(self): """ - Test unsupported arguments (not among int, char * and data_t) + Test unsupported argument type :return: """ - line = 'void entropy_threshold( char * a, data_t * h, char result )' + line = 'void entropy_threshold( char * a, data_t * h, unknown_t result )' self.assertRaises(ValueError, parse_function_arguments, line) def test_empty_params(self): diff --git a/tests/suites/test_suite_platform_printf.data b/tests/suites/test_suite_platform_printf.data index 1ed68d5a2e..e6d335296b 100644 --- a/tests/suites/test_suite_platform_printf.data +++ b/tests/suites/test_suite_platform_printf.data @@ -56,6 +56,10 @@ printf_int:"%d":-2147483648:"-2147483648" printf "%d", -0x80000000 printf_int:"%d":-0x80000000:"-2147483648" +# Test that LONG_MAX is coming out untruncated through the test framework. +printf "%lx", LONG_MAX +printf_long_max:"%lx":LONG_MAX + # The next few test cases exercise how the test framework handles special # characters in strings. printf "%c", SPACE, SPACE diff --git a/tests/suites/test_suite_platform_printf.function b/tests/suites/test_suite_platform_printf.function index f050d6e47e..7c5e1e2cdc 100644 --- a/tests/suites/test_suite_platform_printf.function +++ b/tests/suites/test_suite_platform_printf.function @@ -31,6 +31,31 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void printf_long_max(const char *format, long value) +{ + char *expected = NULL; + char *output = NULL; + /* "0x" plus 2 hex digits per byte */ + const size_t n = sizeof(value) * 2; + + /* We assume that long has no padding bits! */ + ASSERT_ALLOC(expected, n + 1); + expected[0] = '7'; + memset(expected + 1, 'f', sizeof(value) * 2 - 1); + + ASSERT_ALLOC(output, n + 1); + TEST_EQUAL(n, mbedtls_snprintf(output, n + 1, format, value)); + ASSERT_COMPARE(expected, n + 1, output, n + 1); + mbedtls_free(output); + output = NULL; + +exit: + mbedtls_free(output); + mbedtls_free(expected); +} +/* END_CASE */ + /* BEGIN_CASE */ void printf_char2(char *format, int arg1, int arg2, char *result) { From 7768a8e0a65c6e073724849b8009bf5cf2319d13 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Sun, 4 Dec 2022 15:58:48 +0100 Subject: [PATCH 17/24] Remove string hack for mbedtls_mpi_mod_int testing Now that the test framework can pass arbitrary values of type mbedtls_mpi_sint, just do that. Signed-off-by: Gilles Peskine --- tests/suites/test_suite_bignum.function | 37 ++---------------------- tests/suites/test_suite_bignum.misc.data | 32 ++++++++++---------- 2 files changed, 18 insertions(+), 51 deletions(-) diff --git a/tests/suites/test_suite_bignum.function b/tests/suites/test_suite_bignum.function index 2f00ff8d59..7f858e5543 100644 --- a/tests/suites/test_suite_bignum.function +++ b/tests/suites/test_suite_bignum.function @@ -923,47 +923,16 @@ exit: /* END_CASE */ /* BEGIN_CASE */ -void mpi_mod_int(char *input_X, char *input_Y, - char *input_A, int mod_result) +void mpi_mod_int(char *input_X, mbedtls_mpi_sint y, + mbedtls_mpi_sint a, int mod_result) { mbedtls_mpi X; - mbedtls_mpi Y; - mbedtls_mpi A; int res; mbedtls_mpi_uint r; mbedtls_mpi_init(&X); - mbedtls_mpi_init(&Y); - mbedtls_mpi_init(&A); - /* We use MPIs to read Y and A since the test framework limits us to - * ints, so we can't have 64-bit values */ TEST_EQUAL(mbedtls_test_read_mpi(&X, input_X), 0); - TEST_EQUAL(mbedtls_test_read_mpi(&Y, input_Y), 0); - TEST_EQUAL(mbedtls_test_read_mpi(&A, input_A), 0); - - TEST_EQUAL(Y.n, 1); - TEST_EQUAL(A.n, 1); - - /* Convert the MPIs for Y and A to (signed) mbedtls_mpi_sints */ - - /* Since we're converting sign+magnitude to two's complement, we lose one - * bit of value in the output. This means there are some values we can't - * represent, e.g. (hex) -A0000000 on 32-bit systems. These are technically - * invalid test cases, so could be considered "won't happen", but they are - * easy to test for, and this helps guard against human error. */ - - mbedtls_mpi_sint y = (mbedtls_mpi_sint) Y.p[0]; - TEST_ASSERT(y >= 0); /* If y < 0 here, we can't make negative y */ - if (Y.s == -1) { - y = -y; - } - - mbedtls_mpi_sint a = (mbedtls_mpi_sint) A.p[0]; - TEST_ASSERT(a >= 0); /* Same goes for a */ - if (A.s == -1) { - a = -a; - } res = mbedtls_mpi_mod_int(&r, &X, y); TEST_EQUAL(res, mod_result); @@ -973,8 +942,6 @@ void mpi_mod_int(char *input_X, char *input_Y, exit: mbedtls_mpi_free(&X); - mbedtls_mpi_free(&Y); - mbedtls_mpi_free(&A); } /* END_CASE */ diff --git a/tests/suites/test_suite_bignum.misc.data b/tests/suites/test_suite_bignum.misc.data index 11c5d7b664..9d068f1467 100644 --- a/tests/suites/test_suite_bignum.misc.data +++ b/tests/suites/test_suite_bignum.misc.data @@ -1229,45 +1229,45 @@ Test mbedtls_mpi_mod_mpi: -0 (null) % -42 mpi_mod_mpi:"-":"-2a":"":MBEDTLS_ERR_MPI_NEGATIVE_VALUE Base test mbedtls_mpi_mod_int #1 -mpi_mod_int:"3e8":"d":"c":0 +mpi_mod_int:"3e8":0xd:0xc:0 Base test mbedtls_mpi_mod_int #2 (Divide by zero) -mpi_mod_int:"3e8":"0":"0":MBEDTLS_ERR_MPI_DIVISION_BY_ZERO +mpi_mod_int:"3e8":0x0:0x0:MBEDTLS_ERR_MPI_DIVISION_BY_ZERO Base test mbedtls_mpi_mod_int #3 -mpi_mod_int:"-3e8":"d":"1":0 +mpi_mod_int:"-3e8":0xd:0x1:0 Base test mbedtls_mpi_mod_int #4 (Negative modulo) -mpi_mod_int:"3e8":"-d":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE +mpi_mod_int:"3e8":-0xd:0x0:MBEDTLS_ERR_MPI_NEGATIVE_VALUE Base test mbedtls_mpi_mod_int #5 (Negative modulo) -mpi_mod_int:"-3e8":"-d":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE +mpi_mod_int:"-3e8":-0xd:0x0:MBEDTLS_ERR_MPI_NEGATIVE_VALUE Base test mbedtls_mpi_mod_int #6 (By 1) -mpi_mod_int:"3e8":"1":"0":0 +mpi_mod_int:"3e8":0x1:0x0:0 Base test mbedtls_mpi_mod_int #7 (By 2) -mpi_mod_int:"3e9":"2":"1":0 +mpi_mod_int:"3e9":0x2:0x1:0 Base test mbedtls_mpi_mod_int #8 (By 2) -mpi_mod_int:"3e8":"2":"0":0 +mpi_mod_int:"3e8":0x2:0x0:0 Test mbedtls_mpi_mod_int: 0 (null) % 1 -mpi_mod_int:"":"1":"0":0 +mpi_mod_int:"":0x1:0x0:0 Test mbedtls_mpi_mod_int: 0 (null) % 2 -mpi_mod_int:"":"2":"0":0 +mpi_mod_int:"":0x2:0x0:0 Test mbedtls_mpi_mod_int: 0 (null) % -1 -mpi_mod_int:"":"-1":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE +mpi_mod_int:"":-0x1:0x0:MBEDTLS_ERR_MPI_NEGATIVE_VALUE Test mbedtls_mpi_mod_int: 0 (null) % -2 -mpi_mod_int:"":"-2":"0":MBEDTLS_ERR_MPI_NEGATIVE_VALUE +mpi_mod_int:"":-0x2:0x0:MBEDTLS_ERR_MPI_NEGATIVE_VALUE # CURRENTLY FAILS - SEE GITHUB ISSUE #6540 #Test mbedtls_mpi_mod_int: 230772460340063000000100500000300000010 % 5178236083361335880 -> 3386266129388798810 #depends_on:MBEDTLS_HAVE_INT64 -#mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA4847DCCA48":"2EFE6F1A7D28035A":0 +#mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980CEE30A":0x47DCCA4847DCCA48:0x2EFE6F1A7D28035A:0 Test mbedtls_mpi_mod_mpi: 230772460340063000000100500000300000010 % 5178236083361335880 -> 3386266129388798810 mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA4847DCCA48":"2EFE6F1A7D28035A":0 @@ -1275,7 +1275,7 @@ mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA4847DCCA48":"2EFE6F1A7D280 # CURRENTLY FAILS - SEE GITHUB ISSUE #6540 #Test mbedtls_mpi_mod_int: 230772460340062999996714233870911201200 % 5178236083361335880 -> 0 #depends_on:MBEDTLS_HAVE_INT64 -#mpi_mod_int:"AD9D28BF6C4E98FDC2584FEF03A6DFB0":"47DCCA4847DCCA48":"0":0 +#mpi_mod_int:"AD9D28BF6C4E98FDC2584FEF03A6DFB0":0x47DCCA4847DCCA48:0x0:0 Test mbedtls_mpi_mod_mpi: 230772460340062999996714233870911201200 % 5178236083361335880 -> 0 mpi_mod_mpi:"AD9D28BF6C4E98FDC2584FEF03A6DFB0":"47DCCA4847DCCA48":"0":0 @@ -1283,7 +1283,7 @@ mpi_mod_mpi:"AD9D28BF6C4E98FDC2584FEF03A6DFB0":"47DCCA4847DCCA48":"0":0 # CURRENTLY FAILS WHEN MPIS ARE 32-BIT (ISSUE #6450): WHEN FIXED, REMOVE "depends_on" LINE Test mbedtls_mpi_mod_int: 230772460340063000000100500000300000010 % 1205652040 -> 3644370 depends_on:MBEDTLS_HAVE_INT64 -mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA48":"379BD2":0 +mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980CEE30A":0x47DCCA48:0x379BD2:0 Test mbedtls_mpi_mod_mpi: 230772460340063000000100500000300000010 % 1205652040 -> 3644370 mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA48":"379BD2":0 @@ -1291,7 +1291,7 @@ mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980CEE30A":"47DCCA48":"379BD2":0 # CURRENTLY FAILS WHEN MPIS ARE 32-BIT (ISSUE #6450): WHEN FIXED, REMOVE "depends_on" LINE Test mbedtls_mpi_mod_int: 230772460340063000000100500000296355640 % 1205652040 -> 0 depends_on:MBEDTLS_HAVE_INT64 -mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980974738":"47DCCA48":"0":0 +mpi_mod_int:"AD9D28BF6C4E98FDF156BF0980974738":0x47DCCA48:0x0:0 Test mbedtls_mpi_mod_mpi: 230772460340063000000100500000296355640 % 1205652040 -> 0 mpi_mod_mpi:"AD9D28BF6C4E98FDF156BF0980974738":"47DCCA48":"0":0 From 8b32d20c5014d4984b7e7cdee295c19cde4620ca Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 23 Feb 2023 20:47:24 +0100 Subject: [PATCH 18/24] Test the line number returned by parse_test_data Signed-off-by: Gilles Peskine --- tests/scripts/test_generate_test_code.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/scripts/test_generate_test_code.py b/tests/scripts/test_generate_test_code.py index effa46b589..48d3e53af7 100755 --- a/tests/scripts/test_generate_test_code.py +++ b/tests/scripts/test_generate_test_code.py @@ -1298,17 +1298,20 @@ dhm_selftest: # List of (name, function_name, dependencies, args) tests = list(parse_test_data(stream)) test1, test2, test3, test4 = tests + self.assertEqual(test1[0], 3) self.assertEqual(test1[1], 'Diffie-Hellman full exchange #1') self.assertEqual(test1[2], 'dhm_do_dhm') self.assertEqual(test1[3], []) self.assertEqual(test1[4], ['10', '"23"', '10', '"5"']) + self.assertEqual(test2[0], 6) self.assertEqual(test2[1], 'Diffie-Hellman full exchange #2') self.assertEqual(test2[2], 'dhm_do_dhm') self.assertEqual(test2[3], []) self.assertEqual(test2[4], ['10', '"93450983094850938450983409623"', '10', '"9345098304850938450983409622"']) + self.assertEqual(test3[0], 9) self.assertEqual(test3[1], 'Diffie-Hellman full exchange #3') self.assertEqual(test3[2], 'dhm_do_dhm') self.assertEqual(test3[3], []) @@ -1317,6 +1320,7 @@ dhm_selftest: '10', '"9345098792137312973297123912791271"']) + self.assertEqual(test4[0], 12) self.assertEqual(test4[1], 'Diffie-Hellman selftest') self.assertEqual(test4[2], 'dhm_selftest') self.assertEqual(test4[3], []) @@ -1340,11 +1344,13 @@ dhm_do_dhm:10:"93450983094850938450983409623":10:"9345098304850938450983409622" # List of (name, function_name, dependencies, args) tests = list(parse_test_data(stream)) test1, test2 = tests + self.assertEqual(test1[0], 4) self.assertEqual(test1[1], 'Diffie-Hellman full exchange #1') self.assertEqual(test1[2], 'dhm_do_dhm') self.assertEqual(test1[3], ['YAHOO']) self.assertEqual(test1[4], ['10', '"23"', '10', '"5"']) + self.assertEqual(test2[0], 7) self.assertEqual(test2[1], 'Diffie-Hellman full exchange #2') self.assertEqual(test2[2], 'dhm_do_dhm') self.assertEqual(test2[3], []) From fa83a7ec1e80307b7a8c81a5a9cc09842776483d Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 23 Feb 2023 21:54:06 +0100 Subject: [PATCH 19/24] Fix typos in test descriptions Signed-off-by: Gilles Peskine --- tests/suites/test_suite_platform_printf.data | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/suites/test_suite_platform_printf.data b/tests/suites/test_suite_platform_printf.data index e6d335296b..4be2f683a8 100644 --- a/tests/suites/test_suite_platform_printf.data +++ b/tests/suites/test_suite_platform_printf.data @@ -62,22 +62,22 @@ printf_long_max:"%lx":LONG_MAX # The next few test cases exercise how the test framework handles special # characters in strings. -printf "%c", SPACE, SPACE +printf "%c%c", SPACE, SPACE printf_char2:"%c%c":SPACE_CHAR:SPACE_CHAR:" " -printf "%c", NEWLINE, SPACE +printf "%c%c", NEWLINE, SPACE printf_char2:"%c%c":NEWLINE_CHAR:SPACE_CHAR:"\n " -printf "%c", DOUBLE QUOTE, SPACE +printf "%c%c", DOUBLE QUOTE, SPACE printf_char2:"%c%c":DOUBLE_QUOTE_CHAR:SPACE_CHAR:"\" " -printf "%c", COLON, SPACE +printf "%c%c", COLON, SPACE printf_char2:"%c%c":COLON_CHAR:SPACE_CHAR:"\: " -printf "%c", BACKSLASH, SPACE +printf "%c%c", BACKSLASH, SPACE printf_char2:"%c%c":BACKSLASH_CHAR:SPACE_CHAR:"\\ " -printf "%c", SPACE, BACKSLASH +printf "%c%c", SPACE, BACKSLASH printf_char2:"%c%c":SPACE_CHAR:BACKSLASH_CHAR:" \\" printf "%c%c", COLON, COLON From 5472242b67361885f2e6d87a4bf155771294a5ad Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 23 Feb 2023 21:54:59 +0100 Subject: [PATCH 20/24] Explain the format argument expected by the test functions Signed-off-by: Gilles Peskine --- .../test_suite_platform_printf.function | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/tests/suites/test_suite_platform_printf.function b/tests/suites/test_suite_platform_printf.function index 7c5e1e2cdc..f294a04726 100644 --- a/tests/suites/test_suite_platform_printf.function +++ b/tests/suites/test_suite_platform_printf.function @@ -1,4 +1,14 @@ /* BEGIN_HEADER */ + +/* The printf test functions take a format argument from the test data + * for several reasons: + * - For some tests, it makes sense to vary the format. + * - For all tests, it means we're testing the actual printf function + * that parses the format at runtime, and not a compiler optimization. + * (It may be useful to add tests that allow compiler optimizations. + * There aren't any yet at the time of writing.) + */ + #include "mbedtls/platform.h" #include @@ -14,7 +24,8 @@ /* END_HEADER */ /* BEGIN_CASE */ -void printf_int(char *format, int x, char *result) +void printf_int(char *format, /* any format expecting one int argument, e.g. "%d" */ + int x, char *result) { char *output = NULL; const size_t n = strlen(result); @@ -32,7 +43,8 @@ exit: /* END_CASE */ /* BEGIN_CASE */ -void printf_long_max(const char *format, long value) +void printf_long_max(const char *format, /* "%lx" or longer type */ + long value) { char *expected = NULL; char *output = NULL; @@ -57,7 +69,8 @@ exit: /* END_CASE */ /* BEGIN_CASE */ -void printf_char2(char *format, int arg1, int arg2, char *result) +void printf_char2(char *format, /* "%c%c" */ + int arg1, int arg2, char *result) { char *output = NULL; const size_t n = strlen(result); From 9a75131da16c4f0ad9a86bd89c77a59a4399bc04 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 23 Feb 2023 21:55:59 +0100 Subject: [PATCH 21/24] Fix wrong comment Signed-off-by: Gilles Peskine --- tests/suites/test_suite_platform_printf.function | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/suites/test_suite_platform_printf.function b/tests/suites/test_suite_platform_printf.function index f294a04726..1dd6ae5e11 100644 --- a/tests/suites/test_suite_platform_printf.function +++ b/tests/suites/test_suite_platform_printf.function @@ -48,7 +48,7 @@ void printf_long_max(const char *format, /* "%lx" or longer type */ { char *expected = NULL; char *output = NULL; - /* "0x" plus 2 hex digits per byte */ + /* 2 hex digits per byte */ const size_t n = sizeof(value) * 2; /* We assume that long has no padding bits! */ From 578613322a558567a36123e0e40809df81833759 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 26 Apr 2023 19:50:57 +0200 Subject: [PATCH 22/24] Add test cases with a question mark The test framework used to treat them specially (but no longer does). Add these test cases as non-regression for how the test framework allows "?" and especially "??" (which I think in the very distant path needed special handling because the test data was embedded in a .c file, and thus ?? could be interpreted as the prefix of a trigraph). Signed-off-by: Gilles Peskine --- tests/suites/test_suite_platform_printf.data | 12 ++++++++++++ tests/suites/test_suite_platform_printf.function | 1 + 2 files changed, 13 insertions(+) diff --git a/tests/suites/test_suite_platform_printf.data b/tests/suites/test_suite_platform_printf.data index 4be2f683a8..891771b919 100644 --- a/tests/suites/test_suite_platform_printf.data +++ b/tests/suites/test_suite_platform_printf.data @@ -74,6 +74,9 @@ printf_char2:"%c%c":DOUBLE_QUOTE_CHAR:SPACE_CHAR:"\" " printf "%c%c", COLON, SPACE printf_char2:"%c%c":COLON_CHAR:SPACE_CHAR:"\: " +printf "%c%c", QUESTION, SPACE +printf_char2:"%c%c":QUESTION_CHAR:SPACE_CHAR:"? " + printf "%c%c", BACKSLASH, SPACE printf_char2:"%c%c":BACKSLASH_CHAR:SPACE_CHAR:"\\ " @@ -86,6 +89,12 @@ printf_char2:"%c%c":COLON_CHAR:COLON_CHAR:"\:\:" printf "%c%c", COLON, NEWLINE printf_char2:"%c%c":COLON_CHAR:NEWLINE_CHAR:"\:\n" +printf "%c%c", QUESTION, QUESTION +printf_char2:"%c%c":QUESTION_CHAR:QUESTION_CHAR:"??" + +printf "%c%c", QUESTION, NEWLINE +printf_char2:"%c%c":QUESTION_CHAR:NEWLINE_CHAR:"?\n" + printf "%c%c", BACKSLASH, NEWLINE printf_char2:"%c%c":BACKSLASH_CHAR:NEWLINE_CHAR:"\\\n" @@ -95,6 +104,9 @@ printf_char2:"%c%c":BACKSLASH_CHAR:DOUBLE_QUOTE_CHAR:"\\\"" printf "%c%c", BACKSLASH, COLON printf_char2:"%c%c":BACKSLASH_CHAR:COLON_CHAR:"\\\:" +printf "%c%c", BACKSLASH, QUESTION +printf_char2:"%c%c":BACKSLASH_CHAR:QUESTION_CHAR:"\\?" + printf "%c%c", BACKSLASH, BACKSLASH printf_char2:"%c%c":BACKSLASH_CHAR:BACKSLASH_CHAR:"\\\\" diff --git a/tests/suites/test_suite_platform_printf.function b/tests/suites/test_suite_platform_printf.function index 1dd6ae5e11..3c816fe33b 100644 --- a/tests/suites/test_suite_platform_printf.function +++ b/tests/suites/test_suite_platform_printf.function @@ -19,6 +19,7 @@ #define SPACE_CHAR ' ' #define DOUBLE_QUOTE_CHAR '"' #define COLON_CHAR ':' +#define QUESTION_CHAR '?' #define BACKSLASH_CHAR '\\' #define LOWERCASE_N_CHAR 'n' /* END_HEADER */ From 2986accd207c44905ecfe426e4aaf3db064ea746 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 26 Apr 2023 19:57:46 +0200 Subject: [PATCH 23/24] typo Signed-off-by: Gilles Peskine --- tests/scripts/generate_test_code.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index f900fd2ba6..18a29d1e69 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -171,7 +171,7 @@ import string import argparse -# Types regognized as signed integer arguments in test functions. +# Types recognized as signed integer arguments in test functions. SIGNED_INTEGER_TYPES = frozenset([ 'char', 'short', From b70c4e07d0d1fa5492ff591e1b94f2d4cab32c17 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 26 Apr 2023 19:59:28 +0200 Subject: [PATCH 24/24] Adjust code style for pointer types and casts Signed-off-by: Gilles Peskine --- tests/scripts/generate_test_code.py | 6 +++--- tests/scripts/test_generate_test_code.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/scripts/generate_test_code.py b/tests/scripts/generate_test_code.py index 18a29d1e69..839fccdddd 100755 --- a/tests/scripts/generate_test_code.py +++ b/tests/scripts/generate_test_code.py @@ -322,7 +322,7 @@ def gen_function_wrapper(name, local_vars, args_dispatch): :param name: Test function name :param local_vars: Local variables declaration code :param args_dispatch: List of dispatch arguments. - Ex: ['(char *)params[0]', '*((int *)params[1])'] + Ex: ['(char *) params[0]', '*((int *) params[1])'] :return: Test function wrapper. """ # Then create the wrapper @@ -488,7 +488,7 @@ def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch): typ, _ = m.groups() if typ in SIGNED_INTEGER_TYPES: args.append('int') - args_dispatch.append('((mbedtls_test_argument_t*)params[%d])->sint' % arg_idx) + args_dispatch.append('((mbedtls_test_argument_t *) params[%d])->sint' % arg_idx) return 1 if typ in STRING_TYPES: args.append('char*') @@ -498,7 +498,7 @@ def parse_function_argument(arg, arg_idx, args, local_vars, args_dispatch): args.append('hex') # create a structure pointer_initializer = '(uint8_t *) params[%d]' % arg_idx - len_initializer = '((mbedtls_test_argument_t*)params[%d])->len' % (arg_idx+1) + len_initializer = '((mbedtls_test_argument_t *) params[%d])->len' % (arg_idx+1) local_vars.append(' data_t data%d = {%s, %s};\n' % (arg_idx, pointer_initializer, len_initializer)) args_dispatch.append('&data%d' % arg_idx) diff --git a/tests/scripts/test_generate_test_code.py b/tests/scripts/test_generate_test_code.py index 48d3e53af7..fe748aeb46 100755 --- a/tests/scripts/test_generate_test_code.py +++ b/tests/scripts/test_generate_test_code.py @@ -487,8 +487,8 @@ class ParseFuncSignature(TestCase): self.assertEqual(local, '') self.assertEqual(arg_dispatch, ['(char *) params[0]', - '((mbedtls_test_argument_t*)params[1])->sint', - '((mbedtls_test_argument_t*)params[2])->sint']) + '((mbedtls_test_argument_t *) params[1])->sint', + '((mbedtls_test_argument_t *) params[2])->sint']) def test_hex_params(self): """ @@ -500,10 +500,10 @@ class ParseFuncSignature(TestCase): self.assertEqual(args, ['char*', 'hex', 'int']) self.assertEqual(local, ' data_t data1 = {(uint8_t *) params[1], ' - '((mbedtls_test_argument_t*)params[2])->len};\n') + '((mbedtls_test_argument_t *) params[2])->len};\n') self.assertEqual(arg_dispatch, ['(char *) params[0]', '&data1', - '((mbedtls_test_argument_t*)params[3])->sint']) + '((mbedtls_test_argument_t *) params[3])->sint']) def test_unsupported_arg(self): """