Remapping stack succeeds, but later SEGV is raised - c

I ran a simple program written in assembly that under strace that simply executes SYS_exit.
_start:
mov rax, 0x3C
mov rdi, 0x0
syscall
And noticed that there were nothing like mmap memory for the stack:
alrorp#dmspc:~$ strace ./bin
execve("./bin", ["./bin"], 0x7ffd591eda80 /* 65 vars */) = 0
exit(0) = ?
+++ exited with 0 +++
So I tried to do mmap with MAP_FIXED to the stack page-aligned address as follows:
int main(void){
int a = 1;
void *ptr = &a;
void *page_aligned_ptr = (void *)((intptr_t) ptr & -4096);
mmap(page_aligned_ptr, 4096, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
}
The thing is it segfaults after the call to mmap succeeds (i.e. it returns the requested address instead of MAP_FAILED).
mmap(0x7ffdf50db000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffdf50db000
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=NULL} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault
Can you give any hint about this behavior? Core dump seems to be (almost) useless in that case with stack corrupted.
Does something like create a custom mapping for stack even make sense?

Replacing the stack page containing your return address with a new anonymous page of zero bytes obvious leads to a segfault as soon as main returns, and pops 0 into RIP.
Note the si_addr=NULL, IIRC that's the code address where the fault happened. So RIP=0 after running a ret with RSP pointing at a 0. (The ret itself won't fault, but code-fetch from address 0 will.)
Or actually the segfault will be inside the libc wrapper for mmap, which itself has to ret.
Use a debugger to single-step the asm the C compiler created for you.

Related

When accessing mmap adress, signal SIGBUS was received

When I tried to access the address mmap returned, a Bus error is occured.
My code is below:
ftruncate(fd, shared_size);
addr = mmap(shared_start, shared_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
shared_size == 256*1024*1024
shared_start == 401000000000 (I used flag MAP_FIXED)
ftruncate the file to 256M.
-rw-r--r-- 1 root 0 256.0M Mar 4 03:47 mem.alloc
There is nothing wrong when calling mmap, and not all of address range is not allowed to access.
From the gdb information below, we can see, the address 0x40100f11ff00 is not allowed, but address 0x40100fe00000 is allowed:
(gdb) p *((char *)addr+0xf11ff00)
Cannot access memory at address 0x40100f11ff00
(gdb) p *((char *)addr+0xfe1ff00)
Cannot access memory at address 0x40100fe1ff00
(gdb) p *((char *)addr+0xfe00000)
$17 = 0 '\000'
From maps information below, we can see the addresses I accessed above are all within the range of mmap address:
0x401000000000 0x401010000000 0x10000000 0x0 /dev/mem.alloc
However, when writing these inaccessible addresses, a bus error occurs:
Program received signal SIGBUS, Bus error.
PS.When reducing shared_size from 256M to 128M, there is no issue.
I have fixed it. This is a problem that can be easily overlooked. The space mount for dev is too small...so....you known....

Address ranges reserved by gdb?

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

Doing an ASM call / ret in C

I try to do a simple call / ret sequence in assembly (from c code compiled with GCC), by manually writing the ret op code, and making a call to the ret address:
void *addr;
addr = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
// Writing the ret op code
((char*)addr)[0] = 0xC3;
// Going to addr with the ret
asm volatile("call *%0" : : "r" (addr));
But I get a segmentation fault. Anyone would know why, and how to correct ?
In order to be able to execute instructions on a memory page, read and write privileges are not enough; it also needs to be marked executable (PROT_EXEC).

mmap substitute for malloc

I need to find a way to use mmap instead of malloc. How is this possible? (I am not using libc only syscalls) And yes brk() is possible. I used sbrk() but realized its not sys-call... (x86 inline assembly)
I've been looking around and saw this: How to use mmap to allocate a memory in heap? But it didn't help for me, because I had a segfault.
Basically, all I want to do a create 3 slabs of memory for storing characters.
Say,
char * x = malloc(1000);
char * y = malloc(2000);
char * z = malloc(3000);
How is this possible with mmap and how to free it later with munmap?
Did you carefully read the mmap(2) man page? I recommend reading it several times.
Notice that you can only ask the kernel [thru mmap etc...] to manage memory aligned to and multiple of the page size sysconf(_SC_PAGE_SIZE) which is often 4096 bytes (and I am supposing that in my answer).
Then you might do:
size_t page_size = sysconf(_SC_PAGE_SIZE);
assert (page_size == 4096); // otherwise this code is wrong
// 1000 bytes fit into 1*4096
char *x = mmap (NULL, page_size, PROT_READ|PROT_WRITE,
MAP_ANONYMOUS, -1, (off_t)0);
if (x == MMAP_FAILED) perror("mmap x"), exit (EXIT_FAILURE);
// 2000 bytes fit into 1*4096
char *y = mmap (NULL, page_size, PROT_READ|PROT_WRITE,
MAP_ANONYMOUS, -1, (off_t)0);
if (y == MMAP_FAILED) perror("mmap y"), exit (EXIT_FAILURE);
later to free the memory, use
if (munmap(x, page_size))
perror("munmap x"), exit(EXIT_FAILURE);
etc
If you want to allocate 5Kbytes, you'll need two pages (because 5Kbytes < 2*4096 and 5Kbytes > 1*4096) i.e. mmap(NULL, 2*page_size, ...
Actually, all of your x, y, z takes only 8000 bytes and could fit into two, not three, pages... But then you could only munmap that memory together.
Be aware that mmap is a system call which might be quite expensive. malloc implementations take care to avoid calling it too often, that is why they manage previously free-d zones to reuse them later (in further malloc-s) without any syscall. In practice, most malloc implementations manage differently big allocations (e.g. more than a megabyte), which are often mmap-ed at malloc and munmap-ed at free time.... You could study the source code of some malloc. The one from MUSL Libc might be easier to read than the Glibc malloc.
BTW, the file /proc/1234/maps is showing you the memory map of process of pid 1234. Try also cat /proc/self/maps in a terminal, it shows the memory map of that cat process.
You can call mmap to make an anonymous mapping in x86 asm with something like:
mov eax, 192 ; mmap
xor ebx, ebx ; addr = NULL
mov ecx, 4096 ; len = 4096
mov edx, $7 ; prot = PROT_READ|PROT_WRITE|PROT_EXEC
mov esi, $22 ; flags = MAP_PRIVATE|MAP_ANONYMOUS
mov edi, -1 ; fd = -1 (Ignored for MAP_ANONYMOUS)
xor ebp, ebp ; offset = 0 (4096*0) (Ignored for MAP_ANONYMOUS)
int $80 ; make call (There are other ways to do this too)

Why am I getting segmentation fault here?

I have the following code, where I try to write something into the stack. I write at the bottom of the stack, which the application still hasn't touched (Note that stack grows downwards and stackaddr here points to the bottom).
However I get segmentation fault even after doing mprotect to give both write and read permissions to that memory region. I get segmentation fault even if I use the compilation flag -fno-stack-protector. What is happening here?
pthread_attr_t attr;
void * stackaddr;
int * plocal_var;
size_t stacksize;
pthread_getattr_np(pthread_self(), &attr);
pthread_attr_getstack( &attr, &stackaddr, &stacksize );
printf( "stackaddr = %p, stacksize = %d\n", stackaddr, stacksize );
plocal_var = (int*)stackaddr;
mprotect((void*)plocal_var, 4096, PROT_READ | PROT_WRITE);
*plocal_var = 4;
printf( "local_var = %d!\n", *plocal_var );
You are almost certainly trying to mprotect() pages which are not yet mapped. You should check the return code: mprotect() is probably returning -1 and setting errno to ENOMEM (this is documented in the mprotect(2) man page).
Stack pages are mapped on demand, but the kernel is clever enough to distinguish between page faults caused by an access at or above the current stack pointer (which are caused by valid attempts to expand the stack downwards, by decrementing the stack pointer, and then performing a read or write to some positive offset from the new value), and page faults caused by an access below the stack pointer (which are not valid).

Resources