Permiso de ejecución inesperado de mmap cuando se incluyen archivos de ensamblaje incluidos en el proyecto
linux assembly (2)
Estoy golpeando mi cabeza contra la pared con esto.
En mi proyecto, cuando estoy asignando memoria con
mmap
el mapeo (
/proc/self/maps
) muestra que es una región legible y ejecutable a
pesar de
que solo solicité memoria legible.
Después de analizar strace (que se veía bien) y otra depuración, pude identificar lo único que parece evitar este extraño problema: eliminar archivos de ensamblaje del proyecto y dejar solo C. (¡¿qué ?!)
Así que aquí está mi extraño ejemplo, estoy trabajando en Ubunbtu 19.04 y gcc por defecto.
Si compila el ejecutable de destino con el archivo ASM (que está vacío), entonces
mmap
devuelve una región legible y ejecutable, si construye sin él se comportará correctamente.
Vea la salida de
/proc/self/maps
que he incrustado en mi ejemplo.
ejemplo.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 : es un archivo vacío!
Salidas
Con la versión ASM incluida
VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0
Sin la versión incluida de ASM
VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0
Como alternativa a la modificación de sus archivos de ensamblaje con variantes de directivas de sección específicas de GNU, puede agregar
-Wa,--noexecstack
a su línea de comando para construir archivos de ensamblaje.
Por ejemplo, vea cómo lo hago en musl''s
configure
:
https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a
Creo que al menos algunas versiones de clang con ensamblador integrado pueden requerir que se pase como
--noexecstack
(sin
-Wa
), por lo que su script de configuración probablemente debería verificar ambos y ver cuál es aceptado.
También puede usar
-Wl,-z,noexecstack
en el momento del enlace (en
LDFLAGS
) para obtener el mismo resultado.
La desventaja de esto es que no ayuda si su proyecto produce archivos de biblioteca estáticos (
.a
) para su uso por otro software, ya que luego no controla las opciones de tiempo de enlace cuando lo utilizan otros programas.
Linux tiene un
dominio de ejecución
llamado
READ_IMPLIES_EXEC
, que hace que todas las páginas asignadas con
PROT_READ
también reciban
PROT_EXEC
.
Este programa le mostrará si está habilitado para sí mismo:
#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;
}
Si compila eso junto con un archivo
.s
vacío, verá que está habilitado, pero sin uno, estará deshabilitado.
El valor inicial de esto
proviene de la metainformación ELF en su binario
.
Hacer
readelf -Wl example
.
Verá esta línea cuando compiló sin el archivo
.s
vacío:
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
Pero este cuando lo compiló:
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
Tenga en cuenta
RWE
lugar de solo
RW
.
La razón de esto es que el enlazador supone que sus archivos de ensamblaje requieren read-implica-exec a menos que se indique explícitamente que no lo hacen, y si alguna parte de su programa requiere read-implica-exec, entonces está habilitado para todo su programa .
Los archivos de ensamblaje que compila GCC le dicen que no necesita esto, con esta línea (verá esto si compila con
-S
):
.section .note.GNU-stack,"",@progbits
Ponga esa línea en
example.s
, y servirá para decirle al enlazador que tampoco la necesita, y su programa funcionará como se esperaba.