Add page protection logic to BCM self test.
On Android only, assume code is compiled for Execute-only memory, so add
Read permission for the duration of the self test and
then mark the pages Execute-only again.
NB if libcrypto is not compiled for Execute-only memory, then attempting
to change the page permissions to PROT_EXEC at the start of the self
test does not cause it to fail, however changing it to PROT_NONE does.
Bug: 134580074
Test: m && flashall
Test: Manual tests described above.
Change-Id: Ibbf8405a5a9b162ce26532127aaf62c539cd9163
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/37644
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/fipsmodule/bcm.c b/crypto/fipsmodule/bcm.c
index 559ade3..54f113b 100644
--- a/crypto/fipsmodule/bcm.c
+++ b/crypto/fipsmodule/bcm.c
@@ -19,6 +19,10 @@
#include <openssl/crypto.h>
#include <stdlib.h>
+#if defined(BORINGSSL_FIPS)
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
#include <openssl/digest.h>
#include <openssl/hmac.h>
@@ -99,6 +103,7 @@
#if defined(BORINGSSL_FIPS)
#if !defined(OPENSSL_ASAN)
+
// These symbols are filled in by delocate.go (in static builds) or a linker
// script (in shared builds). They point to the start and end of the module, and
// the location of the integrity hash, respectively.
@@ -109,9 +114,31 @@
extern const uint8_t BORINGSSL_bcm_rodata_start[];
extern const uint8_t BORINGSSL_bcm_rodata_end[];
#endif
+
+#if defined(OPENSSL_ANDROID)
+static void BORINGSSL_maybe_set_module_text_permissions(int permission) {
+ // Android may be compiled in execute-only-memory mode, in which case the
+ // .text segment cannot be read. That conflicts with the need for a FIPS
+ // module to hash its own contents, therefore |mprotect| is used to make
+ // the module's .text readable for the duration of the hashing process. In
+ // other build configurations this is a no-op.
+ const uintptr_t page_size = getpagesize();
+ const uintptr_t page_start =
+ ((uintptr_t)BORINGSSL_bcm_text_start) & ~(page_size - 1);
+
+ if (mprotect((void *)page_start,
+ ((uintptr_t)BORINGSSL_bcm_text_end) - page_start,
+ permission) != 0) {
+ perror("BoringSSL: mprotect");
+ }
+}
+#else
+static void BORINGSSL_maybe_set_module_text_permissions(int permission) {}
+#endif // !ANDROID
+
#else
static const uint8_t BORINGSSL_bcm_text_hash[SHA512_DIGEST_LENGTH] = {0};
-#endif
+#endif // !ASAN
static void __attribute__((constructor))
BORINGSSL_bcm_power_on_self_test(void) {
@@ -138,6 +165,8 @@
fprintf(stderr, "HMAC_Init_ex failed.\n");
goto err;
}
+
+ BORINGSSL_maybe_set_module_text_permissions(PROT_READ | PROT_EXEC);
#if defined(BORINGSSL_SHARED_LIBRARY)
uint64_t length = end - start;
HMAC_Update(&hmac_ctx, (const uint8_t *) &length, sizeof(length));
@@ -149,6 +178,8 @@
#else
HMAC_Update(&hmac_ctx, start, end - start);
#endif
+ BORINGSSL_maybe_set_module_text_permissions(PROT_EXEC);
+
if (!HMAC_Final(&hmac_ctx, result, &result_len) ||
result_len != sizeof(result)) {
fprintf(stderr, "HMAC failed.\n");