I'm trying to mmap an 1TB anonymous file under Fedora Linux x86_64 (4G RAM plus 16G swap). But I got ENOMEM "cannot allocate memory" and even for 32G as the following code. Am I missing anything? Appreciate any clue.
#define HEAP_SIZE (1UL << 35)
int main()
{
void *addr = mmap(0, HEAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (addr == MAP_FAILED)
{
perror(NULL);
return 1;
}
printf("mmap %d gbytes succeed\n", HEAP_SIZE/(1UL << 30));
return 0;
}
The default Linux overcommit policy prevents you from allocating this much memory. You don't have anywhere near 1TB of RAM, and the kernel will give you ENOMEM now rather than running the OOM killer later... but you can change this policy.
$ /sbin/sysctl vm.overcommit_memory
vm.overcommit_memory = 0
$ sudo /sbin/sysctl vm.overcommit_memory=1
vm.overcommit_memory = 1
Policy 1 is "always overcommit", which is useful for certain applications. Policy 2 is "never overcommit". The default policy, 0, allows some overcommit but uses heuristics to reject large allocations, like the one which failed on your computer.
Alternative
You could also use the MAP_NORESERVE flag. Note that the kernel will ignore this flag if its policy is to "never overcommit".
Related
I have a program that mmaps memory at higher addresses using MAP_FIXED at TASK_SIZE - PAGE_SIZE.
This program runs fine if I execute it, but if I run it with gdb, it segfaults just after the mmap. Also at this point, the gdb state seems to be completely corrupted and it appears that the execution reaches an address range filled with 0's (could be from the new mappings just created).
Does gdb use this address range in the running process? Have I cleared out some of gdb's state? Is this address range documented somewhere?
Following is my call to mmap and the address calculation -
#define TASK_SIZE64 (0x800000000000UL - 4096)
#define TASK_SIZE TASK_SIZE64
#define PAGE_OFFSET (void*)TASK_SIZE
...
char *load_address = PAGE_OFFSET - file_size_aligned;
if(load_address != mmap(load_address, file_size_aligned, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0)){
err("Failed to allocate memory for raw_binary with: %d\n", errno);
return -1;
}
file_size_aligned comes to a PAGE_SIZE. This is one of the allocations. There is one more that starts from load_address and allocates few more pages backwards (with PROT_READ and PROT_WRITE only).
Does gdb use this address range in the running process?
No.
Have I cleared out some of gdb's state?
No.
Is this address range documented somewhere?
Possibly in kernel sources.
Your program makes invalid assumptions about available address space, and "blows itself up" when run with ASLR turned off (which GDB does by default).
You can confirm this by running your program outside GDB, but with ASLR disabled. It should also crash. Try one of these:
# echo 0 > /proc/sys/kernel/randomize_va_space
or
setarch $(uname -m) -R /path/to/exe
You can also confirm that your program will run under GDB if you enable ASLR:
gdb /path/to/exe
(gdb) set disable-randomization off
(gdb) run
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.
I am trying to access the hardware registers of an A20 SOM by mapping them to userspace. In this case, target is the PIO, listed at physical address 0x01C20800.
The official Olimex Debian7 (wheezy) image is being used. Kernel Linux a20-olimex 3.4.90+
I was able to verify the location by using the devmem2 tool and Allwinner's documentation on the said memory space (switched the pinmode and level with devmem).
The mmap call on the other hand
*map = mmap(
NULL,
BLOCK_SIZE, // = (4 * 1024)
PROT_READ | PROT_WRITE,
MAP_SHARED,
*mem_fd,
*addr_p
);
fails with mmap error: Invalid argument
Here's a more complete version of the code: http://pastebin.com/mfEuVdbJ
Don't worry about the pointers as the same code does work when accessing UART0 at 0x01C28000.
Although only UART0 (and UART4), which is used as serial console.
I've decompiled the script.bin (still in use despite DTB) without success, as UART 0, 7 and 8 are enabled there.
I am also logged in as user root
I would still guess something related to permissions but I'm pretty lost right now since devmem has no problem at all
> root#a20-olimex:~# devmem2 0x01c20800 w /dev/mem opened. Memory mapped
> at address 0xb6f85000.
While sourcejedi didn't certainly fix my issue, he gave me the right approach.
I took a look at the forementioned devmem tool's source to discover that the mmap call's address is masked
address & ~MAP_MASK to get the entire page, which is essentially the same operation as in my comment.
However, to get back to the right place after the mapping has been done, you have to add the mask back
final_address = mapped_address + (target_address & MAP_MASK);
This resulted in following code (based on OP's pastebin)
Where
MAP_MASK = (sysconf(_SC_PAGE_SIZE) - 1) in this case 4095
int map_peripheral(unsigned long *addr_p, int *mem_fd, void **map, volatile unsigned int **addr)
{
if (!(*addr_p)) {
printf("Called map_peripheral with uninitilized struct.\n");
return -1;
}
// Open /dev/mem
if ((*mem_fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
printf("Failed to open /dev/mem, try checking permissions.\n");
return -1;
}
*map = mmap(
NULL,
MAP_SIZE,
PROT_READ | PROT_WRITE,
MAP_SHARED,
*mem_fd, // file descriptor to physical memory virtual file '/dev/mem'
*addr_p & ~MAP_MASK // address in physical map to be exposed
/************* magic is here **************************************/
);
if (*map == MAP_FAILED) {
perror("mmap error");
return -1;
}
*addr = (volatile unsigned int *)(*map + (*addr_p & MAP_MASK));
/************* and here ******************************************/
return 0;
}
if you read the friendly manual
EINVAL Invalid argument (POSIX.1)
is the error code. (Not EPERM!). So we look it up for the specific function
EINVAL We don't like addr, length, or offset (e.g., they are too large,
or not aligned on a page boundary).
BLOCK_SIZE, // 1024 - ?
You want a multiple of sysconf(_SC_PAGE_SIZE). In practice it will be 4096. I won't bother with the fully general math to calculate it - you'll find examples if you need it.
I am allocating memory using "huge pages(1MB size)" and using mmap. After allocating 4 GB of memory ,mmap returns fail.
mmap(NULL, memsize, PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS |MAP_POPULATE | MAP_HUGETLB, -1, 0);
here memsize = 1GB
I am calling above statement in a loop. Upto 4 iterations it is fine. In 5th iteration mmap is failed.
mmap(NULL, memsize, PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS |MAP_POPULATE , -1, 0);
Above statement (without hugepages) works perfectly for any number of iterations. Am I missing any information related to hugepages?
I tried "MAP_NORESERVE" flag also as mentioned in mmap fail after 4GB.
Any sort of information will be greatly appreciated. Thank you.
Change the allocated "number of huge pages" in file
/proc/sys/vm/nr_hugepages
according to the amount of memory you want to allocate.
Earlier it says:
>cat /proc/meminfo | grep HugePages
HugePages_Total = 2500
4GB => it has 2048*2Mb= 4Gb
2048 huge pages already consumed.
one more GB of memory need (1GB/2MB= 512) 512 more huge pages. But 2500 - 2048 =452 only left. But you need 512. Thats the problem why mmap failed. If you edit the above mentioned file(/proc/sys/vm/nr_hugepages) contents to 2560, it allows 5GB. Change it according to the amount of memory you need. Thanks to # Klas Lindbäck. I referred back the link, small research exposed the working
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