если кто-то жалуется на gets (), почему бы не сделать то же самое с scanf ("% s", ...)? - PullRequest
5 голосов
/ 05 июня 2010

С man gets:

Никогда не используйте get (). Потому что это так невозможно сказать, не зная данных заранее сколько символы получает () будет читать, и потому что gets () будет продолжать хранить символы за концом буфера, это чрезвычайно опасно для использования. Он был использован, чтобы сломать компьютер безопасность. Вместо этого используйте fgets ().

Почти везде я вижу, что scanf используется таким образом, чтобы иметь такую ​​же проблему ( переполнение буфера / переполнение буфера ): scanf("%s",string). Эта проблема существует в этом случае? Почему на справочной странице scanf нет упоминаний об этом? Почему gcc не предупреждает при компиляции с -Wall?

пс: я знаю, что есть способ указать в строке формата максимальную длину строки с помощью scanf:

char str[10];
scanf("%9s",str);

edit: я не прошу определить, верен ли предыдущий код или нет. У меня такой вопрос: если scanf("%s",string) всегда не прав, почему нет предупреждений и ничего нет об этом на странице руководства?

Ответы [ 5 ]

5 голосов
/ 05 июня 2010

Ответ прост: никто не написал код в GCC для выдачи этого предупреждения.

Как вы указали, предупреждение для конкретного случая "%s" (без ширины поля) вполне уместно.

Однако имейте в виду, что это относится только к случаям scanf(), vscanf(), fscanf() и vfscanf(). Этот спецификатор формата может быть совершенно безопасен с sscanf() и vsscanf(), поэтому в этом случае предупреждение не должно выдаваться. Это означает, что вы не можете просто добавить его в существующий код анализа «scanf-style-format-string»; вам придется разделить это на опции "fscanf-style-format-string" и "sscanf-style-format-string".

Я уверен, что если вы создадите патч для последней версии GCC, у вас есть все шансы быть принятым (и, конечно, вам также нужно будет отправить патчи для заголовочных файлов glibc).

4 голосов
/ 05 июня 2010

Использование gets() никогда не безопасно. scanf() можно безопасно использовать, как вы сказали в своем вопросе. Тем не менее, определение того, используете ли вы его безопасно, является более сложной задачей для компилятора (например, если вы вызываете scanf() в функции, в которой вы передаете буфер и число символов в качестве аргументов, он выиграет » быть в состоянии сказать); в этом случае он должен предполагать, что вы знаете, что делаете.

3 голосов
/ 05 июня 2010

Когда компилятор смотрит на строку форматирования scanf, он видит строку! Это при условии, что строка форматирования не вводится во время выполнения. Некоторые компиляторы, такие как GCC, имеют некоторые дополнительные функции для анализа строки форматирования, если она введена во время компиляции. Эта дополнительная функциональность не является исчерпывающей, потому что в некоторых ситуациях требуются накладные расходы во время выполнения, что является НЕТ НЕТ для языков, таких как C. Например, вы можете обнаружить небезопасное использование, не вставляя дополнительный скрытый код в этом случае:

char* str;
size_t size;
scanf("%z", &size);
str = malloc(size);
scanf("%9s"); // how can the compiler determine if this is a safe call?!

Конечно, есть способы написать безопасный код с помощью scanf, если указать число символов для чтения и достаточно памяти для хранения строки. В случае gets невозможно указать количество символов для чтения.

1 голос
/ 05 июня 2010

Я не уверен, почему на странице man для scanf не упоминается вероятность переполнения буфера, но vanilla scanf не является безопасным вариантом. Довольно датированная ссылка - http://blogs.msdn.com/b/rsamona/archive/2005/10/24/484449.aspx показывает это как случай. Также проверьте это (не gcc, но, тем не менее, информативно) - http://blogs.msdn.com/b/parthas/archive/2006/12/06/application-crash-on-replacing-sscanf-with-sscanf-s.aspx

0 голосов
/ 05 июня 2010

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

...