Резервирует ли ОС фиксированный объем действительного виртуального пространства для стека или чего-то еще? Могу ли я произвести переполнение стека, просто используя большие локальные переменные?
Я написал небольшую C
программу, чтобы проверить мое предположение. Он работает на 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[0] = f0ceabe0
и&a[n-1] = f16eabdf
Карта proc показывает стек: 7ffff0cea000-7ffff16ec000. (10248 * 1024B)
Тогда я попытался увеличить n = 11240 * 1024
Запуск программы дает &a[0] = b6b36690
и&a[n-1] = b763068f
Карта proc показывает стек: 7fffb6b35000-7fffb7633000. (11256 * 1024B)
ulimit -s
печатает 10240
на моем ПК.
Как видите, в обоих случаях размер стека больше, чем ulimit -s
дает. И стек растет с большей локальной переменной. Вершина стека как-то на 3-5 КБ больше &a[0]
(AFAIK красная зона - 128B).
Так как же эта карта стека распределяется?
источник
ulimit -s
выдачей 10240, как при условиях OP, и я получаю SIGSEGV, как и ожидалось (это то, что требуется для POSIX: «Если этот предел превышен, для потока будет сгенерирован SIGSEGV. «). Я подозреваю ошибку в ядре OP.Ядро Linux 4.2
rlim[RLIMIT_STACK]
что соответствует POSIXgerlimit(RLIMIT_STACK)
acct_stack_growth
Минимальная тестовая программа
Затем мы можем протестировать его с помощью минимальной 64-битной программы NASM:
Убедитесь, что вы отключили ASLR и удалили переменные окружения, так как они будут помещаться в стек и занимают место:
Предел где-то чуть ниже моего
ulimit -s
(8MiB для меня). Похоже, это из-за того, что дополнительные данные System V изначально помещаются в стек в дополнение к среде: параметры командной строки Linux 64 в Assembly | Переполнение стекаЕсли вы серьезно относитесь к этому, TODO создаст минимальный образ initrd, который начинает запись с вершины стека и спускается, а затем запускает его с помощью QEMU + GDB . Поместите
dprintf
в цикл печать адреса стека и точку останова вacct_stack_growth
. Это будет великолепно.Связанный:
источник
По умолчанию максимальный размер стека составляет 8 МБ на процесс,
но его можно изменить с помощью
ulimit
:Отображение значения по умолчанию в кБ:
Установить на неограниченное количество:
ulimit -s unlimited
влияет на текущую оболочку и подоболочки и их дочерние процессы.
(
ulimit
это встроенная команда оболочки)Вы можете показать фактический диапазон адресов стека, используемый с:
cat /proc/$PID/maps | grep -F '[stack]'
в Linux.
источник
ulimit -s
КБ действительным для программы. В моем случае это 10240KB. Но когда я объявляю локальный массивchar a[10240*1024]
и устанавливаюa[0]=1
, программа завершается корректно. Почему?int n = 10240*1024; char a[n]; memset(a,'x',n);
... Сег .a[]
в вашем стеке 10 МБ не было места. Компилятор мог видеть, что не может быть рекурсивного вызова, и сделал специальное распределение, или что-то еще, например прерывистый стек или некоторую косвенность.