From a237722118fea83d9fb261327d8b7357de43148b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Manuel=20P=C3=A9gouri=C3=A9-Gonnard?=
 <manuel.pegourie-gonnard@arm.com>
Date: Tue, 28 Jul 2020 10:53:06 +0200
Subject: [PATCH] Add MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This option allows to test the constant-flow nature of selected code, using
MemSan and the fundamental observation behind ctgrind that the set of
operations allowed on undefined memory by dynamic analysers is the same as the
set of operations allowed on secret data to avoid leaking it to a local
attacker via side channels, namely, any operation except branching and
dereferencing.

(This isn't the full story, as on some CPUs some instructions have variable
execution depending on the inputs, most notably division and on some cores
multiplication. However, testing that no branch or memory access depends on
secret data is already a good start.)

Signed-off-by: Manuel Pégourié-Gonnard <manuel.pegourie-gonnard@arm.com>
---
 include/mbedtls/check_config.h | 10 ++++++++++
 include/mbedtls/config.h       | 13 +++++++++++++
 library/version_features.c     |  3 +++
 programs/ssl/query_config.c    |  8 ++++++++
 scripts/config.pl              |  1 +
 tests/scripts/all.sh           | 12 ++++++++++++
 tests/suites/helpers.function  | 16 ++++++++++++++++
 7 files changed, 63 insertions(+)

diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h
index 8ce73ceff..2bcf960e9 100644
--- a/include/mbedtls/check_config.h
+++ b/include/mbedtls/check_config.h
@@ -199,6 +199,16 @@
 #error "MBEDTLS_ENTROPY_FORCE_SHA256 defined, but not all prerequisites"
 #endif
 
+#if defined(__has_feature)
+#if __has_feature(memory_sanitizer)
+#define MBEDTLS_HAS_MEMSAN
+#endif
+#endif
+#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN) &&  !defined(MBEDTLS_HAS_MEMSAN)
+#error "MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN requires building with MemorySanitizer"
+#endif
+#undef MBEDTLS_HAS_MEMSAN
+
 #if defined(MBEDTLS_TEST_NULL_ENTROPY) && \
     ( !defined(MBEDTLS_ENTROPY_C) || !defined(MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES) )
 #error "MBEDTLS_TEST_NULL_ENTROPY defined, but not all prerequisites"
diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h
index f7e55aef5..967668080 100644
--- a/include/mbedtls/config.h
+++ b/include/mbedtls/config.h
@@ -551,6 +551,19 @@
 //#define MBEDTLS_ECP_RANDOMIZE_MXZ_ALT
 //#define MBEDTLS_ECP_NORMALIZE_MXZ_ALT
 
+/**
+ * \def MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN
+ *
+ * Enable testing of the constant-flow nature of some sensitive functions with
+ * clang's MemorySanitizer. This causes some existing tests to also test
+ * non-functional properties of the code under test.
+ *
+ * This setting requires compiling with clang -fsanitize=memory.
+ *
+ * Uncomment to enable testing of the constant-flow nature of seletected code.
+ */
+//#define MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN
+
 /**
  * \def MBEDTLS_TEST_NULL_ENTROPY
  *
diff --git a/library/version_features.c b/library/version_features.c
index 51662bfd2..6b2a0c1b7 100644
--- a/library/version_features.c
+++ b/library/version_features.c
@@ -279,6 +279,9 @@ static const char *features[] = {
 #if defined(MBEDTLS_ECP_NORMALIZE_MXZ_ALT)
     "MBEDTLS_ECP_NORMALIZE_MXZ_ALT",
 #endif /* MBEDTLS_ECP_NORMALIZE_MXZ_ALT */
+#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN)
+    "MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN",
+#endif /* MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN */
 #if defined(MBEDTLS_TEST_NULL_ENTROPY)
     "MBEDTLS_TEST_NULL_ENTROPY",
 #endif /* MBEDTLS_TEST_NULL_ENTROPY */
diff --git a/programs/ssl/query_config.c b/programs/ssl/query_config.c
index 968549ac8..1e5c311d2 100644
--- a/programs/ssl/query_config.c
+++ b/programs/ssl/query_config.c
@@ -741,6 +741,14 @@ int query_config( const char *config )
     }
 #endif /* MBEDTLS_ECP_NORMALIZE_MXZ_ALT */
 
+#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN)
+    if( strcmp( "MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN", config ) == 0 )
+    {
+        MACRO_EXPANSION_TO_STR( MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN );
+        return( 0 );
+    }
+#endif /* MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN */
+
 #if defined(MBEDTLS_TEST_NULL_ENTROPY)
     if( strcmp( "MBEDTLS_TEST_NULL_ENTROPY", config ) == 0 )
     {
diff --git a/scripts/config.pl b/scripts/config.pl
index 006a58de7..38fa97cf8 100755
--- a/scripts/config.pl
+++ b/scripts/config.pl
@@ -128,6 +128,7 @@ MBEDTLS_REMOVE_3DES_CIPHERSUITES
 MBEDTLS_REMOVE_ARC4_CIPHERSUITES
 MBEDTLS_RSA_NO_CRT
 MBEDTLS_SSL_HW_RECORD_ACCEL
+MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN
 MBEDTLS_TEST_NULL_ENTROPY
 MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION
 MBEDTLS_ZLIB_SUPPORT
diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh
index d831d344c..02f61d6e3 100755
--- a/tests/scripts/all.sh
+++ b/tests/scripts/all.sh
@@ -1031,6 +1031,18 @@ component_test_full_cmake_clang () {
     if_build_succeeded env OPENSSL_CMD="$OPENSSL_NEXT" tests/compat.sh -e '^$' -f 'ARIA\|CHACHA'
 }
 
+component_test_memsan_constant_flow () {
+    msg "build: cmake memsan, full config with constant flow testing"
+    scripts/config.pl full
+    scripts/config.pl set MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN
+    scripts/config.pl unset MBEDTLS_AESNI_C # memsan doesn't grok asm
+    CC=clang cmake -D CMAKE_BUILD_TYPE:String=MemSan .
+    make
+
+    msg "test: main suites (memsan constant flow)"
+    make test
+}
+
 component_test_default_no_deprecated () {
     # Test that removing the deprecated features from the default
     # configuration leaves something consistent.
diff --git a/tests/suites/helpers.function b/tests/suites/helpers.function
index cd9346e5c..95b658da4 100644
--- a/tests/suites/helpers.function
+++ b/tests/suites/helpers.function
@@ -46,6 +46,22 @@ typedef UINT32 uint32_t;
 #include <strings.h>
 #endif
 
+#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN)
+#include <sanitizer/msan_interface.h>
+
+/* Use macros to avoid messing up with origin tracking */
+#define TEST_CF_SECRET  __msan_allocated_memory
+// void __msan_allocated_memory(const volatile void* data, size_t size);
+#define TEST_CF_PUBLIC  __msan_unpoison
+// void __msan_unpoison(const volatile void *a, size_t size);
+
+#else /* MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN */
+
+#define TEST_CF_SECRET(ptr, size)
+#define TEST_CF_PUBLIC(ptr, size)
+
+#endif /* MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN */
+
 /* Type for Hex parameters */
 typedef struct data_tag
 {