This is on an M1 mini, running 11.1 with an updated Xcode. (All pending updates have been applied.) SIP has not been disabled (if it can even be, on the M1).
We have a program we're using to test an mmap() failure in our application being ported. We build the test program like this:
cc -v -arch arm64 -m64 -Wl,-no_adhoc_codesign -o mapfail mapfail.c
Then, we sign it. It appears to be correctly signed:
@macarm[git:master]$ codesign -vvv mapfail
mapfail: valid on disk
mapfail: satisfies its Designated Requirement
@macarm[git:master]$
We have enabled developer mode on the system. We also added my user to the _developer group. My user is not an administrator, but I tried it as one and got the same thing.
When we run the program it gets a segmentation fault (SIGSEGV), so we want to debug it with lldb, but this happens:
@macarm[git:master]$ lldb mapfail
(lldb) target create "mapfail"
Current executable set to '/Users/layer/mapfail' (arm64).
(lldb) run
error: process exited with status -1 (attach failed ((os/kern) invalid argument))
(lldb)
Searches for this have yielded no information, even on the Apple developer forums.
Code for mapfail.c:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <mach/mach.h>
#include <mach/machine/vm_param.h>
#define nat long
#define UseMAP_ANON
/* define UseDevZero */
#define FIROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
#define FIROUNDDOWN(a, n) ((a) & ~((n) - 1))
int bucket_o_zeros = -1;
int ChunkSize;
# ifdef UseMAP_ANON
# define FIMAP_ANON MAP_ANON
# else
# define FIMAP_ANON 0
# endif
typedef struct {
char *base; /* lowest address -- 64k aligned */
char *pos; /* 1+ highest address allocated */
char *commit; /* 1+ highest address committed */
} heap_descriptor;
heap_descriptor test1, test2;
unsigned nat test1_base, test1_size;
int
ok_to_map(unsigned nat base, unsigned nat top)
{
/* check if the specified memory is free */
unsigned nat address = base;
vm_size_t size;
mach_port_t object_name;
task_t task = mach_task_self();
struct vm_region_basic_info_64 info;
mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
kern_return_t retval;
int res = 0;
retval = vm_region_64(task, &address, &size, VM_REGION_BASIC_INFO_64,
(vm_region_info_64_t)&info, &info_count, &object_name);
if (retval == KERN_NO_SPACE) {
res = 1;
} else if ((retval == KERN_SUCCESS) && (address > top)) {
res = 1;
}
if (object_name != MACH_PORT_NULL)
mach_port_deallocate(mach_task_self(), object_name);
printf("maping from 0x%lx to 0x%lx is %s\n", base, top, res ? "OK" : "BAD");
return res;
}
char *
setup_heap( heap_descriptor *hd, unsigned nat base, unsigned nat size)
{
/* allocate memory at the given address, and return the address
* which was allocated
*/
char *res = (char *)MAP_FAILED;
/* initial setup of heap when there is nothing to map in or commit */
base = FIROUNDDOWN(base, ChunkSize);
size = FIROUNDUP(size, ChunkSize);
/* we demand that OS puts memory at our base, so we can grow later */
if (base == 0 || ok_to_map(base, base+size)) {
res = (char *) mmap((void *) base, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | FIMAP_ANON | (base ? MAP_FIXED : 0),
bucket_o_zeros, 0);
}
if (res != (char *) MAP_FAILED) {
/* success - check alignment */
if (base == 0) {
nat diff;
base = (unsigned nat) res;
res = (char *) FIROUNDUP((nat) res, ChunkSize);
diff = ((unsigned nat)res - base);
if (diff > 0) {
/* this had been over-requested already */
size -= ChunkSize;
/* must munmap the two ends */
munmap((caddr_t) base, diff);
munmap((caddr_t) (res + size), diff);
}
}
/* initialize the heap descriptor */
hd->pos = hd->base = res;
hd->commit = (char *) (hd->base + size);
return hd->base;
} else {
return 0; /* failure */
}
}
int
try_setup_heap(char *kind, heap_descriptor *hd, unsigned nat base, unsigned nat size)
{
/* try to allocate where first requested, and then let the system decide.
* return true or false depending on whether it worked.
*/
char *result_base;
base = FIROUNDUP(base,ChunkSize);
size = FIROUNDUP(size,ChunkSize);
/* ask for a specific area */
if (setup_heap(hd, base, size)) return 1; /* success */
/* now ask for any location */
if ((result_base = setup_heap(hd, 0, size + ChunkSize)) == 0) {
fprintf(stderr,
"Unable to reserve at %ld (0x%lx) bytes of memory for the %s heap\n",
size, size, kind);
return 0;
} else {
fprintf(stderr,
"Unable to reserve 0x%lx for the %s heap,\n using 0x%lx instead\n",
base, kind, (unsigned nat) result_base);
return 1;
}
}
int main(int argc, char **argv, char**envp)
{
ChunkSize = getpagesize();
#if defined(UseDevZero)
if(bucket_o_zeros == -1){
bucket_o_zeros = open("/dev/zero", O_RDWR);
}
#endif
if (argc > 2) {
sscanf(argv[1], "%lx", &test1_base);
sscanf(argv[2], "%lx", &test1_size);
if (try_setup_heap("test1", &test1, test1_base, test1_size)) {
printf("test1 heap mapped from 0x%lx to 0x%lx\n",
(unsigned nat) test1.base, (unsigned nat)test1.commit);
} else {
printf("test1 not mapped\n");
}
} else {
unsigned nat addr = 0x100000000;
unsigned nat size = 0x100000;
int i, n, didit=0;;
sscanf(argv[1], "%d", &n);
for (i=0; i < n; i++) {
if (try_setup_heap("test2", &test2, addr, size)) {
printf("test1 heap mapped from 0x%lx to 0x%lx\n",
(unsigned nat) test2.base, (unsigned nat)test2.commit);
didit++;
}
addr += 0x100000000;
}
printf("Total mappings: %d\n", didit);
}
}
/*
* To compile on M1 with debugging:
* cc -v -arch arm64 -m64 -Wl,-no_adhoc_codesign -o mapfail mapfail.c
*/