Как Linux ptrace может быть небезопасным или содержать условие гонки? - PullRequest
5 голосов
/ 11 декабря 2010

Я хотел бы реализовать песочницу, ptrace() запустив процесс, который я запускаю, и все его потомки будут создаваться (включая внуков и т. Д.). ptrace() родительский процесс, то есть супервизор. будет простой программой на C или Python, и концептуально она будет ограничивать доступ к файловой системе (на основе имени пути и направления доступа (чтение или запись) и доступа к сокету (например, запретить создание сокета).

На что следует обратить внимание, чтобы процесс ptrace() d и его дочерние элементы (рекурсивно) не могли обойти песочницу? Есть ли что-то особенное, что должен сделать руководитель в fork() время, чтобы избежать условий гонки? Можно ли прочитать аргументы имени файла, например, rename() от дочернего процесса без состояния гонки?

Вот что я уже планировал сделать:

  • PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE, чтобы избежать (некоторых) расов кодификации при fork() ing
  • по умолчанию запрещает все системные вызовы и составляет белый список разрешенных системных вызовов
  • убедитесь, что варианты системных вызовов *at() (такие как openat) должным образом защищены

На что еще стоит обратить внимание?

Ответы [ 2 ]

12 голосов
/ 12 декабря 2010

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

Thread 1                           Supervisor             Thread 2
-----------------------------------------------------------------------------------------------------
strcpy(filename, "/dev/null");
open(filename, O_RDONLY);
                                   Check filename - OK
                                                          strcpy(filename, "/home/user/.ssh/id_rsa");
(in kernel) opens "/home/user/.ssh/id_rsa"

Один из способов остановить это - запретить вызов clone() с флагом CLONE_VM и, кроме того, предотвратить любое создание доступных для записи отображений памяти MAP_SHARED (или, по крайней мере, отслеживатьиз них такие, что вы отрицаете любой системный вызов, который пытается напрямую ссылаться на данные из такого сопоставления).Вы также можете скопировать любой такой аргумент в неиспользуемый bounce-буфер перед тем, как продолжить системный вызов.Это эффективно предотвратит запуск любого многопоточного приложения в «песочнице».

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

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

3 голосов
/ 11 декабря 2010

Разве ptrace не получает уведомления только по факту?Я не думаю, что у вас есть шанс на самом деле остановить системный вызов, только чтобы убить его так быстро, как только вы сможете увидеть что-то «злое».

Кажется, вы больше ищете что-тонапример, SELinux или AppArmor, где вы можете гарантировать, что ни один незаконный вызов не пройдет.

...