Libseccomp: В: не может быть разных фильтров с SCMP_ACT_NOTIFY в разных потоках

Созданный на 6 нояб. 2020  ·  9Комментарии  ·  Источник: seccomp/libseccomp

libseccomp сохраняет fd уведомления в глобальной переменной: state.notify_fd . Это делает невозможным использование libseccomp для многопоточного приложения с разными фильтрами в разных потоках (то есть без использования TSYNC).

libseccomp имеет seccomp_reset(NULL, ...) для сброса глобальной переменной state.notify_fd . Но seccomp_reset() имеет неприятные последствия для сброса state.nr_seccomp = -1 .

Эта проблема была замечена при попытке разрешить модульные тесты в libseccomp-golang, см. Https://github.com/seccomp/libseccomp-golang/pull/59#issuecomment -723045033

Копия @yvesf @rata

prioritlow question

Самый полезный комментарий

Похоже, здесь мы все согласны, поэтому я собираюсь закрыть этот вопрос. @alban, если вы считаете, что мы

Спасибо всем.

Все 9 Комментарий

@drakenclimber задерживает грядущие выпуски v2.5.1, пока мы не получим некоторые разъяснения по этому поводу. Я еще не уверен, что полностью понимаю исходный отчет, но поскольку v2.5.1 является первым выпуском, реализующим концепцию seccomp_reset(NULL, ...) , давайте подождем минуту, пока мы не сможем все проверить ...

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

@drakenclimber задерживает грядущие выпуски v2.5.1, пока мы не получим некоторые разъяснения по этому поводу. Я еще не уверен, что полностью понимаю исходный отчет, но поскольку v2.5.1 является первым выпуском, реализующим концепцию seccomp_reset(NULL, ...) , давайте подождем минуту, пока мы не сможем все проверить ...

Согласовано. Сделаю.

libseccomp сохраняет fd уведомления в глобальной переменной: state.notify_fd . Это делает невозможным использование libseccomp для многопоточного приложения с разными фильтрами в разных потоках (то есть без использования TSYNC).

Не могли бы вы подробнее рассказать о последнем предложении? Я не уверен, что понимаю, что вы пытаетесь передать.

FWIW, FD уведомления seccomp является глобальным объектом процесса, вы можете запросить его у ядра только один раз. Вы можете прочитать https://github.com/seccomp/libseccomp/issues/273, чтобы получить дополнительную информацию по проблеме.

libseccomp имеет seccomp_reset(NULL, ...) для сброса глобальной переменной state.notify_fd . Но seccomp_reset() имеет неприятные последствия для сброса state.nr_seccomp = -1 .

Почему сброс state.nr_seccomp = -1 является серьезной проблемой? Если поле nr_seccomp сбрасывается до -1 в следующий раз, когда запрашивается операция, которая может использовать seccomp(2) библиотека проверяет, поддерживается ли seccomp(2) и использует его, если он доступен. Да, это может привести к дополнительным вызовам seccomp(2) но это не должно быть серьезной проблемой, это касается вашего варианта использования?

@alban - Аналогичный вопрос от меня. Я потратил немного времени, пытаясь изобрести разумный вариант использования многопоточного, многосекундного comp-фильтра (на C), и действительно ничего не смог придумать.

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

Не могли бы вы поделиться каким-нибудь псевдокодом или высокоуровневым дизайном того, о чем вы думаете? Я бы с радостью прототипировал его на C.

Не могли бы вы подробнее рассказать о последнем предложении? Я не уверен, что понимаю, что вы пытаетесь передать.

Работая над модульными тестами в libseccomp-golang, я понял, что тестируется следующий сценарий:

  1. При первом выполнении модульного теста политика seccomp была применена без SECCOMP_FILTER_FLAG_TSYNC (это означает, что она применяется на уровне потока, а не на уровне процесса), но с SECCOMP_FILTER_FLAG_NEW_LISTENER (поэтому libseccomp сохранит fd в state.notify_fd ).
  2. Тот же модульный тест выполняется снова в том же процессе, но в другом потоке (используя runtime.LockOSThread в Go, чтобы убедиться в этом). Но libseccomp повторно использует fd из предыдущего фильтра seccomp (из state.notify_fd ) вместо получения нового fd для нового фильтра. Тогда тест не проходит, потому что мы ожидаем получить события на неправильном seccomp fd.

FWIW, FD уведомления seccomp является глобальным объектом процесса, вы можете запросить его у ядра только один раз. Вы можете прочитать # 273, чтобы получить больше информации по проблеме.

Насколько я понял, ядро ​​ограничивает нас получением только одного FD уведомления seccomp в дереве фильтров. Но в приведенном выше сценарии два потока из одного процесса используют разные деревья фильтров, так что с точки зрения ядра это должно быть нормально.

Этот сценарий использования разных деревьев фильтров с SECCOMP_FILTER_FLAG_NEW_LISTENER не был создан специально, он просто появился как следствие модульных тестов libseccomp-golang, запущенных в одном процессе. Но поскольку основная причина заключается в libseccomp, использующем глобальную переменную state.notify_fd которая совместно используется потоками, я подумал, что должен открыть здесь эту ошибку, чтобы начать обсуждение. Однако я был бы в порядке, если бы он был закрыт как «WONTFIX» (я не знаю, потребуется ли поддержка такого сценария другим пользователям libseccomp). В этом случае мы можем просто написать модульные тесты libseccomp-golang другим способом (т. Е. Используя отдельный процесс для каждой итерации теста); нам все равно придется это сделать (чтобы избежать смешивания фильтров уровня потока и фильтров уровня процесса - это будет отклонено ядром).

Работая над модульными тестами в libseccomp-golang, я понял, что тестируется следующий сценарий:

  1. При первом выполнении модульного теста политика seccomp была применена без SECCOMP_FILTER_FLAG_TSYNC (это означает, что она применяется на уровне потока, а не на уровне процесса), но с SECCOMP_FILTER_FLAG_NEW_LISTENER (поэтому libseccomp сохранит fd в state.notify_fd ).
  2. Тот же модульный тест выполняется снова в том же процессе, но в другом потоке (используя runtime.LockOSThread в Go, чтобы убедиться в этом). Но libseccomp повторно использует fd из предыдущего фильтра seccomp (из state.notify_fd ) вместо получения нового fd для нового фильтра. Тогда тест не проходит, потому что мы ожидаем получить события на неправильном seccomp fd.

Ага, теперь в этом больше смысла. Я часто задавался вопросом, следует ли сделать TSYNC по умолчанию для привязок libseccomp-golang; учитывая неоднозначность нити в Go, это кажется гораздо более безопасным выбором.

Насколько я понял, ядро ​​ограничивает нас получением только одного FD уведомления seccomp в дереве фильтров. Но в приведенном выше сценарии два потока из одного процесса используют разные деревья фильтров, так что с точки зрения ядра это должно быть нормально.

Этот сценарий использования разных деревьев фильтров с SECCOMP_FILTER_FLAG_NEW_LISTENER не был создан специально, он просто появился как следствие модульных тестов libseccomp-golang, запущенных в одном процессе. Но поскольку основная причина заключается в libseccomp, использующем глобальную переменную state.notify_fd которая совместно используется потоками, я подумал, что должен открыть здесь эту ошибку, чтобы начать обсуждение. Однако я был бы в порядке, если бы он был закрыт как «WONTFIX» (я не знаю, потребуется ли поддержка такого сценария другим пользователям libseccomp). В этом случае мы можем просто написать модульные тесты libseccomp-golang другим способом (т. Е. Используя отдельный процесс для каждой итерации теста); нам все равно придется это сделать (чтобы избежать смешивания фильтров уровня потока и фильтров уровня процесса - это будет отклонено ядром).

Мне будет интересно узнать мнение @drakenclimber по этому

Чтобы «исправить» это в libseccomp, нам нужно сделать так, чтобы поток libseccomp знал о потоках, что сопряжено с рядом проблем и ловушек, так что я в настоящее время считаю, что это было бы плохой идеей. Однако в качестве аргумента, если бы мы сделали так, чтобы поток libseccomp знал о потоке, мы также могли бы сделать внутреннее глобальное состояние специфическим для потока и / или, возможно, состоянием, специфичным для дерева фильтров; в любом из этих случаев я думаю, что текущий API (включая seccomp_reset(NULL, ...) ) по-прежнему будет разумным, поэтому у меня возникает соблазн оставить все как есть с API. Если у кого-то возникнут какие-либо сомнения по этому поводу, сообщите нам об этом в ближайшее время.

@drakenclimber? Если не возражать против вышеизложенного, я думаю, что мы вернулись к выпуску v2.5.1.

Мне будет интересно узнать мнение @drakenclimber по этому

Согласен. Я потратил некоторое время на изучение исходного @tych0, а также самого кода ядра. Его предполагаемый вариант использования - это процесс мониторинга, запускающий обработчик уведомлений, в то время как контейнерные процессы приходят и уходят. Когда эти контейнерные процессы вызывают системный вызов, который имеет уведомление, процесс мониторинга может выполнить дополнительную логику, чтобы разрешить / отклонить запрос.

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

Одно в сторону - мне удалось заставить ядро ​​возвращать процессу несколько файловых дескрипторов уведомлений. Создав несколько потоков pthread и заставив их немедленно загрузить фильтр seccomp с действием уведомления, иногда мне удавалось получить два или три разных fd уведомлений, возвращаемых процессу пользовательского пространства. Поскольку это настолько нереально, я бы склонился к тому, чтобы отметить эту проблему ядра как WONTFIX.

@drakenclimber? Если не возражать против вышеизложенного, я думаю, что мы вернулись к выпуску v2.5.1.

Большой. Я смогу начать работу над этим в начале следующей недели.

Похоже, здесь мы все согласны, поэтому я собираюсь закрыть этот вопрос. @alban, если вы считаете, что мы

Спасибо всем.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги