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
Related
In the user space programm I am allocating some memory via mmap as the following function call:
void *memory;
int fd;
fd = open(filepath, O_RDWR);
if (fd < 0)
return errno;
memory = mmap(NULL, 4096, PROT_WRITE, MAP_SHARED, fd, 0);
if (memory == MAP_FAILED)
return -1;
//syscall() goes here
In the kernel space in my system call I am trying to copy data to the memory mapped region as follows:
copy_to_user(memory,src,4096);
EDIT: added error checking code to the post for clarification
The copy_to_user() call is repeatedly failing in this case, whereas if I would have done a memory = malloc() it was succeeding always.
Am I getting some permission flags wrong in this case for mmap ?
Does the open succeed? What about mmap? Is the target file big enough? Can you write to the file through the mapping in userspace?
Also, the repeated 4096 is a strong hit your code is wrong. Userspace should pass the expected size instead.
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.
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.
Playing around with mmap for the fun of it, I have the following code:
(.. snip ..)
fd = open("/home/me/straight_a.txt", O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
m = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_FILE|MAP_PRIVATE, fd, 0);
if (m == MAP_FAILED) {
perror("mmap");
exit(1);
}
printf("m is %p\n", m);
printf("*m = %c\n", *m);
printf("*(m+1) = %c\n", *(m+1));
(.. snip ..)
This works as expected. But before I got to this, I tried...
m = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, fd, 0);
... and mmap errored out with:
mmap: Permission denied
In general, what's the difference between the two flags (man page isn't to generous on this subject)? What sort of permission (and where) am I missing?
EDIT
Like it usually happens.. partially figured it out.
Turns out open needed an O_RDWR flag.
So, am I correct to assume that:
MAP_PRIVATE - changes are made in memory only, not saved to disk?
MAP_SHARED - changes would be saved to disk...
... but I'm not saving anything to disk anywhere, I thought? Just operating on memory.
You opened the file in read-only mode. Then you attempted to mmap part of it in read/write mode with MAP_SHARED set. In this context, MAP_SHARED implies that if you write to the mmap'd region your changes will be committed back to the mapped file itself. You can't do this because you opened the file in read-only mode.
MAP_PRIVATE works because writes to the mmap'd region are not committed back to the original file. When you write to the region, the pages that were written to are copied to a different region of memory, possibly backed by swap space.
Writes to a MAP_SHARED segment are carried through to the underlying file. You opened the file with O_RDONLY, which conflicts with the PROT_WRITE flag, thereby preventing MAP_SHARED from being able to write back to the file.
MAP_PRIVATE does not carry writes back to the underlying file, so the fact that you opened the file O_RDONLY is not an issue.
I am trying to use mmap in user space to read the physical memory where 'mem_map' starts. It's an array that contains all the physical pages. This is a i386 machine running 3.0 kernel.
The code is like this:
....
//define page size
//
#define PAGE_SIZE 0x1000 //4096 bytes
#define PAGE_MASK (PAGE_SIZE - 1)
....
/* open /dev/mem file*/
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1) {
printf("/dev/mem could not be opened.\n");
perror("open");
exit(1);
} else {
printf("/dev/mem opened.\n");
}
/* Map one page */
printf(" mem_map is at physical addr: 0x%x\n", mem_map_phy_addr);
map_base = mmap(0, PAGE_SIZE, PROT_READ, MAP_SHARED, fd, (mem_map_phy_addr & ~PAGE_MASK)); //mem_map_phy_addr is at 0x356f2000
if(map_base == (void *) -1) {
printf("Memory map failed. err num = %d\n",errno);
perror("mmap"); //failed here
} else {
printf("Memory mapped at address %p.\n", map_base);
}
I ran this as a root. The output is:
/dev/mem opened.
mem_map is at physical addr: 0x356f2000
Memory map failed. err num = 1
mmap: Operation not permitted
To be sure, I googled the problem and added the following line to my /etc/sysctl.conf file:
vm.mmap_min_addr = 0
But this doesn't work either.
Anyone knows why mem_map operation like this is not permitted and how I can get around it?
Thanks.
It sounds like the kernel has been compiled with CONFIG_STRICT_DEVMEM enabled. This is a security feature to prevent user space access to (possibly sensitive) physical memory above 1MB (IIRC). You might be able to disable this with sysctl dev.mem.restricted.
I had a similar problem which occured when I was trying use flashrom on an APU2c4 Board with Arch Linux.
The sysctl option dev.mem.restricted wasn't available in my system and using a self compiled kernel was no option for me.
I worked around the problem by setting the iomem Kernelparameter to relaxed via Grub:
# /boot/grub/grub.cfg
linux /boot/vmlinuz-linux iomem=relaxed
Of course a reboot is nessesary for this solution.
Reference:
https://www.reddit.com/r/libreboot/comments/6wvyry/flashrom_failures_to_access/
https://www.flashrom.org/FAQ
https://www.kernel.org/doc/Documentation/admin-guide/kernel-parameters.txt