Как обнаружить ошибки `snprintf`? - PullRequest
0 голосов
/ 08 января 2019
int snprintf(char * restrict s, size_t n, const char * restrict format, ...);

snprintf() прекрасно предотвращает превышение цели s. И все же, когда для полного результата недостаточно места назначения, как обнаружить эту и другие ошибки?

Достаточно ли следующего?

char buf[11 + 10 + 1];
if (snprintf(buf, sizeof buf, "Random int %d", rand()) >= sizeof buf) {
  fprintf(stderr, "Buffer too small");  // Maybe `int` was 64-bit?
  exit (EXIT_FAILURE);
}

1 Ответ

0 голосов
/ 08 января 2019

Это часть Могу я ответить на свой вопрос? . Дополнительные ответы приветствуются.

Как обнаружить snprintf ошибки в C?

Используйте более широкое значение size_t или unsigned cast.

if ((size_t) snprintf(... ) >= sizeof buf) {
  error();
}

или педантично

int length_needed = snprintf(... );
if (length_needed < 0 || (unsigned) length_needed >= sizeof buf) {
  error();
}

Тестирование на усечение

Иногда очень важно обнаружить усеченную строку из snprintf().

char buf[13];
char *command = "format_drive";
char *sub_command = "cancel";  
snprintf(buf, sizeof buf, "%s %s", command, sub_command);
system(buf); // system("format_drive") leads to bye-bye data

Код ОК

Один тест, если возвращаемое значение соответствует или превышает размер массива назначения, равен почти .

char buf[20];
if (snprintf(buf, sizeof buf, "Random int %d", rand()) >= sizeof buf) {
  fprintf(stderr, "Buffer too small");
  exit(EXIT_FAILURE);
}

Отрицательное значение

Функция snprintf возвращает количество символов, которое было бы записано, если бы n было достаточно большим, не считая завершающий нулевой символ, или отрицательное значение, если произошла ошибка кодирования. Таким образом, вывод с нулевым символом завершается полностью, если и только если возвращаемое значение неотрицательно и меньше n. C11dr §7.21.6.5 3

Надежный код будет напрямую проверять наличие отрицательного значения для редкой ошибки кодирования . if (some_int <= some_size_t), к сожалению, недостаточно, поскольку int будет преобразовано в size_t. int отрицательное возвращаемое значение становится большим положительным size_t. Это обычно намного больше, чем размер массива, но не указывается таковым.

// Pedantic check for negative values 
int length_needed = snprintf(... as above ...);
if (length_needed < 0 || length_needed >= sizeof buf) {
  fprintf(stderr, "Buffer too small");
  exit(EXIT_FAILURE);
}

Знак несоответствия

Некоторые предупреждения компилятора скуливают о сравнении целых чисел различной значимости, таких как gcc's -Wsign-compare с int и size_t. Приведение к size_t кажется разумным.

предупреждение: сравнение между целочисленными выражениями со знаком и без знака [-Wsign-compare]

// Quiet different sign-ness warnings
int length_needed = snprintf(... as above ...);
if (length_needed < 0 || (size_t) length_needed >= sizeof buf) {
  fprintf(stderr, "Buffer too small");
  exit(EXIT_FAILURE);
}

Pedantic

C не указывает, что положительные значения int являются поддиапазоном size_t. size_t может быть unsigned short, а затем SIZE_MAX < INT_MAX. (Я не знаю такой реализации.) Таким образом, приведение к (size_t) some_int может изменить значение. Вместо этого приведение положительного возвращаемого значения к unsigned (INT_MAX <= UINT_MAX всегда истинно) не изменит значение и обеспечит сравнение с самым широким типом без знака между unsigned и size_t.

// Quiet different sign-ness warnings
int length_needed = snprintf(... as above ...);
if (length_needed < 0 || (unsigned) length_needed >= sizeof buf) {
  fprintf(stderr, "Buffer too small");
  exit(EXIT_FAILURE);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...