Add a few more assertions to compiler_test.cc.

Also turn assertions into static_assert where we can.

These should be no-ops with existing assertions. The int assertion is
tighter, but we already assert this in constant_time_declassify_int. We
cannot support 64-bit int because it messes up integer promotion rules.

Change-Id: I628d2d7decdfa8bc01d8c6013bc7c20f927d63b1
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57785
Reviewed-by: Adam Langley <agl@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
diff --git a/crypto/compiler_test.cc b/crypto/compiler_test.cc
index 29375a5..9102337 100644
--- a/crypto/compiler_test.cc
+++ b/crypto/compiler_test.cc
@@ -57,27 +57,49 @@
 }
 
 TEST(CompilerTest, IntegerRepresentation) {
-  EXPECT_EQ(8, CHAR_BIT);
-  EXPECT_EQ(0xff, static_cast<int>(UCHAR_MAX));
+  static_assert(CHAR_BIT == 8, "BoringSSL only supports 8-bit chars");
+  static_assert(UCHAR_MAX == 0xff, "BoringSSL only supports 8-bit chars");
 
-  // uint8_t is assumed to be unsigned char. I.e., casting to uint8_t should be
-  // as good as unsigned char for strict aliasing purposes.
+  // Require that |unsigned char| and |uint8_t| be the same type. We require
+  // that type-punning through |uint8_t| is not a strict aliasing violation. In
+  // principle, type-punning should be done with |memcpy|, which would make this
+  // moot.
+  //
+  // However, C made too many historical mistakes with the types and signedness
+  // of character strings. As a result, aliasing between all variations on 8-bit
+  // chars are a practical necessity for all real C code. We do not support
+  // toolchains that break this assumption.
+  static_assert(
+      std::is_same<unsigned char, uint8_t>::value,
+      "BoringSSL requires uint8_t and unsigned char be the same type");
   uint8_t u8 = 0;
   unsigned char *ptr = &u8;
   (void)ptr;
 
   // Sized integers have the expected size.
-  EXPECT_EQ(1u, sizeof(uint8_t));
-  EXPECT_EQ(2u, sizeof(uint16_t));
-  EXPECT_EQ(4u, sizeof(uint32_t));
-  EXPECT_EQ(8u, sizeof(uint64_t));
+  static_assert(sizeof(uint8_t) == 1u, "uint8_t has the wrong size");
+  static_assert(sizeof(uint16_t) == 2u, "uint16_t has the wrong size");
+  static_assert(sizeof(uint32_t) == 4u, "uint32_t has the wrong size");
+  static_assert(sizeof(uint64_t) == 8u, "uint64_t has the wrong size");
 
   // size_t does not exceed uint64_t.
-  EXPECT_LE(sizeof(size_t), 8u);
+  static_assert(sizeof(size_t) <= 8u, "size_t must not exceed uint64_t");
 
-  // int must be 32-bit or larger.
-  EXPECT_LE(0x7fffffff, INT_MAX);
-  EXPECT_LE(0xffffffffu, UINT_MAX);
+  // Require that |int| be exactly 32 bits. OpenSSL historically mixed up
+  // |unsigned| and |uint32_t|, so we require it be at least 32 bits. Requiring
+  // at most 32-bits is a bit more subtle. C promotes arithemetic operands to
+  // |int| when they fit. But this means, if |int| is 2N bits wide, multiplying
+  // two maximum-sized |uintN_t|s is undefined by integer overflow!
+  //
+  // We attempt to handle this for |uint16_t|, assuming a 32-bit |int|, but we
+  // make no attempts to correct for this with |uint32_t| for a 64-bit |int|.
+  // Thus BoringSSL does not support ILP64 platforms.
+  //
+  // This test is on |INT_MAX| and |INT32_MAX| rather than sizeof because it is
+  // theoretically allowed for sizeof(int) to be 4 but include padding bits.
+  static_assert(INT_MAX == INT32_MAX, "BoringSSL requires int be 32-bit");
+  static_assert(UINT_MAX == UINT32_MAX,
+                "BoringSSL requires unsigned be 32-bit");
 
   CheckRepresentation(static_cast<signed char>(127));
   CheckRepresentation(static_cast<signed char>(1));