scanf% d segfault на большом входе - PullRequest
6 голосов
/ 02 июля 2011

Итак, я запустил некоторый статический анализатор кода поверх некоторого кода на C, и одна вещь, которая удивила меня, была предупреждением о:

int val;
scanf("%d", &val);

, который сказал, что для достаточно большого ввода это может привести к segfault.И, конечно же, это действительно может произойти.Теперь исправление достаточно простое (укажите некоторую ширину; в конце концов, мы знаем, сколько мест может иметь максимально допустимое целое число в зависимости от архитектуры), но меня интересует то, ПОЧЕМУ это происходит в первую очередь и почему это не так.не считается ошибкой в ​​libc (и это просто исправить)?

Теперь я предполагаю, что есть какая-то причина для такого поведения в первую очередь, что я пропускаю?

Редактировать: Хорошо, так как вопрос, кажется, не такой четкий, немного болееОбъяснение: Нет, анализатор кода не предупреждает о scanf в целом, но о том, что scanf читает цифру без определенной ширины.

Итак, вот минимальный рабочий пример:

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

int main() {
    int val;
    scanf("%d", &val);
    printf("Number not large enough.\n");
    return 0;
}

Мыможет получить ошибку, отправив гигантский номер (используя, например, Python):

import subprocess
cmd = "./test"
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, shell=True)
p.communicate("9"*50000000000000)
# program will segfault, if not make number larger

Ответы [ 3 ]

3 голосов
/ 20 сентября 2011

Если статическим анализатором является cppcheck, то он предупреждает об этом из-за ошибки в glibc, которая с тех пор была исправлена: http://sources.redhat.com/bugzilla/show_bug.cgi?id=13138

2 голосов
/ 02 июля 2011

отредактировано, так как я упустил тот факт, что вы кормите им статический анализатор кода

Если формат %d соответствует размеру int, то переполнения не должны быть такими, как онозаписывается в val через указатель, поскольку он всегда должен быть int.Попробуйте передать указатель на long int и посмотрите, выдаст ли анализатор предупреждение.Попробуйте изменить %d на %ld, сохраняя указатель long int, и посмотрите, будет ли снова выдано предупреждение.

Полагаю, стандарты должны что-то сказать о %d, типе, который ему нужен.Может быть, анализатор обеспокоен тем, что в некоторых системах int может быть короче, чем означает %d?Это звучит странно для меня.


Запустив ваш пример, скомпилированный с помощью gcc (и у меня есть python 2.6.6), я получаю

Traceback (most recent call last):
  File "./feed.py", line 4, in <module>
    p.communicate("9"*50000000000000)
OverflowError: cannot fit 'long' into an index-sized integer
Number not large enough.

Затем я попытался запустить этотвместо этого:

perl -e 'print "1"x6000000000000000;' |./test

и изменил часть C, чтобы записать

printf("%d Number not large enough.\n", val);

Я получаю в качестве вывода

5513204 Number not large enough.

, где число меняется при каждом запуске ...никогда не сегментируйте ... реализация GNU scanf безопасна ... хотя полученное число неверно ...

1 голос
/ 02 июля 2011

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

Вы можете добиться аналогичного эффекта с двойниками. Вытянув до крайности, вы можете написать 1, за которым следует тысяча нулей и показатель степени -1000 (чистое значение равно 1). На самом деле, когда я тестировал это несколько лет назад, Solaris обрабатывал 1000 цифр с апломбом; это было чуть более 1024, что он столкнулся с проблемой.

Итак, существует элемент QoI - качество реализации. Существует также элемент «чтобы следовать стандарту C, scanf() не может прекратить чтение, пока не встретится без цифр». Это противоречивые цели.

...