Jay
Jay

Reputation: 394

`mprotect`ing a custom ELF section leads to segmentation fault

I have come across this interesting StackOverflow post: How do you get the start and end addresses of a custom ELF section?

and I thought whether it might be possible to apply mprotect() to the function which is stored in the custom ELF section.

However, I am getting a segmentation fault, and using the GDB leads to not very helpful information where I fail to understand its cause.

Here is a very simple test code:

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#define PAGESIZE    4096
// Custom ELF section needs to be page-aligned to be mprotect
void foo() __attribute__((aligned(PAGESIZE)))  __attribute__ ((section ("custom_sec")));

extern struct fun_info *__start_custom_sec;
extern struct fun_info *__stop_custom_sec;

int main()
{
    // Sanity check.
    printf("Custom section range: %p to %p\n",
       (void *)&__start_custom_sec,
       (void *)&__stop_custom_sec);

    if(mprotect(&__start_custom_sec, getpagesize(), PROT_READ | PROT_WRITE) == -1) {
        perror("mprotect()");
        return 1;
    }
}
void foo() { printf("Foo\n"); }
gcc stackoverflow.c -o so 
./so

This will lead to the segmentation fault.

I noticed that instead of PROT_READ | PROT_WRITE, I use PROT_EXEC; I do not get such a segmentation fault. PROT_EXEC, according to the documentation, simply means "this memory is enabled to be executed", so my conjecture is that when I use mprotect to protect this custom ELF section with no execution permission, fini function needs to execute the mprotected memory region which is causing the segmentation fault. Hence, if I were to give PROT_EXEC permission, this problem is fixed.

I got this conjecture based on the output from the GDB because even backtracing leads to very ambiguous results without much room for me to debug:

>>> bt
#0  0x0000555555555014 in _fini ()
#1  0x00007ffff7de8d88 in _dl_fini () at dl-fini.c:240
#2  0x00007ffff7a6f940 in __run_exit_handlers (status=0, listp=0x7ffff7dd35d8 <__exit_funcs>, run_list_atexit=run_list_atexit@entry=true, run_dtors=run_dtors@entry=true) at exit.c:83
#3  0x00007ffff7a6f99a in __GI_exit (status=<optimized out>) at exit.c:105
#4  0x00007ffff7a5a2e8 in __libc_start_main (main=0x5555555547e0 <main>, argc=1, argv=0x7fffffffe518, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffe508) at ../csu/libc-start.c:325
#5  0x00005555555546da in _start ()

Please let me know why this program leads to a segmentation fault? I am not even calling foo in the code, which perplexes me.

The purpose of why I was looking to do this kind of thing was to see, for a lot bigger program, whether it is possible to align multiple functions into this custom section and together protect such a function with certain mprotect flags.


UPDATE EDIT WITH A POTENTIAL SOLUTION:

@user253751's solution helped me able to think in a different direction, so I started working toward that direction to solve this problem. Then @Chris Dodd's gave me a hint on what I needed to do in order to solve this problem, which is how I was able to devise the following potential solution.

I'm not sure if I did it exactly correctly, but I tried to adhere to Chris's comment and created a linker script as follows:

so_linker.ld

SECTIONS
{
    .custom_sec BLOCK(4096) : ALIGN(4096) {
    _start_custom_sec = .;
            *(.custom_sec)
    _end_custom_sec = .;
    }
}

INSERT AFTER .rodata;

and then modified the test code as this:

#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#define PAGESIZE    4096
void foo()  __attribute__ ((section (".custom_sec")));

const void * _start_custom_sec;
const void * _end_custom_sec;

int main()
{
    // Sanity check.
    printf("Custom section range: %p to %p\t Foo: %p\n",
       (void *)&_start_custom_sec,
       (void *)&_end_custom_sec,
       (void *)&foo);

    if(mprotect(&foo, getpagesize(), PROT_READ | PROT_WRITE) == -1) {
        perror("mprotect()");
        return 1;
    }
}
void foo() { printf("Foo\n"); }

Now, compile above program with the following command: gcc -T so_linker.ld stackoverflow.c -o so

and by checking the section arrangement, we can now see that this custom section is ensured to be pushed outside of any other code that may potentially touch it (using the command readelf --sections W so).

  [11] .init             PROGBITS        0000000000000620 000620 000017 00  AX  0   0  4
  [12] .plt              PROGBITS        0000000000000640 000640 000060 10  AX  0   0 16
  [13] .plt.got          PROGBITS        00000000000006a0 0006a0 000008 00  AX  0   0  8
  [14] .text             PROGBITS        00000000000006b0 0006b0 000212 00  AX  0   0 16
  [15] .fini             PROGBITS        00000000000008c4 0008c4 000009 00  AX  0   0  4
  [16] .rodata           PROGBITS        00000000000008d0 0008d0 000044 00   A  0   0  8
  [17] .custom_sec       PROGBITS        0000000000001000 001000 000013 00  AX  0   0 4096

So there are a few reasons why I had to change many things in the code. The most important thing to note is that I need to create a section with . in order to successfully re-align it using the linker script (e.g., custom_sec -> .custom_sec).

Edit 2: Fixed the linker script, now it should return correct beginning / end of the .custom_sec section.

Upvotes: 3

Views: 185

Answers (1)

It looks like there is some code on the same page as your custom section. When you set the page to not executable, you crash when executing that code.

You might need to use a section attribute or a linker script to ensure the section goes on its own page that is shared with nothing else.

Upvotes: 3

Related Questions