mmapping /dev/zero on Mac OSX gives invalid argument - c

So I am trying to port some code from Linux to Mac OSX. During porting I came across this code.
static void allocZeroMap(unsigned long size, int prot, const char *name)
{
struct map *newnode;
int fd;
char buf[11];
fd = open("/dev/zero", O_RDWR);
if (fd == -1) {
printf("couldn't open /dev/zero\n");
exit(EXIT_FAILURE);
}
newnode = zmalloc(sizeof(struct map));
newnode->name = strdup(name);
newnode->size = size;
newnode->prot = prot;
newnode->type = INITIAL_MAP;
newnode->ptr = mmap(NULL, size, prot, MAP_ANONYMOUS | MAP_SHARED, fd, 0);
if (newnode->ptr == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
newnode->name = zmalloc(80);
sprintf(newnode->name, "anon(%s)", name);
num_initial_mappings++;
list_add_tail(&newnode->list, &initial_mappings->list);
sizeUnit(size, buf);
printf("mapping[%d]: (zeropage %s) %p (%s)\n",
num_initial_mappings - 1, name, newnode->ptr, buf);
close(fd);
}
So I figured that I would need to just switch newnode->ptr = mmap(NULL, size, prot, MAP_ANONYMOUS | MAP_SHARED, fd, 0); to newnode->ptr = mmap(NULL, size, prot, MAP_ANON | MAP_SHARED, fd, 0); because Mac OSX does not have the MAP_ANONYMOUS flag and uses MAP_ANON instead.
But when I run the program, that mmap call fails with Invalid argument. So my next thought was check the man page for what causes Invalid arguments errors. The first cause is that MAP_FIXED is set and the addr argument is not page aligned, but I'm not calling MAP_FIXED. Second it's says it will return Invalid argument if you don't specify either MAP_PRIVATE or MAP_SHARED but I'm using MAP_SHARED so that shouldn't be it. Next it says the len argument must not be negative, so I printed the size variable and ran it and it's a positive value. The final reason is that the offset argument is not page aligned, but I am passing zero so that shouldn't be the problem either.
So next I checked stack overflow and saw this question mmap with /dev/zero . I tried using MAP_FILE instead of MAP_ANON but now I get Operation not supported by device, which the man page says means I am missing MAP_ANON. So I tried adding MAP_ANON so that I have mmap(NULL, size, prot, MAP_FILE | MAP_ANON| MAP_SHARED, fd, 0); but that returns Invalid argument as well.
So how do I mmap /dev/zero on Mac OSX or at least create a zero-filled memory map on Mac OSX?

You should pass -1 instead of fd as the value of the file descriptor as per mmap() man page. Also as you said MAP_ANON is the correct option for MacOS (at least older versions) and there is not need for MAP_FILE.

Related

Question about mapping a file and memory using mmap() [duplicate]

I created a process which calls mmap with MAP_SHARED flag set,when i attempt to copy a string to that address i receive Bus error core dumped,could some one please explain the reason behind it and how to fix it. Following is my code
int main()
{
int fd=0;
char* ret = NULL;
void *map_addr = NULL;
fd = open("./shared_file.txt", O_RDWR, S_IRUSR | S_IWUSR);
if(fd == -1) {
printf("errno = %d\n",errno);
printf("Aborting process1###########\n");
abort();
}
map_addr = mmap(NULL, 5*sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
if(map_addr == MAP_FAILED) {
printf("mmap failed error no =%d\n",errno);
close(fd);
return -1;
}
printf("map_addr = %p#################\n",(int*)map_addr);
printf("processid = %d#################\n",(int)getpid());
ret = strcpy((char*)map_addr,"Stack Overflow");
if(ret == (char*)map_addr)
printf("strcpy success\n");
/*if(msync(map_addr, sizeof(int), MS_SYNC))
printf("msync failed errno = %d\n",errno);*/
close(fd);
sleep(120);
return (0);
}
The cause of a bus error is usually an attempt to dereference a pointer that has not been initialized properly and contains junk data that are not accessible in a multiple of 4 or 1 or as related to datatype sizes.
First you should check if the shared_file.txt file size is >= 20 bytes(assuming sizeof int is 4 bytes) as specified in the mmap() length argument(where you put 5*(sizeof(int))) in the line below:
map_addr = mmap(NULL, 5*sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
If file size is less than 20 bytes, you could use fallocate call to pre allocate the memory.
If shared_file.txt file size is <= 20 bytes and then you ask mmap to map 20 bytes, it could cause a bus error when you write beyond the actual no. of bytes available in file, because that would be access to an undefined portion of a memory. MMAP_FAILED will not be returned in this case, during memory initialization.
A quick check is to see if you can write a single character in the mmap char* pointer. If you can't( you will get a SIGBUS), that means file size is zero.

Bus error attempting to access memory mapped circle buffer

I'm using a queue created with a circle buffer in C by mapping a file to two halves of the same underlying buffer, one after the other. If I attempt to access the buffer immediately after creating it (both before and after mapping the file on top), a bus error is thrown.
I'm programming on a x86 machine, so (if I understand correctly), the only reason this would occur is if the memory location is physically inaccessible. If this is the case, why would mmap return a physically unavailable address?
My code for creating a new code can be seen below.
struct queue create_queue() {
size_t pagesize = getpagesize();
size_t sz = ((BUFFSIZE*sizeof(char *))/pagesize)*(pagesize+1); //align to page
int fd = fileno(tmpfile());
void *buffer = mmap(NULL, 2*sz, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
mmap(buffer, sz, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED, fd, 0);
mmap(buffer+sz, sz, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED, fd, 0);
struct queue new_queue;
new_queue.buffer = (char **)buffer;
new_queue.front = 0;
new_queue.back = 0;
sem_init(&(new_queue.sem), 0, 0);
return new_queue;
}
The problem is that:
size_t sz = ((BUFFSIZE*sizeof(char *))/pagesize)*(pagesize+1); //align to page
does set sz to a multiple of the page size, but to a muiliple of the page size plus one. An alternative approach is:
size_t sz = ((BUFFSIZE*sizeof(char *)+pagesize-1)/pagesize)*pagesize; //align to page

why does mmap fail?

I'm given a physical address, specifically 0x000000368d76c0. I'm trying to mmap it into my program. The code that I'm using is
void *mmap64;
off_t offset = 0x000000368d76c0;
int memFd = open("/dev/mem", O_RDWR);
if (-1 == memFd)
perror("Error ");
mmap64 = mmap(0, sizeof(uint64_t), PROT_WRITE | PROT_READ, MAP_SHARED, memFd, offset);
if (MAP_FAILED == mmap64) {
perror("Error ");
return -1;
}
For some reason when I run this code I get a failure on mmap. Specifically it says Error Invalid argument. I'm pretty sure it is because of the offset value, but I don't know what is wrong with it.
I would appreciate any help on it.
According to mmap(2) - Linux manual page,
offset must be a multiple of the page size as
returned by sysconf(_SC_PAGE_SIZE).
When the page size is 4096 (a page size used in x86 CPU), 0x000000368d76c0 is not a multiple of 4096 and will be considered as invalid.
For that reason, you will have to adjust the offset.

Invalid argument for read-write mmap?

I'm getting -EINVAL for some reason, and it's not clear to me why. Here's where I open and attempt to mmap the file:
if ((fd = open(argv[1], O_RDWR)) < 0)
{
fprintf(stderr, "Failed to open %s: %s\n", argv[1], strerror(errno));
return 1;
}
struct stat statbuf;
if (fstat(fd, &statbuf))
{
fprintf(stderr, "stat filed: %s\n", strerror(errno));
return 1;
}
char* fbase = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (fbase == MAP_FAILED)
{
fprintf(stderr, "mmap failed: %s\n", strerror(errno));
return 1;
}
EDIT: I should add, the error is occurring in the mmap.
Turns out changing the MAP_SHARED to MAP_PRIVATE allows this to succeed.
This reason this was failing is subtle: My code is running inside a VirtualBox VM, and the file I was attempting to mmap was in a shared directory on my host machine. The VirtualBox virtual filesystem apparently doesn't implement mmap with the MAP_SHARED option across the boundary of the hypervisor.
If you'll read jxh's helpful comments on both my question and on his answer, it turns out that this code was working for him because he was likely attempting to mmap a host filesystem file into the host memory.
My observation that switching from MAP_SHARED to MAP_PRIVATE is also consistent with this: since privately mapped memory is invisible to other processes, the virtual filesystem driver will probably have no objection to mapping the memory.
The solution was to move the file I wanted to map into the guest's hard drive and perform manipulation from there.
Your statbuf.st_size is 0. mmap() will fail if the length parameter is 0.
There are 3 listed reasons for EINVAL error mmap():
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
...
We don't like addr, length, or offset (e.g., they are too large, or not aligned on a page boundary).
(since Linux 2.6.12) length was 0.
flags contained neither MAP_PRIVATE or MAP_SHARED, or contained both of these values.
edit grub to add iomem=relaxed and reboot, make sure cat /proc/cmdline shows entry for iomem=relaxed after boot, re-run your program and check
[root#fedora ~]# cat /proc/cmdline
BOOT_IMAGE=(hd0,gpt2)/vmlinuz-5.18.19-200.fc36.x86_64 root=/dev/mapper/fedora_fedora-root ro rd.lvm.lv=fedora_fedora/root iomem=relaxed rhgb quiet

Why is mmap not working here

I get segmentation fault when I run the following piece of code...
int * x = mmap( 0, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE, 0, 0 );
x[0] = 42; // <--- Segmentation fault happens due to this
What is wrong here?
You've specified the incorrect flags and file descriptor. It looks like what you want is an anonymous (not backed by a file) mapping. If that's the case, the correct call would be:
x = mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
You need to use the MAP_ANONYMOUS flag to tell Linux there is no file. And you should pass -1 for the file descriptor, not 0.
OK, I got it. I forgot to place MAP_ANONYMOUS, so it should had been like this...
int * x = mmap( 0, 4096, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE,
0, 0 );
Its working this way.
man mmap says:
On success, mmap() returns a pointer to the mapped area. On error, the value MAP_FAILED (that is, (void *) -1) is returned, and errno is set appropriately
Check, whether x == MAP_FAILED or not. May be this is the case.
And you should always check that the result of mmap is not MAP_FAILED (that is, (void *) -1) and use errno to get the error code in that case.
Your mmap could fail (e.g. because of resource limits set with setrlimit, or because the swap space is full).

Resources