Czy system operacyjny rezerwuje stałą ilość ważnego miejsca wirtualnego na stos lub coś innego? Czy jestem w stanie wygenerować przepełnienie stosu tylko przy użyciu dużych zmiennych lokalnych?
Napisałem mały C
program, aby sprawdzić moje założenia. Działa na 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;
}
Uruchomienie programu daje &a[0] = f0ceabe0
i&a[n-1] = f16eabdf
Mapy proc pokazują stos: 7ffff0cea000-7ffff16ec000. (10248 * 1024B)
Potem próbowałem zwiększyć n = 11240 * 1024
Uruchomienie programu daje &a[0] = b6b36690
i&a[n-1] = b763068f
Mapy proc pokazują stos: 7fffb6b35000-7fffb7633000. (11256 * 1024B)
ulimit -s
drukuje 10240
na moim komputerze.
Jak widać, w obu przypadkach rozmiar stosu jest większy niż ulimit -s
daje. A stos rośnie wraz z większą zmienną lokalną. Na górze stosu jest jakieś 3-5kB więcej &a[0]
(AFAIK, czerwona strefa to 128B).
Jak więc alokuje się tę mapę stosów?
źródło
ulimit -s
podaniem 10240, jak w warunkach OP, i otrzymuję SIGSEGV zgodnie z oczekiwaniami (jest to wymagane przez POSIX: „Jeśli ten limit zostanie przekroczony, SIGSEGV zostanie wygenerowany dla wątku. „). Podejrzewam błąd w jądrze OP.Jądro Linux 4.2
rlim[RLIMIT_STACK]
co odpowiada POSIXgerlimit(RLIMIT_STACK)
acct_stack_growth
Minimalny program testowy
Następnie możemy to przetestować za pomocą minimalnego 64-bitowego programu NASM:
Upewnij się, że wyłączasz ASLR i usuwasz zmienne środowiskowe, ponieważ będą one umieszczane na stosie i zajmują miejsce:
Limit jest gdzieś nieco poniżej mojego
ulimit -s
(8 MB dla mnie). Wygląda na to, że dzieje się tak z powodu dodatkowych danych określonych w Systemie V, które początkowo zostały umieszczone na stosie oprócz środowiska: Parametry wiersza polecenia systemu Linux 64 w zestawie | Przepełnienie stosuJeśli poważnie o tym mówisz, TODO zrób minimalny obraz initrd, który zacznie pisać od góry stosu i zejdzie w dół, a następnie uruchom go za pomocą QEMU + GDB . Umieść
dprintf
pętlę drukującą adres stosu i punkt przerwania naacct_stack_growth
. Będzie chwalebnie.Związane z:
źródło
Domyślnie maksymalny rozmiar stosu jest skonfigurowany na 8 MB na proces,
ale można go zmienić za pomocą
ulimit
:Pokazuje wartość domyślną w kB:
Ustaw na nieograniczony:
ulimit -s unlimited
wpływające na bieżącą powłokę i podpowłoki oraz ich procesy potomne.
(
ulimit
jest wbudowanym poleceniem powłoki)Możesz pokazać rzeczywisty zakres adresów stosu w użyciu z:
cat /proc/$PID/maps | grep -F '[stack]'
w systemie Linux.
źródło
ulimit -s
KB będzie poprawny dla programu. W moim przypadku jest to 10240 KB. Ale kiedy deklaruję lokalną tablicęchar a[10240*1024]
i zestawa[0]=1
, program kończy się poprawnie. Dlaczego?int n = 10240*1024; char a[n]; memset(a,'x',n);
... seg wina.a[]
nie został przydzielony do twojego stosu 10 MB. Kompilator mógł zobaczyć, że nie może być wywołania rekurencyjnego i dokonał specjalnego przydziału lub czegoś innego, jak nieciągłe stosy lub jakieś pośrednie.