Libseccomp: ОШИБКА: не генерировать BPF для псевдо-системных вызовов

Созданный на 15 июн. 2020  ·  20Комментарии  ·  Источник: seccomp/libseccomp

См. Проблему № 249, в частности этот комментарий :

Зачем проверять несуществующий системный вызов 4294957285 в фактически сгенерированном bpf?

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

Благодарим @ vt-alt за сообщение об этой ошибке.

bug prioritmedium

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

РЕПРОДУКТОР УДАЛЕН В СЛУЧАЕ УЛУЧШЕННОЙ ТЯГИ НИЖЕ

Чтобы было ясно, мы все равно должны генерировать псевдосистемные вызовы в PFC для целей отладки, но мы не должны генерировать (бесполезное) правило фильтра BPF.

Спасибо за создание проблемы из моего отчета.

Чтобы было ясно, мы все равно должны генерировать псевдосистемные вызовы в PFC для целей отладки, но мы не должны генерировать (бесполезное) правило фильтра BPF.

Пожалуйста, подумайте еще раз. Я считаю, что это только усложнит и затемнит ситуацию, если PFC не будет отражать BPF.

Пожалуйста, подумайте еще раз. Я считаю, что это только усложнит и затемнит ситуацию, если PFC не будет отражать BPF.

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

Для тех, кто понимает концепцию псевдосистемных вызовов, удаление их из вывода PFC - тривиальное упражнение. Также стоит упомянуть, что вывод PFC не предназначен для того, чтобы быть точной копией вывода BPF, это просто инструмент отладки и простой способ визуализировать сгенерированный код фильтра.

простой способ визуализировать сгенерированный код фильтра

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

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

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

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

Я понимаю вашу озабоченность @ vt-alt, и, возможно, в будущем выпуске мы сделаем это с помощью PFC, но я считаю, что удаление псевдосистемных вызовов из вывода PFC является ошибкой.

@drakenclimber, у вас есть твердое мнение по этому

Доработанный репродуктор для x86_64:

#include <stdlib.h>
#include <errno.h>

#include <seccomp.h>

#include "util.h"

int main(int argc, char *argv[])
{
        int rc;
        struct util_options opts;
        scmp_filter_ctx ctx = NULL;

        rc = util_getopt(argc, argv, &opts);
        if (rc < 0)
                goto out;

        ctx = seccomp_init(SCMP_ACT_KILL);
        if (ctx == NULL)
                return ENOMEM;

        rc = seccomp_arch_add(ctx, SCMP_ARCH_X32);
        if (rc < 0)
                goto out;

        rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0);
        if (rc < 0)
                goto out;
        rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(arm_fadvise64_64), 0);
        if (rc < 0)
                goto out;

        rc = util_filter_output(&opts, ctx);
        if (rc)
                goto out;

out:
        seccomp_release(ctx);
        return (rc < 0 ? -rc : rc);
}

ОБНОВЛЕНО: исправлена ​​проблема с ЦКИП

Еще не полностью протестирован, но это может быть исправление - можете ли вы убедиться, что это разумно для алгоритма оптимизации сбалансированного дерева @drakenclimber?

diff --git a/src/arch-arm.c b/src/arch-arm.c
index 3465111..4dd4b63 100644
--- a/src/arch-arm.c
+++ b/src/arch-arm.c
@@ -54,7 +54,7 @@ int arm_syscall_resolve_name_munge(const char *name)
        if (sys == __NR_SCMP_ERROR)
                return sys;

-       return sys + __SCMP_NR_BASE;
+       return (sys | __SCMP_NR_BASE);
 }

 /**
@@ -68,7 +68,7 @@ int arm_syscall_resolve_name_munge(const char *name)
  */
 const char *arm_syscall_resolve_num_munge(int num)
 {
-       return arm_syscall_resolve_num(num - __SCMP_NR_BASE);
+       return arm_syscall_resolve_num(num & (~__SCMP_NR_BASE));
 }

 const struct arch_def arch_def_arm = {
diff --git a/src/arch-x32.c b/src/arch-x32.c
index 7b97fb3..3890968 100644
--- a/src/arch-x32.c
+++ b/src/arch-x32.c
@@ -43,7 +43,7 @@ int x32_syscall_resolve_name_munge(const char *name)
        if (sys == __NR_SCMP_ERROR)
                return sys;

-       return sys + X32_SYSCALL_BIT;
+       return (sys | X32_SYSCALL_BIT);
 }

 /**
@@ -57,7 +57,7 @@ int x32_syscall_resolve_name_munge(const char *name)
  */
 const char *x32_syscall_resolve_num_munge(int num)
 {
-       return x32_syscall_resolve_num(num - X32_SYSCALL_BIT);
+       return x32_syscall_resolve_num(num & (~X32_SYSCALL_BIT));
 }

 const struct arch_def arch_def_x32 = {
diff --git a/src/gen_bpf.c b/src/gen_bpf.c
index 55a7958..ae9c3f4 100644
--- a/src/gen_bpf.c
+++ b/src/gen_bpf.c
@@ -1555,6 +1555,10 @@ static int _gen_bpf_syscalls(struct bpf_state *state,
        for (s_iter = s_tail; s_iter != NULL; s_iter = s_iter->pri_prv) {
                if (!s_iter->valid)
                        continue;
+               /* skip pseudo-syscalls */
+               if ((s_iter->num & 0x80000000) &&
+                   (state->attr->api_tskip == 0 || s_iter->num != -1))
+                       continue;

                if (*bintree_levels > 0 &&
                    ((syscall_cnt + empty_cnt) % SYSCALLS_PER_NODE) == 0)

Я понимаю вашу озабоченность @ vt-alt, и, возможно, в будущем выпуске мы сделаем это с помощью PFC, но я считаю, что удаление псевдосистемных вызовов из вывода PFC является ошибкой.

@drakenclimber, у вас есть твердое мнение по этому

Не совсем, но я думаю, что корень проблемы в том, что выход PFC используется разными способами:

  1. Как упоминалось выше, это простой способ для новичков (бывших) пользователей примерно проверить свой фильтр.
  2. Более продвинутые пользователи ожидают, что это будет близкое приближение к реальному фильтру BPF.

Возможно, мы могли бы добавить флаг --no-pseudo-syscalls в логику PFC? Тогда он может остаться прежним для начинающих пользователей, но продвинутые пользователи могут получить лучшее приближение BPF.

_UPDATED: исправлена ​​проблема с TSKIP_

Еще не полностью протестирован, но это может быть исправление - можете ли вы убедиться, что это разумно для алгоритма оптимизации сбалансированного дерева @drakenclimber?

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

_UPDATED: исправлена ​​проблема с TSKIP_
Еще не полностью протестирован, но это может быть исправление - можете ли вы убедиться, что это разумно для алгоритма оптимизации сбалансированного дерева @drakenclimber?

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

И, конечно же, я начну с теста репродуктора, который у вас был выше. Спасибо!

_UPDATED: исправлена ​​проблема с TSKIP_

Еще не полностью протестирован, но это может быть исправление - можете ли вы убедиться, что это разумно для алгоритма оптимизации сбалансированного дерева @drakenclimber?

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

Я тоже еще не полностью протестировал свои изменения :), но я почти уверен, что нам понадобится что-то подобное, чтобы двоичное дерево работало с удалением псевдо-системных вызовов. Я сгенерировал двоичное дерево BPF, используя это, и это выглядит разумно. По мере того, как мы приближаемся к готовому к производству решению, я полностью проверю его.

@@ -1532,11 +1532,31 @@ static int _gen_bpf_syscalls(struct bpf_state *state,
                _sys_sort(db_secondary->syscalls, &s_head, &s_tail, optimize);

        if (optimize == 2) {
+               /* since pseudo-syscalls are removed from the filter, we need
+                * to calculate the syscall count by hand
+                */
+               for (s_iter = s_tail; s_iter != NULL; s_iter = s_iter->pri_prv) {
+                       if (!s_iter->valid)
+                               continue;
+
+                       /* skip pseudo-syscalls */
+                       if ((s_iter->num & 0x80000000) &&
+                           (state->attr->api_tskip == 0 || s_iter->num != -1))
+                               continue;
+
+                       syscall_cnt++;
+               }
+
                rc = _gen_bpf_init_bintree(&bintree_hashes, &bintree_syscalls,
-                                          bintree_levels, db->syscall_cnt,
+                                          bintree_levels, syscall_cnt,
                                           &empty_cnt);
                if (rc < 0)
                        goto out;
+
+               /* reset the syscall_cnt variable because later in this
+                * function it's used as a counter
+                */
+               syscall_cnt = 0;
        }

Это можно было бы сделать умнее / быстрее, если бы у нас была переменная в структуре db отслеживающая "действительный" счетчик системных вызовов.

Возможно, мы могли бы добавить в логику PFC флаг --no-pseudo-syscalls?

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

Я тоже еще не полностью протестировал свои изменения :), но я почти уверен, что нам понадобится что-то подобное, чтобы двоичное дерево работало с удалением псевдо-системных вызовов. Я сгенерировал двоичное дерево BPF, используя это, и это выглядит разумно. По мере того, как мы приближаемся к готовому к производству решению, я полностью проверю его.

Я подозревал, что это нарушит оптимизацию дерева.

@drakenclimber, учитывая, что это влияет на сортировку дерева гораздо больше, чем стандартная оптимизация, вы хотите решить эту проблему? Не стесняйтесь украсть столько или меньше кода, который я скопировал и вставил выше, насколько это имеет смысл.

Одна вещь, которую я думаю, мы должны сделать, несмотря на это, - это изменения в "arch-arm.c" и "arch-x32.c", поскольку это имеет больше смысла.

Возможно, мы могли бы добавить в логику PFC флаг --no-pseudo-syscalls?

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

Признаюсь, я напечатал раньше, чем подумал. ;)

Да, это должна быть опция фильтра, и это неправильно. Я согласен; давайте сделаем так, как вы изложили выше. Если мы продолжим получать вопросы, мы можем вернуться к ним.

Я тоже еще не полностью протестировал свои изменения :), но я почти уверен, что нам понадобится что-то подобное, чтобы двоичное дерево работало с удалением псевдо-системных вызовов. Я сгенерировал двоичное дерево BPF, используя это, и это выглядит разумно. По мере того, как мы приближаемся к готовому к производству решению, я полностью проверю его.

Я подозревал, что это нарушит оптимизацию дерева.

@drakenclimber, учитывая, что это влияет на сортировку дерева гораздо больше, чем стандартная оптимизация, вы хотите решить эту проблему? Не стесняйтесь украсть столько или меньше кода, который я скопировал и вставил выше, насколько это имеет смысл.

Одна вещь, которую я думаю, мы должны сделать, несмотря на это, - это изменения в "arch-arm.c" и "arch-x32.c", поскольку это имеет больше смысла.

Конечно. Я могу владеть этим.

@drakenclimber "> Как упоминалось выше, для новых (бывших) пользователей это простой способ примерно проверить свой фильтр"

«Псевдо-системный вызов» не существует в ядре, и это тоже не очень известная концепция. Это чисто изобретение libseccomp и здесь не объясняется. Заявлено только, что это отрицательные числа, и они появляются, когда данный системный вызов не существует для данной архитектуры. Чем это отличается от несуществующего системного вызова? С какой целью они отрицательные? Концепция псевдосистемного вызова действительно сбивает с толку новых пользователей.

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

Позвольте мне добавить еще один момент. Все это в сфере безопасности, где работает только тщательное и детальное понимание. Вы создаете новые непонятные концепции (псевдосистемные вызовы) и разницу между представлениями (bpf и pfc). Неужели это действительно предназначено для новых пользователей, чтобы еще больше запутать их?

@ vt-alt благодарим вас за проявленное беспокойство, но в выпуске v2.5.0 мы собираемся подавить псевдосистемные вызовы из фильтра BPF и продолжать отображать их в фильтре PFC. Я понимаю, что вы можете не согласиться с этим решением, но прошу вас уважать это решение. В будущих выпусках мы будем лучше объяснять цель псевдосистемных вызовов (это для поддержки нескольких ABI) и документировать ее на страницах руководства; Я создал проблему для этого (ссылка ниже), приглашаем вас принять участие в этом обсуждении и любых связанных с этим PR. Также возможно, что мы пересмотрим наш подход к фильтру PFC в будущем, но я не хочу здесь ничего обещать.

Еще раз благодарю вас за то, что вы обратили наше внимание на проблему BPF; вы помогли улучшить следующий выпуск libseccomp!

Закрытие через # 264.

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