Add a value barrier to constant-time selects.
Clang recognizes the (mask & a) | (~mask & b) pattern as a select. While
it often optimizes this into a cmov, it sometimes inserts branches
instead, particularly when it detects a string of cmovs with the same
condition.
In the long term, we need language-level support for expressing our
constraints. In the short term, introduce value barriers to prevent the
compiler from reasoning about our bit tricks. Thanks to Chandler Carruth
for suggesting this pattern. It should be reasonably robust, short of
value-based PGO or the compiler learning to reason about empty inline
assembly blocks.
Apply barriers to our various constant-time selects. We should invest
more in the valgrind-based tooling to figure out if there are other
instances.
Change-Id: Icc24ce36a61f7fec021a762c27197b9c5bd28c5d
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/36484
Reviewed-by: Chandler Carruth <chandlerc@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/constant_time_test.cc b/crypto/constant_time_test.cc
index 59a7bb1..ae80003 100644
--- a/crypto/constant_time_test.cc
+++ b/crypto/constant_time_test.cc
@@ -153,3 +153,19 @@
}
}
}
+
+TEST(ConstantTimeTest, ValueBarrier) {
+ for (int i = 0; i < 10; i++) {
+ crypto_word_t word;
+ RAND_bytes(reinterpret_cast<uint8_t *>(&word), sizeof(word));
+ EXPECT_EQ(word, value_barrier_w(word));
+
+ uint32_t u32;
+ RAND_bytes(reinterpret_cast<uint8_t *>(&u32), sizeof(u32));
+ EXPECT_EQ(u32, value_barrier_u32(u32));
+
+ uint64_t u64;
+ RAND_bytes(reinterpret_cast<uint8_t *>(&u64), sizeof(u64));
+ EXPECT_EQ(u64, value_barrier_u64(u64));
+ }
+}