2015-12-01 2 views
1

Я побежал этот код на макинтош, а также на Linux:Почему эта строка не переполняет буфер?

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

int main (int argc, char *argv[]){ 

    int value = 5; 
    char buffer_one[8], buffer_two[8]; 

    strcpy(buffer_one, "one"); 
    strcpy(buffer_two, "two"); 

    printf("[BEFORE] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two); 
    printf("[BEFORE] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one); 
    printf("[BEFORE] value is at %p and is %i (0x%08x)\n", &value, value, value); 

    printf("\n[STRCPY] copying %i bytes into buffer two\n\n", strlen(argv[1])); 
    strcpy(buffer_two, argv[1]); 

    printf("[AFTER] buffer_two is at %p and contains \'%s\'\n", buffer_two, buffer_two); 
    printf("[AFTER] buffer_one is at %p and contains \'%s\'\n", buffer_one, buffer_one); 
    printf("[AFTER] value is at %p and is %i (0x%08x)\n", &value, value, value); 
} 

На макинтош, если я вошел «1234567890» в качестве аргумента командной строки, то 90 разливалась в буфер один, как я бы ожидать, поскольку буфер из 8 байтов было превышено на 2.

Однако, если я запустил его в своей системе Linux, для переполнения буфера требуется больше символов. Почему/почему я могу уйти с подачей буфера в Linux?

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

Если я нахожу 1234567890 на своем mac, 1234567890 будет напечатан в буфере два, а 90 будет напечатан в буферном. Как 90 может по-прежнему вмещать внутренний буфер два, даже если он переполнен. (это одна и та же концепция в Linux, но для переполнения требуется более 10 байт)

ответ

1

Способ, которым он может быть «внутри», заключается в том, что в конце «внутри» не существует остановки. Вы запрашиваете char[] длиной 8 и получаете его, предположительно, второй находится рядом с ним (хотя компилятор может свободно переставлять такие вещи, если захочет). Затем вы пишете, скажем, 10 символов в первый буфер. Когда вы печатаете этот буфер, он не знает, что в нем должно быть только 8 символов, он знает, где он начинается и продолжается, пока не дойдет до символа NUL.

Итак, он пойдет и распечатает всю строку. Другой буфер, который был рядом с ним, также знает, где начинается его память, где оказывается, что строка 9 из строки переполнена в ее пространство. Распечатайте его, чтобы он перешел в первую ячейку памяти для своей строки и распечатал до тех пор, пока не достигнет NUL, который в этом случае равен 90.

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

6

Ответ на ваш первый вопрос заключается в том, что выравнивание переменных в памяти определяется реализацией. (См. Раздел 6.2.8 «Выравнивание объектов» в C11 draft.) В принципе, для разных компиляторов может потребоваться различное минимальное количество байтов между двумя объектами в памяти. Компилятор, который вы использовали на Mac, упаковал два 8-байтовых буфера рядом друг с другом в стеке, вероятно, потому что выравнивание char[] составляет 8 или меньше байтов. Компилятор, который вы использовали в Linux, оставил больше байтов между двумя адресами, вероятно, потому, что выравнивание char[] составляет 16 байт.

Для вашего второго вопроса buffer_one и buffer_two - это всего лишь два адреса в непрерывном фрагменте памяти, к которому у вашей программы есть доступ. В этом случае из-за реализации стека buffer_two появляется с более низким адресом, чем buffer_one, поэтому данные, записанные в buffer_two, переполняются до buffer_one. Причина, по которой вы печатаете «1234567890» от buffer_two и «90» от buffer_one, состоит в том, что printf() начинает считывать байты по указанному вами адресу до тех пор, пока не прочитает нуль-терминатор (0x00).

Итак, когда вы strcpy() «1234567890» в buffer_two, вы на самом деле писать 11 байт, включая нуль-терминатора (0x00) в конце строки. На вашем Mac buffer_two и buffer_one были разделены на 8 байт, поэтому, когда printf() читает от buffer_two, он считывает 10 символов перед тем, как увидеть нуль-терминатор, который, оказывается, после адреса, на который указывает buffer_one. Когда printf() читает от buffer_one, он считывает 2 символа перед тем, как увидеть нуль-терминатор.

Смежные вопросы