2013-06-02 2 views
3

Моя программа выполняет следующие действия в хронологическом порядкеDUP(), а затем Close() из нескольких потоков или процессов

  1. программа запускается с корневыми разрешениями.
  2. Среди других задач файл, доступный только для чтения с правами root, - open() ред.
  3. Коренные привилегии отбрасываются.
  4. Процессы детей порождаются clone() и установлены флаги CLONE_FILES | CLONE_FS | CLONE_IO, а это означает, что, хотя они используют отдельные области виртуальной памяти, они имеют одну и ту же таблицу дескрипторов файлов (и другие элементы ввода-вывода).
  5. Все дочерние процессы execve() их собственные программы (флаг FD_CLOEXEC не используется).
  6. Оригинальная программа завершается.

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

Одно из возможных решений, которое я рассматриваю сейчас, имеет шаг 3a, где fd файла - dup(), выделенный один раз для каждого дочернего процесса, и каждый ребенок получает свой собственный fd (как argv). Тогда каждая дочерняя программа просто close() их fd, так что после того, как все fds, указывающие на файл, будут close() d «фактический файл» закрыт.

Но так ли это работает? И безопасно ли это сделать (т. Е. Файл действительно закрыт)? Если нет, есть ли другой/лучший метод?

+1

Если оба родителя и дети все закрывают их fd, файл закрывается. Есть ли причина использовать «клон»? Я думаю, что простая «вилка» будет демонстрировать такое же поведение. –

+0

'Если оба родителя и дети все закрывают свой fd, тогда файл закрывается.« Это была бы хорошая новость, спасибо. 'Есть ли причина использовать клон? Я думаю, что простая вилка будет демонстрировать очень то же поведение. «Правда, но причина, по которой я использую« clone() », связана с производительностью: все дочерние программы должны обрабатывать один и тот же действительно большой набор сокетов (через« epoll ») , Совместное использование таблицы fd позволяет избежать излишних издержек. Причина, по которой я упоминаю, что в вопросе, заключается в том, что я не на 100%, что эта деталь не имеет значения. – Will

+0

Можно ли читать (/ разбирать) файл в родительском процессе и сохранять результат в памяти, разделяемой с помощью 'mmap', как шаг 3a? Это пропустит проблему передачи fd дочерним процессам и будет следовать принципу отказа от привилегий как можно скорее. –

ответ

1

При использовании dup(), как я предложил выше, возможно, просто отлично, я теперь - через день после того, как задал этот вопрос, - понял, что есть более хороший способ сделать это, по крайней мере, с точки зрения безопасность потока.

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

Так что подождите, почему бы не просто позвонить open() несколько раз (один раз для каждого ребенка) на нужный файл перед тем как сбросить корень? Из руководства по open():

Вызов открыть() создает новое описание открытия файла, запись в общесистемной таблице открытых файлов. Эта запись записывает смещение файла и флаги состояния файла (модифицируется с помощью операции fcntl (2) F_SETFL). Дескриптор файла является ссылкой на одну из этих записей; эта ссылка не затрагивается, если имя пути впоследствии удаляется или модифицируется, чтобы ссылаться на другой файл. Новое описание открытого файла изначально не передается ни с каким другим процессом, но совместное использование может возникать через fork (2).

Может использоваться как это:

int fds[CHILD_C]; 
for (int i = 0; i < CHILD_C; i++) { 
    fds[i] = open("/foo/bar", O_RDONLY); 
    // check for errors here 
} 
drop_privileges(); 
// etc 

Затем каждый ребенок получает ссылку на один из этих FDS через argv и делает что-то вроде:

  1. FILE *stream = fdopen(atoi(argv[FD_STRING_I]), "r")
  2. читать все необходимое от stream
  3. fclose(stream) (это также закрывает базовый файловый дескриптор)

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: В соответствии с кучей тестов, которые я запускал, это действительно безопасно и здорово. Однако я проверил только open() ing с O_RDONLY. Использование O_RDWR или O_WRONLY может быть или не быть безопасным.

Смежные вопросы