问题
I am banging my head into the wall with this.
In my project, when I'm allocating memory with mmap
the mapping (/proc/self/maps
) shows that it is an readable and executable region despite I requested only readable memory.
After looking into strace (which was looking good) and other debugging, I was able to identify the only thing that seems to avoid this strange problem: removing assembly files from the project and leaving only pure C. (what?!)
So here is my strange example, I am working on Ubunbtu 19.04 and default gcc.
If you compile the target executable with the ASM file (which is empty) then mmap
returns a readable and executable region, if you build without then it behave correctly. See the output of /proc/self/maps
which I have embedded in my example.
example.c
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
int main()
{
void* p;
p = mmap(NULL, 8192,PROT_READ,MAP_ANONYMOUS|MAP_PRIVATE,-1,0);
{
FILE *f;
char line[512], s_search[17];
snprintf(s_search,16,"%lx",(long)p);
f = fopen("/proc/self/maps","r");
while (fgets(line,512,f))
{
if (strstr(line,s_search)) fputs(line,stderr);
}
fclose(f);
}
return 0;
}
example.s: Is an empty file!
Outputs
With the ASM included version
VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0
Without the ASM included version
VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0
回答1:
Linux has an execution domain called READ_IMPLIES_EXEC
, which causes all pages allocated with PROT_READ
to also be given PROT_EXEC
. This program will show you whether that's enabled for itself:
#include <stdio.h>
#include <sys/personality.h>
int main(void) {
printf("Read-implies-exec is %s\n", personality(0xffffffff) & READ_IMPLIES_EXEC ? "true" : "false");
return 0;
}
If you compile that along with an empty .s
file, you'll see that it's enabled, but without one, it'll be disabled. The initial value of this comes from the ELF meta-information in your binary. Do readelf -Wl example
. You'll see this line when you compiled without the empty .s
file:
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
But this one when you compiled with it:
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
Note RWE
instead of just RW
. The reason for this is that the linker assumes that your assembly files require read-implies-exec unless it's explicitly told that they don't, and if any part of your program requires read-implies-exec, then it's enabled for your whole program. The assembly files that GCC compiles tell it that it doesn't need this, with this line (you'll see this if you compile with -S
):
.section .note.GNU-stack,"",@progbits
Put that line in example.s
, and it will serve to tell the linker that it doesn't need it either, and your program will then work as expected.
回答2:
As an alternative to modifying your assembly files with GNU-specific section directive variants, you can add -Wa,--noexecstack
to your command line for building assembly files. For example, see how I do it in musl's configure
:
https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a
I believe at least some versions of clang with integrated-assembler may require it to be passed as --noexecstack
(without the -Wa
), so your configure script should probably check both and see which is accepted.
You can also use -Wl,-z,noexecstack
at link time (in LDFLAGS
) to get the same result. The disadvantage of this is that it doesn't help if your project produces static (.a
) library files for use by other software, since you then don't control the link-time options when it's used by other programs.
来源:https://stackoverflow.com/questions/58260465/unexpected-exec-permission-from-mmap-when-assembly-files-included-in-the-project