On Sat, 15 Aug 2020, Stefan Dösinger wrote:
- Setting -pagezero_size to anything less than 4 GB seems to make macOS refuse to run the executable. So this makes it impossible to map anything into the lower 4 GB of the address space. For now, I've worked it around by moving the address at which user_shared_data is allocated.
Shouldn't you be able to forcefully remap parts in the zero page? To my knowledge the only thing you can't do is unmap pages that the dynamic loader thinks are allocated - if it gets them again when loading a different .dylib it will crash and burn. But you can map stuff there with MAP_FIXED yourself as long as you can be sure you don't overwrite anything important - which shouldn't be the case in the zero page.
I tried this out with this small test snippet on Catalina:
#include <sys/mman.h> #include <fcntl.h> #include <stdio.h>
int main(int argc, char* argv[]) { void *target = (void*)0x7ffe0000; char *ptr = mmap(target, 4*4096, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE | MAP_FIXED, 0, 0); if (ptr == MAP_FAILED) { perror("mmap"); return 1; } printf("mmap fixed at %p returned %p\n", target, ptr); return 0; }
If linked without pagezero_size, I get "mmap: Cannot allocate memory", while it succeeds if linked with that option.
- Memory mappings can't be writable and executable at the same time. If one mmap()s a page and request it to be both writable and executable, writing to it fails, same if changing protection with mprotect().
In Catalina you can bypass that with the right protected runtime entitlements. That means you'll have to sign your executable with a manifest that enables the protected runtime and requests the entitlements. Do you know if this still works in Big Sur x86_64 and/or big sur ARM?
When trying this out on Catalina on x86_64, such mappings work just fine normally. If I enabled the hardened runtime by signing it with "codesign -o runtime", the mmap calls that request writable+executable memory fail with EPERM. If I add entitlements to the signing, either com.apple.security.cs.allow-unsigned-executable-memory or com.apple.security.cs.disable-executable-page-protection it succeeds again.
On Big Sur on arm64, the mmap calls don't fail but do return a pointer to some memory, but the supposedly writable+executable memory fails if written to. The same happens there, if I opt in to the hardened runtime, the mmap call fails, but if I add those entitlements, I get back to the original behaviour - mmap succeeds, but the memory actually isn't writable.
// Martin