стек против обнаружения переполнения кучи

В системе с разбивкой по запросу, такой как Linux, где страницы могут составлять около 4 КБ из того, что я прочитал, она обеспечивает защиту, проверяя, превышает ли размер стека или кучи количество страниц, предоставленных каждой. КОГДА я создаю две переменные

char *s = (char *) malloc(100);   
char sa[100];

В цикле for я могу написать s[i] = 'c'; почти 4000 раз, прежде чем возникнет ошибка памяти, тогда как с sa[i] = 'c'; EDIT: я получаю ошибку segmentation fault или stack smashing для всего, что превышает размер массива.

Я могу понять, что в первом случае есть ошибка страницы, и он видит, что больше страниц не было выделено в кучу, следовательно, нарушение памяти. Но что происходит во втором случае, проверяет ли gcc во время выполнения все предварительно выделенные переменные?
РЕДАКТИРОВАТЬ: я публикую весь код ниже

int main(int argc,char* argv[]){
char *s = (char *) malloc(20);
char sa[400] = {0};
int i ,count;
printf(" enter the number of chars to write: ");
scanf("%d",&count);
for (i=0;i<count;i++){
printf("%d\n",i);
sa[i] = 'a';
//s[i] = 'a';
}
free(s);

}

person dasman    schedule 14.08.2011    source источник
comment
С точки зрения языка оба случая полностью не определены. Добавлен тег gcc для конкретного вопроса реализации.   -  person Bo Persson    schedule 14.08.2011
comment
К вашему сведению, ни в коем случае не гарантируется, что вы можете записать длину массива только с переменной, выделенной стеком. Я получил до 2880 на моей машине x86_64 (где новая страница попадает как &sa[i] == 0x7ffffffff000).   -  person user786653    schedule 14.08.2011
comment
Я предполагаю, что мы проигнорируем вопрос, почему вы пишете в массив за пределами выделенного диапазона, верно?   -  person Christian Semrau    schedule 14.08.2011
comment
поэтому я читал о gcc и канарейках (или охранных байтах ... проверяет ли gcc перезапись защитного байта каждый раз, когда стек растет ?.   -  person dasman    schedule 14.08.2011
comment
@ christian-semrau да ...   -  person dasman    schedule 14.08.2011
comment
@dasman: пожалуйста, опубликуйте весь цикл, я подозреваю, что sa никогда не выделяется более одного раза.   -  person Mat    schedule 14.08.2011
comment
Что значит стек растет? Если используется цикл for, стек сбрасывается в одно и то же положение каждый раз при циклическом цикле. Или цикл for включает рекурсивный вызов?   -  person QuentinUK    schedule 14.08.2011


Ответы (1)


Во многих 32-битных операционных системах стек растет вниз. Вы используете только положительные индексы в массиве, поэтому это зависит от того, насколько глубоко вложен ваш вызов функции. Когда вы индексируете массив, вы сначала перезапишете канарейку. Таким образом, сначала возникает ошибка разбиения стека. Затем вы начнете перезаписывать аргументы функции и адрес возврата. Без канарейки это приведет к тому, что функция return будет прыгать в бездну земли, обычно вызывая segfault. Не всегда он может случайно попасть в действительный код, что является логикой атак на переполнение буфера стека.

По мере того, как вы продолжаете, вы, в конечном итоге, будете писать дальше вершины стека на нераспределенные страницы. Тогда Segfault. В небольшой тестовой программе с несколькими вложенными вызовами, которые происходят быстро, плюс-минус несколько килобайт.

Также попробуйте с отрицательными смещениями. Это может продолжаться какое-то время, иначе не вызовет никаких неприятностей, поскольку вы пишете в нераспределенное пространство стека. Ошибка сегментации возникает, когда вы пишете сверх выделенного размера стека, обычно мегабайта. В Windows вы вызываете страницу защиты стека, генерируя исключение, для которого назван этот сайт.

person Hans Passant    schedule 14.08.2011
comment
Разве ОС не рассматривает стек, кучу и код как разные сегменты? Я думал, что изначально стек получает страницу, а куча получает страницу, поэтому по мере их роста выделяется больше страниц. так как же переполнение стека может перезаписать раздел кода? - person dasman; 16.08.2011
comment
Это не так. Не знаю, почему вы пришли к такому выводу. - person Hans Passant; 16.08.2011
comment
Я прочитал в тексте ОС, что компилятор c генерирует сегменты программы как код, стек, глобальные объекты кучи и т. Д., Но Linux помещает код в сегмент [код пользователя], а все остальные данные (стек, куча и т. Д.) В сегмент [данные пользователя] а затем использует подкачку. Итак, я понимаю, что Linux не сегментирует стек и кучу. Но если начальное выделение для сегмента [пользовательских данных] меньше, чем требуется (например, рекурсия), то как это расширить стек ?. - person dasman; 16.08.2011
comment
Linux постоянно меняется, и я не знаю, что он делает в наши дни. Windows использует зарезервированные страницы памяти, которые превращаются в настоящие из-за ошибок страниц. Linux не поддерживал это какое-то время, я не удивлюсь, если он поддержит сейчас. Ничего из этого не имеет значения, возможно, вы пропустили рост стопки в начале предложения. Вы пишете на уже выделенных страницах. - person Hans Passant; 16.08.2011
comment
Хорошо, стек растет вниз, а куча растет в логическом адресном пространстве. Таким образом, я могу писать в стеке до тех пор, пока для одного из логических адресов, на которые я ссылаюсь, не будет выделена страница - ›seg fault. в моем коде выше у меня инициализированная char sa [400]. Разрушение стека происходит, когда я зацикливаюсь около 600 раз ... но все, что находится в диапазоне ›800, приводит к ошибке seg. почему разбивание стека не всегда происходит первым? segfaults обнаруживаются при сбое страницы, но разбиение стека обнаруживается только в конце цикла ?. - person dasman; 16.08.2011
comment
Потому что запись, вызывающая segfault, происходит до возврата функции, которая обнаруживает, что канарейку топают. Ключевое понимание - это именно то, когда канарейку проверяют. Пока функция не вернется. - person Hans Passant; 16.08.2011