O sistema operacional reserva a quantidade fixa de espaço virtual válido para a pilha ou outra coisa? Posso produzir um estouro de pilha apenas usando grandes variáveis locais?
Eu escrevi um pequeno C
programa para testar minha suposição. Está sendo executado no X86-64 CentOS 6.5.
#include <string.h>
#include <stdio.h>
int main()
{
int n = 10240 * 1024;
char a[n];
memset(a, 'x', n);
printf("%x\n%x\n", &a[0], &a[n-1]);
getchar();
return 0;
}
A execução do programa fornece &a[0] = f0ceabe0
e&a[n-1] = f16eabdf
Os mapas proc mostram a pilha: 7ffff0cea000-7ffff16ec000. (10248 * 1024B)
Então eu tentei aumentar n = 11240 * 1024
A execução do programa fornece &a[0] = b6b36690
e&a[n-1] = b763068f
Os mapas proc mostram a pilha: 7fffb6b35000-7fffb7633000. (11256 * 1024B)
ulimit -s
imprime 10240
no meu PC.
Como você pode ver, em ambos os casos, o tamanho da pilha é maior do que o indicado ulimit -s
. E a pilha cresce com maior variável local. O topo da pilha está de alguma forma 3-5kB a mais &a[0]
(AFAIK, a zona vermelha é 128B).
Então, como esse mapa de pilha é alocado?
fonte
ulimit -s
fornecendo 10240, como nas condições do OP, e recebo um SIGSEGV conforme o esperado (é o que é exigido pelo POSIX: "Se esse limite for excedido, o SIGSEGV será gerado para o encadeamento. "). Suspeito de um bug no kernel do OP.Kernel Linux 4.2
rlim[RLIMIT_STACK]
que corresponde ao POSIXgerlimit(RLIMIT_STACK)
acct_stack_growth
Programa de teste mínimo
Em seguida, podemos testá-lo com um programa NASM mínimo de 64 bits:
Certifique-se de desativar o ASLR e remover as variáveis de ambiente, pois elas vão para a pilha e ocupam espaço:
O limite está um pouco abaixo do meu
ulimit -s
(8MiB para mim). Parece que isso se deve a dados adicionais especificados no System V, inicialmente colocados na pilha, além do ambiente: Parâmetros da linha de comando do Linux 64 no Assembly | Estouro de pilhaSe você é sério sobre isso, o TODO cria uma imagem mínima do initrd que começa a gravar a partir do topo da pilha e diminui e, em seguida, execute-a com QEMU + GDB . Coloque um
dprintf
no loop imprimindo o endereço da pilha e um ponto de interrupção emacct_stack_growth
. Será glorioso.Relacionado:
fonte
Por padrão, o tamanho máximo da pilha é configurado para 8 MB por processo,
mas pode ser alterado usando
ulimit
:Mostrando o padrão em kB:
Defina como ilimitado:
ulimit -s unlimited
afetando o shell e subshells atuais e seus processos filhos.
(
ulimit
é um comando interno do shell)Você pode mostrar o intervalo de endereços da pilha real em uso com:
cat /proc/$PID/maps | grep -F '[stack]'
no Linux.
fonte
ulimit -s
KB válido para o programa. No meu caso, é 10240KB. Mas quando eu declaro uma matriz localchar a[10240*1024]
e o conjuntoa[0]=1
, o programa sai corretamente. Por quê?int n = 10240*1024; char a[n]; memset(a,'x',n);
... falha de seg.a[]
não foi alocado na sua pilha de 10 MB. O compilador pode ter visto que não poderia haver uma chamada recursiva e fez alocação especial, ou algo mais como uma pilha descontínua ou algum indireto.