Libseccomp: RFE: valores de retorno de erro definidos com mais precisão

Criado em 7 out. 2017  ·  24Comentários  ·  Fonte: seccomp/libseccomp

Observando os valores de retorno de erro libseccomp sobre chamadas do sistema, não é possível determinar qual dos seguintes motivos causou os erros:

  1. syscall não existe em algum arco
  2. syscall não pode ser correspondido em algum arco (porque é multiplexado, pense em socket/socketcall)
  3. outros casos de erro

Ao construir o filtro seccomp, o chamador pode considerar alguns desses motivos fatais, mas não os outros, portanto, informações de erro mais detalhadas (conjunto mais amplo de valores de erro do que apenas -EINVAL ) seriam necessárias.

Veja também systemd PR 6952 .

enhancement prioritmedium

Comentários muito úteis

Ótimo, obrigado. Eu tenho um patchset incompleto que vou terminar e enviar como um PR para revisão.

Todos 24 comentários

Oi @topimiettinen , desculpe ter demorado tanto para chegar a isso, mas acho que é hora de corrigir isso.

@drakenclimber este vai ser um doozy. Todas as APIs libseccomp devem ter páginas de manual neste momento (se não tiverem, precisamos criar um problema para isso), com todas as páginas de manual tendo alguns comentários "valores negativos em caso de erro" ondulados na seção VALOR DE RETORNO. Acho que precisamos fazer o seguinte:

  • auditar manualmente cada chamada de API para gerar uma lista de possíveis valores de retorno
  • decidir se esses valores de retorno fazem sentido, modifique o código se não
  • documente cada valor de retorno possível na página de manual associada com uma breve explicação do que o código de erro indica

Pensamentos?

Oi @topimiettinen , desculpe ter demorado tanto para chegar a isso, mas acho que é hora de corrigir isso.

@drakenclimber este vai ser um doozy. Todas as APIs libseccomp devem ter páginas de manual neste momento (se não tiverem, precisamos criar um problema para isso), com todas as páginas de manual tendo alguns comentários "valores negativos em caso de erro" ondulados na seção VALOR DE RETORNO. Acho que precisamos fazer o seguinte:

* manually audit each API call to generate a list of possible return values

* decide if these return values make sense, modify the code if they don't

* document each possible return value in the associated manpage with a brief explanation of what the error code indicates

Pensamentos?

Dang .... Concordo de má vontade com tudo o que você escreveu acima, especialmente o esforço necessário :). Acertar os códigos de retorno e documentá-los será um grande esforço.

E sim, embora não seja um trabalho glamoroso, acho que é fundamental. Eu tenho trabalhado em algumas coisas do cgroup e recentemente nos deparamos com uma implementação de contêiner que não entendeu completamente um recurso do cgroup ... eles poderiam.

É ótimo ver atividade sobre este assunto! Não sou contra uma revisão completa, mas a solicitação original se limitava apenas para poder distinguir diferentes modos de falha, o que é um pouco ortogonal. A revisão certamente ajudaria, até mesmo um pré-requisito até certo ponto, eu acho.

É ótimo ver atividade sobre este assunto! Não sou contra uma revisão completa, mas a solicitação original se limitava apenas para poder distinguir diferentes modos de falha, o que é um pouco ortogonal. A revisão certamente ajudaria, até mesmo um pré-requisito até certo ponto, eu acho.

Precisamos fazer a revisão em algum momento, e pode muito bem ser agora. Quanto mais adiarmos, menos úteis os códigos de erro se tornarão para os chamadores e todo o objetivo da libseccomp é tornar essas coisas mais fáceis de usar :)

Dang .... Concordo de má vontade com tudo o que você escreveu acima, especialmente o esforço necessário :). Acertar os códigos de retorno e documentá-los será um grande esforço.

Sim, essa é uma das razões pelas quais esse problema está parado há tanto tempo, mas eu tenho adiado o suficiente (pelo menos @drakenclimber pode dizer que ele está adiando há menos de um ano!). Mais tarde hoje (amanhã?) vou dividir isso em partes/múltiplas questões (com algumas sugestões) para torná-lo um pouco mais fácil de resolver em partes.

Como um pouco positivo, parece que libseccomp realmente usa apenas nove valores errno exclusivos (de acordo com uma verificação muito grosseira):

# grep -e "-E[A-Z0-9]\+" src/*.{h,c} | sed 's/.*-\(E[A-Z0-9]\+\).*/\1/' | sort -u
EACCES
EDOM
EEXIST
EFAULT
EINVAL
ENOMEM
EOPNOTSUPP
EPERM
ESRCH

... isso deve ajudar a reduzir um pouco o espaço do problema, especialmente se pudermos concordar com o valor semântico comum para cada código de erro na biblioteca (o que definitivamente devemos fazer).

Isso é um pouco mais tarde do que o pretendido, mas aqui está uma lista completa das funções que compõem a API libseccomp:

const struct scmp_version *seccomp_version(void)
unsigned int seccomp_api_get(void)
int seccomp_api_set(unsigned int level)
scmp_filter_ctx seccomp_init(uint32_t def_action)
int seccomp_reset(scmp_filter_ctx ctx, uint32_t def_action)
void seccomp_release(scmp_filter_ctx ctx)
int seccomp_merge(scmp_filter_ctx ctx_dst, scmp_filter_ctx ctx_src)
uint32_t seccomp_arch_resolve_name(const char *arch_name)
uint32_t seccomp_arch_native(void)
int seccomp_arch_exist(const scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_add(scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_remove(scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_load(const scmp_filter_ctx ctx)
int seccomp_attr_get(const scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t *value)
int seccomp_attr_set(scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t value)
char *seccomp_syscall_resolve_num_arch(uint32_t arch_token, int num)
int seccomp_syscall_resolve_name_arch(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name_rewrite(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name(const char *name)
int seccomp_syscall_priority(scmp_filter_ctx ctx, int syscall, uint8_t priority)
int seccomp_rule_add_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
int seccomp_rule_add_exact_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add_exact(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
int seccomp_notify_alloc(struct seccomp_notif **req, struct seccomp_notif_resp **resp)
void seccomp_notify_free(struct seccomp_notif *req, struct seccomp_notif_resp *resp)
int seccomp_notify_receive(int fd, struct seccomp_notif *req)
int seccomp_notify_respond(int fd, struct seccomp_notif_resp *resp)
int seccomp_notify_id_valid(int fd, uint64_t id)
int seccomp_notify_fd(const scmp_filter_ctx ctx)
int seccomp_export_pfc(const scmp_filter_ctx ctx, int fd)
int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd)

... dessas funções, precisamos apenas nos preocupar com as funções que retornam "int".

Possíveis agrupamentos de funções que devem ter caminhos de código e valores de retorno semelhantes.

  • grupo A
int seccomp_arch_exist(const scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_add(scmp_filter_ctx ctx, uint32_t arch_token)
int seccomp_arch_remove(scmp_filter_ctx ctx, uint32_t arch_token)
  • Grupo B
int seccomp_attr_get(const scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t *value)
int seccomp_attr_set(scmp_filter_ctx ctx, enum scmp_filter_attr attr, uint32_t value)
  • Grupo C
int seccomp_syscall_resolve_name_arch(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name_rewrite(uint32_t arch_token, const char *name)
int seccomp_syscall_resolve_name(const char *name)
  • Grupo D
int seccomp_rule_add_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
int seccomp_rule_add_exact_array(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, const struct scmp_arg_cmp *arg_array)
int seccomp_rule_add_exact(scmp_filter_ctx ctx, uint32_t action, int syscall, unsigned int arg_cnt, ...)
  • Grupo E
int seccomp_notify_receive(int fd, struct seccomp_notif *req)
int seccomp_notify_respond(int fd, struct seccomp_notif_resp *resp)
int seccomp_notify_id_valid(int fd, uint64_t id)
int seccomp_notify_fd(const scmp_filter_ctx ctx)
  • Grupo F
int seccomp_load(const scmp_filter_ctx ctx)
int seccomp_export_bpf(const scmp_filter_ctx ctx, int fd)

... se a função não estiver em um dos grupos acima, provavelmente é única em seu caminho de código e/ou valor de retorno.

O grupo C retorna apenas __NR_SCMP_ERROR.

O grupo D pode retornar um de EINVAL, EPERM, EOPNOTSUPP, ENOMEM, EDOM, EFAULT, EEXIST.

seccomp_load() pode retornar valores EINVAL, ENOMEM, ESRCH e também errno de prctl() (somente EACCES, EFAULT, EINVAL para PR_SET_NO_NEW_PRIVS e PR_SET_SECCOMP) e seccomp() (EACCES, EFAULT, EINVAL, ENOMEM, ESRCH) de acordo com seu manual Páginas.

Quando se trata de libseccomp passando erros de syscall de volta para o chamador, por exemplo, prctl() e seccomp(), estou pensando que só precisamos ocultar aqueles atrás de um único valor errno (talvez ENOSYS?) quaisquer alterações no kernel (ou diferenças ABI).

Se isso se tornar um problema para depuração, talvez possamos introduzir um novo attr que passaria o valor errno diretamente de volta ao chamador.

Possivelmente, mas seria bom ter certeza de que não há quebra de ABI/API se os usuários de libseccomp já estiverem esperando certos valores de errno. O problema original era que algumas chamadas de sistema existem apenas em algumas arquiteturas (como ugetrlimit apenas em x86_32), mas isso não pode ser distinguido de um nome de chamada de sistema digitado incorretamente, portanto, códigos de erro diferentes (possivelmente sintéticos) seriam necessários .

Bem, como dissemos anteriormente, atualmente não garantimos valores errno específicos, apenas "valores negativos em caso de falha", então, embora seja lamentável quebrar os usuários que estão fazendo suposições sobre valores errno específicos, acho que mudar as coisas fornecer uma forte garantia de erro nas versões do kernel e ABIs no futuro é uma troca que vale a pena.

Bem, como dissemos anteriormente, atualmente não garantimos valores errno específicos, apenas "valores negativos em caso de falha", então, embora seja lamentável quebrar os usuários que estão fazendo suposições sobre valores errno específicos, acho que mudar as coisas fornecer uma forte garantia de erro nas versões do kernel e ABIs no futuro é uma troca que vale a pena.

Eu concordo.

Assim que essa avaliação estiver completa e atualizarmos os valores de errno em caso de falhas, eu me sentiria mais confortável fazendo algum tipo de garantia dos valores de errno retornados.

A ideia de agrupamento talvez não tenha sido a melhor, então vamos começar uma lista para acompanhar tudo isso _(continuarei atualizando isso à medida que progredirmos)_:

  • [x] seccomp_reset
    Atualmente retorna: EINVAL, ENOMEM.

  • [x] seccomp_merge
    Atualmente retorna: EINVAL, EDOM, EEXIST, ENOMEM.

  • [x] seccomp_arch_exist
    Atualmente retorna: EINVAL, EEXIST.

  • [x] seccomp_arch_add
    Atualmente retorna: EINVAL, EEXIST, ENOMEM, EDOM.

  • [x] seccomp_arch_remove
    Atualmente retorna: EINVAL, EEXIST.

  • [x] seccomp_load
    Atualmente retorna: EINVAL, ENOMEM, ESRCH, ECANCELED.

  • [x] seccomp_attr_get
    Atualmente retorna: EINVAL, EEXIST.

  • [x] seccomp_attr_set
    Atualmente retorna: EINVAL, EACCES, EOPNOTSUPP, EEXIST.

  • [x] seccomp_syscall_resolve_name_arch
    Já bem definido, retorna o valor de syscall ou __NR_SCMP_ERROR em caso de falha.

  • [x] seccomp_syscall_resolve_name_rewrite
    Já bem definido, retorna o valor de syscall ou __NR_SCMP_ERROR em caso de falha.

  • [x] seccomp_syscall_resolve_name
    Já bem definido, retorna o valor de syscall ou __NR_SCMP_ERROR em caso de falha.

  • [x] seccomp_syscall_priority
    Atualmente retorna: EINVAL, EDOM, EFAULT, ENOMEM.

  • [x] seccomp_rule_add_array
    Atualmente retorna: EINVAL, EOPNOTSUPP, ENOMEM, EDOM, EFAULT, EEXIST.

  • [x] seccomp_rule_add
    Atualmente retorna: EINVAL, EOPNOTSUPP, ENOMEM, EDOM, EFAULT, EEXIST.

  • [x] seccomp_rule_add_exact_array
    Atualmente retorna: EINVAL, EOPNOTSUPP, ENOMEM, EDOM, EFAULT, EEXIST.

  • [x] seccomp_rule_add_exact
    Atualmente retorna: EINVAL, EOPNOTSUPP, ENOMEM, EDOM, EFAULT, EEXIST.

  • [x] seccomp_notify_alloc
    Atualmente retorna: EOPNOTSUPP, ENOMEM, EFAULT, ECANCELED. A página de manual já especifica -1 em caso de erro, o que provavelmente se refere apenas ao seccomp() errno.

  • [x] seccomp_notify_receive
    Atualmente retorna: EOPNOTSUPP e ECANCELED. A página de manual já especifica -1 em caso de erro, o que provavelmente se refere apenas ao seccomp() errno.

  • [x] seccomp_notify_respond
    Atualmente retorna: EOPNOTSUPP e ECANCELED. A página de manual já especifica -1 em caso de erro, o que provavelmente se refere apenas ao seccomp() errno.

  • [x] seccomp_notify_id_valid
    Atualmente retorna: EOPNOTSUPP e ECANCELED. A página de manual já especifica -ENOENT em caso de erro (ID inválido), que provavelmente se refere apenas ao seccomp() errno.

  • [x] seccomp_notify_fd
    Já bem definido, retorna a notificação fd.

  • [x] seccomp_export_pfc
    Atualmente retorna: EINVAL e ECANCELED.

  • [x] seccomp_export_bpf
    Atualmente retorna: EINVAL, ENOMEM e ECANCELED.

Agora que temos uma lista de quais funções retornam quais códigos de erro, estou me sentindo um pouco melhor sobre isso, especialmente porque já somos bastante consistentes com a forma como usamos nossos códigos de erro. Essa última parte deve ajudar tremendamente.

Vou começar um PR para que possamos começar a coletar correções e feedback sobre as mudanças, postarei aqui em breve.

Dada a natureza bastante "especial" do ENOSYS, estou tendo reservas sobre o uso do errno como nosso kernel/libc catch-all. Vou ter que dar uma olhada em outros valores, alguém tem algum sentimento/pensamento forte sobre isso?

Ainda falta muita coisa, principalmente edições de manpage e comentários de código (sem mencionar testes), mas você pode olhar para o seguinte branch para ter uma ideia do que estou pensando:

Dada a natureza bastante "especial" do ENOSYS, estou tendo reservas sobre o uso do errno como nosso kernel/libc catch-all. Vou ter que dar uma olhada em outros valores, alguém tem algum sentimento/pensamento forte sobre isso?

E o EIO?

E o EIO?

Não sei se isso tornaria o erro muito mais acionável. Que tal estender a API com funções que não usam valores errno, mas por exemplo:

  • SCMP_ERROR_UNKNOWN_SYSCALL: o syscall não é conhecido por libseccomp: o chamador pode usar isso para rejeitar a entrada do usuário (por exemplo, erro de digitação no nome do syscall)
  • SCMP_ERROR_SYSCALL_NOT_FOR_THIS_ARCH: o syscall é conhecido por libseccomp mas não está disponível aqui: o chamador pode ignorar este erro apenas para esta arquitetura
  • SCMP_ERROR_API_USAGE: libseccomp detecta um problema com a lógica de chamada que não deve acontecer no código escrito corretamente: o chamador pode acionar assert()
  • SCMP_ERROR_KERNEL_OTHER: libseccomp estava OK com a entrada e a sequência de chamadas, mas o kernel retornou alguns erros bem definidos, por exemplo, ENOMEM, EPERM, ENOSYS: o chamador deve verificar o errno para ação. Caso o motivo do erro seja conhecido por libseccomp devido a um erro feito pelo chamador (digamos EFAULT), talvez SCMP_ERROR_API_USAGE possa ser usado.
  • SCMP_ERROR_KERNEL_API_USAGE: libseccomp estava OK com a entrada etc, mas o kernel não gostou por razões não tão claras e óbvias. Isso pode indicar uma mudança no kernel, configuração desabilitada, bug em libseccomp ou chamador, entrada de usuário muito estranha etc. análise adicional, não assert()able.

Alternativamente, a API com errnos pode permanecer como está, mas uma nova função pode ser usada para solicitar o código de erro acima.

Talvez @poettering ou @keszybz possam comentar também.

Não sei se isso tornaria o erro muito mais acionável. Que tal estender a API com funções que não usam valores errno...

Bem, antes que pudéssemos considerar fazer algo assim (e não tenho certeza se queremos fazer isso), precisamos estabelecer códigos de retorno estáveis ​​e com suporte. É nisso que estamos trabalhando aqui e no que estamos buscando para a v2.5.

Vamos obter os códigos de retorno estáveis/suportados na v2.5 e podemos ver como isso acontece, se precisarmos fazer algo adicional, podemos considerá-lo para a v2.6.

E o EIO?

Eu fui retirado disso por um tempo devido a outras prioridades de trabalho e algumas coisas do kernel, mas agora que estou de volta ao libseccomp estou percebendo que usar EIO aqui parece errado. Deixe-me pensar um pouco mais sobre isso.

E o ECANCELED como o código de erro do kernel abrangente?

Estou curioso para saber o que você acha @drakenclimber , você ficou quieto sobre isso por um tempo.

Eu fui retirado disso por um tempo devido a outras prioridades de trabalho e algumas coisas do kernel, mas agora que estou de volta ao libseccomp estou percebendo que usar EIO aqui parece errado. Deixe-me pensar um pouco mais sobre isso.

Mesmo aqui. As coisas andam meio agitadas ultimamente :/.

Estou curioso para saber o que você acha @drakenclimber , você ficou quieto sobre isso por um tempo.

Certo. Eu quero ler todo o tópico novamente, e então eu vou entrar na conversa.

E o ECANCELED como o código de erro do kernel abrangente?

Admito que não estava muito familiarizado com ECANCELED. Eu examinei brevemente a fonte do kernel para seu uso e fiz uma pesquisa no google também. ECANCELED não tem colisões com libseccomp, prctl() ou outras APIs anteriores que usamos, e acho que pode encapsular razoavelmente qualquer erro que o kernel nos apresente.

tl; dr - Eu sou legal com ECANCELED como nosso catch-all para erros de kernel.

Ótimo, obrigado. Eu tenho um patchset incompleto que vou terminar e enviar como um PR para revisão.

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

Questões relacionadas

alban picture alban  ·  9Comentários

vasanthaganeshk picture vasanthaganeshk  ·  9Comentários

pcmoore picture pcmoore  ·  10Comentários

erdumbledore picture erdumbledore  ·  3Comentários

pcmoore picture pcmoore  ·  20Comentários