2009-11-24 3 views
4

Я знаю, что можно переполнить обычный код:Переполнение над scanf ("% 8s", строка)?

char string [9];

scanf ("% s", string).

Но возможно ли переполнение scanf («% 8s», строка)? 8 является просто примером.

Я знаю, что «% 8s» работает как разграничить, но я также заметил, когда я строка ввода длиннее 8 символов, программа будет завершена из-за:

* стек Smashing обнаружен *: ./a .out прекращено

======= Backtrace: =========

...

Очевидно, есть флаг, который определяет стек Smashing возбуждает GCC по умолчанию , Так как это разбивка стека, то я предполагаю, что все еще возможно переполнение и выполнение произвольного кода.

В отличие от обычного переполнения, который управляет вызывающим устройством scanf («% s»), если scanf («% 8s») может переполняться, он будет переполняться в функции scanf, чтобы при попытке scanf получить контроль.

Но scanf - это системный вызов, который требует переключения режимов (переход из пользовательского режима в режим ядра), и внутренне он будет вызывать такие вещи, как чтение в stdin и т. Д. Поэтому не уверен, что мы можем переполняться в режиме ядра или что-то в этом роде. .

Комментарии приветствуются!

ОБНОВЛЕНИЕ >>

символ строки [9] предполагается, в приведенном выше примере. char string [8] в следующем действительном коде.

Вопрос действительно о кажущейся противоречивой истории между безопасным scanf («% 8s») и абортом GCC из-за разрыва стека.

упрощенный код:

void foo(pass some pointer) { 
char input[8]; 
int input_number = 0; 

while (1) { // looping console 
    printf some info; 
    scanf("%8s", input); 

    input_number = atoi(input); 

    if ((strlen(input) == 1) && (strncmp(input, "q", 1) == 0)) { 
     input_number = -1; 
    } 
    switch (input_number) { 
     case -1: to quit the console if input = 'q'; 
     default: to print info that pointer refers to; 
     ... 
    } 

} 

} 

Примечание:

  1. Foo называется кем-то другим.
  2. Хотя строка содержит 8 байтов в реальном код с "% 8s", я не думаю, что это привести к разгрому.
+0

scanf - это функция библиотеки времени выполнения - нет режима переключения, поскольку он работает в пользовательском пространстве, если только он не должен запрашивать заполнение буфера, и в этом случае он будет вызывать чтение или чтение. – wallyk

+2

, как указано несколько раз в ответах, добавлен нулевой байт, поэтому вам понадобится буфер с 9 символами, чтобы принять до 8 символов ввода. – ysth

+1

Как много людей указали, ваше предположение в «Примечание 2.» неправильно. Этот пример позволяет использовать один байт переполнения, что и обнаруживает gcc. – caf

ответ

8

См http://www.opengroup.org/onlinepubs/009695399/functions/scanf.html:

В каждом реестре ective состоит из одного из следующих ... Необязательное ненулевое десятичное целое число, определяющее максимальную ширину поля.

s
Соответствует последовательности байтов, которые не являются символами пробела. Приложение должно гарантировать, что соответствующий аргумент является указателем на начальный байт массива char, signed char или unsigned char, достаточно большим, чтобы принять последовательность и завершающий нулевой код символа, который должен быть добавлен автоматически.

Поэтому он не будет переполнять 9-байтовый буфер строк.

+0

Если он не переполнится, почему GCC расскажет историю Stack Smashing? – Figo

+1

@Figo: потому что вы допустили ошибку. Покажите свой код! – ysth

+0

@ysth: код отправлен. – Figo

1

Если строка выделена для менее 8 чартеров, она, безусловно, перезапишет буфер, а scanf не добавит нулевой терминатор. Но до тех пор, пока у вас достаточно места в строке для вашей ценности, вы не должны переписываться.

+0

Для переполнения потребуется не менее 9 байтов. – wallyk

+0

На самом деле, я думаю, что scanf положит '\ 0' в конец. В C-стандарте говорится «завершающий нулевой символ, который будет добавлен автоматически». Также цитируется paxdiablo – Figo

+0

Я имел в виду, что у вас также не было места для нулевого. – rerun

3

Не никогда использование scanf (или fscanf по этому вопросу), если вы хотите, чтобы ваш вход, чтобы быть надежным.

Вы должны использовать fgets (или аналогично вариант «защищенный от переполнения буфера»), а затем используйте sscanf.

Основная проблема с scanf и fscanf является то, что ваш указатель файла может оказаться в неопределенном положении, если линия не ожидаемый формат (то есть, если scanf не получится). С помощью метода fgets/sscanf гораздо проще гарантировать, что вы находитесь на границе линии, без необходимости использовать ftell и fseek для перемещения по файлу.

Что касается ваших конкретных запросов о том, будет ли буфер переполнится, стандарт C имеет это сказать:

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

Итак, для формата "%8s" вам понадобится 9-символьный массив.

Я подозреваю, что у вас есть еще одна проблема в вашем коде. С тестовой программы:

#include <stdio.h> 
int main(int argc, char* argv[]) { 
    char x1; 
    char a[9]; 
    char x2; 
    x1 = x2 = ' '; 
    scanf ("%s",a); 
    printf ("[%c] [%s] [%c]\n",x1,a,x2); 
    return 0; 
} 

я получаю:

pax> ./qq.exe 
dfjdhadgha...lghjdfgjhd 
[s] [dfjdhadgha...lghjdfgjhd] [ ] 
    6 [main] qq 4744 _cygtls::handle_exceptions: Error while dumping state 
    (probably corrupted stack) 
    Segmentation fault (core dumped) 

Когда я меняю ту же программу использовать "%8s", я получаю (для одного и того же входа):

pax> ./qq.exe 
dfjdhadgha...lghjdfgjhd 
[ ] [dfjdhadg] [ ] 
+1

Да, я знаю. Но сейчас мне интересно узнать, имеет ли scanf (% 8s) ту же проблему, что и scanf, поскольку GCC сообщает мне, что все еще происходит разбиение стека! – Figo

+0

Опишите "надежный"? Приведите несколько примеров? – ysth

+0

@ysth: (1) Получите свой ввод в виде строк. (2) Убедитесь, что вы получаете целые строки (\ n char в конце), в противном случае ошибка «слишком длинная». (3) Используйте sscanf on line - вы можете делать это столько раз, сколько хотите на линии, не беспокоясь о базовом файле. – paxdiablo

1

Как указывалось ysth, массив должен содержать строку и завершающий нуль-символ, поэтому с использованием 8-байтового массива (особенно если он выделен в стеке, как и в вашем коде) скорее всего, испортит это.