Ответ 1
Если вы используете sprintf()
или vsprintf()
, вам нужно сначала выделить буфер, и вы должны быть уверены, что буфер достаточно большой, чтобы содержать то, что пишет sprintf. В противном случае sprintf()
с радостью перезапишет любую память, лежащую за пределами буфера.
char* x = malloc(5 * sizeof(char));
// writes "123456" +null but overruns the buffer
sprintf(x,"%s%s%s", "12", "34", "56");
... записывает '6' и завершающий null
за пределами конца пространства, выделенного для x
, либо повреждает какую-то другую переменную, либо вызывает ошибку сегментации.
Если вам повезет, он растопчет память между выделенными блоками и не принесет вреда - на этот раз. Это приводит к периодическим ошибкам - сложнее всего диагностировать. Хорошо использовать такой инструмент, как ElectricFence, который приводит к быстрому переполнению.
Не злонамеренный пользователь, который предоставляет слишком длинный ввод, может привести к непредсказуемому поведению программы. Злоумышленник может использовать это как способ ввести в систему собственный исполняемый код.
Одним из способов защиты от этого является использование snprintf()
, который усекает строку до максимальной длины, которую вы указали.
char *x = malloc(5 * sizeof(char));
int size = snprintf(x, 5, "%s%s%s", "12", "34", "56"); // writes "1234" + null
Возвращаемое значение size
- это длина, которая была бы записана, если бы было доступно пространство - не включая завершающий ноль.
В этом случае, если size
больше или равно 5, то вы знаете, что усечение произошло - и если вы не хотите усечения, вы можете выделить новую строку и попробовать snprintf()
снова.
char *x = malloc(BUF_LEN * sizeof(char));
int size = snprintf(x, 5, "%s%s%s", "12", "34", "56");
if (size >= BUF_LEN) {
realloc(&x,(size + 1) * sizeof(char));
snprintf(x, size + 1 , "%s%s%s", "12", "34", "56");
}
(это довольно наивный алгоритм, но он иллюстрирует суть)
asprintf()
делает это за один шаг - вычисляет длину строки, выделяет этот объем памяти и записывает строку в нее.
char *x;
int size = asprintf(&x, "%s%s%s", "12", "34", "56");
Во всех случаях после того, как вы закончили x
, вам нужно освободить его, или вы потеряете память:
free(x);
asprintf()
является неявным malloc()
, поэтому вы должны проверить, работает ли он, так же, как и с malloc()
или любым другим системным вызовом.
if (size == -1 ) {
/* deal with error in some way */
}
Обратите внимание, что asprintf()
является частью расширений GNU и BSD для libc - вы не можете быть уверены, что он будет доступен в любой среде Си. sprintf()
и snprintf()
являются частью стандартов POSIX и C99.