Libseccomp: ERRO: não gere BPF para pseudo-escalas

Criado em 15 jun. 2020  ·  20Comentários  ·  Fonte: seccomp/libseccomp

Veja o problema nº 249, especificamente este comentário :

Por que verificar se há syscall 4294957285 inexistente no bpf realmente gerado?

Não deveríamos e não costumávamos fazer isso, mas parece que a libseccomp atual tem um bug aqui.

Crédito para @ vt-alt por relatar esse bug.

bug prioritmedium

Todos 20 comentários

REPRODUTOR REMOVIDO EM FAVOR DO PROJETO MELHORADO ABAIXO

Para ser claro, ainda devemos emitir as pseudo-syscalls no PFC, para fins de depuração, mas não devemos emitir uma regra de filtro BPF (inútil).

Obrigado por criar o problema a partir do meu relatório.

Para ser claro, ainda devemos emitir as pseudo-syscalls no PFC, para fins de depuração, mas não devemos emitir uma regra de filtro BPF (inútil).

Por favor, repense isso. Acredito que isso só complicará e obscurecerá as coisas se o PFC não refletir o BPF.

Por favor, repense isso. Acredito que isso só complicará e obscurecerá as coisas se o PFC não refletir o BPF.

Se o syscall estiver faltando no PFC, receberemos uma série de relatórios de bugs falsos falando sobre como a biblioteca está falhando ao adicionar um filtro para syscalls (inexistentes).

Para aqueles que entendem o conceito de pseudo-syscalls, é um exercício trivial removê-los da saída do PFC. Também vale a pena mencionar que a saída PFC não se destina a ser uma cópia exata da saída BPF, ela está lá simplesmente como uma ferramenta de depuração e uma maneira fácil de visualizar o código de filtro gerado.

maneira fácil de visualizar o código de filtro gerado

Mas, então, ele não irá visualizar o código do filtro _gerado_, pois no código gerado pseudo syscalls devem estar ausentes.

Se o syscall estiver faltando no PFC, receberemos uma série de relatórios de bugs falsos falando sobre como a biblioteca está falhando ao adicionar um filtro para syscalls (inexistentes).

É muito mais fácil entender que as verificações de pseudo escala de escala não devem estar presentes no código (como em 'saída otimizada', porque '' não existem tais escalas de escala para o arco '), do que a diferença lógica (presença e ausência de condicionais) em a visualização do código e o código real.

Eu preferiria que o PFC refletisse o bpf do que usar scmp_bpf_disasm pois a saída do PFC é muito mais fácil de ler.

Eu entendo sua preocupação @ vt-alt, e talvez em uma versão futura façamos isso com o PFC, mas é minha opinião que remover as pseudo-syscalls da saída do PFC é um erro.

@drakenclimber , você tem alguma opinião forte sobre isso?

Reprodutor revisado para 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);
}

ATUALIZADO: corrigido um problema com o TSKIP

Ainda não foi totalmente testado, mas pode ser uma correção - você pode verificar se isso é razoável para o algoritmo de otimização de árvore balanceada @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)

Eu entendo sua preocupação @ vt-alt, e talvez em uma versão futura façamos isso com o PFC, mas é minha opinião que remover as pseudo-syscalls da saída do PFC é um erro.

@drakenclimber , você tem alguma opinião forte sobre isso?

Na verdade não, mas acho que a raiz do problema é que a saída do PFC está sendo usada de várias maneiras divergentes:

  1. Como mencionado acima, é uma maneira fácil para os novos (mais) usuários verificarem de forma aproximada seu filtro
  2. Usuários mais avançados esperam que seja uma aproximação do filtro BPF real

Talvez pudéssemos adicionar um sinalizador --no-pseudo-syscalls à lógica PFC? Então, ele pode permanecer o mesmo para usuários iniciantes, mas usuários avançados podem obter uma melhor aproximação do BPF.

_UPDATED: corrigido um problema com TSKIP_

Ainda não foi totalmente testado, mas pode ser uma correção - você pode verificar se isso é razoável para o algoritmo de otimização de árvore balanceada @drakenclimber?

Vai fazer. Quero ver se consigo fazer um teste automatizado que reproduza esse cenário.

_UPDATED: corrigido um problema com TSKIP_
Ainda não foi totalmente testado, mas pode ser uma correção - você pode verificar se isso é razoável para o algoritmo de otimização de árvore balanceada @drakenclimber?

Vai fazer. Quero ver se consigo fazer um teste automatizado que reproduza esse cenário.

E é claro que vou começar com o teste do reprodutor que você tem acima. Obrigado!

_UPDATED: corrigido um problema com TSKIP_

Ainda não foi totalmente testado, mas pode ser uma correção - você pode verificar se isso é razoável para o algoritmo de otimização de árvore balanceada @drakenclimber?

A árvore binária pré-calcula quando o acumulador precisa ser modificado, e é assim que sabe inserir a lógica jge . Arrancar syscalls (como a proposta acima) enquanto constrói o filtro quebra essa lógica.

Eu também não testei totalmente minhas alterações :), mas tenho certeza de que precisaremos de algo assim para fazer a árvore binária funcionar com a remoção de pseudo-syscalls. Eu gerei uma árvore binária BPF usando isso e parece razoável. À medida que nos aproximamos de uma solução pronta para produção, vou verificá-la totalmente.

@@ -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;
        }

Isso poderia ser mais inteligente / rápido se tivermos uma variável na estrutura db rastreando a contagem de chamadas "válida".

Talvez pudéssemos adicionar um sinalizador --no-pseudo-syscalls à lógica PFC?

Onde esta bandeira iria? Não acho que queremos isso como uma opção de tempo de construção. Suponho que poderíamos adicionar uma opção de filtro, mas não estou muito animado com isso. Eu votaria em manter as pseudo-escalas no PFC, mas retirá-las do BPF (obviamente) por enquanto, e se precisarmos aumentar isso em algum momento no futuro, podemos.

Eu também não testei totalmente minhas alterações :), mas tenho certeza de que precisaremos de algo assim para fazer a árvore binária funcionar com a remoção de pseudo-syscalls. Eu gerei uma árvore binária BPF usando isso e parece razoável. À medida que nos aproximamos de uma solução pronta para produção, vou verificá-la totalmente.

Suspeitei que isso iria quebrar a otimização da árvore.

@drakenclimber considerando que isso impacta muito mais a classificação da árvore do que a otimização padrão, você quer resolver esse problema? Sinta-se à vontade para roubar o máximo ou o mínimo possível do código que copiei e colei acima.

Uma coisa que acho que devemos fazer são as alterações em "arch-arm.c" e "arch-x32.c", pois faz mais sentido.

Talvez pudéssemos adicionar um sinalizador --no-pseudo-syscalls à lógica PFC?

Onde esta bandeira iria? Não acho que queremos isso como uma opção de tempo de construção. Suponho que poderíamos adicionar uma opção de filtro, mas não estou muito animado com isso. Eu votaria em manter as pseudo-escalas no PFC, mas retirá-las do BPF (obviamente) por enquanto, e se precisarmos aumentar isso em algum momento no futuro, podemos.

Admito que digitei antes de pensar. ;)

Sim, teria que ser uma opção de filtro e isso parece errado. Eu concordo; vamos fazer o que você descreveu acima. Se continuarmos a receber perguntas, podemos revisitar.

Eu também não testei totalmente minhas alterações :), mas tenho certeza de que precisaremos de algo assim para fazer a árvore binária funcionar com a remoção de pseudo-syscalls. Eu gerei uma árvore binária BPF usando isso e parece razoável. À medida que nos aproximamos de uma solução pronta para produção, vou verificá-la totalmente.

Suspeitei que isso iria quebrar a otimização da árvore.

@drakenclimber considerando que isso impacta muito mais a classificação da árvore do que a otimização padrão, você quer resolver esse problema? Sinta-se à vontade para roubar o máximo ou o mínimo possível do código que copiei e colei acima.

Uma coisa que acho que devemos fazer são as alterações em "arch-arm.c" e "arch-x32.c", pois faz mais sentido.

Certo. Eu posso possuir este.

@drakenclimber "> Como mencionado acima, é uma maneira fácil para os novos (mais) usuários verificarem de forma aproximada seu filtro"

"Pseudo syscall" não existe no kernel e também não é um conceito bem conhecido. É puramente a invenção de libseccomp e não é explicado aqui. Apenas afirmados são números negativos e aparecem quando o syscall dado não existe para a arquitetura. Como isso é diferente de syscall inexistente? Com que propósito eles são negativos? O conceito de pseudo syscall é realmente confuso para novos usuários.

Eu não posso acreditar que novos (er) usuários querem ver syscalls inexistentes verificados em seus filtros.

Deixe-me acrescentar mais um ponto. Tudo isso é no campo da segurança, onde só funciona uma compreensão cuidadosa e detalhada. Você está criando novos conceitos obscuros (de pseudo syscalls) e a diferença entre as representações (bpf e pfc). Isso é realmente destinado a novos usuários, para confundi-los mais?

@ vt-alt, obrigado por expressar sua preocupação, mas para o lançamento v2.5.0 iremos suprimir as pseudo-syscalls do filtro BPF e continuar a exibi-las no filtro PFC. Reconheço que você pode discordar dessa decisão, mas peço que respeite essa decisão. Em versões futuras, faremos um trabalho melhor explicando o propósito por trás das pseudo-syscalls (é para suporte de ABI múltiplo) e documentando-o nas páginas de manual; Eu criei um problema para isso (link abaixo), você é bem-vindo e incentivado a participar dessa discussão e de quaisquer RPs resultantes. Também é possível revisarmos nossa abordagem para o filtro PFC no futuro, mas não quero prometer nada aqui.

Mais uma vez, obrigado por nos chamar a atenção para o problema do BPF; você ajudou a melhorar o próximo lançamento da libseccomp!

Fechando via # 264.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

Xyene picture Xyene  ·  15Comentários

pcmoore picture pcmoore  ·  23Comentários

drakenclimber picture drakenclimber  ·  18Comentários

diekmann picture diekmann  ·  3Comentários

vasanthaganeshk picture vasanthaganeshk  ·  9Comentários