Change the use of setjmp/longjmp in parameter failure callback

Change the use of setjmp and longjmp in signalling parameter validation failures
when using the MBEDTLS_CHECK_PARAMS config.h option. This change allows
all calls which might result in a call to the parameter validation failure
handler to always be caught, even without use of the new macros, by placing a
setjmp() in the outer function which calls the test function, which the handler
can jump to.

This has several benefits:
    * it allows us to remove the clang compiler warning (-Wclobbered) caused
      by local auto variables being in the same function as the call to setjmp.
    * removes the need to wrap all function calls in the test functions with the
      TEST_ASSERT() macro. Now all parameter validation function calls should be
      caught.
diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function
index 2d1f692..3ae5471 100644
--- a/tests/suites/helpers.function
+++ b/tests/suites/helpers.function
@@ -27,10 +27,6 @@
 #include <setjmp.h>
 #define MBEDTLS_PARAM_FAILED(x)    mbedtls_param_failed( #x )
 
-#if defined(__GNUC__) && !defined(__clang__)
-#pragma GCC diagnostic ignored "-Wno-uninitialized"
-#endif
-
 #endif /* MBEDTLS_CHECK_PARAMS */
 
 #ifdef _MSC_VER
@@ -75,12 +71,19 @@
 #define DISPATCH_UNSUPPORTED_SUITE      -5  /* Test suite not supported by the
                                                build */
 
+typedef enum
+{
+    PARAMFAIL_TESTSTATE_IDLE = 0,           /* No parameter failure call test */
+    PARAMFAIL_TESTSTATE_PENDING,            /* Test call to the parameter failure
+                                             * is pending */
+    PARAMFAIL_TESTSTATE_CALLED              /* The test call to the parameter
+                                             * failure function has been made */
+} paramfail_test_state_t;
+
 
 /*----------------------------------------------------------------------------*/
 /* Macros */
 
-#if defined(MBEDTLS_CHECK_PARAMS)
-
 /**
  * \brief   This macro tests the expression passed to it as a test step or
  *          individual test in a test case.
@@ -96,53 +99,17 @@
  *
  * \param   TEST    The test expression to be tested.
  */
-#define TEST_ASSERT( TEST )                             \
-    do {                                                \
-        if ( setjmp( param_fail_jmp ) == 0 )            \
-        {                                               \
-            if( ! (TEST) )                              \
-            {                                           \
-                test_fail( #TEST, __LINE__, __FILE__ ); \
-                goto exit;                              \
-            }                                           \
-        }                                               \
-        else                                            \
-        {                                               \
-            test_fail( #TEST, __LINE__, __FILE__ );     \
-            goto exit;                                  \
-        }                                               \
-       memset( param_fail_jmp, 0, sizeof(jmp_buf) );    \
+
+#define TEST_ASSERT( TEST )                                 \
+    do {                                                    \
+       if( ! (TEST) )                                       \
+       {                                                    \
+          test_fail( #TEST, __LINE__, __FILE__ );           \
+          goto exit;                                        \
+       }                                                    \
     } while( 0 )
 
-/**
- * \brief   This macro tests and individual function call as a test step or
- *          individual test in a test case.
- *
- *          It does not require a library function to return a value, and cannot
-            tets a return error code that can be tested.
- *
- *          When MBEDTLS_CHECK_PARAMS is enabled, calls to the parameter failure
- *          callback, MBEDTLS_PARAM_FAIL, will be assumed to be a test failure.
- *
- *          This macro is not suitable for negative parameter validation tests
- *          as it assumes the test step will not create an error.
- *
- * \param   TEST    The test statement to be executed.
- */
-#define TEST_FN( TEST )                                 \
-    do {                                                \
-        if ( setjmp( param_fail_jmp ) == 0 )            \
-        {                                               \
-            TEST;                                       \
-        }                                               \
-        else                                            \
-        {                                               \
-            test_fail( #TEST, __LINE__, __FILE__ );     \
-            goto exit;                                  \
-        }                                               \
-       memset( param_fail_jmp, 0, sizeof(jmp_buf) );    \
-    } while( 0 )
-
+#if defined(MBEDTLS_CHECK_PARAMS)
 /**
  * \brief   This macro tests the statement passed to it as a test step or
  *          individual test in a test case. The macro assumes the test will fail
@@ -163,18 +130,16 @@
  *
  * \param   TEST                The test expression to be tested.
  */
-#define TEST_INVALID_PARAM_RET( PARAM_ERR_VALUE, TEST ) \
-    do {                                                \
-        if ( setjmp( param_fail_jmp ) == 0 )            \
-        {                                               \
-            if( (TEST) != PARAM_ERR_VALUE)              \
-            {                                           \
-                test_fail( #TEST, __LINE__, __FILE__ ); \
-                goto exit;                              \
-            }                                           \
-       }                                                \
-       memset( param_fail_jmp, 0, sizeof(jmp_buf) );    \
-    } while( 0 )
+#define TEST_INVALID_PARAM_RET( PARAM_ERR_VALUE, TEST )                     \
+    do {                                                                    \
+        test_info.paramfail_test_state = PARAMFAIL_TESTSTATE_PENDING;       \
+        if( (TEST) != (PARAM_ERR_VALUE) &&                                  \
+            test_info.paramfail_test_state != PARAMFAIL_TESTSTATE_CALLED )  \
+        {                                                                   \
+            test_fail( #TEST, __LINE__, __FILE__ );                         \
+            goto exit;                                                      \
+        }                                                                   \
+   } while( 0 )
 
 /**
  * \brief   This macro tests the statement passed to it as a test step or
@@ -196,33 +161,20 @@
  *
  * \param   TEST                The test expression to be tested.
  */
-#define TEST_INVALID_PARAM( TEST ) \
-    do {                                                \
-        if ( setjmp( param_fail_jmp ) == 0 )            \
-        {                                               \
-            TEST;                                       \
-            test_fail( #TEST, __LINE__, __FILE__ );     \
-            goto exit;                                  \
-       }                                                \
-       memset( param_fail_jmp, 0, sizeof(jmp_buf) );    \
+#define TEST_INVALID_PARAM( TEST )                                          \
+    do {                                                                    \
+        memcpy(jmp_tmp, param_fail_jmp, sizeof(jmp_buf));                   \
+        if ( setjmp( param_fail_jmp ) == 0 )                                \
+        {                                                                   \
+            TEST;                                                           \
+            test_fail( #TEST, __LINE__, __FILE__ );                         \
+            goto exit;                                                      \
+        }                                                                   \
+        memcpy(param_fail_jmp, jmp_tmp, sizeof(jmp_buf));                   \
     } while( 0 )
 
 #else
 
-#define TEST_ASSERT( TEST )                             \
-    do {                                                \
-        if( ! (TEST) )                                  \
-        {                                               \
-            test_fail( #TEST, __LINE__, __FILE__ );     \
-            goto exit;                                  \
-        }                                               \
-    } while( 0 )
-
-#define TEST_FN( TEST )                                 \
-    do {                                                \
-            TEST;                                       \
-    } while( 0 )
-
 #define TEST_INVALID_PARAM_RET( PARAM_ERR_VALUE, TEST ) \
     do {                                                \
         if( (TEST) != (PARAM_ERR_VALUE) )               \
@@ -273,9 +225,9 @@
 /*----------------------------------------------------------------------------*/
 /* Global variables */
 
-
 static struct
 {
+    paramfail_test_state_t paramfail_test_state;
     int failed;
     const char *test;
     const char *filename;
@@ -289,6 +241,7 @@
 
 #if defined(MBEDTLS_CHECK_PARAMS)
 jmp_buf param_fail_jmp;
+jmp_buf jmp_tmp;
 #endif
 
 /*----------------------------------------------------------------------------*/
@@ -308,6 +261,15 @@
 
 /*----------------------------------------------------------------------------*/
 /* Helper Functions */
+
+static void test_fail( const char *test, int line_no, const char* filename )
+{
+    test_info.failed = 1;
+    test_info.test = test;
+    test_info.line_no = line_no;
+    test_info.filename = filename;
+}
+
 static int platform_setup()
 {
     int ret = 0;
@@ -327,11 +289,22 @@
 #if defined(MBEDTLS_CHECK_PARAMS)
 void mbedtls_param_failed( char* failure_condition, char* file, int line )
 {
-    (void)failure_condition;
-    (void)file;
-    (void)line;
+    /* If we are testing the callback function...  */
+    if ( test_info.paramfail_test_state == PARAMFAIL_TESTSTATE_PENDING )
+    {
+        test_info.paramfail_test_state = PARAMFAIL_TESTSTATE_CALLED;
+    }
+    else
+    {
+        /* ...else we treat this as an error */
 
-    longjmp( param_fail_jmp, 1 );
+        /* Record the location of the failure, but not as a failure yet, in case
+         * it was part of the test */
+        test_fail( failure_condition, line, file );
+        test_info.failed = 0;
+
+        longjmp( param_fail_jmp, 1 );
+    }
 }
 #endif
 
@@ -623,14 +596,6 @@
     return( 0 );
 }
 
-static void test_fail( const char *test, int line_no, const char* filename )
-{
-    test_info.failed = 1;
-    test_info.test = test;
-    test_info.line_no = line_no;
-    test_info.filename = filename;
-}
-
 int hexcmp( uint8_t * a, uint8_t * b, uint32_t a_len, uint32_t b_len )
 {
     int ret = 0;
diff --git a/tests/suites/host_test.function b/tests/suites/host_test.function
index b354af4..3c43032 100644
--- a/tests/suites/host_test.function
+++ b/tests/suites/host_test.function
@@ -546,6 +546,7 @@
             if( unmet_dep_count == 0 )
             {
                 test_info.failed = 0;
+                test_info.paramfail_test_state = PARAMFAIL_TESTSTATE_IDLE;
 
 #if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
                 /* Suppress all output from the library unless we're verbose
diff --git a/tests/suites/main_test.function b/tests/suites/main_test.function
index 2ba919c..ca4783d 100644
--- a/tests/suites/main_test.function
+++ b/tests/suites/main_test.function
@@ -134,9 +134,39 @@
 #line $line_no "suites/main_test.function"
 };
 
+/**
+ * \brief        Execute the test function.
+ *
+ *               This is a wrapper function around the test function execution
+ *               to allow the setjmp() call used to catch any calls to the
+ *               parameter failure callback, to be used. Calls to setjmp()
+ *               can invalidate the state of any local auto variables.
+ *
+ * \param fp     Function pointer to the test function
+ * \param params Parameters to pass
+ *
+ */
+void execute_function_ptr(TestWrapper_t fp, void **params)
+{
+#if defined(MBEDTLS_CHECK_PARAMS)
+    if ( setjmp( param_fail_jmp ) == 0 )
+    {
+        fp( params );
+    }
+    else
+    {
+        /* Unexpected parameter validation error */
+        test_info.failed = 1;
+    }
+
+    memset( param_fail_jmp, 0, sizeof(jmp_buf) );
+#else
+    fp( params );
+#endif
+}
 
 /**
- * \brief       Dispatches test functions based on function index.
+ * \brief        Dispatches test functions based on function index.
  *
  * \param exp_id    Test function index.
  *
@@ -153,7 +183,7 @@
     {
         fp = test_funcs[func_idx];
         if ( fp )
-            fp( params );
+            execute_function_ptr(fp, params);
         else
             ret = DISPATCH_UNSUPPORTED_SUITE;
     }