Skip to content

Commit

Permalink
Handle errors from mmap and mprotect due to Apple Hardened Runtime.
Browse files Browse the repository at this point in the history
On Apple Silicon, when Hardened Runtime is in force and the
application lacks the "Allow Unsigned Executable Memory Entitlement",
mmap and mprotect return EACCES when an attempt is made to create or
modify memory so that it is simultaneously writable and executable.

This commit handles these cases by retrying the operation without the
PROT_EXEC flag, and setting global variables so that future operations
omit the flag.
  • Loading branch information
gareth-rees committed Dec 23, 2022
1 parent 73cc748 commit 136b2b6
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 8 deletions.
17 changes: 17 additions & 0 deletions code/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -710,6 +710,23 @@
#define WB_DEFER_HIT 1 /* boring scans after barrier hit */


/* Apple Hardened Runtime
*
* .hardened-runtime: On Apple Silicon, applications may be compiled
* with Hardened Runtime enabled. These applications have restricted
* capabilities: in particular, unless the "Allow Unsigned Executable
* Memory Entitlement" is enabled, these applications cannot create
* memory that is simultaneously writable and executable. Attempts to
* do so using mmap() and mprotect() fail with EACCES.
*
* See <https://developer.apple.com/documentation/security/hardened_runtime>
*/
#if defined(MPS_OS_XC) && defined(MPS_ARCH_A6)
#define MAYBE_HARDENED_RUNTIME 1
#else
#define MAYBE_HARDENED_RUNTIME 0
#endif

#endif /* config_h */


Expand Down
22 changes: 19 additions & 3 deletions code/protix.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,29 @@

#include "vm.h"

#include <errno.h>
#include <limits.h>
#include <signal.h> /* sig_atomic_t */
#include <stddef.h>
#include <sys/mman.h>
#include <sys/types.h>

SRCID(protix, "$Id$");


/* Value for memory protection corresponding to AccessSetEMPTY. */

static sig_atomic_t prot_all = PROT_READ | PROT_WRITE | PROT_EXEC;


/* ProtSet -- set protection
*
* This is just a thin veneer on top of mprotect(2).
*/

void ProtSet(Addr base, Addr limit, AccessSet mode)
{
int flags;
int flags, result;

AVER(sizeof(size_t) == sizeof(Addr));
AVER(base < limit);
Expand All @@ -82,15 +90,23 @@ void ProtSet(Addr base, Addr limit, AccessSet mode)
flags = PROT_READ | PROT_EXEC;
break;
case AccessSetEMPTY:
flags = PROT_READ | PROT_WRITE | PROT_EXEC;
flags = prot_all;
break;
default:
NOTREACHED;
flags = PROT_NONE;
}

/* .assume.mprotect.base */
if(mprotect((void *)base, (size_t)AddrOffset(base, limit), flags) != 0)
result = mprotect((void *)base, (size_t)AddrOffset(base, limit), flags);
if (MAYBE_HARDENED_RUNTIME && result != 0 && errno == EACCES
&& (flags & PROT_WRITE) && (flags & PROT_EXEC))
{
/* See <config.h#hardened-runtime>. */
prot_all = PROT_READ | PROT_WRITE;
result = mprotect((void *)base, (size_t)AddrOffset(base, limit), flags & prot_all);
}
if (result != 0)
NOTREACHED;
}

Expand Down
25 changes: 20 additions & 5 deletions code/vmix.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
#include "vm.h"

#include <errno.h> /* errno */
#include <signal.h> /* sig_atomic_t */
#include <sys/mman.h> /* see .feature.li in config.h */
#include <sys/types.h> /* mmap, munmap */
#include <unistd.h> /* getpagesize */
Expand Down Expand Up @@ -156,11 +157,17 @@ void VMFinish(VM vm)
}


/* Value to use for protection of newly allocated pages. */

static sig_atomic_t vm_prot = PROT_READ | PROT_WRITE | PROT_EXEC;


/* VMMap -- map the given range of memory */

Res VMMap(VM vm, Addr base, Addr limit)
{
Size size;
void *result;

AVERT(VM, vm);
AVER(sizeof(void *) == sizeof(Addr));
Expand All @@ -172,11 +179,19 @@ Res VMMap(VM vm, Addr base, Addr limit)

size = AddrOffset(base, limit);

if(mmap((void *)base, (size_t)size,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_ANON | MAP_PRIVATE | MAP_FIXED,
-1, 0)
== MAP_FAILED) {
result = mmap((void *)base, (size_t)size, vm_prot,
MAP_ANON | MAP_PRIVATE | MAP_FIXED,
-1, 0);
if (MAYBE_HARDENED_RUNTIME && result == MAP_FAILED && errno == EACCES
&& (vm_prot & PROT_WRITE) && (vm_prot & PROT_EXEC))
{
/* See <config.h#hardened-runtime>. */
vm_prot = PROT_READ | PROT_WRITE;
result = mmap((void *)base, (size_t)size, vm_prot,
MAP_ANON | MAP_PRIVATE | MAP_FIXED,
-1, 0);
}
if (result == MAP_FAILED) {
AVER(errno == ENOMEM); /* .assume.mmap.err */
return ResMEMORY;
}
Expand Down

0 comments on commit 136b2b6

Please sign in to comment.