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

Как использовать dlopen, как я могу справиться с изменениями в файле библиотеки, который я загрузил?

У меня есть программа, написанная на С++, которая использует dlopen для загрузки динамической библиотеки (Linux, i386,.so). Когда файл библиотеки впоследствии изменен, моя программа имеет тенденцию к сбою. Это понятно, так как предположительно файл просто отображается в память.

Мой вопрос: иначе, чем просто создать себе копию файла и расшифровать его, есть ли способ загрузить общий объект, который безопасен в отношении последующих модификаций, или каким-либо способом восстановления от изменений к общему объекту, который Я загрузил?

Разъяснение: Вопрос не в том, "как я могу установить новую библиотеку без сбоев в программе", это "если кто-то, кого я не контролирую, копирует библиотеки, возможно ли я защищаюсь от этого?"

4b9b3361

Ответ 1

Если вы rm библиотеке перед установкой новой, я думаю, ваша система сохранит выделенную папку, файл открыт и ваша программа запущена. (И когда ваша программа, наконец, выйдет, тогда будут освобождены файловые ресурсы в основном скрытые, но все еще.)

Обновить: Хорошо, после уточнения. Динамический компоновщик фактически полностью "решает" эту проблему, передав флаг MAP_COPY, если он доступен, в mmap(2). Однако MAP_COPY не существует в Linux и не является запланированной функцией будущего. Второй вариант - MAP_DENYWRITE, который, как я полагаю, загрузчик действительно использует, и который находится в Linux API, и который раньше делал Linux. Он записывает ошибки при отображении области. Он должен все же разрешить rm и заменить. Проблема здесь в том, что любой, у кого есть доступ для чтения к файлу, может отображать его и блокировать записи, что открывает локальное отверстие DoS. (Рассмотрим /etc/utmp. Есть предложение использовать бит разрешения выполнения, чтобы исправить это.)

Вам это не понравится, но есть тривиальный патч ядра, который восстановит функциональность MAP_DENYWRITE. У Linux все еще есть функция, она просто очищает бит в случае mmap(2). Вы должны исправить его в коде, который дублируется для каждой архитектуры, для ia32 Я считаю, что файл arch/x86/ia32/sys_ia32.c.

asmlinkage long sys32_mmap2(unsigned long addr, unsigned long len,
                            unsigned long prot, unsigned long flags,
                            unsigned long fd, unsigned long pgoff)
{
        struct mm_struct *mm = current->mm;
        unsigned long error;
        struct file *file = NULL;

        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); // fix this line to not clear MAP_DENYWRITE

Это должно быть нормально, если у вас нет вредоносных локальных пользователей с учетными данными. Это не удаленный DoS, а только локальный.

Ответ 2

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

Пакетные менеджеры, такие как RPM, делают это автоматически - поэтому вы можете обновлять общие библиотеки и исполняемые файлы во время их работы, но старые версии продолжают работать.

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

Ответ 3

Невозможно защитить от того, кто переписывает вашу библиотеку, если у нее есть разрешение на запись файла.

Поскольку dlopen память отображает файл библиотеки, все изменения в файле видны в каждом процессе, в котором он открыт.

Функция dlopen использует сопоставление памяти, поскольку это наиболее эффективный способ использования разделяемых библиотек. Частная копия потеряла бы память.

Как говорили другие, правильный способ замены разделяемой библиотеки в Unix - использовать unlink или переименовать не, чтобы перезаписать библиотеку с новой копией. Команда install сделает это правильно.

Ответ 4

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

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

Мне хотелось бы, чтобы ОС "трансформировала эту область только для чтения в область копирования на запись". Я не думаю, что что-то подобное существует.

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

Кто редактирует ваши библиотеки из-под вас, так или иначе? Найдите этого человека и ударите его по голове сковородой.

Ответ 5

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

Мое предложение вдохновлено ответом @Paul Tomblin на этот вопрос о временных файлах в Linux. Некоторые из других ответов здесь предложили существование этого механизма, но не описали способ его использования из клиентского приложения по вашему желанию.

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

Если вы хотите загрузить библиотеку, выполните следующие действия:

  • скопируйте файл во временное место, возможно, начиная с mkstemp()
  • загрузите временную копию библиотеки, используя dlopen()
  • unlink() временный файл
  • выполняется как обычно, файловые ресурсы будут автоматически удалены, когда вы dlclose()

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

Изменить: ответ @Zan Lynx указывает, что создание пользовательских копий динамических библиотек может быть расточительным, если они реплицируются в несколько процессов. Поэтому мое предложение, вероятно, имеет смысл только в том случае, если оно применяется разумно - только к тем библиотекам, которым угрожает точевать (предположительно небольшое подмножество всех библиотек, которые не включают файлы в /lib или/usr/lib).