Подтвердить что ты не робот

Зачем использовать asprintf() вместо sprintf()?

Мне трудно понять, зачем вам нужен asprintf. Здесь в руководстве это говорит

Функции asprintf() и vasprintf() являются аналогами sprintf(3) и vsprintf(3), за исключением того, что они выделяют строку, достаточно большую для хранения вывода, включая завершающий нулевой байт, и возвращают указатель на него через первый аргумент, Этот указатель должен быть передан free(3) чтобы освободить выделенное хранилище, когда оно больше не нужно.

Вот пример, который я пытаюсь понять:

asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));

Какая разница, если буфер выделяет строку, достаточно большую по сравнению со словами char * = (string)

4b9b3361

Ответ 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.

Ответ 2

Преимуществом является безопасность.

Многочисленные программы позволили использовать системные эксплойты, когда буферы с программным обеспечением были переполнены при заполнении данными, предоставленными пользователем.

Имея asprintf выделение буфера для вас, гарантии, которые не могут произойти.

Однако вы должны проверить возвращаемое значение asprintf, чтобы гарантировать, что распределение памяти действительно сработало. См. http://blogs.23.nu/ilja/2006/10/antville-12995/