Когда количественно определять игнорируемое совпадение с образцом в функции C sscanf

Cppcheck 1,67 поднял проблему переносимости в моем исходном коде в этой строке:

sscanf(s, "%d%*[,;.]%d", &f, &a);

Это сообщение, которое я получил от него:

scanf без ограничений ширины поля может привести к сбою с огромными входными данными в некоторых версиях libc.

Первоначальное намерение строки формата состояло в том, чтобы принять один из трех возможных символов-ограничителей между двумя целыми числами, и сегодня — благодаря Cppcheck[1] — я вижу это %*[,;.] принимает даже строки символов-ограничителей. Однако я сомневаюсь, что моя строка формата может вызвать сбой, потому что неограниченная часть игнорируется.

Возможно ли проблема с переполнением буфера? …может быть за кадром?


[1]
Как заблудиться между дальнозоркостью и слепотой:

Я пытался исправить это с помощью %1*[,;.] (после какой-то API документ), но Cppcheck настоял на проблеме, поэтому я тоже пытался %*1[,;.] с таким же «успехом». Кажется, я должен это пока подавить …

3

Решение

Поздравляем с обнаружением ошибки в Cppcheck 1.67 (текущая версия).

У вас есть в основном три обходных пути:

  1. Просто игнорируйте ложное срабатывание.
  2. Пересмотрите свой формат (назначьте это поле, возможно, так как вы хотите соответствовать только одному символу).

    char tmp;
    if(3 != sscanf(s, "%d %c%d", &f, &tmp, &a) || tmp!=',' && tmp!=';' && tmp!= '.')
    goto error;
    
  3. Подавить предупреждение напрямую (предпочтительно встроенные подавления):

    //cppcheck-suppress invalidscanf_libc
    if(2 != sscanf(s, "%d%1*[,;.]%d", &f, &a))
    goto error;
    

Не забудьте сообщить об ошибке как «дефект / ложное срабатывание», чтобы вы могли удалиться и забыть об этом как можно быстрее.

2

Другие решения

Когда количественно определять игнорируемое совпадение с образцом в функции 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
1