Add X.509 CRT test for nested calls for CRT frame / PK acquire
diff --git a/tests/suites/test_suite_x509parse.function b/tests/suites/test_suite_x509parse.function
index 2df187d..25b0d7f 100644
--- a/tests/suites/test_suite_x509parse.function
+++ b/tests/suites/test_suite_x509parse.function
@@ -597,6 +597,99 @@
 }
 /* END_CASE */
 
+/* BEGIN_CASE depends_on:MBEDTLS_X509_CRT_PARSE_C */
+void x509_nested_acquire( data_t * buf )
+{
+    /* This tests exercises the behavior of the library when
+     * facing nested calls to mbedtls_x509_crt_xxx_acquire().
+     * This is allowed if !MBEDTLS_X509_ALWAYS_FLUSH or
+     * MBEDTLS_THREADING_C, but forbidden otherwise. */
+
+    mbedtls_x509_crt crt;
+    mbedtls_x509_crt_init( &crt );
+    TEST_ASSERT( mbedtls_x509_crt_parse_der( &crt, buf->x, buf->len ) == 0 );
+
+    /* Nested aquire for CRT frames */
+    {
+        int ret;
+        mbedtls_x509_crt_frame const *frame1;
+        mbedtls_x509_crt_frame const *frame2;
+
+        /* Perform a (hopefully) innocent acquire-release pair first. */
+
+        TEST_ASSERT( mbedtls_x509_crt_frame_acquire( &crt, &frame1 ) == 0 );
+        TEST_ASSERT( mbedtls_x509_crt_frame_release( &crt ) == 0 );
+
+        /* Perform two nested acquire calls. */
+
+        TEST_ASSERT( mbedtls_x509_crt_frame_acquire( &crt, &frame1 ) == 0 );
+
+        ret = mbedtls_x509_crt_frame_acquire( &crt, &frame2 );
+#if defined(MBEDTLS_X509_ALWAYS_FLUSH) &&      \
+    !defined(MBEDTLS_THREADING_C)
+        TEST_ASSERT( ret == MBEDTLS_ERR_X509_FATAL_ERROR );
+#else
+        TEST_ASSERT( ret == 0 );
+        TEST_ASSERT( mbedtls_x509_crt_frame_release( &crt ) == 0 );
+#endif
+
+        TEST_ASSERT( mbedtls_x509_crt_frame_release( &crt ) == 0 );
+
+        ret = mbedtls_x509_crt_frame_release( &crt );
+
+        /* In contexts which use resource counting, we expect an
+         * error on an attempted release() without prior acquire(). */
+#if defined(MBEDTLS_X509_ALWAYS_FLUSH) &&      \
+    !defined(MBEDTLS_THREADING_C)
+        TEST_ASSERT( ret == 0 );
+#else
+        TEST_ASSERT( ret == MBEDTLS_ERR_X509_FATAL_ERROR );
+#endif
+    }
+
+    /* Nested aquire for PK contexts */
+    {
+        int ret;
+        mbedtls_pk_context *pk1;
+        mbedtls_pk_context *pk2;
+
+        /* Perform a (hopefully) innocent acquire-release pair first. */
+
+        TEST_ASSERT( mbedtls_x509_crt_pk_acquire( &crt, &pk1 ) == 0 );
+        TEST_ASSERT( mbedtls_x509_crt_pk_release( &crt ) == 0 );
+
+        /* Perform two nested acquire calls. */
+
+        TEST_ASSERT( mbedtls_x509_crt_pk_acquire( &crt, &pk1 ) == 0 );
+
+        ret = mbedtls_x509_crt_pk_acquire( &crt, &pk2 );
+#if defined(MBEDTLS_X509_ALWAYS_FLUSH) &&      \
+    !defined(MBEDTLS_THREADING_C)
+        TEST_ASSERT( ret == MBEDTLS_ERR_X509_FATAL_ERROR );
+#else
+        TEST_ASSERT( ret == 0 );
+        TEST_ASSERT( mbedtls_x509_crt_pk_release( &crt ) == 0 );
+#endif
+
+        TEST_ASSERT( mbedtls_x509_crt_pk_release( &crt ) == 0 );
+
+        ret = mbedtls_x509_crt_pk_release( &crt );
+
+        /* In contexts which use resource counting, we expect an
+         * error on an attempted release() without prior acquire(). */
+#if defined(MBEDTLS_X509_ALWAYS_FLUSH) &&      \
+    !defined(MBEDTLS_THREADING_C)
+        TEST_ASSERT( ret == 0 );
+#else
+        TEST_ASSERT( ret == MBEDTLS_ERR_X509_FATAL_ERROR );
+#endif
+    }
+
+exit:
+    mbedtls_x509_crt_free( &crt );
+}
+/* END_CASE */
+
 /* BEGIN_CASE depends_on:MBEDTLS_X509_CRL_PARSE_C:!MBEDTLS_X509_REMOVE_INFO */
 void x509parse_crl( data_t * buf, char * result_str, int result )
 {