So out of curiosity, I tried to run this code today (compiled with gcc -m32 1.c
):
int main(void)
{
// EB is the opcode for jmp rel/8
// FE is hex for -2
// So this is essentially an infinite loop
((void(*)(void))"\xEB\xFE")();
}
... and it worked! No segfaults, the program (correctly?) goes into an infinite loop. Looking at the disassembly (objdump -d a.out
), you can see the call to... whatever is at address 0x8048480
:
080483d6 <main>:
....
80483e7: b8 80 84 04 08 mov $0x8048480,%eax
80483ec: ff d0 call *%eax
....
objdump -s -j .rodata a.out
gives:
Contents of section .rodata:
8048478 03000000 01000200 ebfe00 ...........
~~~~
So it is indeed executing the string, which is stored in the .rodata
section. So I ran readelf --sections a.out
and got:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 08048154 000154 000013 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 08048168 000168 000020 00 A 0 0 4
[ 3] .note.gnu.build-i NOTE 08048188 000188 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 080481ac 0001ac 000020 04 A 5 0 4
[ 5] .dynsym DYNSYM 080481cc 0001cc 000040 10 A 6 1 4
[ 6] .dynstr STRTAB 0804820c 00020c 000045 00 A 0 0 1
[ 7] .gnu.version VERSYM 08048252 000252 000008 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0804825c 00025c 000020 00 A 6 1 4
[ 9] .rel.dyn REL 0804827c 00027c 000008 08 A 5 0 4
[10] .rel.plt REL 08048284 000284 000008 08 AI 5 23 4
[11] .init PROGBITS 0804828c 00028c 000023 00 AX 0 0 4
[12] .plt PROGBITS 080482b0 0002b0 000020 04 AX 0 0 16
[13] .plt.got PROGBITS 080482d0 0002d0 000008 00 AX 0 0 8
[14] .text PROGBITS 080482e0 0002e0 000182 00 AX 0 0 16
[15] .fini PROGBITS 08048464 000464 000014 00 AX 0 0 4
[16] .rodata PROGBITS 08048478 000478 00000b 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 08048484 000484 000034 00 A 0 0 4
[18] .eh_frame PROGBITS 080484b8 0004b8 0000e0 00 A 0 0 4
[19] .init_array INIT_ARRAY 08049f0c 000f0c 000004 04 WA 0 0 4
[20] .fini_array FINI_ARRAY 08049f10 000f10 000004 04 WA 0 0 4
[21] .dynamic DYNAMIC 08049f14 000f14 0000e8 08 WA 6 0 4
[22] .got PROGBITS 08049ffc 000ffc 000004 04 WA 0 0 4
[23] .got.plt PROGBITS 0804a000 001000 000010 04 WA 0 0 4
[24] .data PROGBITS 0804a010 001010 000008 00 WA 0 0 4
[25] .bss NOBITS 0804a018 001018 000004 00 WA 0 0 1
[26] .comment PROGBITS 00000000 001018 00001a 01 MS 0 0 1
[27] .symtab SYMTAB 00000000 001034 0003f0 10 28 45 4
[28] .strtab STRTAB 00000000 001424 0001bd 00 0 0 1
[29] .shstrtab STRTAB 00000000 0015e1 000105 00 0 0 1
So in the ELF binary, the section is marked non-executable. But in memory, the page is executable (cat /proc/xxx/maps
):
08048000-08049000 r-xp 00000000 08:01 663551 /home/andrew/Desktop/a.out
08049000-0804a000 r--p 00000000 08:01 663551 /home/andrew/Desktop/a.out
0804a000-0804b000 rw-p 00001000 08:01 663551 /home/andrew/Desktop/a.out
My original guess was that the sections too closely-spaced (there are both AX
and A
sections in the 08048000-08049000
range), so Linux is forced to give the page the union of the ELF permission bits (AX | A == AX
). However, even after increasing the size of the .rodata
section (by adding many long strings), all of the pages containing the .rodata
section are still executable. Why is this?
(For the record, I'm running on Linux kernel 4.11.7, GCC 7.1.1, and compiling as 64-bit still exhibits this behavior)