I have a very simple code to test mmap on low memory address.
unsigned long *p = mmap ((void*)(4096*16), 4096, PROT_READ|PROT_WRITE|PROT_EXEC,
MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS|MAP_GROWSDOWN, -1, 0);
fprintf (stderr, "p=0x%lx\n", (unsigned long)p);`
*p = 2554;
printf ("p=0x%lx; *p=%ld\n", (unsigned long)p, *p);
When I run the code and I get the following output:
p=0x10000
Segmentation fault (core dumped)
in dmesg log, I can see the following prints:
segfault at 10000 ip 00000000004006cc sp 00007fff5845f4c0 error 6
Overall, it seems the mmap is success, but the write operation is failed. I cannot explain these two conflict observations. Please help me. Thanks.
You might find that the segmentation fault no longer occurs if you omit
MAP_GROWSDOWNfrom theflagsparameter in themmapcall.If you examine the
/proc/$PID/mapsfile after themmapcall, you might see an oddity (withMAP_GROWSDOWNincluded inflags). The address seems to be one page above the requested address, and the size of that mapping seems to be one page smaller than what you request. In short, the start address of that mapping is off by 4096 bytes. I found no mention of this oddity in the documentation forMAP_GROWSDOWN, and it seems more like a bug than a feature to me. Whether or not you see that particular oddity might depend on what version of the kernel you're using (I assume from the tag that you're using a Linux kernel). In any case it might be educational to examine that file while the process is active, even if your code works as intended withoutMAP_GROWSDOWN.One way to keep the process active long enough to examine its
mapsfile is to set a breakpoint ingdb. Anywhere in the function which callsmmapshould suffice, if you step far enough (just past themmapcall). The$PIDin that pathname above is intended to represent the process ID of the process which callsmmap. You can obtain that process ID from suitablepsoutput, or from output ofinfo inferioringdb.To address your specific question, the success of the
mmapcall reflects the mapping listed in themapsfile (even though the size of that mapping is zero in your example), while the failure reflects the discrepancy between the return value ofmmap(0x10000) and the start of the mapping (0x11000). With 4096 as the size (as in your example) no address would allow the assignment to*pbut with a larger size adding 4096 to the return value ofmmapwould give you a working address (assuming your kernel behaves the same way mine does). If the beginning of the mapping were equal to themmapreturn value (as it is in the absence ofMAP_GROWSDOWN) there would be no discrepancy.