Commit dec31e17 authored by Mark Mentovai's avatar Mark Mentovai Committed by Commit Bot

mac-arm64: Use MAP_JIT for code not signed to use the hardened runtime

In a change from x86_64, macOS on arm64 requires MAP_JIT for code not
using the hardened runtime. However, if MAP_JIT is specified for signed
code lacking the JIT entitlement, mmap and mprotect fail with EINVAL, so
it’s still not possible to use MAP_JIT everywhere it could conceivably
be required.

A wasm attempt to mprotect a region as PROT_READ | PROT_WRITE |
PROT_EXEC was succeeding in signed builds but failing in
non-hardened-runtime processes with EPERM as a result of the mapping
lacking MAP_JIT.

In a previous (post-facto drive-by) review of this code
(https://chromium-review.googlesource.com/c/2078792), I noted “I would
have preferred the caller coming in with affirmative knowledge of
whether it wanted JIT or not” but the callers aren’t necessarily aware
of the entitlement regime they’re expected to run under.

Bug: 1144200
Change-Id: I2edde64922dfbd8ba7158cc741b0e82d680235b8
CQ-Include-Trybots: luci.chromium.try:mac-arm64-rel
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2523446Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Commit-Queue: Mark Mentovai <mark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#825684}
parent 14750589
......@@ -19,6 +19,7 @@
#include "base/mac/mac_util.h"
#include "base/mac/scoped_cftyperef.h"
#include <Availability.h>
#include <Security/Security.h>
#include <mach/mach.h>
#endif
......@@ -37,6 +38,24 @@
#define MAP_ANONYMOUS MAP_ANON
#endif
#if defined(OS_APPLE)
// SecTaskGetCodeSignStatus is marked as unavailable on macOS, although it’s
// available on iOS and other Apple operating systems. It is, in fact, present
// on the system since macOS 10.12.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wavailability"
uint32_t SecTaskGetCodeSignStatus(SecTaskRef task)
#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_12
// When redeclaring something previously declared as unavailable, the
// weak_import attribute won’t be applied unless manually set.
__attribute__((weak_import))
#endif // DT < 10.12
API_AVAILABLE(macos(10.12));
#pragma clang diagnostic pop
#endif // OS_APPLE
namespace base {
namespace {
......@@ -67,19 +86,50 @@ const char* PageTagToName(PageTag tag) {
#if defined(OS_APPLE)
// Tests whether the version of macOS supports the MAP_JIT flag and if the
// current process is signed with the allow-jit entitlement.
// current process is signed with the hardened runtime and the allow-jit
// entitlement, returning whether MAP_JIT should be used to allocate regions
// that will contain JIT-compiled executable code.
bool UseMapJit() {
if (!mac::IsAtLeastOS10_14())
if (!mac::IsAtLeastOS10_14()) {
// MAP_JIT existed before macOS 10.14, but had somewhat different semantics.
// Only one MAP_JIT region was permitted per process, but calling code here
// will very likely require more than one such region. Since MAP_JIT is not
// strictly necessary to write code to a region and then execute it on these
// older OSes, don’t use it at all.
return false;
}
// Until determining that the hardened runtime is enabled, early returns will
// return true, so that MAP_JIT will be used. This is important on arm64,
// which only allows pages to be simultaneously writable and executable when
// in a region allocated with MAP_JIT, regardless of code signing options. On
// arm64, an attempt to set a non-MAP_JIT page as simultaneously writable and
// executable fails with EPERM. Although this is not enforced on x86_64,
// MAP_JIT is harmless in that case.
ScopedCFTypeRef<SecTaskRef> task(SecTaskCreateFromSelf(kCFAllocatorDefault));
ScopedCFTypeRef<CFErrorRef> error;
ScopedCFTypeRef<CFTypeRef> value(SecTaskCopyValueForEntitlement(
task.get(), CFSTR("com.apple.security.cs.allow-jit"),
error.InitializeInto()));
if (error)
if (!task) {
return true;
}
uint32_t flags = SecTaskGetCodeSignStatus(task);
if ((flags & kSecCodeSignatureRuntime) != 0) {
// The hardened runtime is not enabled. Note that kSecCodeSignatureRuntime
// == CS_RUNTIME.
return true;
}
// The hardened runtime is enabled. From this point on, early returns must
// return false, indicating that MAP_JIT is not to be used. It’s an error
// (EINVAL) to use MAP_JIT with the hardened runtime unless the JIT
// entitlement is specified.
ScopedCFTypeRef<CFTypeRef> jit_entitlement(SecTaskCopyValueForEntitlement(
task.get(), CFSTR("com.apple.security.cs.allow-jit"), nullptr));
if (!jit_entitlement)
return false;
return mac::CFCast<CFBooleanRef>(value.get()) == kCFBooleanTrue;
return mac::CFCast<CFBooleanRef>(jit_entitlement.get()) == kCFBooleanTrue;
}
#endif // defined(OS_APPLE)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment