Канонический пример уничтожения стека в программе на C - PullRequest
1 голос
/ 06 октября 2010

Может, кто-нибудь объяснит мне типичный пример, когда (!) Вы уничтожаете стек в C-программе? Я использую GCC, в Ubuntu. Спасибо.

Ответы [ 4 ]

5 голосов
/ 06 октября 2010

Это зависит от того, что вы подразумеваете под «уничтожить стек», но это распространенная ошибка, которая обычно приводит к повреждению важных резидентных данных стека:

void dumb()
{
    char small[2];
    strcpy(small, "too long to fit"); // Writes past the end of "small", overwriting vital information
}

Это общий источник безопасностиотверстия.Он может потенциально использоваться для перехвата указателя инструкций, позволяя выполнить вредоносный код.См. переполнение буфера .

Другая ошибка, которую можно описать как «уничтожение стека», - это случай бесконечной рекурсии (прокрутите вниз на этой странице):

int add(int n)
{
    return n + add(n + 1);
}

Который, поскольку ему не хватает условия выхода, будет помещать в стек столько кадров, что в конечном итоге он будет «заполнен». (Если компилятор не может применить оптимизация хвостового вызова ; см. Ниже)

Оба эти примера компилируются без предупреждения с использованием GCC 4.4.3.


Примечание: Как отметил Билли ONeal ниже, поведение этих примеров специфично для x86, а не для языка C, и может варьироваться от одного компилятора к другому.Это не означает, что они не демонстрируют, как можно разбить стек в конкретной (и чрезвычайно распространенной) реализации C.

2 голосов
/ 07 октября 2010

Вот еще несколько примеров, где стек может быть разбит.

char* foo()
{
    char str[256];
    return str;
}

void bar()
{
    char* str = foo();
    strcpy(str, "Holy sweet Moses! I blew my stack!!");
}

Или

void foo()
{
    char* str; // uninitialized; has garbage value
    strcpy(str, "Holy sweet Moses! I blew my stack!!");
    // well, could be anything you are trashing
}

void foo()
{
    int* ptr; // uninitialized; has garbage value
    *ptr = "0xDEADBEEF";
    // well, could be anything you are trashing
}
0 голосов
/ 06 октября 2010

Я не совсем уверен, что вы имеете в виду, но всякий раз, когда вы выходите из функции, "стек" для этой функции уничтожается. Например:

void foo(void) {
    // a, b and c are "allocated" on the stack here
    int a, b, c;

}    // a, b and c are destroyed here

На самом деле стек никогда не разрушается, как вы думаете. Есть указатель на вершину стека и функции ссылочных позиций относительно текущей вершины стека. При выходе из функции указатель TOS уменьшается на определенную величину, но никакого реального уничтожения не происходит. Таким образом, теоретически вы можете получить доступ к значениям функции после ее выхода, хотя это будет плохой идеей.

Возможно, вы захотите взглянуть на это:

Как работает стек вызовов функций?

Соглашения о вызовах в C и C ++

0 голосов
/ 06 октября 2010

Вы не можете, по крайней мере, не в соответствии со стандартом C. Возможно, вы сможете использовать встроенные функции ассемблера GCC, чтобы связываться с указателем стека.

РЕДАКТИРОВАТЬ: я предполагаю, что вызов exit, abort или terminate (завершение только в C ++) приведет к разрушению стека: P

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...