Estou batendo minha cabeça na parede com isso.
No meu projeto, quando estou alocando memória com mmap
o mapping ( /proc/self/maps
) mostra que é uma região legível e executável, apesar de ter solicitado apenas memória legível.
Depois de analisar o strace (que estava com boa aparência) e outras depurações, consegui identificar a única coisa que parece evitar esse problema estranho: remover arquivos de montagem do projeto e deixar apenas C. puro (o que ?!)
Então, aqui está meu exemplo estranho, estou trabalhando no Ubunbtu 19.04 e no gcc padrão.
Se você compilar o executável de destino com o arquivo ASM (que está vazio), mmap
retornará uma região legível e executável, se você construir sem ele, ele se comportará corretamente. Veja a saída da /proc/self/maps
qual eu integrei no meu exemplo.
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 : é um arquivo vazio!
Saídas
Com a versão incluída do ASM
VirtualBox:~/mechanics/build$ gcc example.c example.s -o example && ./example
7f78d6e08000-7f78d6e0a000 r-xp 00000000 00:00 0
Sem a versão incluída do ASM
VirtualBox:~/mechanics/build$ gcc example.c -o example && ./example
7f1569296000-7f1569298000 r--p 00000000 00:00 0
-Wa,--noexecstack
.Respostas:
O Linux possui um domínio de execução chamado
READ_IMPLIES_EXEC
, que faz com que todas as páginas alocadasPROT_READ
também sejam fornecidasPROT_EXEC
. Este programa mostra se está habilitado para si:Se você compilar isso junto com um
.s
arquivo vazio , verá que ele está ativado, mas sem um, ele será desativado. O valor inicial disso vem das meta-informações ELF no seu binário . Façareadelf -Wl example
. Você verá esta linha quando compilar sem o.s
arquivo vazio :Mas este quando você compilou:
Observe em
RWE
vez de apenasRW
. A razão para isso é que o vinculador pressupõe que seus arquivos de montagem requerem leitura implícita-exec, a menos que seja explicitamente informado que não o fazem, e se alguma parte do seu programa exigir leitura implícita-exec, ele será ativado para todo o programa . Os arquivos de montagem que o GCC compila informam que não precisa disso, com esta linha (você verá isso se compilar com-S
):Coloque essa linha
example.s
e servirá para informar ao vinculador que ele também não precisa, e seu programa funcionará conforme o esperado.fonte
.o
arquivos! De qualquer forma, acho que esse é o mecanismo quegcc -zexecstack
usa e por que ele torna não apenas a pilha, mas tudo executável.-Wa,--noexecstack
. Eu acho que é uma borda afiada muito desagradável. A perda silenciosa de nx-stacks deve ser uma vulnerabilidade de segurança. O pessoal da Binutil deve corrigi-lo..note.GNU-stack,"",@progbits
fosse explicado - agora é opaco, equivalente a "essa sequência mágica de caracteres causa esse efeito", mas a sequência parece claramente ter algum tipo de semântica.Como alternativa para modificar seus arquivos de montagem com variantes de diretiva de seção específicas do GNU, você pode adicionar
-Wa,--noexecstack
à sua linha de comando para criar arquivos de montagem. Por exemplo, veja como eu faço isso no musl'sconfigure
:https://git.musl-libc.org/cgit/musl/commit/configure?id=adefe830dd376be386df5650a09c313c483adf1a
Acredito que pelo menos algumas versões do clang com o assembler-assembler podem exigir que ele seja passado como
--noexecstack
(sem o-Wa
), portanto, seu script de configuração provavelmente deve verificar os dois e ver qual é aceito.Você também pode usar
-Wl,-z,noexecstack
no tempo do link (inLDFLAGS
) para obter o mesmo resultado. A desvantagem disso é que não ajuda se o seu projeto produz.a
arquivos de biblioteca estáticos ( ) para uso por outro software, já que você não controla as opções de tempo do link quando usadas por outros programas.fonte