getline () и fgets (): управление распределением памяти - PullRequest
19 голосов
/ 03 мая 2019

Для чтения строк из файла существуют функции getline() и fgets() POSIX (игнорируя страшные gets()).Здравый смысл заключается в том, что getline() предпочтительнее, чем fgets(), потому что он выделяет буфер строки по мере необходимости.

Мой вопрос: не опасно ли это?Что если случайно или со злым умыслом кто-то создаст файл размером 100 ГБ без байта '\n' - не заставит ли мой вызов getline() выделить безумное количество памяти?

Ответы [ 6 ]

15 голосов
/ 03 мая 2019

Мой вопрос: разве это не опасно?Что если случайно или со злым умыслом кто-то создаст файл размером 100 Гбайт без байта '\ n' - не заставит ли мой вызов getline () выделять безумный объем памяти?

Да,то, что вы описываете, является вероятным риском.Тем не менее,

  • , если программе требуется загрузить всю строку в память за один раз, а затем позволить getline() попытаться сделать это, по сути, не более рискованно, чем писать свой собственный код, чтобы сделать это с fgets()
  • если у вас есть программа с такой уязвимостью, вы можете уменьшить риск, используя setrlimit(), чтобы ограничить общий объем (виртуальной) памяти, которую она может зарезервировать.Это может быть использовано, чтобы вызвать сбой, вместо того, чтобы успешно выделять достаточно памяти, чтобы помешать работе остальной системы.

В целом, я бы сказал, лучше всего написать код, который не требует вводав единицах полных строк (все сразу) в первую очередь, но такой подход имеет свои сложности.

8 голосов
/ 03 мая 2019

Это может быть опасно, да.Не знаю, как это будет работать на других компьютерах, но выполнение приведенного ниже кода заморозило мой компьютер до такой степени, что потребовалась полная перезагрузка:

/* DANGEROUS CODE */

#include <stdio.h>

int main(void)
{
    FILE *f;
    char *s;
    size_t n = 0;

    f = fopen("/dev/zero", "r");
    getline(&s, &n, f);

    return 0;
}
3 голосов
/ 03 мая 2019

Функция getline использует внутренне malloc и realloc и возвращает -1, если они терпят неудачу, поэтому результат ничем не отличается от того, что вы пытались вызвать malloc(100000000000). А именно, errno получает значение ENOMEM, а getline возвращает -1.

Таким образом, у вас возникнет та же проблема, независимо от того, использовали ли вы getline или пытались сделать то же самое с fgets и ручным распределением памяти, чтобы обеспечить чтение всей строки.

1 голос
/ 03 мая 2019

Некоторые рекомендации по кодированию (например, MISRA C) могут запрещать использование динамического выделения памяти (например, getline()). Для этого есть причины, например, предотвращение утечки памяти.

Если вы знаете максимальный размер всех допустимых строк, вы можете избежать выделения памяти, используя fgets() вместо getline(), и таким образом удалить одну потенциальную точку утечки памяти.

0 голосов
/ 03 мая 2019

На самом деле это зависит от того, как вы хотите обрабатывать слишком длинные строки.

fgets с буфером приличного размера будет работать в целом, и вы можете обнаружить, что он "потерпел неудачу" - конец буфера не имеет символа новой строки. Можно всегда избегать использования strlen () для подтверждения переполнения буфера, но это другой вопрос.

Возможно, ваша стратегия состоит в том, чтобы просто пропустить строки, которые не могут быть обработаны, или, возможно, остальная часть строки - это просто комментарий, который вы в любом случае проигнорируете, и в этом случае легко поместить fgets в цикл отказаться от остальной части строки без штрафов за распределение.

Если вы хотите прочитать всю строку независимо от того, тогда getline может быть лучшей стратегией для вас. Злоумышленнику потребуется много места на диске, чтобы вызвать плохое поведение, которое вы описываете, или, возможно, передать / dev / random или аналогично имени входного файла.

Опять же, если getline не может перераспределить, он потерпит неудачу таким образом, что вы сможете восстановить, хотя, если вы повторно используете буфер для многострочного чтения, вы можете захотеть освободить буфер, который он имеет после ошибка перед попыткой прочитать больше, так как она все еще выделена и, возможно, выросла настолько, насколько могла, до сбоя.

0 голосов
/ 03 мая 2019

getline() перераспределить буфер для вас, чтобы немного облегчить управление памятью в вашей программе.

Но на самом деле это может привести к выделению большого фрагмента памяти.Если это вызывает беспокойство, вам следует предпринять дополнительные шаги, чтобы использовать функции, которые не выделяют память неявно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...