Зачем использовать Asprintf?

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

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

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

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

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

2 ответа

Решение

Если вы используете sprintf() или vsprintf(), вам сначала нужно выделить буфер, и вы должны быть уверены, что буфер достаточно большой, чтобы содержать то, что пишет sprintf. В противном случае sprintf с радостью перезапишет любую память, лежащую за пределами буфера.

char* x = malloc(5 * sizeof(char));
sprintf(x,"%s%s%s", "12", "34", "56"); // writes "123456" +null but overruns the buffer

... пишет "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, 5, "%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 - вы не можете быть уверены, что он будет доступен в любой среде C. sprintf() а также snprintf() являются частью стандартов POSIX и C99.

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

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

имеющий asprintf выделить буфер для вас гарантии, что не может произойти.

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

Другие вопросы по тегам