Cppcheck 1,67 поднял проблему переносимости в моем исходном коде в этой строке:
sscanf(s, "%d%*[,;.]%d", &f, &a);
Это сообщение, которое я получил от него:
scanf без ограничений ширины поля может привести к сбою с огромными входными данными в некоторых версиях libc.
Первоначальное намерение строки формата состояло в том, чтобы принять один из трех возможных символов-ограничителей между двумя целыми числами, и сегодня — благодаря Cppcheck[1] — я вижу это %*[,;.]
принимает даже строки символов-ограничителей. Однако я сомневаюсь, что моя строка формата может вызвать сбой, потому что неограниченная часть игнорируется.
Возможно ли проблема с переполнением буфера? …может быть за кадром?
[1]
Как заблудиться между дальнозоркостью и слепотой:
Я пытался исправить это с помощью %1*[,;.]
(после какой-то API документ), но Cppcheck настоял на проблеме, поэтому я тоже пытался %*1[,;.]
с таким же «успехом». Кажется, я должен это пока подавить …
Поздравляем с обнаружением ошибки в Cppcheck 1.67 (текущая версия).
У вас есть в основном три обходных пути:
Пересмотрите свой формат (назначьте это поле, возможно, так как вы хотите соответствовать только одному символу).
char tmp;
if(3 != sscanf(s, "%d %c%d", &f, &tmp, &a) || tmp!=',' && tmp!=';' && tmp!= '.')
goto error;
Подавить предупреждение напрямую (предпочтительно встроенные подавления):
//cppcheck-suppress invalidscanf_libc
if(2 != sscanf(s, "%d%1*[,;.]%d", &f, &a))
goto error;
Не забудьте сообщить об ошибке как «дефект / ложное срабатывание», чтобы вы могли удалиться и забыть об этом как можно быстрее.
Когда количественно определять игнорируемое совпадение с образцом в функции C sscanf?
Вероятно, это хорошая идея — всегда количественно определять (см. Ниже), но чрезмерное количественное определение также может отвлекать от ваших намерений. В приведенном выше случае, где один символ разделителя должен быть пропущен, количественное определение, безусловно, будет полезным.
Возможно ли проблема с переполнением буфера? … может быть, за кадром?
Там не будет сбоев, вызванных ваш код. Что касается вопроса «за кадром», я экспериментировал с большими входными строками. В протестированной мной библиотеке C не было внутреннего переполнения буфера. Я попробовал C lib, который поставляется с Borland C ++ 5.6.4 и обнаружил, что Я не мог вызвать переполнение буфера с большими входами (более 400 миллионов символов).
Удивительно, Cppcheck был не совсем неправ — Существует проблема переносимости, но другая:
#include <stdio.h>
#include <assert.h>
#include <sstream>
int traced_sscanf_set(const int count, const bool limited)
{
const char sep = '.';
printf("\n");
std::stringstream ss;
ss << "123" << std::string(count, sep) << "456";
std::string s = ss.str();
printf("string of size %d with %d '%c's in it\n", s.size(), count, sep);
std::stringstream fs;
fs << "%d%";
if (limited) {
fs << count;
}
fs << "*["<< sep << "]%d";
std::string fmt = fs.str();
printf("fmt: \"%s\"\n", fmt.c_str());
int a = 0;
int b = 0;
const sscanfResult = sscanf(s.c_str(), fmt.c_str(), &a, &b);
printf("sscanfResult=%d, a=%d, b=%d\n", sscanfResult, a, b);
return sscanfResult;
}
void test_sscanf()
{
assert(traced_sscanf_set(0x7fff, true)==2);
assert(traced_sscanf_set(0x7fff, false)==2);
assert(traced_sscanf_set(0x8000, true)==2);
assert(traced_sscanf_set(0x8000, false)==1);
}
Библиотека, которую я проверил, внутренне ограничивает потребляемый (и пропущенный) ввод до 32767 (215-1) символы, если в параметре format явно не указано ограничение.
Для тех, кто заинтересован, вот вывод трассировки:
string of size 32773 with 32767 '.'s in it
fmt: "%d%32767*[.]%d"sscanfResult=2, a=123, b=456
string of size 32773 with 32767 '.'s in it
fmt: "%d%*[.]%d"sscanfResult=2, a=123, b=456
string of size 32774 with 32768 '.'s in it
fmt: "%d%32768*[.]%d"sscanfResult=2, a=123, b=456
string of size 32774 with 32768 '.'s in it
fmt: "%d%*[.]%d"sscanfResult=1, a=123, b=0