Разрушение стека Strcat

При запуске следующего преднамеренного кода, разрушающего стек, strcat копирует значение источника ровно десять раз.

#include <stdio.h>
#include <stdlib.h>

int main() {
    char a[16];
    char b[16];
    char c[32];

    strcpy(a, "abcdefghijklmnop");
    printf("a = %s\nb = %s\nc = %s\n\n", a, b, c);

    strcpy(b, "ABCDEFGHIJKLMNOP");
    printf("a = %s\nb = %s\nc = %s\n\n", a, b, c);

    strcpy(c, b);
    printf("a = %s\nb = %s\nc = %s\n\n", a, b, c);

    strcat(c, b);
    printf("a = %s\nb = %s\nc = %s\n\n", a, b, c);

    return 0;
}

Выход:

a = abcdefghijklmnop b = c =

a = abcdefghijklmnopABCDEFGHIJKLMNOP b = ABCDEFGHIJKLMNOP c =

a = abcdefghijklmnopABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP b = ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP c = ABCDEFGHIJKLMNOP

a = abcdefghijklmnopABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP b = ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP c = ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP

** обнаружен сбой стека *: ./strcpytest завершен

Параметры здания:

gcc -O0 -g3 -Стена -c -fmessage-length=0

Код выполняется на архитектуре x86_64.

Почему он объединяется только десять раз?


person Forest    schedule 09.05.2014    source источник
comment
Я предполагаю x86 или x86_64. Верно?   -  person Deduplicator    schedule 09.05.2014
comment
Пожалуйста, добавьте эту важную информацию к вашему вопросу.   -  person Deduplicator    schedule 09.05.2014


Ответы (3)


Поведение strcpy() и strcat() не определено для перекрывающихся строк. Таким образом, обе ваши записи в c[] вызывают подозрения, и вместо того, чтобы просто тестировать разрушение стека, вы также тестируете обработку компилятором этого неопределенного поведения.

Я ожидаю, что строка strcpy(c, b) завершится ошибкой, но реализация должна каким-то образом получить длину b, прежде чем она перезапишет конечный ноль в начале c. Это может произойти, например, при копировании с последнего байта на первый.

strcat(c, b) можно реализовать более простым способом. Может быть, данных в десять раз достаточно, чтобы достичь некоторого предела, который завершает их.

Если вы хотите просто проверить повреждение стека, не используйте эти методы. Вместо этого просто используйте один массив и запишите его в конце с помощью цикла, например. "для (i = 0; i ‹ 1000000; i++) c[i] = 'h';"

person John Bickers    schedule 09.05.2014

Если ваша платформа использует защиту памяти и ее стек растет вниз, например x86 (Каково направление роста стека в большинстве современных систем?), выбор любой позиции стека и запись оттуда по возрастающим адресам (например, для строки...) означает, что вы не идите в направлении расширения стека, но происхождения стека, в конечном итоге преодолевая его и погружаясь на смерть на намеренно не отображаемую защитную страницу, и в этом случае вы получаете приятное сообщение об ошибке, о котором вы упомянули.

Сообщение об ошибке может быть персонализировано таким образом, чтобы дать вам подсказку о том, что произошло.

В стороне: изменение чего-либо вообще может изменить поведение вашего кода, поскольку платформа не обязана вести себя таким образом.
2-я сторона: поразительно то, что strcpy(c, b); ведет себя так, как если бы он не перезаписывал терминатор начала строки. в b и заканчивается в c.

person Deduplicator    schedule 09.05.2014

Я получаю такой вывод, используя компилятор gcc:

a = abcdefghijklmnop
b = 
c = 

a = abcdefghijklmnopABCDEFGHIJKLMNOP
b = ABCDEFGHIJKLMNOP
c = 

a = abcdefghijklmnopABCDEFGHIJKLMNOP
b = ABCDEFGHIJKLMNOP
c = ABCDEFGHIJKLMNOP

a = 
b = ABCDEFGHIJKLMNOP
c = ABCDEFGHIJKLMNOPABCDEFGHIJKLMNOP

Это потому, что вы не указываете требуемый размер для терминатора. Просто попробуйте:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main() {
    char a[17];
    char b[17];
    char c[33];

    strcpy(a, "abcdefghijklmnop");
    printf("a = %s\nb = %s\nc = %s\n\n", a, b, c);

    strcpy(b, "ABCDEFGHIJKLMNOP");
    printf("a = %s\nb = %s\nc = %s\n\n", a, b, c);

    strcpy(c, b);
    printf("a = %s\nb = %s\nc = %s\n\n", a, b, c);

    strcat(c, b);
    printf("a = %s\nb = %s\nc = %s\n\n", a, b, c);

    return 0;
}

Теперь все в порядке. Спасибо Болдрик.

person jahan    schedule 09.05.2014
comment
Использование предыдущей версии gcc (Kali, gcc 4.2?) в другой системе дает указанный вами результат. Вывод, упомянутый в вопросе, был получен при использовании gcc 4.8.2 в Ubuntu. Если это зависит от компилятора, т.е. - person Forest; 09.05.2014
comment
Вы должны были оставить место для нулевого терминатора. - person jahan; 09.05.2014
comment
Это преднамеренный код, разрушающий стек. Вопрос о поведении strcat. - person Forest; 09.05.2014
comment
Причина поведения strcat заключается в завершающем символе. C предполагает, что строка представляет собой массив символов с завершающим нулевым символом. Этот нулевой символ имеет значение 0 в кодировке ASCII и может быть представлен просто как 0 или '\0'. Это значение используется для обозначения конца значимых данных в строке. Если это значение отсутствует, многие строковые функции C будут продолжать обрабатывать данные за пределами значимых данных и часто за концом самого символьного массива, пока не обнаружат в памяти нулевой байт! - person jahan; 09.05.2014
comment
@Jahan: Пожалуйста, прочитайте настоящий вопрос. Кроме того, он никогда не находил нулевые байты, потому что переписывал их раньше. - person Deduplicator; 09.05.2014