Intereting Posts
Поиск плавающих чисел Max и Min из файла в C Gcc использует sqrt без включения math.h Ошибка: выражение должно быть указателем на полный тип объекта (?) Основной пример MPI не работает Использование -> для доступа к свойству struct, но получение compiler errors, указывающее мне использовать ->? Что делает этот кусок кода и что означает каждый из используемых символов? Используемый язык – это C, и я всего лишь новичок в кодировании C Программирование: malloc и бесплатно в цикле Cuda C – Ошибка компоновщика – неопределенная ссылка Есть ли ключевое слово GCC, чтобы позволить переупорядочить структуру? Как создать звуковой сигнал с помощью символа «\ a»? Ограничение размера кучи в C Создание привязок SWIG с помощью CMake Как лучше всего справляться с 16-битной wchar_t Windows? Почему мое сравнение строк не удается? 32-разрядный код _asm для 64-разрядного ассемблерного кода в Windows

Переполнение над scanf (“% 8s”, строка)?

Я знаю, что можно переполнить обычный код:

char string [9];

scanf (“% s”, строка).

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

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

* обнаружено разбиение стека * : ./a.out завершено

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

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

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

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

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

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

char string [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», я не думаю, что это приведет к разгрому.

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

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

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

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

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

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

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

Что касается вашего конкретного запроса о переполнении буфера, в стандарте C есть следующее:

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

Таким образом, для формата "%8s" вам нужен 9-символьный массив.

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

 #include  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] [ ] 

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

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