Если вы rm
библиотеку перед установкой новой, я думаю, ваша система сохранит выделенный индексный дескриптор, файл открытым и вашу программу будет работать. (И когда ваша программа, наконец, завершает работу, освобождаются файловые ресурсы, которые в основном скрыты, но все еще существуют.)
Обновление : Хорошо, пост-разъяснение. Динамический компоновщик фактически полностью «решает» эту проблему, передав флаг MAP_COPY
, если он доступен, в mmap (2)
. Однако MAP_COPY
не существует в Linux и не планируется в будущем. Второй лучший вариант - MAP_DENYWRITE
, который, как мне кажется, действительно используется загрузчиком, и который находится в Linux API, и что делал Linux. Он записывает ошибки во время отображения региона. Он должен по-прежнему позволять перезапуск и замену. Проблема здесь в том, что любой, у кого есть доступ для чтения к файлу, может сопоставить его и заблокировать запись, что открывает локальную дыру для 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, а только локальный.
Если вы устанавливаете новую версию библиотеки, правильная процедура - создать новый файл в том же каталоге, а затем переименуйте его поверх старого. Старый файл останется, пока он открыт, и продолжит использоваться.
Менеджеры пакетов, такие как RPM, делают это автоматически - так что вы можете обновлять разделяемые библиотеки и исполняемые файлы во время их работы - но старые версии продолжают работать.
В случае, если вам нужно взять новую версию, перезапустить процесс или перезагрузить библиотеку - перезапуск процесса звучит лучше - ваша программа может выполняться сама. Даже init может это сделать.
Если вы можете выяснить, где ваша библиотека отображена в памяти, вы можете mprotect
ее записывать и выполнять простую запись на каждую страницу (например, чтение и записать первый байт каждой страницы). Это должно дать вам частную копию каждой страницы.
Если mprotect не работает (возможно, исходный файл был открыт только для чтения), то вы можете скопировать регион в другое место, переназначить область (используя mmap
) в частную, доступную для записи область, затем скопируйте область обратно.
Я бы хотел, чтобы в ОС было «преобразование этой доступной только для чтения области в область копирования при записи» . Однако я не думаю, что что-то подобное существует.
В любом из этих сценариев, по-прежнему существует окно уязвимости - кто-то может изменить библиотеку, пока dlopen вызывает инициализаторы или до того, как произойдет ваш вызов переназначения. Вы небезопасны, если не сможете исправить динамический компоновщик, как описывает @DigitalRoss.
Кто вообще редактирует ваши библиотеки из-под вас? Найдите этого человека и ударьте его сковородой по голове.
Невозможно защититься от перезаписи вашей библиотеки кем-либо, если у них есть разрешение на запись в файл.
Поскольку память dlopen
отображает файл библиотеки, все изменения в файле видны в каждом открытом процессе.
Функция dlopen
использует отображение памяти, потому что это наиболее эффективный с точки зрения памяти способ использования разделяемых библиотек. Частная копия будет тратить память.
Как говорили другие, правильный способ заменить разделяемую библиотеку в Unix - это использовать unlink или переименование, не , чтобы перезаписать библиотеку новой копией. Команда install
сделает это правильно.
Это интригующий вопрос. Я ненавижу находить подобные дыры в Linux и люблю искать способы их исправить.
Мое предложение вдохновлено ответом @Paul Tomblin на этот вопрос о временных файлах в Linux . Некоторые из других ответов здесь предполагают существование этого механизма, но не описывают метод его использования из клиентского приложения, как вы просили.
Я не тестировал это, поэтому я не знаю, насколько хорошо он будет работать . Кроме того, могут возникнуть незначительные проблемы безопасности, связанные с состоянием гонки, связанным с коротким периодом времени между созданием временного файла и его отключением. Кроме того, вы уже отметили возможность создания копии библиотеки, что я и предлагаю. Мой поворот в том, что ваша временная копия существует как запись в файловой системе только мгновение, независимо от того, как долго вы фактически держите библиотеку открытой.
Если вы хотите загрузить библиотеку, выполните следующие действия:
Было бы неплохо, если бы существовал действительно простой способ выполнить шаг «копировать файл», не требуя от вас фактического копирования файла. На ум приходит жесткое связывание, но я не думаю, что это сработает для этих целей. Было бы идеально, если бы в Linux был механизм копирования при записи, который был бы таким же простым в использовании, как link (), но я не знаю о таком средстве.
Изменить: ответ @Zan Lynx указывает на то, что создание собственных копий динамических библиотек может быть расточительным, если они реплицируются в несколько процессов. Поэтому мое предложение, вероятно, имеет смысл только в том случае, если оно применяется разумно - только к тем библиотекам, которые подвержены риску быть растоптанными (предположительно, небольшое подмножество всех библиотек, которое не включает файлы в / lib или /usr/lib).