Почему это не ошибка в qmail?

Я читал "Некоторые мысли DJB о безопасности после десяти лет Qmail 1.0", и он перечислил эту функцию для перемещения дескриптора файла:

int fd_move(to,from)
int to;
int from;
{
  if (to == from) return 0;
  if (fd_copy(to,from) == -1) return -1;
  close(from);
  return 0;
}

Мне пришло в голову, что этот код не проверяет возвращаемое значение завершения, таким образом, я прочитал страницу справочника для близкого (2), и кажется, что это может перестать работать с EINTR, в этом случае соответствующее поведение, казалось бы, было бы для вызова близко снова с тем же аргументом.

Так как этот код был написан кем-то с намного большим опытом, чем я и в C и в UNIX, и дополнительно стоял неизменный в qmail больше десятилетия, я предполагаю, что должен быть некоторый нюанс, который я пропускаю, который делает этот код корректным. Кто-либо может объяснить что нюанс мне?

7
задан jemfinch 3 April 2010 в 06:11
поделиться

4 ответа

Когда дескриптор файла дублируется, как в функции fd_copy или dup2 , вы приведет к тому, что несколько дескрипторов файла будут ссылаться на одно и то же (то есть на один и тот же файл структуры в ядре). Закрытие одного из них просто уменьшит его счетчик ссылок. Никакая операция не выполняется с базовым объектом, если он не является последним закрытием. В результате такие условия, как EINTR и EIO , невозможны.

1
ответ дан 7 December 2019 в 16:40
поделиться

У меня есть два ответа:

  1. Он пытался выделить общий код, и часто в таких примерах для краткости отсутствует проверка ошибок и ясность.
  2. close (2) может возвращать EINTER, но делает это на практике, и если да, то что бы вы разумно сделали? Повторить попытку один раз? Повторять до успеха? Что, если вы получите EIO? Это может означать почти что угодно, поэтому у вас действительно нет разумного выхода, кроме как зарегистрировать это и двигаться дальше.Если вы попытаетесь повторить попытку после EIO, вы можете получить EBADF, что тогда? Предположим, что дескриптор закрыт, и двигаться дальше?

Каждый системный вызов может возвращать EINTR, особенно тот, который блокирует, как read (2), ожидающий медленного человека. Это более вероятный сценарий, и хорошая процедура «получить входные данные из терминала» действительно проверит это. Это также означает, что write (2) может завершиться ошибкой даже при записи файла журнала. Вы пытаетесь зарегистрировать ошибку, которую генерировал регистратор, или вам просто нужно сдаться?

2
ответ дан 7 December 2019 в 16:40
поделиться

Другая возможность заключается в том, что его функция используется только в приложении (или его части), которое что-то сделало, чтобы гарантировать, что вызов не будет прерван сигналом. Если вы не собираетесь делать что-либо важное с сигналами, вам не нужно реагировать на них, и может иметь смысл замаскировать их все, а не заключать каждый отдельный системный вызов блокировки в повторную попытку EINTR. За исключением, конечно, тех, которые вас убьют, поэтому SIGKILL и часто SIGPIPE, если вы обрабатываете его путем выхода, вместе с SIGSEGV и аналогичными фатальными ошибками, которые в любом случае никогда не будут доставлены в правильное приложение пользовательского пространства.

В любом случае, если он говорит только о безопасности, то, вполне возможно, ему не нужно повторять попытку close . Если закрыть сбой с EIO, то он не сможет повторить попытку, это будет постоянный сбой. Следовательно, для корректности его программы не обязательно, чтобы close выполнялись успешно. Вполне может быть, что для корректности его программы не обязательно повторять попытку close на EINTR.

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

Изменить: мне пришло в голову, что повторная попытка EINTR потенциально может сама по себе быть недостатком безопасности при определенных условиях. Он вводит новое поведение в этот раздел кода: он может бесконечно зацикливаться в ответ на поток сигналов, тогда как раньше он делал одну попытку закрыть и затем возвращался. Я не знаю наверняка, что это вызовет у qmail какие-либо проблемы (в конце концов, close сам по себе не дает никаких гарантий, как скоро он вернется). Но если отказ после одной попытки действительно упрощает анализ кода, это может быть разумным шагом. Или не.

Вы можете подумать, что повторная попытка предотвращает ошибку DoS, когда сигнал вызывает ложный сбой. Но повторная попытка допускает еще одну (более сложную) ошибку DoS, когда поток сигналов вызывает неопределенное время ожидания. С точки зрения двоичного кода «может ли это приложение быть DoSed?», Который представляет собой вопрос абсолютной безопасности, который интересовал DJB, когда он писал qmail и djbdns, это не имеет значения. Если что-то может случиться один раз, то обычно это означает, что это может случиться много раз.

0
ответ дан 7 December 2019 в 16:40
поделиться

Только неработающие модули могут возвращать EINTR без вашего явного запроса. Разумная семантика для signal () позволяет перезапускать системные вызовы («стиль BSD»). При построении программы в системе с семантикой sysv (сигналы прерывания) вы всегда должны заменять вызовы signal () вызовами bsd_signal () , которые вы можете определить самостоятельно в условия sigaction () , если он не существует.

Также стоит отметить, что никакие системы не будут возвращать EINTR при получении сигнала, если вы не установили обработчики сигналов. Если действие по умолчанию остается на месте или если для сигнала задано бездействие, прерывание системных вызовов невозможно.

0
ответ дан 7 December 2019 в 16:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: